diff options
author | Grant Likely <grant.likely@secretlab.ca> | 2010-05-25 00:38:26 -0600 |
---|---|---|
committer | Grant Likely <grant.likely@secretlab.ca> | 2010-05-25 00:38:26 -0600 |
commit | b1e50ebcf24668e57f058deb48b0704b5391ed0f (patch) | |
tree | 17e1b69b249d0738317b732186340c9dd053f1a1 /drivers/char | |
parent | 0c2a2ae32793e3500a15a449612485f5d17dd431 (diff) | |
parent | 7e125f7b9cbfce4101191b8076d606c517a73066 (diff) |
Merge remote branch 'origin' into secretlab/next-spi
Diffstat (limited to 'drivers/char')
54 files changed, 5718 insertions, 2809 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 3141dd3b6e5..e21175be25d 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -276,11 +276,19 @@ config N_HDLC Allows synchronous HDLC communications with tty device drivers that support synchronous HDLC such as the Microgate SyncLink adapter. - This driver can only be built as a module ( = code which can be + This driver can be built as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called n_hdlc. If you want to do that, say M here. +config N_GSM + tristate "GSM MUX line discipline support (EXPERIMENTAL)" + depends on EXPERIMENTAL + depends on NET + help + This line discipline provides support for the GSM MUX protocol and + presents the mux as a set of 61 individual tty devices. + config RISCOM8 tristate "SDL RISCom/8 card support" depends on SERIAL_NONSTANDARD diff --git a/drivers/char/Makefile b/drivers/char/Makefile index f957edf7e45..d39be4cf1f5 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_SYNCLINK) += synclink.o obj-$(CONFIG_SYNCLINKMP) += synclinkmp.o obj-$(CONFIG_SYNCLINK_GT) += synclink_gt.o obj-$(CONFIG_N_HDLC) += n_hdlc.o +obj-$(CONFIG_N_GSM) += n_gsm.o obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o obj-$(CONFIG_SX) += sx.o generic_serial.o obj-$(CONFIG_RIO) += rio/ generic_serial.o diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index 870f12cfed9..12049094999 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -178,86 +178,6 @@ struct agp_bridge_data { #define PGE_EMPTY(b, p) (!(p) || (p) == (unsigned long) (b)->scratch_page) -/* Intel registers */ -#define INTEL_APSIZE 0xb4 -#define INTEL_ATTBASE 0xb8 -#define INTEL_AGPCTRL 0xb0 -#define INTEL_NBXCFG 0x50 -#define INTEL_ERRSTS 0x91 - -/* Intel i830 registers */ -#define I830_GMCH_CTRL 0x52 -#define I830_GMCH_ENABLED 0x4 -#define I830_GMCH_MEM_MASK 0x1 -#define I830_GMCH_MEM_64M 0x1 -#define I830_GMCH_MEM_128M 0 -#define I830_GMCH_GMS_MASK 0x70 -#define I830_GMCH_GMS_DISABLED 0x00 -#define I830_GMCH_GMS_LOCAL 0x10 -#define I830_GMCH_GMS_STOLEN_512 0x20 -#define I830_GMCH_GMS_STOLEN_1024 0x30 -#define I830_GMCH_GMS_STOLEN_8192 0x40 -#define I830_RDRAM_CHANNEL_TYPE 0x03010 -#define I830_RDRAM_ND(x) (((x) & 0x20) >> 5) -#define I830_RDRAM_DDT(x) (((x) & 0x18) >> 3) - -/* This one is for I830MP w. an external graphic card */ -#define INTEL_I830_ERRSTS 0x92 - -/* Intel 855GM/852GM registers */ -#define I855_GMCH_GMS_MASK 0xF0 -#define I855_GMCH_GMS_STOLEN_0M 0x0 -#define I855_GMCH_GMS_STOLEN_1M (0x1 << 4) -#define I855_GMCH_GMS_STOLEN_4M (0x2 << 4) -#define I855_GMCH_GMS_STOLEN_8M (0x3 << 4) -#define I855_GMCH_GMS_STOLEN_16M (0x4 << 4) -#define I855_GMCH_GMS_STOLEN_32M (0x5 << 4) -#define I85X_CAPID 0x44 -#define I85X_VARIANT_MASK 0x7 -#define I85X_VARIANT_SHIFT 5 -#define I855_GME 0x0 -#define I855_GM 0x4 -#define I852_GME 0x2 -#define I852_GM 0x5 - -/* Intel i845 registers */ -#define INTEL_I845_AGPM 0x51 -#define INTEL_I845_ERRSTS 0xc8 - -/* Intel i860 registers */ -#define INTEL_I860_MCHCFG 0x50 -#define INTEL_I860_ERRSTS 0xc8 - -/* Intel i810 registers */ -#define I810_GMADDR 0x10 -#define I810_MMADDR 0x14 -#define I810_PTE_BASE 0x10000 -#define I810_PTE_MAIN_UNCACHED 0x00000000 -#define I810_PTE_LOCAL 0x00000002 -#define I810_PTE_VALID 0x00000001 -#define I830_PTE_SYSTEM_CACHED 0x00000006 -#define I810_SMRAM_MISCC 0x70 -#define I810_GFX_MEM_WIN_SIZE 0x00010000 -#define I810_GFX_MEM_WIN_32M 0x00010000 -#define I810_GMS 0x000000c0 -#define I810_GMS_DISABLE 0x00000000 -#define I810_PGETBL_CTL 0x2020 -#define I810_PGETBL_ENABLED 0x00000001 -#define I965_PGETBL_SIZE_MASK 0x0000000e -#define I965_PGETBL_SIZE_512KB (0 << 1) -#define I965_PGETBL_SIZE_256KB (1 << 1) -#define I965_PGETBL_SIZE_128KB (2 << 1) -#define I965_PGETBL_SIZE_1MB (3 << 1) -#define I965_PGETBL_SIZE_2MB (4 << 1) -#define I965_PGETBL_SIZE_1_5MB (5 << 1) -#define G33_PGETBL_SIZE_MASK (3 << 8) -#define G33_PGETBL_SIZE_1M (1 << 8) -#define G33_PGETBL_SIZE_2M (2 << 8) - -#define I810_DRAM_CTL 0x3000 -#define I810_DRAM_ROW_0 0x00000001 -#define I810_DRAM_ROW_0_SDRAM 0x00000001 - struct agp_device_ids { unsigned short device_id; /* first, to make table easier to read */ enum chipset_type chipset; diff --git a/drivers/char/agp/ali-agp.c b/drivers/char/agp/ali-agp.c index d2ce68f27e4..fd793519ea2 100644 --- a/drivers/char/agp/ali-agp.c +++ b/drivers/char/agp/ali-agp.c @@ -204,6 +204,7 @@ static const struct agp_bridge_driver ali_generic_bridge = { .aperture_sizes = ali_generic_sizes, .size_type = U32_APER_SIZE, .num_aperture_sizes = 7, + .needs_scratch_page = true, .configure = ali_configure, .fetch_size = ali_fetch_size, .cleanup = ali_cleanup, diff --git a/drivers/char/agp/amd-k7-agp.c b/drivers/char/agp/amd-k7-agp.c index a7637d72cef..b6b1568314c 100644 --- a/drivers/char/agp/amd-k7-agp.c +++ b/drivers/char/agp/amd-k7-agp.c @@ -142,6 +142,7 @@ static int amd_create_gatt_table(struct agp_bridge_data *bridge) { struct aper_size_info_lvl2 *value; struct amd_page_map page_dir; + unsigned long __iomem *cur_gatt; unsigned long addr; int retval; u32 temp; @@ -178,6 +179,13 @@ static int amd_create_gatt_table(struct agp_bridge_data *bridge) readl(page_dir.remapped+GET_PAGE_DIR_OFF(addr)); /* PCI Posting. */ } + for (i = 0; i < value->num_entries; i++) { + addr = (i * PAGE_SIZE) + agp_bridge->gart_bus_addr; + cur_gatt = GET_GATT(addr); + writel(agp_bridge->scratch_page, cur_gatt+GET_GATT_OFF(addr)); + readl(cur_gatt+GET_GATT_OFF(addr)); /* PCI Posting. */ + } + return 0; } @@ -375,6 +383,7 @@ static const struct agp_bridge_driver amd_irongate_driver = { .aperture_sizes = amd_irongate_sizes, .size_type = LVL2_APER_SIZE, .num_aperture_sizes = 7, + .needs_scratch_page = true, .configure = amd_irongate_configure, .fetch_size = amd_irongate_fetch_size, .cleanup = amd_irongate_cleanup, diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c index fd50ead59c7..67ea3a60de7 100644 --- a/drivers/char/agp/amd64-agp.c +++ b/drivers/char/agp/amd64-agp.c @@ -210,6 +210,7 @@ static const struct agp_bridge_driver amd_8151_driver = { .aperture_sizes = amd_8151_sizes, .size_type = U32_APER_SIZE, .num_aperture_sizes = 7, + .needs_scratch_page = true, .configure = amd_8151_configure, .fetch_size = amd64_fetch_size, .cleanup = amd64_cleanup, @@ -499,6 +500,10 @@ static int __devinit agp_amd64_probe(struct pci_dev *pdev, u8 cap_ptr; int err; + /* The Highlander principle */ + if (agp_bridges_found) + return -ENODEV; + cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP); if (!cap_ptr) return -ENODEV; @@ -562,6 +567,8 @@ static void __devexit agp_amd64_remove(struct pci_dev *pdev) amd64_aperture_sizes[bridge->aperture_size_idx].size); agp_remove_bridge(bridge); agp_put_bridge(bridge); + + agp_bridges_found--; } #ifdef CONFIG_PM @@ -709,6 +716,11 @@ static struct pci_device_id agp_amd64_pci_table[] = { MODULE_DEVICE_TABLE(pci, agp_amd64_pci_table); +static DEFINE_PCI_DEVICE_TABLE(agp_amd64_pci_promisc_table) = { + { PCI_DEVICE_CLASS(0, 0) }, + { } +}; + static struct pci_driver agp_amd64_pci_driver = { .name = "agpgart-amd64", .id_table = agp_amd64_pci_table, @@ -734,7 +746,6 @@ int __init agp_amd64_init(void) return err; if (agp_bridges_found == 0) { - struct pci_dev *dev; if (!agp_try_unsupported && !agp_try_unsupported_boot) { printk(KERN_INFO PFX "No supported AGP bridge found.\n"); #ifdef MODULE @@ -750,17 +761,10 @@ int __init agp_amd64_init(void) return -ENODEV; /* Look for any AGP bridge */ - dev = NULL; - err = -ENODEV; - for_each_pci_dev(dev) { - if (!pci_find_capability(dev, PCI_CAP_ID_AGP)) - continue; - /* Only one bridge supported right now */ - if (agp_amd64_probe(dev, NULL) == 0) { - err = 0; - break; - } - } + agp_amd64_pci_driver.id_table = agp_amd64_pci_promisc_table; + err = driver_attach(&agp_amd64_pci_driver.driver); + if (err == 0 && agp_bridges_found == 0) + err = -ENODEV; } return err; } diff --git a/drivers/char/agp/ati-agp.c b/drivers/char/agp/ati-agp.c index 3b2ecbe86eb..dc30e224349 100644 --- a/drivers/char/agp/ati-agp.c +++ b/drivers/char/agp/ati-agp.c @@ -341,6 +341,7 @@ static int ati_create_gatt_table(struct agp_bridge_data *bridge) { struct aper_size_info_lvl2 *value; struct ati_page_map page_dir; + unsigned long __iomem *cur_gatt; unsigned long addr; int retval; u32 temp; @@ -395,6 +396,12 @@ static int ati_create_gatt_table(struct agp_bridge_data *bridge) readl(page_dir.remapped+GET_PAGE_DIR_OFF(addr)); /* PCI Posting. */ } + for (i = 0; i < value->num_entries; i++) { + addr = (i * PAGE_SIZE) + agp_bridge->gart_bus_addr; + cur_gatt = GET_GATT(addr); + writel(agp_bridge->scratch_page, cur_gatt+GET_GATT_OFF(addr)); + } + return 0; } @@ -415,6 +422,7 @@ static const struct agp_bridge_driver ati_generic_bridge = { .aperture_sizes = ati_generic_sizes, .size_type = LVL2_APER_SIZE, .num_aperture_sizes = 7, + .needs_scratch_page = true, .configure = ati_configure, .fetch_size = ati_fetch_size, .cleanup = ati_cleanup, diff --git a/drivers/char/agp/efficeon-agp.c b/drivers/char/agp/efficeon-agp.c index 793f39ea961..aa109cbe0e6 100644 --- a/drivers/char/agp/efficeon-agp.c +++ b/drivers/char/agp/efficeon-agp.c @@ -28,6 +28,7 @@ #include <linux/page-flags.h> #include <linux/mm.h> #include "agp.h" +#include "intel-agp.h" /* * The real differences to the generic AGP code is diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index fb86708e47e..4b51982fd23 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -1214,7 +1214,7 @@ struct agp_memory *agp_generic_alloc_user(size_t page_count, int type) return NULL; for (i = 0; i < page_count; i++) - new->pages[i] = 0; + new->pages[i] = NULL; new->page_count = 0; new->type = type; new->num_scratch_pages = pages; diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index aa4248efc5d..d836a71bf06 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -11,1531 +11,13 @@ #include <linux/agp_backend.h> #include <asm/smp.h> #include "agp.h" +#include "intel-agp.h" + +#include "intel-gtt.c" int intel_agp_enabled; EXPORT_SYMBOL(intel_agp_enabled); -/* - * If we have Intel graphics, we're not going to have anything other than - * an Intel IOMMU. So make the correct use of the PCI DMA API contingent - * on the Intel IOMMU support (CONFIG_DMAR). - * Only newer chipsets need to bother with this, of course. - */ -#ifdef CONFIG_DMAR -#define USE_PCI_DMA_API 1 -#endif - -#define PCI_DEVICE_ID_INTEL_E7221_HB 0x2588 -#define PCI_DEVICE_ID_INTEL_E7221_IG 0x258a -#define PCI_DEVICE_ID_INTEL_82946GZ_HB 0x2970 -#define PCI_DEVICE_ID_INTEL_82946GZ_IG 0x2972 -#define PCI_DEVICE_ID_INTEL_82G35_HB 0x2980 -#define PCI_DEVICE_ID_INTEL_82G35_IG 0x2982 -#define PCI_DEVICE_ID_INTEL_82965Q_HB 0x2990 -#define PCI_DEVICE_ID_INTEL_82965Q_IG 0x2992 -#define PCI_DEVICE_ID_INTEL_82965G_HB 0x29A0 -#define PCI_DEVICE_ID_INTEL_82965G_IG 0x29A2 -#define PCI_DEVICE_ID_INTEL_82965GM_HB 0x2A00 -#define PCI_DEVICE_ID_INTEL_82965GM_IG 0x2A02 -#define PCI_DEVICE_ID_INTEL_82965GME_HB 0x2A10 -#define PCI_DEVICE_ID_INTEL_82965GME_IG 0x2A12 -#define PCI_DEVICE_ID_INTEL_82945GME_HB 0x27AC -#define PCI_DEVICE_ID_INTEL_82945GME_IG 0x27AE -#define PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB 0xA010 -#define PCI_DEVICE_ID_INTEL_PINEVIEW_M_IG 0xA011 -#define PCI_DEVICE_ID_INTEL_PINEVIEW_HB 0xA000 -#define PCI_DEVICE_ID_INTEL_PINEVIEW_IG 0xA001 -#define PCI_DEVICE_ID_INTEL_G33_HB 0x29C0 -#define PCI_DEVICE_ID_INTEL_G33_IG 0x29C2 -#define PCI_DEVICE_ID_INTEL_Q35_HB 0x29B0 -#define PCI_DEVICE_ID_INTEL_Q35_IG 0x29B2 -#define PCI_DEVICE_ID_INTEL_Q33_HB 0x29D0 -#define PCI_DEVICE_ID_INTEL_Q33_IG 0x29D2 -#define PCI_DEVICE_ID_INTEL_B43_HB 0x2E40 -#define PCI_DEVICE_ID_INTEL_B43_IG 0x2E42 -#define PCI_DEVICE_ID_INTEL_GM45_HB 0x2A40 -#define PCI_DEVICE_ID_INTEL_GM45_IG 0x2A42 -#define PCI_DEVICE_ID_INTEL_EAGLELAKE_HB 0x2E00 -#define PCI_DEVICE_ID_INTEL_EAGLELAKE_IG 0x2E02 -#define PCI_DEVICE_ID_INTEL_Q45_HB 0x2E10 -#define PCI_DEVICE_ID_INTEL_Q45_IG 0x2E12 -#define PCI_DEVICE_ID_INTEL_G45_HB 0x2E20 -#define PCI_DEVICE_ID_INTEL_G45_IG 0x2E22 -#define PCI_DEVICE_ID_INTEL_G41_HB 0x2E30 -#define PCI_DEVICE_ID_INTEL_G41_IG 0x2E32 -#define PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB 0x0040 -#define PCI_DEVICE_ID_INTEL_IRONLAKE_D_IG 0x0042 -#define PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB 0x0044 -#define PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB 0x0062 -#define PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB 0x006a -#define PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG 0x0046 -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB 0x0100 -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_IG 0x0102 -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB 0x0104 -#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_IG 0x0106 - -/* cover 915 and 945 variants */ -#define IS_I915 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_E7221_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82915G_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82915GM_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82945G_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82945GM_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82945GME_HB) - -#define IS_I965 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82946GZ_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82G35_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965Q_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965G_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965GM_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965GME_HB) - -#define IS_G33 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G33_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q35_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q33_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_HB) - -#define IS_PINEVIEW (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_HB) - -#define IS_SNB (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB) - -#define IS_G4X (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_EAGLELAKE_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q45_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G45_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_GM45_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G41_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_B43_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB || \ - IS_SNB) - -extern int agp_memory_reserved; - - -/* Intel 815 register */ -#define INTEL_815_APCONT 0x51 -#define INTEL_815_ATTBASE_MASK ~0x1FFFFFFF - -/* Intel i820 registers */ -#define INTEL_I820_RDCR 0x51 -#define INTEL_I820_ERRSTS 0xc8 - -/* Intel i840 registers */ -#define INTEL_I840_MCHCFG 0x50 -#define INTEL_I840_ERRSTS 0xc8 - -/* Intel i850 registers */ -#define INTEL_I850_MCHCFG 0x50 -#define INTEL_I850_ERRSTS 0xc8 - -/* intel 915G registers */ -#define I915_GMADDR 0x18 -#define I915_MMADDR 0x10 -#define I915_PTEADDR 0x1C -#define I915_GMCH_GMS_STOLEN_48M (0x6 << 4) -#define I915_GMCH_GMS_STOLEN_64M (0x7 << 4) -#define G33_GMCH_GMS_STOLEN_128M (0x8 << 4) -#define G33_GMCH_GMS_STOLEN_256M (0x9 << 4) -#define INTEL_GMCH_GMS_STOLEN_96M (0xa << 4) -#define INTEL_GMCH_GMS_STOLEN_160M (0xb << 4) -#define INTEL_GMCH_GMS_STOLEN_224M (0xc << 4) -#define INTEL_GMCH_GMS_STOLEN_352M (0xd << 4) - -#define I915_IFPADDR 0x60 - -/* Intel 965G registers */ -#define I965_MSAC 0x62 -#define I965_IFPADDR 0x70 - -/* Intel 7505 registers */ -#define INTEL_I7505_APSIZE 0x74 -#define INTEL_I7505_NCAPID 0x60 -#define INTEL_I7505_NISTAT 0x6c -#define INTEL_I7505_ATTBASE 0x78 -#define INTEL_I7505_ERRSTS 0x42 -#define INTEL_I7505_AGPCTRL 0x70 -#define INTEL_I7505_MCHCFG 0x50 - -#define SNB_GMCH_CTRL 0x50 -#define SNB_GMCH_GMS_STOLEN_MASK 0xF8 -#define SNB_GMCH_GMS_STOLEN_32M (1 << 3) -#define SNB_GMCH_GMS_STOLEN_64M (2 << 3) -#define SNB_GMCH_GMS_STOLEN_96M (3 << 3) -#define SNB_GMCH_GMS_STOLEN_128M (4 << 3) -#define SNB_GMCH_GMS_STOLEN_160M (5 << 3) -#define SNB_GMCH_GMS_STOLEN_192M (6 << 3) -#define SNB_GMCH_GMS_STOLEN_224M (7 << 3) -#define SNB_GMCH_GMS_STOLEN_256M (8 << 3) -#define SNB_GMCH_GMS_STOLEN_288M (9 << 3) -#define SNB_GMCH_GMS_STOLEN_320M (0xa << 3) -#define SNB_GMCH_GMS_STOLEN_352M (0xb << 3) -#define SNB_GMCH_GMS_STOLEN_384M (0xc << 3) -#define SNB_GMCH_GMS_STOLEN_416M (0xd << 3) -#define SNB_GMCH_GMS_STOLEN_448M (0xe << 3) -#define SNB_GMCH_GMS_STOLEN_480M (0xf << 3) -#define SNB_GMCH_GMS_STOLEN_512M (0x10 << 3) -#define SNB_GTT_SIZE_0M (0 << 8) -#define SNB_GTT_SIZE_1M (1 << 8) -#define SNB_GTT_SIZE_2M (2 << 8) -#define SNB_GTT_SIZE_MASK (3 << 8) - -static const struct aper_size_info_fixed intel_i810_sizes[] = -{ - {64, 16384, 4}, - /* The 32M mode still requires a 64k gatt */ - {32, 8192, 4} -}; - -#define AGP_DCACHE_MEMORY 1 -#define AGP_PHYS_MEMORY 2 -#define INTEL_AGP_CACHED_MEMORY 3 - -static struct gatt_mask intel_i810_masks[] = -{ - {.mask = I810_PTE_VALID, .type = 0}, - {.mask = (I810_PTE_VALID | I810_PTE_LOCAL), .type = AGP_DCACHE_MEMORY}, - {.mask = I810_PTE_VALID, .type = 0}, - {.mask = I810_PTE_VALID | I830_PTE_SYSTEM_CACHED, - .type = INTEL_AGP_CACHED_MEMORY} -}; - -static struct _intel_private { - struct pci_dev *pcidev; /* device one */ - u8 __iomem *registers; - u32 __iomem *gtt; /* I915G */ - int num_dcache_entries; - /* gtt_entries is the number of gtt entries that are already mapped - * to stolen memory. Stolen memory is larger than the memory mapped - * through gtt_entries, as it includes some reserved space for the BIOS - * popup and for the GTT. - */ - int gtt_entries; /* i830+ */ - int gtt_total_size; - union { - void __iomem *i9xx_flush_page; - void *i8xx_flush_page; - }; - struct page *i8xx_page; - struct resource ifp_resource; - int resource_valid; -} intel_private; - -#ifdef USE_PCI_DMA_API -static int intel_agp_map_page(struct page *page, dma_addr_t *ret) -{ - *ret = pci_map_page(intel_private.pcidev, page, 0, - PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); - if (pci_dma_mapping_error(intel_private.pcidev, *ret)) - return -EINVAL; - return 0; -} - -static void intel_agp_unmap_page(struct page *page, dma_addr_t dma) -{ - pci_unmap_page(intel_private.pcidev, dma, - PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); -} - -static void intel_agp_free_sglist(struct agp_memory *mem) -{ - struct sg_table st; - - st.sgl = mem->sg_list; - st.orig_nents = st.nents = mem->page_count; - - sg_free_table(&st); - - mem->sg_list = NULL; - mem->num_sg = 0; -} - -static int intel_agp_map_memory(struct agp_memory *mem) -{ - struct sg_table st; - struct scatterlist *sg; - int i; - - DBG("try mapping %lu pages\n", (unsigned long)mem->page_count); - - if (sg_alloc_table(&st, mem->page_count, GFP_KERNEL)) - return -ENOMEM; - - mem->sg_list = sg = st.sgl; - - for (i = 0 ; i < mem->page_count; i++, sg = sg_next(sg)) - sg_set_page(sg, mem->pages[i], PAGE_SIZE, 0); - - mem->num_sg = pci_map_sg(intel_private.pcidev, mem->sg_list, - mem->page_count, PCI_DMA_BIDIRECTIONAL); - if (unlikely(!mem->num_sg)) { - intel_agp_free_sglist(mem); - return -ENOMEM; - } - return 0; -} - -static void intel_agp_unmap_memory(struct agp_memory *mem) -{ - DBG("try unmapping %lu pages\n", (unsigned long)mem->page_count); - - pci_unmap_sg(intel_private.pcidev, mem->sg_list, - mem->page_count, PCI_DMA_BIDIRECTIONAL); - intel_agp_free_sglist(mem); -} - -static void intel_agp_insert_sg_entries(struct agp_memory *mem, - off_t pg_start, int mask_type) -{ - struct scatterlist *sg; - int i, j; - - j = pg_start; - - WARN_ON(!mem->num_sg); - - if (mem->num_sg == mem->page_count) { - for_each_sg(mem->sg_list, sg, mem->page_count, i) { - writel(agp_bridge->driver->mask_memory(agp_bridge, - sg_dma_address(sg), mask_type), - intel_private.gtt+j); - j++; - } - } else { - /* sg may merge pages, but we have to separate - * per-page addr for GTT */ - unsigned int len, m; - - for_each_sg(mem->sg_list, sg, mem->num_sg, i) { - len = sg_dma_len(sg) / PAGE_SIZE; - for (m = 0; m < len; m++) { - writel(agp_bridge->driver->mask_memory(agp_bridge, - sg_dma_address(sg) + m * PAGE_SIZE, - mask_type), - intel_private.gtt+j); - j++; - } - } - } - readl(intel_private.gtt+j-1); -} - -#else - -static void intel_agp_insert_sg_entries(struct agp_memory *mem, - off_t pg_start, int mask_type) -{ - int i, j; - u32 cache_bits = 0; - - if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB || - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB) - { - cache_bits = I830_PTE_SYSTEM_CACHED; - } - - for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { - writel(agp_bridge->driver->mask_memory(agp_bridge, - page_to_phys(mem->pages[i]), mask_type), - intel_private.gtt+j); - } - - readl(intel_private.gtt+j-1); -} - -#endif - -static int intel_i810_fetch_size(void) -{ - u32 smram_miscc; - struct aper_size_info_fixed *values; - - pci_read_config_dword(agp_bridge->dev, I810_SMRAM_MISCC, &smram_miscc); - values = A_SIZE_FIX(agp_bridge->driver->aperture_sizes); - - if ((smram_miscc & I810_GMS) == I810_GMS_DISABLE) { - dev_warn(&agp_bridge->dev->dev, "i810 is disabled\n"); - return 0; - } - if ((smram_miscc & I810_GFX_MEM_WIN_SIZE) == I810_GFX_MEM_WIN_32M) { - agp_bridge->previous_size = - agp_bridge->current_size = (void *) (values + 1); - agp_bridge->aperture_size_idx = 1; - return values[1].size; - } else { - agp_bridge->previous_size = - agp_bridge->current_size = (void *) (values); - agp_bridge->aperture_size_idx = 0; - return values[0].size; - } - - return 0; -} - -static int intel_i810_configure(void) -{ - struct aper_size_info_fixed *current_size; - u32 temp; - int i; - - current_size = A_SIZE_FIX(agp_bridge->current_size); - - if (!intel_private.registers) { - pci_read_config_dword(intel_private.pcidev, I810_MMADDR, &temp); - temp &= 0xfff80000; - - intel_private.registers = ioremap(temp, 128 * 4096); - if (!intel_private.registers) { - dev_err(&intel_private.pcidev->dev, - "can't remap memory\n"); - return -ENOMEM; - } - } - - if ((readl(intel_private.registers+I810_DRAM_CTL) - & I810_DRAM_ROW_0) == I810_DRAM_ROW_0_SDRAM) { - /* This will need to be dynamically assigned */ - dev_info(&intel_private.pcidev->dev, - "detected 4MB dedicated video ram\n"); - intel_private.num_dcache_entries = 1024; - } - pci_read_config_dword(intel_private.pcidev, I810_GMADDR, &temp); - agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); - writel(agp_bridge->gatt_bus_addr | I810_PGETBL_ENABLED, intel_private.registers+I810_PGETBL_CTL); - readl(intel_private.registers+I810_PGETBL_CTL); /* PCI Posting. */ - - if (agp_bridge->driver->needs_scratch_page) { - for (i = 0; i < current_size->num_entries; i++) { - writel(agp_bridge->scratch_page, intel_private.registers+I810_PTE_BASE+(i*4)); - } - readl(intel_private.registers+I810_PTE_BASE+((i-1)*4)); /* PCI posting. */ - } - global_cache_flush(); - return 0; -} - -static void intel_i810_cleanup(void) -{ - writel(0, intel_private.registers+I810_PGETBL_CTL); - readl(intel_private.registers); /* PCI Posting. */ - iounmap(intel_private.registers); -} - -static void intel_i810_tlbflush(struct agp_memory *mem) -{ - return; -} - -static void intel_i810_agp_enable(struct agp_bridge_data *bridge, u32 mode) -{ - return; -} - -/* Exists to support ARGB cursors */ -static struct page *i8xx_alloc_pages(void) -{ - struct page *page; - - page = alloc_pages(GFP_KERNEL | GFP_DMA32, 2); - if (page == NULL) - return NULL; - - if (set_pages_uc(page, 4) < 0) { - set_pages_wb(page, 4); - __free_pages(page, 2); - return NULL; - } - get_page(page); - atomic_inc(&agp_bridge->current_memory_agp); - return page; -} - -static void i8xx_destroy_pages(struct page *page) -{ - if (page == NULL) - return; - - set_pages_wb(page, 4); - put_page(page); - __free_pages(page, 2); - atomic_dec(&agp_bridge->current_memory_agp); -} - -static int intel_i830_type_to_mask_type(struct agp_bridge_data *bridge, - int type) -{ - if (type < AGP_USER_TYPES) - return type; - else if (type == AGP_USER_CACHED_MEMORY) - return INTEL_AGP_CACHED_MEMORY; - else - return 0; -} - -static int intel_i810_insert_entries(struct agp_memory *mem, off_t pg_start, - int type) -{ - int i, j, num_entries; - void *temp; - int ret = -EINVAL; - int mask_type; - - if (mem->page_count == 0) - goto out; - - temp = agp_bridge->current_size; - num_entries = A_SIZE_FIX(temp)->num_entries; - - if ((pg_start + mem->page_count) > num_entries) - goto out_err; - - - for (j = pg_start; j < (pg_start + mem->page_count); j++) { - if (!PGE_EMPTY(agp_bridge, readl(agp_bridge->gatt_table+j))) { - ret = -EBUSY; - goto out_err; - } - } - - if (type != mem->type) - goto out_err; - - mask_type = agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type); - - switch (mask_type) { - case AGP_DCACHE_MEMORY: - if (!mem->is_flushed) - global_cache_flush(); - for (i = pg_start; i < (pg_start + mem->page_count); i++) { - writel((i*4096)|I810_PTE_LOCAL|I810_PTE_VALID, - intel_private.registers+I810_PTE_BASE+(i*4)); - } - readl(intel_private.registers+I810_PTE_BASE+((i-1)*4)); - break; - case AGP_PHYS_MEMORY: - case AGP_NORMAL_MEMORY: - if (!mem->is_flushed) - global_cache_flush(); - for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { - writel(agp_bridge->driver->mask_memory(agp_bridge, - page_to_phys(mem->pages[i]), mask_type), - intel_private.registers+I810_PTE_BASE+(j*4)); - } - readl(intel_private.registers+I810_PTE_BASE+((j-1)*4)); - break; - default: - goto out_err; - } - - agp_bridge->driver->tlb_flush(mem); -out: - ret = 0; -out_err: - mem->is_flushed = true; - return ret; -} - -static int intel_i810_remove_entries(struct agp_memory *mem, off_t pg_start, - int type) -{ - int i; - - if (mem->page_count == 0) - return 0; - - for (i = pg_start; i < (mem->page_count + pg_start); i++) { - writel(agp_bridge->scratch_page, intel_private.registers+I810_PTE_BASE+(i*4)); - } - readl(intel_private.registers+I810_PTE_BASE+((i-1)*4)); - - agp_bridge->driver->tlb_flush(mem); - return 0; -} - -/* - * The i810/i830 requires a physical address to program its mouse - * pointer into hardware. - * However the Xserver still writes to it through the agp aperture. - */ -static struct agp_memory *alloc_agpphysmem_i8xx(size_t pg_count, int type) -{ - struct agp_memory *new; - struct page *page; - - switch (pg_count) { - case 1: page = agp_bridge->driver->agp_alloc_page(agp_bridge); - break; - case 4: - /* kludge to get 4 physical pages for ARGB cursor */ - page = i8xx_alloc_pages(); - break; - default: - return NULL; - } - - if (page == NULL) - return NULL; - - new = agp_create_memory(pg_count); - if (new == NULL) - return NULL; - - new->pages[0] = page; - if (pg_count == 4) { - /* kludge to get 4 physical pages for ARGB cursor */ - new->pages[1] = new->pages[0] + 1; - new->pages[2] = new->pages[1] + 1; - new->pages[3] = new->pages[2] + 1; - } - new->page_count = pg_count; - new->num_scratch_pages = pg_count; - new->type = AGP_PHYS_MEMORY; - new->physical = page_to_phys(new->pages[0]); - return new; -} - -static struct agp_memory *intel_i810_alloc_by_type(size_t pg_count, int type) -{ - struct agp_memory *new; - - if (type == AGP_DCACHE_MEMORY) { - if (pg_count != intel_private.num_dcache_entries) - return NULL; - - new = agp_create_memory(1); - if (new == NULL) - return NULL; - - new->type = AGP_DCACHE_MEMORY; - new->page_count = pg_count; - new->num_scratch_pages = 0; - agp_free_page_array(new); - return new; - } - if (type == AGP_PHYS_MEMORY) - return alloc_agpphysmem_i8xx(pg_count, type); - return NULL; -} - -static void intel_i810_free_by_type(struct agp_memory *curr) -{ - agp_free_key(curr->key); - if (curr->type == AGP_PHYS_MEMORY) { - if (curr->page_count == 4) - i8xx_destroy_pages(curr->pages[0]); - else { - agp_bridge->driver->agp_destroy_page(curr->pages[0], - AGP_PAGE_DESTROY_UNMAP); - agp_bridge->driver->agp_destroy_page(curr->pages[0], - AGP_PAGE_DESTROY_FREE); - } - agp_free_page_array(curr); - } - kfree(curr); -} - -static unsigned long intel_i810_mask_memory(struct agp_bridge_data *bridge, - dma_addr_t addr, int type) -{ - /* Type checking must be done elsewhere */ - return addr | bridge->driver->masks[type].mask; -} - -static struct aper_size_info_fixed intel_i830_sizes[] = -{ - {128, 32768, 5}, - /* The 64M mode still requires a 128k gatt */ - {64, 16384, 5}, - {256, 65536, 6}, - {512, 131072, 7}, -}; - -static void intel_i830_init_gtt_entries(void) -{ - u16 gmch_ctrl; - int gtt_entries = 0; - u8 rdct; - int local = 0; - static const int ddt[4] = { 0, 16, 32, 64 }; - int size; /* reserved space (in kb) at the top of stolen memory */ - - pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl); - - if (IS_I965) { - u32 pgetbl_ctl; - pgetbl_ctl = readl(intel_private.registers+I810_PGETBL_CTL); - - /* The 965 has a field telling us the size of the GTT, - * which may be larger than what is necessary to map the - * aperture. - */ - switch (pgetbl_ctl & I965_PGETBL_SIZE_MASK) { - case I965_PGETBL_SIZE_128KB: - size = 128; - break; - case I965_PGETBL_SIZE_256KB: - size = 256; - break; - case I965_PGETBL_SIZE_512KB: - size = 512; - break; - case I965_PGETBL_SIZE_1MB: - size = 1024; - break; - case I965_PGETBL_SIZE_2MB: - size = 2048; - break; - case I965_PGETBL_SIZE_1_5MB: - size = 1024 + 512; - break; - default: - dev_info(&intel_private.pcidev->dev, - "unknown page table size, assuming 512KB\n"); - size = 512; - } - size += 4; /* add in BIOS popup space */ - } else if (IS_G33 && !IS_PINEVIEW) { - /* G33's GTT size defined in gmch_ctrl */ - switch (gmch_ctrl & G33_PGETBL_SIZE_MASK) { - case G33_PGETBL_SIZE_1M: - size = 1024; - break; - case G33_PGETBL_SIZE_2M: - size = 2048; - break; - default: - dev_info(&agp_bridge->dev->dev, - "unknown page table size 0x%x, assuming 512KB\n", - (gmch_ctrl & G33_PGETBL_SIZE_MASK)); - size = 512; - } - size += 4; - } else if (IS_G4X || IS_PINEVIEW) { - /* On 4 series hardware, GTT stolen is separate from graphics - * stolen, ignore it in stolen gtt entries counting. However, - * 4KB of the stolen memory doesn't get mapped to the GTT. - */ - size = 4; - } else { - /* On previous hardware, the GTT size was just what was - * required to map the aperture. - */ - size = agp_bridge->driver->fetch_size() + 4; - } - - if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82830_HB || - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82845G_HB) { - switch (gmch_ctrl & I830_GMCH_GMS_MASK) { - case I830_GMCH_GMS_STOLEN_512: - gtt_entries = KB(512) - KB(size); - break; - case I830_GMCH_GMS_STOLEN_1024: - gtt_entries = MB(1) - KB(size); - break; - case I830_GMCH_GMS_STOLEN_8192: - gtt_entries = MB(8) - KB(size); - break; - case I830_GMCH_GMS_LOCAL: - rdct = readb(intel_private.registers+I830_RDRAM_CHANNEL_TYPE); - gtt_entries = (I830_RDRAM_ND(rdct) + 1) * - MB(ddt[I830_RDRAM_DDT(rdct)]); - local = 1; - break; - default: - gtt_entries = 0; - break; - } - } else if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB || - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB) { - /* - * SandyBridge has new memory control reg at 0x50.w - */ - u16 snb_gmch_ctl; - pci_read_config_word(intel_private.pcidev, SNB_GMCH_CTRL, &snb_gmch_ctl); - switch (snb_gmch_ctl & SNB_GMCH_GMS_STOLEN_MASK) { - case SNB_GMCH_GMS_STOLEN_32M: - gtt_entries = MB(32) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_64M: - gtt_entries = MB(64) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_96M: - gtt_entries = MB(96) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_128M: - gtt_entries = MB(128) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_160M: - gtt_entries = MB(160) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_192M: - gtt_entries = MB(192) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_224M: - gtt_entries = MB(224) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_256M: - gtt_entries = MB(256) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_288M: - gtt_entries = MB(288) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_320M: - gtt_entries = MB(320) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_352M: - gtt_entries = MB(352) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_384M: - gtt_entries = MB(384) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_416M: - gtt_entries = MB(416) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_448M: - gtt_entries = MB(448) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_480M: - gtt_entries = MB(480) - KB(size); - break; - case SNB_GMCH_GMS_STOLEN_512M: - gtt_entries = MB(512) - KB(size); - break; - } - } else { - switch (gmch_ctrl & I855_GMCH_GMS_MASK) { - case I855_GMCH_GMS_STOLEN_1M: - gtt_entries = MB(1) - KB(size); - break; - case I855_GMCH_GMS_STOLEN_4M: - gtt_entries = MB(4) - KB(size); - break; - case I855_GMCH_GMS_STOLEN_8M: - gtt_entries = MB(8) - KB(size); - break; - case I855_GMCH_GMS_STOLEN_16M: - gtt_entries = MB(16) - KB(size); - break; - case I855_GMCH_GMS_STOLEN_32M: - gtt_entries = MB(32) - KB(size); - break; - case I915_GMCH_GMS_STOLEN_48M: - /* Check it's really I915G */ - if (IS_I915 || IS_I965 || IS_G33 || IS_G4X) - gtt_entries = MB(48) - KB(size); - else - gtt_entries = 0; - break; - case I915_GMCH_GMS_STOLEN_64M: - /* Check it's really I915G */ - if (IS_I915 || IS_I965 || IS_G33 || IS_G4X) - gtt_entries = MB(64) - KB(size); - else - gtt_entries = 0; - break; - case G33_GMCH_GMS_STOLEN_128M: - if (IS_G33 || IS_I965 || IS_G4X) - gtt_entries = MB(128) - KB(size); - else - gtt_entries = 0; - break; - case G33_GMCH_GMS_STOLEN_256M: - if (IS_G33 || IS_I965 || IS_G4X) - gtt_entries = MB(256) - KB(size); - else - gtt_entries = 0; - break; - case INTEL_GMCH_GMS_STOLEN_96M: - if (IS_I965 || IS_G4X) - gtt_entries = MB(96) - KB(size); - else - gtt_entries = 0; - break; - case INTEL_GMCH_GMS_STOLEN_160M: - if (IS_I965 || IS_G4X) - gtt_entries = MB(160) - KB(size); - else - gtt_entries = 0; - break; - case INTEL_GMCH_GMS_STOLEN_224M: - if (IS_I965 || IS_G4X) - gtt_entries = MB(224) - KB(size); - else - gtt_entries = 0; - break; - case INTEL_GMCH_GMS_STOLEN_352M: - if (IS_I965 || IS_G4X) - gtt_entries = MB(352) - KB(size); - else - gtt_entries = 0; - break; - default: - gtt_entries = 0; - break; - } - } - if (gtt_entries > 0) { - dev_info(&agp_bridge->dev->dev, "detected %dK %s memory\n", - gtt_entries / KB(1), local ? "local" : "stolen"); - gtt_entries /= KB(4); - } else { - dev_info(&agp_bridge->dev->dev, - "no pre-allocated video memory detected\n"); - gtt_entries = 0; - } - - intel_private.gtt_entries = gtt_entries; -} - -static void intel_i830_fini_flush(void) -{ - kunmap(intel_private.i8xx_page); - intel_private.i8xx_flush_page = NULL; - unmap_page_from_agp(intel_private.i8xx_page); - - __free_page(intel_private.i8xx_page); - intel_private.i8xx_page = NULL; -} - -static void intel_i830_setup_flush(void) -{ - /* return if we've already set the flush mechanism up */ - if (intel_private.i8xx_page) - return; - - intel_private.i8xx_page = alloc_page(GFP_KERNEL | __GFP_ZERO | GFP_DMA32); - if (!intel_private.i8xx_page) - return; - - intel_private.i8xx_flush_page = kmap(intel_private.i8xx_page); - if (!intel_private.i8xx_flush_page) - intel_i830_fini_flush(); -} - -/* The chipset_flush interface needs to get data that has already been - * flushed out of the CPU all the way out to main memory, because the GPU - * doesn't snoop those buffers. - * - * The 8xx series doesn't have the same lovely interface for flushing the - * chipset write buffers that the later chips do. According to the 865 - * specs, it's 64 octwords, or 1KB. So, to get those previous things in - * that buffer out, we just fill 1KB and clflush it out, on the assumption - * that it'll push whatever was in there out. It appears to work. - */ -static void intel_i830_chipset_flush(struct agp_bridge_data *bridge) -{ - unsigned int *pg = intel_private.i8xx_flush_page; - - memset(pg, 0, 1024); - - if (cpu_has_clflush) - clflush_cache_range(pg, 1024); - else if (wbinvd_on_all_cpus() != 0) - printk(KERN_ERR "Timed out waiting for cache flush.\n"); -} - -/* The intel i830 automatically initializes the agp aperture during POST. - * Use the memory already set aside for in the GTT. - */ -static int intel_i830_create_gatt_table(struct agp_bridge_data *bridge) -{ - int page_order; - struct aper_size_info_fixed *size; - int num_entries; - u32 temp; - - size = agp_bridge->current_size; - page_order = size->page_order; - num_entries = size->num_entries; - agp_bridge->gatt_table_real = NULL; - - pci_read_config_dword(intel_private.pcidev, I810_MMADDR, &temp); - temp &= 0xfff80000; - - intel_private.registers = ioremap(temp, 128 * 4096); - if (!intel_private.registers) - return -ENOMEM; - - temp = readl(intel_private.registers+I810_PGETBL_CTL) & 0xfffff000; - global_cache_flush(); /* FIXME: ?? */ - - /* we have to call this as early as possible after the MMIO base address is known */ - intel_i830_init_gtt_entries(); - - agp_bridge->gatt_table = NULL; - - agp_bridge->gatt_bus_addr = temp; - - return 0; -} - -/* Return the gatt table to a sane state. Use the top of stolen - * memory for the GTT. - */ -static int intel_i830_free_gatt_table(struct agp_bridge_data *bridge) -{ - return 0; -} - -static int intel_i830_fetch_size(void) -{ - u16 gmch_ctrl; - struct aper_size_info_fixed *values; - - values = A_SIZE_FIX(agp_bridge->driver->aperture_sizes); - - if (agp_bridge->dev->device != PCI_DEVICE_ID_INTEL_82830_HB && - agp_bridge->dev->device != PCI_DEVICE_ID_INTEL_82845G_HB) { - /* 855GM/852GM/865G has 128MB aperture size */ - agp_bridge->previous_size = agp_bridge->current_size = (void *) values; - agp_bridge->aperture_size_idx = 0; - return values[0].size; - } - - pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl); - - if ((gmch_ctrl & I830_GMCH_MEM_MASK) == I830_GMCH_MEM_128M) { - agp_bridge->previous_size = agp_bridge->current_size = (void *) values; - agp_bridge->aperture_size_idx = 0; - return values[0].size; - } else { - agp_bridge->previous_size = agp_bridge->current_size = (void *) (values + 1); - agp_bridge->aperture_size_idx = 1; - return values[1].size; - } - - return 0; -} - -static int intel_i830_configure(void) -{ - struct aper_size_info_fixed *current_size; - u32 temp; - u16 gmch_ctrl; - int i; - - current_size = A_SIZE_FIX(agp_bridge->current_size); - - pci_read_config_dword(intel_private.pcidev, I810_GMADDR, &temp); - agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); - - pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl); - gmch_ctrl |= I830_GMCH_ENABLED; - pci_write_config_word(agp_bridge->dev, I830_GMCH_CTRL, gmch_ctrl); - - writel(agp_bridge->gatt_bus_addr|I810_PGETBL_ENABLED, intel_private.registers+I810_PGETBL_CTL); - readl(intel_private.registers+I810_PGETBL_CTL); /* PCI Posting. */ - - if (agp_bridge->driver->needs_scratch_page) { - for (i = intel_private.gtt_entries; i < current_size->num_entries; i++) { - writel(agp_bridge->scratch_page, intel_private.registers+I810_PTE_BASE+(i*4)); - } - readl(intel_private.registers+I810_PTE_BASE+((i-1)*4)); /* PCI Posting. */ - } - - global_cache_flush(); - - intel_i830_setup_flush(); - return 0; -} - -static void intel_i830_cleanup(void) -{ - iounmap(intel_private.registers); -} - -static int intel_i830_insert_entries(struct agp_memory *mem, off_t pg_start, - int type) -{ - int i, j, num_entries; - void *temp; - int ret = -EINVAL; - int mask_type; - - if (mem->page_count == 0) - goto out; - - temp = agp_bridge->current_size; - num_entries = A_SIZE_FIX(temp)->num_entries; - - if (pg_start < intel_private.gtt_entries) { - dev_printk(KERN_DEBUG, &intel_private.pcidev->dev, - "pg_start == 0x%.8lx, intel_private.gtt_entries == 0x%.8x\n", - pg_start, intel_private.gtt_entries); - - dev_info(&intel_private.pcidev->dev, - "trying to insert into local/stolen memory\n"); - goto out_err; - } - - if ((pg_start + mem->page_count) > num_entries) - goto out_err; - - /* The i830 can't check the GTT for entries since its read only, - * depend on the caller to make the correct offset decisions. - */ - - if (type != mem->type) - goto out_err; - - mask_type = agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type); - - if (mask_type != 0 && mask_type != AGP_PHYS_MEMORY && - mask_type != INTEL_AGP_CACHED_MEMORY) - goto out_err; - - if (!mem->is_flushed) - global_cache_flush(); - - for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { - writel(agp_bridge->driver->mask_memory(agp_bridge, - page_to_phys(mem->pages[i]), mask_type), - intel_private.registers+I810_PTE_BASE+(j*4)); - } - readl(intel_private.registers+I810_PTE_BASE+((j-1)*4)); - agp_bridge->driver->tlb_flush(mem); - -out: - ret = 0; -out_err: - mem->is_flushed = true; - return ret; -} - -static int intel_i830_remove_entries(struct agp_memory *mem, off_t pg_start, - int type) -{ - int i; - - if (mem->page_count == 0) - return 0; - - if (pg_start < intel_private.gtt_entries) { - dev_info(&intel_private.pcidev->dev, - "trying to disable local/stolen memory\n"); - return -EINVAL; - } - - for (i = pg_start; i < (mem->page_count + pg_start); i++) { - writel(agp_bridge->scratch_page, intel_private.registers+I810_PTE_BASE+(i*4)); - } - readl(intel_private.registers+I810_PTE_BASE+((i-1)*4)); - - agp_bridge->driver->tlb_flush(mem); - return 0; -} - -static struct agp_memory *intel_i830_alloc_by_type(size_t pg_count, int type) -{ - if (type == AGP_PHYS_MEMORY) - return alloc_agpphysmem_i8xx(pg_count, type); - /* always return NULL for other allocation types for now */ - return NULL; -} - -static int intel_alloc_chipset_flush_resource(void) -{ - int ret; - ret = pci_bus_alloc_resource(agp_bridge->dev->bus, &intel_private.ifp_resource, PAGE_SIZE, - PAGE_SIZE, PCIBIOS_MIN_MEM, 0, - pcibios_align_resource, agp_bridge->dev); - - return ret; -} - -static void intel_i915_setup_chipset_flush(void) -{ - int ret; - u32 temp; - - pci_read_config_dword(agp_bridge->dev, I915_IFPADDR, &temp); - if (!(temp & 0x1)) { - intel_alloc_chipset_flush_resource(); - intel_private.resource_valid = 1; - pci_write_config_dword(agp_bridge->dev, I915_IFPADDR, (intel_private.ifp_resource.start & 0xffffffff) | 0x1); - } else { - temp &= ~1; - - intel_private.resource_valid = 1; - intel_private.ifp_resource.start = temp; - intel_private.ifp_resource.end = temp + PAGE_SIZE; - ret = request_resource(&iomem_resource, &intel_private.ifp_resource); - /* some BIOSes reserve this area in a pnp some don't */ - if (ret) - intel_private.resource_valid = 0; - } -} - -static void intel_i965_g33_setup_chipset_flush(void) -{ - u32 temp_hi, temp_lo; - int ret; - - pci_read_config_dword(agp_bridge->dev, I965_IFPADDR + 4, &temp_hi); - pci_read_config_dword(agp_bridge->dev, I965_IFPADDR, &temp_lo); - - if (!(temp_lo & 0x1)) { - - intel_alloc_chipset_flush_resource(); - - intel_private.resource_valid = 1; - pci_write_config_dword(agp_bridge->dev, I965_IFPADDR + 4, - upper_32_bits(intel_private.ifp_resource.start)); - pci_write_config_dword(agp_bridge->dev, I965_IFPADDR, (intel_private.ifp_resource.start & 0xffffffff) | 0x1); - } else { - u64 l64; - - temp_lo &= ~0x1; - l64 = ((u64)temp_hi << 32) | temp_lo; - - intel_private.resource_valid = 1; - intel_private.ifp_resource.start = l64; - intel_private.ifp_resource.end = l64 + PAGE_SIZE; - ret = request_resource(&iomem_resource, &intel_private.ifp_resource); - /* some BIOSes reserve this area in a pnp some don't */ - if (ret) - intel_private.resource_valid = 0; - } -} - -static void intel_i9xx_setup_flush(void) -{ - /* return if already configured */ - if (intel_private.ifp_resource.start) - return; - - if (IS_SNB) - return; - - /* setup a resource for this object */ - intel_private.ifp_resource.name = "Intel Flush Page"; - intel_private.ifp_resource.flags = IORESOURCE_MEM; - - /* Setup chipset flush for 915 */ - if (IS_I965 || IS_G33 || IS_G4X) { - intel_i965_g33_setup_chipset_flush(); - } else { - intel_i915_setup_chipset_flush(); - } - - if (intel_private.ifp_resource.start) { - intel_private.i9xx_flush_page = ioremap_nocache(intel_private.ifp_resource.start, PAGE_SIZE); - if (!intel_private.i9xx_flush_page) - dev_info(&intel_private.pcidev->dev, "can't ioremap flush page - no chipset flushing"); - } -} - -static int intel_i915_configure(void) -{ - struct aper_size_info_fixed *current_size; - u32 temp; - u16 gmch_ctrl; - int i; - - current_size = A_SIZE_FIX(agp_bridge->current_size); - - pci_read_config_dword(intel_private.pcidev, I915_GMADDR, &temp); - - agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); - - pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl); - gmch_ctrl |= I830_GMCH_ENABLED; - pci_write_config_word(agp_bridge->dev, I830_GMCH_CTRL, gmch_ctrl); - - writel(agp_bridge->gatt_bus_addr|I810_PGETBL_ENABLED, intel_private.registers+I810_PGETBL_CTL); - readl(intel_private.registers+I810_PGETBL_CTL); /* PCI Posting. */ - - if (agp_bridge->driver->needs_scratch_page) { - for (i = intel_private.gtt_entries; i < intel_private.gtt_total_size; i++) { - writel(agp_bridge->scratch_page, intel_private.gtt+i); - } - readl(intel_private.gtt+i-1); /* PCI Posting. */ - } - - global_cache_flush(); - - intel_i9xx_setup_flush(); - - return 0; -} - -static void intel_i915_cleanup(void) -{ - if (intel_private.i9xx_flush_page) - iounmap(intel_private.i9xx_flush_page); - if (intel_private.resource_valid) - release_resource(&intel_private.ifp_resource); - intel_private.ifp_resource.start = 0; - intel_private.resource_valid = 0; - iounmap(intel_private.gtt); - iounmap(intel_private.registers); -} - -static void intel_i915_chipset_flush(struct agp_bridge_data *bridge) -{ - if (intel_private.i9xx_flush_page) - writel(1, intel_private.i9xx_flush_page); -} - -static int intel_i915_insert_entries(struct agp_memory *mem, off_t pg_start, - int type) -{ - int num_entries; - void *temp; - int ret = -EINVAL; - int mask_type; - - if (mem->page_count == 0) - goto out; - - temp = agp_bridge->current_size; - num_entries = A_SIZE_FIX(temp)->num_entries; - - if (pg_start < intel_private.gtt_entries) { - dev_printk(KERN_DEBUG, &intel_private.pcidev->dev, - "pg_start == 0x%.8lx, intel_private.gtt_entries == 0x%.8x\n", - pg_start, intel_private.gtt_entries); - - dev_info(&intel_private.pcidev->dev, - "trying to insert into local/stolen memory\n"); - goto out_err; - } - - if ((pg_start + mem->page_count) > num_entries) - goto out_err; - - /* The i915 can't check the GTT for entries since it's read only; - * depend on the caller to make the correct offset decisions. - */ - - if (type != mem->type) - goto out_err; - - mask_type = agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type); - - if (mask_type != 0 && mask_type != AGP_PHYS_MEMORY && - mask_type != INTEL_AGP_CACHED_MEMORY) - goto out_err; - - if (!mem->is_flushed) - global_cache_flush(); - - intel_agp_insert_sg_entries(mem, pg_start, mask_type); - agp_bridge->driver->tlb_flush(mem); - - out: - ret = 0; - out_err: - mem->is_flushed = true; - return ret; -} - -static int intel_i915_remove_entries(struct agp_memory *mem, off_t pg_start, - int type) -{ - int i; - - if (mem->page_count == 0) - return 0; - - if (pg_start < intel_private.gtt_entries) { - dev_info(&intel_private.pcidev->dev, - "trying to disable local/stolen memory\n"); - return -EINVAL; - } - - for (i = pg_start; i < (mem->page_count + pg_start); i++) - writel(agp_bridge->scratch_page, intel_private.gtt+i); - - readl(intel_private.gtt+i-1); - - agp_bridge->driver->tlb_flush(mem); - return 0; -} - -/* Return the aperture size by just checking the resource length. The effect - * described in the spec of the MSAC registers is just changing of the - * resource size. - */ -static int intel_i9xx_fetch_size(void) -{ - int num_sizes = ARRAY_SIZE(intel_i830_sizes); - int aper_size; /* size in megabytes */ - int i; - - aper_size = pci_resource_len(intel_private.pcidev, 2) / MB(1); - - for (i = 0; i < num_sizes; i++) { - if (aper_size == intel_i830_sizes[i].size) { - agp_bridge->current_size = intel_i830_sizes + i; - agp_bridge->previous_size = agp_bridge->current_size; - return aper_size; - } - } - - return 0; -} - -/* The intel i915 automatically initializes the agp aperture during POST. - * Use the memory already set aside for in the GTT. - */ -static int intel_i915_create_gatt_table(struct agp_bridge_data *bridge) -{ - int page_order; - struct aper_size_info_fixed *size; - int num_entries; - u32 temp, temp2; - int gtt_map_size = 256 * 1024; - - size = agp_bridge->current_size; - page_order = size->page_order; - num_entries = size->num_entries; - agp_bridge->gatt_table_real = NULL; - - pci_read_config_dword(intel_private.pcidev, I915_MMADDR, &temp); - pci_read_config_dword(intel_private.pcidev, I915_PTEADDR, &temp2); - - if (IS_G33) - gtt_map_size = 1024 * 1024; /* 1M on G33 */ - intel_private.gtt = ioremap(temp2, gtt_map_size); - if (!intel_private.gtt) - return -ENOMEM; - - intel_private.gtt_total_size = gtt_map_size / 4; - - temp &= 0xfff80000; - - intel_private.registers = ioremap(temp, 128 * 4096); - if (!intel_private.registers) { - iounmap(intel_private.gtt); - return -ENOMEM; - } - - temp = readl(intel_private.registers+I810_PGETBL_CTL) & 0xfffff000; - global_cache_flush(); /* FIXME: ? */ - - /* we have to call this as early as possible after the MMIO base address is known */ - intel_i830_init_gtt_entries(); - - agp_bridge->gatt_table = NULL; - - agp_bridge->gatt_bus_addr = temp; - - return 0; -} - -/* - * The i965 supports 36-bit physical addresses, but to keep - * the format of the GTT the same, the bits that don't fit - * in a 32-bit word are shifted down to bits 4..7. - * - * Gcc is smart enough to notice that "(addr >> 28) & 0xf0" - * is always zero on 32-bit architectures, so no need to make - * this conditional. - */ -static unsigned long intel_i965_mask_memory(struct agp_bridge_data *bridge, - dma_addr_t addr, int type) -{ - /* Shift high bits down */ - addr |= (addr >> 28) & 0xf0; - - /* Type checking must be done elsewhere */ - return addr | bridge->driver->masks[type].mask; -} - -static void intel_i965_get_gtt_range(int *gtt_offset, int *gtt_size) -{ - u16 snb_gmch_ctl; - - switch (agp_bridge->dev->device) { - case PCI_DEVICE_ID_INTEL_GM45_HB: - case PCI_DEVICE_ID_INTEL_EAGLELAKE_HB: - case PCI_DEVICE_ID_INTEL_Q45_HB: - case PCI_DEVICE_ID_INTEL_G45_HB: - case PCI_DEVICE_ID_INTEL_G41_HB: - case PCI_DEVICE_ID_INTEL_B43_HB: - case PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB: - case PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB: - case PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB: - case PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB: - *gtt_offset = *gtt_size = MB(2); - break; - case PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB: - case PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB: - *gtt_offset = MB(2); - - pci_read_config_word(intel_private.pcidev, SNB_GMCH_CTRL, &snb_gmch_ctl); - switch (snb_gmch_ctl & SNB_GTT_SIZE_MASK) { - default: - case SNB_GTT_SIZE_0M: - printk(KERN_ERR "Bad GTT size mask: 0x%04x.\n", snb_gmch_ctl); - *gtt_size = MB(0); - break; - case SNB_GTT_SIZE_1M: - *gtt_size = MB(1); - break; - case SNB_GTT_SIZE_2M: - *gtt_size = MB(2); - break; - } - break; - default: - *gtt_offset = *gtt_size = KB(512); - } -} - -/* The intel i965 automatically initializes the agp aperture during POST. - * Use the memory already set aside for in the GTT. - */ -static int intel_i965_create_gatt_table(struct agp_bridge_data *bridge) -{ - int page_order; - struct aper_size_info_fixed *size; - int num_entries; - u32 temp; - int gtt_offset, gtt_size; - - size = agp_bridge->current_size; - page_order = size->page_order; - num_entries = size->num_entries; - agp_bridge->gatt_table_real = NULL; - - pci_read_config_dword(intel_private.pcidev, I915_MMADDR, &temp); - - temp &= 0xfff00000; - - intel_i965_get_gtt_range(>t_offset, >t_size); - - intel_private.gtt = ioremap((temp + gtt_offset) , gtt_size); - - if (!intel_private.gtt) - return -ENOMEM; - - intel_private.gtt_total_size = gtt_size / 4; - - intel_private.registers = ioremap(temp, 128 * 4096); - if (!intel_private.registers) { - iounmap(intel_private.gtt); - return -ENOMEM; - } - - temp = readl(intel_private.registers+I810_PGETBL_CTL) & 0xfffff000; - global_cache_flush(); /* FIXME: ? */ - - /* we have to call this as early as possible after the MMIO base address is known */ - intel_i830_init_gtt_entries(); - - agp_bridge->gatt_table = NULL; - - agp_bridge->gatt_bus_addr = temp; - - return 0; -} - - static int intel_fetch_size(void) { int i; @@ -1982,6 +464,7 @@ static const struct agp_bridge_driver intel_generic_driver = { .aperture_sizes = intel_generic_sizes, .size_type = U16_APER_SIZE, .num_aperture_sizes = 7, + .needs_scratch_page = true, .configure = intel_configure, .fetch_size = intel_fetch_size, .cleanup = intel_cleanup, @@ -2003,38 +486,12 @@ static const struct agp_bridge_driver intel_generic_driver = { .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; -static const struct agp_bridge_driver intel_810_driver = { - .owner = THIS_MODULE, - .aperture_sizes = intel_i810_sizes, - .size_type = FIXED_APER_SIZE, - .num_aperture_sizes = 2, - .needs_scratch_page = true, - .configure = intel_i810_configure, - .fetch_size = intel_i810_fetch_size, - .cleanup = intel_i810_cleanup, - .tlb_flush = intel_i810_tlbflush, - .mask_memory = intel_i810_mask_memory, - .masks = intel_i810_masks, - .agp_enable = intel_i810_agp_enable, - .cache_flush = global_cache_flush, - .create_gatt_table = agp_generic_create_gatt_table, - .free_gatt_table = agp_generic_free_gatt_table, - .insert_memory = intel_i810_insert_entries, - .remove_memory = intel_i810_remove_entries, - .alloc_by_type = intel_i810_alloc_by_type, - .free_by_type = intel_i810_free_by_type, - .agp_alloc_page = agp_generic_alloc_page, - .agp_alloc_pages = agp_generic_alloc_pages, - .agp_destroy_page = agp_generic_destroy_page, - .agp_destroy_pages = agp_generic_destroy_pages, - .agp_type_to_mask_type = agp_generic_type_to_mask_type, -}; - static const struct agp_bridge_driver intel_815_driver = { .owner = THIS_MODULE, .aperture_sizes = intel_815_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 2, + .needs_scratch_page = true, .configure = intel_815_configure, .fetch_size = intel_815_fetch_size, .cleanup = intel_8xx_cleanup, @@ -2056,39 +513,12 @@ static const struct agp_bridge_driver intel_815_driver = { .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; -static const struct agp_bridge_driver intel_830_driver = { - .owner = THIS_MODULE, - .aperture_sizes = intel_i830_sizes, - .size_type = FIXED_APER_SIZE, - .num_aperture_sizes = 4, - .needs_scratch_page = true, - .configure = intel_i830_configure, - .fetch_size = intel_i830_fetch_size, - .cleanup = intel_i830_cleanup, - .tlb_flush = intel_i810_tlbflush, - .mask_memory = intel_i810_mask_memory, - .masks = intel_i810_masks, - .agp_enable = intel_i810_agp_enable, - .cache_flush = global_cache_flush, - .create_gatt_table = intel_i830_create_gatt_table, - .free_gatt_table = intel_i830_free_gatt_table, - .insert_memory = intel_i830_insert_entries, - .remove_memory = intel_i830_remove_entries, - .alloc_by_type = intel_i830_alloc_by_type, - .free_by_type = intel_i810_free_by_type, - .agp_alloc_page = agp_generic_alloc_page, - .agp_alloc_pages = agp_generic_alloc_pages, - .agp_destroy_page = agp_generic_destroy_page, - .agp_destroy_pages = agp_generic_destroy_pages, - .agp_type_to_mask_type = intel_i830_type_to_mask_type, - .chipset_flush = intel_i830_chipset_flush, -}; - static const struct agp_bridge_driver intel_820_driver = { .owner = THIS_MODULE, .aperture_sizes = intel_8xx_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 7, + .needs_scratch_page = true, .configure = intel_820_configure, .fetch_size = intel_8xx_fetch_size, .cleanup = intel_820_cleanup, @@ -2115,6 +545,7 @@ static const struct agp_bridge_driver intel_830mp_driver = { .aperture_sizes = intel_830mp_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 4, + .needs_scratch_page = true, .configure = intel_830mp_configure, .fetch_size = intel_8xx_fetch_size, .cleanup = intel_8xx_cleanup, @@ -2141,6 +572,7 @@ static const struct agp_bridge_driver intel_840_driver = { .aperture_sizes = intel_8xx_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 7, + .needs_scratch_page = true, .configure = intel_840_configure, .fetch_size = intel_8xx_fetch_size, .cleanup = intel_8xx_cleanup, @@ -2167,6 +599,7 @@ static const struct agp_bridge_driver intel_845_driver = { .aperture_sizes = intel_8xx_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 7, + .needs_scratch_page = true, .configure = intel_845_configure, .fetch_size = intel_8xx_fetch_size, .cleanup = intel_8xx_cleanup, @@ -2193,6 +626,7 @@ static const struct agp_bridge_driver intel_850_driver = { .aperture_sizes = intel_8xx_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 7, + .needs_scratch_page = true, .configure = intel_850_configure, .fetch_size = intel_8xx_fetch_size, .cleanup = intel_8xx_cleanup, @@ -2219,6 +653,7 @@ static const struct agp_bridge_driver intel_860_driver = { .aperture_sizes = intel_8xx_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 7, + .needs_scratch_page = true, .configure = intel_860_configure, .fetch_size = intel_8xx_fetch_size, .cleanup = intel_8xx_cleanup, @@ -2240,79 +675,12 @@ static const struct agp_bridge_driver intel_860_driver = { .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; -static const struct agp_bridge_driver intel_915_driver = { - .owner = THIS_MODULE, - .aperture_sizes = intel_i830_sizes, - .size_type = FIXED_APER_SIZE, - .num_aperture_sizes = 4, - .needs_scratch_page = true, - .configure = intel_i915_configure, - .fetch_size = intel_i9xx_fetch_size, - .cleanup = intel_i915_cleanup, - .tlb_flush = intel_i810_tlbflush, - .mask_memory = intel_i810_mask_memory, - .masks = intel_i810_masks, - .agp_enable = intel_i810_agp_enable, - .cache_flush = global_cache_flush, - .create_gatt_table = intel_i915_create_gatt_table, - .free_gatt_table = intel_i830_free_gatt_table, - .insert_memory = intel_i915_insert_entries, - .remove_memory = intel_i915_remove_entries, - .alloc_by_type = intel_i830_alloc_by_type, - .free_by_type = intel_i810_free_by_type, - .agp_alloc_page = agp_generic_alloc_page, - .agp_alloc_pages = agp_generic_alloc_pages, - .agp_destroy_page = agp_generic_destroy_page, - .agp_destroy_pages = agp_generic_destroy_pages, - .agp_type_to_mask_type = intel_i830_type_to_mask_type, - .chipset_flush = intel_i915_chipset_flush, -#ifdef USE_PCI_DMA_API - .agp_map_page = intel_agp_map_page, - .agp_unmap_page = intel_agp_unmap_page, - .agp_map_memory = intel_agp_map_memory, - .agp_unmap_memory = intel_agp_unmap_memory, -#endif -}; - -static const struct agp_bridge_driver intel_i965_driver = { - .owner = THIS_MODULE, - .aperture_sizes = intel_i830_sizes, - .size_type = FIXED_APER_SIZE, - .num_aperture_sizes = 4, - .needs_scratch_page = true, - .configure = intel_i915_configure, - .fetch_size = intel_i9xx_fetch_size, - .cleanup = intel_i915_cleanup, - .tlb_flush = intel_i810_tlbflush, - .mask_memory = intel_i965_mask_memory, - .masks = intel_i810_masks, - .agp_enable = intel_i810_agp_enable, - .cache_flush = global_cache_flush, - .create_gatt_table = intel_i965_create_gatt_table, - .free_gatt_table = intel_i830_free_gatt_table, - .insert_memory = intel_i915_insert_entries, - .remove_memory = intel_i915_remove_entries, - .alloc_by_type = intel_i830_alloc_by_type, - .free_by_type = intel_i810_free_by_type, - .agp_alloc_page = agp_generic_alloc_page, - .agp_alloc_pages = agp_generic_alloc_pages, - .agp_destroy_page = agp_generic_destroy_page, - .agp_destroy_pages = agp_generic_destroy_pages, - .agp_type_to_mask_type = intel_i830_type_to_mask_type, - .chipset_flush = intel_i915_chipset_flush, -#ifdef USE_PCI_DMA_API - .agp_map_page = intel_agp_map_page, - .agp_unmap_page = intel_agp_unmap_page, - .agp_map_memory = intel_agp_map_memory, - .agp_unmap_memory = intel_agp_unmap_memory, -#endif -}; - static const struct agp_bridge_driver intel_7505_driver = { .owner = THIS_MODULE, .aperture_sizes = intel_8xx_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 7, + .needs_scratch_page = true, .configure = intel_7505_configure, .fetch_size = intel_8xx_fetch_size, .cleanup = intel_8xx_cleanup, @@ -2334,40 +702,6 @@ static const struct agp_bridge_driver intel_7505_driver = { .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; -static const struct agp_bridge_driver intel_g33_driver = { - .owner = THIS_MODULE, - .aperture_sizes = intel_i830_sizes, - .size_type = FIXED_APER_SIZE, - .num_aperture_sizes = 4, - .needs_scratch_page = true, - .configure = intel_i915_configure, - .fetch_size = intel_i9xx_fetch_size, - .cleanup = intel_i915_cleanup, - .tlb_flush = intel_i810_tlbflush, - .mask_memory = intel_i965_mask_memory, - .masks = intel_i810_masks, - .agp_enable = intel_i810_agp_enable, - .cache_flush = global_cache_flush, - .create_gatt_table = intel_i915_create_gatt_table, - .free_gatt_table = intel_i830_free_gatt_table, - .insert_memory = intel_i915_insert_entries, - .remove_memory = intel_i915_remove_entries, - .alloc_by_type = intel_i830_alloc_by_type, - .free_by_type = intel_i810_free_by_type, - .agp_alloc_page = agp_generic_alloc_page, - .agp_alloc_pages = agp_generic_alloc_pages, - .agp_destroy_page = agp_generic_destroy_page, - .agp_destroy_pages = agp_generic_destroy_pages, - .agp_type_to_mask_type = intel_i830_type_to_mask_type, - .chipset_flush = intel_i915_chipset_flush, -#ifdef USE_PCI_DMA_API - .agp_map_page = intel_agp_map_page, - .agp_unmap_page = intel_agp_unmap_page, - .agp_map_memory = intel_agp_map_memory, - .agp_unmap_memory = intel_agp_unmap_memory, -#endif -}; - static int find_gmch(u16 device) { struct pci_dev *gmch_device; @@ -2392,103 +726,137 @@ static int find_gmch(u16 device) static const struct intel_driver_description { unsigned int chip_id; unsigned int gmch_chip_id; - unsigned int multi_gmch_chip; /* if we have more gfx chip type on this HB. */ char *name; const struct agp_bridge_driver *driver; const struct agp_bridge_driver *gmch_driver; } intel_agp_chipsets[] = { - { PCI_DEVICE_ID_INTEL_82443LX_0, 0, 0, "440LX", &intel_generic_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82443BX_0, 0, 0, "440BX", &intel_generic_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82443GX_0, 0, 0, "440GX", &intel_generic_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82810_MC1, PCI_DEVICE_ID_INTEL_82810_IG1, 0, "i810", + { PCI_DEVICE_ID_INTEL_82443LX_0, 0, "440LX", &intel_generic_driver, NULL }, + { PCI_DEVICE_ID_INTEL_82443BX_0, 0, "440BX", &intel_generic_driver, NULL }, + { PCI_DEVICE_ID_INTEL_82443GX_0, 0, "440GX", &intel_generic_driver, NULL }, + { PCI_DEVICE_ID_INTEL_82810_MC1, PCI_DEVICE_ID_INTEL_82810_IG1, "i810", NULL, &intel_810_driver }, - { PCI_DEVICE_ID_INTEL_82810_MC3, PCI_DEVICE_ID_INTEL_82810_IG3, 0, "i810", + { PCI_DEVICE_ID_INTEL_82810_MC3, PCI_DEVICE_ID_INTEL_82810_IG3, "i810", NULL, &intel_810_driver }, - { PCI_DEVICE_ID_INTEL_82810E_MC, PCI_DEVICE_ID_INTEL_82810E_IG, 0, "i810", + { PCI_DEVICE_ID_INTEL_82810E_MC, PCI_DEVICE_ID_INTEL_82810E_IG, "i810", NULL, &intel_810_driver }, - { PCI_DEVICE_ID_INTEL_82815_MC, PCI_DEVICE_ID_INTEL_82815_CGC, 0, "i815", + { PCI_DEVICE_ID_INTEL_82815_MC, PCI_DEVICE_ID_INTEL_82815_CGC, "i815", &intel_815_driver, &intel_810_driver }, - { PCI_DEVICE_ID_INTEL_82820_HB, 0, 0, "i820", &intel_820_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82820_UP_HB, 0, 0, "i820", &intel_820_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82830_HB, PCI_DEVICE_ID_INTEL_82830_CGC, 0, "830M", + { PCI_DEVICE_ID_INTEL_82820_HB, 0, "i820", &intel_820_driver, NULL }, + { PCI_DEVICE_ID_INTEL_82820_UP_HB, 0, "i820", &intel_820_driver, NULL }, + { PCI_DEVICE_ID_INTEL_82830_HB, PCI_DEVICE_ID_INTEL_82830_CGC, "830M", &intel_830mp_driver, &intel_830_driver }, - { PCI_DEVICE_ID_INTEL_82840_HB, 0, 0, "i840", &intel_840_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82845_HB, 0, 0, "845G", &intel_845_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82845G_HB, PCI_DEVICE_ID_INTEL_82845G_IG, 0, "830M", + { PCI_DEVICE_ID_INTEL_82840_HB, 0, "i840", &intel_840_driver, NULL }, + { PCI_DEVICE_ID_INTEL_82845_HB, 0, "845G", &intel_845_driver, NULL }, + { PCI_DEVICE_ID_INTEL_82845G_HB, PCI_DEVICE_ID_INTEL_82845G_IG, "830M", &intel_845_driver, &intel_830_driver }, - { PCI_DEVICE_ID_INTEL_82850_HB, 0, 0, "i850", &intel_850_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82854_HB, PCI_DEVICE_ID_INTEL_82854_IG, 0, "854", + { PCI_DEVICE_ID_INTEL_82850_HB, 0, "i850", &intel_850_driver, NULL }, + { PCI_DEVICE_ID_INTEL_82854_HB, PCI_DEVICE_ID_INTEL_82854_IG, "854", &intel_845_driver, &intel_830_driver }, - { PCI_DEVICE_ID_INTEL_82855PM_HB, 0, 0, "855PM", &intel_845_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82855GM_HB, PCI_DEVICE_ID_INTEL_82855GM_IG, 0, "855GM", + { PCI_DEVICE_ID_INTEL_82855PM_HB, 0, "855PM", &intel_845_driver, NULL }, + { PCI_DEVICE_ID_INTEL_82855GM_HB, PCI_DEVICE_ID_INTEL_82855GM_IG, "855GM", &intel_845_driver, &intel_830_driver }, - { PCI_DEVICE_ID_INTEL_82860_HB, 0, 0, "i860", &intel_860_driver, NULL }, - { PCI_DEVICE_ID_INTEL_82865_HB, PCI_DEVICE_ID_INTEL_82865_IG, 0, "865", + { PCI_DEVICE_ID_INTEL_82860_HB, 0, "i860", &intel_860_driver, NULL }, + { PCI_DEVICE_ID_INTEL_82865_HB, PCI_DEVICE_ID_INTEL_82865_IG, "865", &intel_845_driver, &intel_830_driver }, - { PCI_DEVICE_ID_INTEL_82875_HB, 0, 0, "i875", &intel_845_driver, NULL }, - { PCI_DEVICE_ID_INTEL_E7221_HB, PCI_DEVICE_ID_INTEL_E7221_IG, 0, "E7221 (i915)", + { PCI_DEVICE_ID_INTEL_82875_HB, 0, "i875", &intel_845_driver, NULL }, + { PCI_DEVICE_ID_INTEL_E7221_HB, PCI_DEVICE_ID_INTEL_E7221_IG, "E7221 (i915)", NULL, &intel_915_driver }, - { PCI_DEVICE_ID_INTEL_82915G_HB, PCI_DEVICE_ID_INTEL_82915G_IG, 0, "915G", + { PCI_DEVICE_ID_INTEL_82915G_HB, PCI_DEVICE_ID_INTEL_82915G_IG, "915G", NULL, &intel_915_driver }, - { PCI_DEVICE_ID_INTEL_82915GM_HB, PCI_DEVICE_ID_INTEL_82915GM_IG, 0, "915GM", + { PCI_DEVICE_ID_INTEL_82915GM_HB, PCI_DEVICE_ID_INTEL_82915GM_IG, "915GM", NULL, &intel_915_driver }, - { PCI_DEVICE_ID_INTEL_82945G_HB, PCI_DEVICE_ID_INTEL_82945G_IG, 0, "945G", + { PCI_DEVICE_ID_INTEL_82945G_HB, PCI_DEVICE_ID_INTEL_82945G_IG, "945G", NULL, &intel_915_driver }, - { PCI_DEVICE_ID_INTEL_82945GM_HB, PCI_DEVICE_ID_INTEL_82945GM_IG, 0, "945GM", + { PCI_DEVICE_ID_INTEL_82945GM_HB, PCI_DEVICE_ID_INTEL_82945GM_IG, "945GM", NULL, &intel_915_driver }, - { PCI_DEVICE_ID_INTEL_82945GME_HB, PCI_DEVICE_ID_INTEL_82945GME_IG, 0, "945GME", + { PCI_DEVICE_ID_INTEL_82945GME_HB, PCI_DEVICE_ID_INTEL_82945GME_IG, "945GME", NULL, &intel_915_driver }, - { PCI_DEVICE_ID_INTEL_82946GZ_HB, PCI_DEVICE_ID_INTEL_82946GZ_IG, 0, "946GZ", + { PCI_DEVICE_ID_INTEL_82946GZ_HB, PCI_DEVICE_ID_INTEL_82946GZ_IG, "946GZ", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_82G35_HB, PCI_DEVICE_ID_INTEL_82G35_IG, 0, "G35", + { PCI_DEVICE_ID_INTEL_82G35_HB, PCI_DEVICE_ID_INTEL_82G35_IG, "G35", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_82965Q_HB, PCI_DEVICE_ID_INTEL_82965Q_IG, 0, "965Q", + { PCI_DEVICE_ID_INTEL_82965Q_HB, PCI_DEVICE_ID_INTEL_82965Q_IG, "965Q", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_82965G_HB, PCI_DEVICE_ID_INTEL_82965G_IG, 0, "965G", + { PCI_DEVICE_ID_INTEL_82965G_HB, PCI_DEVICE_ID_INTEL_82965G_IG, "965G", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_82965GM_HB, PCI_DEVICE_ID_INTEL_82965GM_IG, 0, "965GM", + { PCI_DEVICE_ID_INTEL_82965GM_HB, PCI_DEVICE_ID_INTEL_82965GM_IG, "965GM", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_82965GME_HB, PCI_DEVICE_ID_INTEL_82965GME_IG, 0, "965GME/GLE", + { PCI_DEVICE_ID_INTEL_82965GME_HB, PCI_DEVICE_ID_INTEL_82965GME_IG, "965GME/GLE", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_7505_0, 0, 0, "E7505", &intel_7505_driver, NULL }, - { PCI_DEVICE_ID_INTEL_7205_0, 0, 0, "E7205", &intel_7505_driver, NULL }, - { PCI_DEVICE_ID_INTEL_G33_HB, PCI_DEVICE_ID_INTEL_G33_IG, 0, "G33", + { PCI_DEVICE_ID_INTEL_7505_0, 0, "E7505", &intel_7505_driver, NULL }, + { PCI_DEVICE_ID_INTEL_7205_0, 0, "E7205", &intel_7505_driver, NULL }, + { PCI_DEVICE_ID_INTEL_G33_HB, PCI_DEVICE_ID_INTEL_G33_IG, "G33", NULL, &intel_g33_driver }, - { PCI_DEVICE_ID_INTEL_Q35_HB, PCI_DEVICE_ID_INTEL_Q35_IG, 0, "Q35", + { PCI_DEVICE_ID_INTEL_Q35_HB, PCI_DEVICE_ID_INTEL_Q35_IG, "Q35", NULL, &intel_g33_driver }, - { PCI_DEVICE_ID_INTEL_Q33_HB, PCI_DEVICE_ID_INTEL_Q33_IG, 0, "Q33", + { PCI_DEVICE_ID_INTEL_Q33_HB, PCI_DEVICE_ID_INTEL_Q33_IG, "Q33", NULL, &intel_g33_driver }, - { PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB, PCI_DEVICE_ID_INTEL_PINEVIEW_M_IG, 0, "GMA3150", + { PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB, PCI_DEVICE_ID_INTEL_PINEVIEW_M_IG, "GMA3150", NULL, &intel_g33_driver }, - { PCI_DEVICE_ID_INTEL_PINEVIEW_HB, PCI_DEVICE_ID_INTEL_PINEVIEW_IG, 0, "GMA3150", + { PCI_DEVICE_ID_INTEL_PINEVIEW_HB, PCI_DEVICE_ID_INTEL_PINEVIEW_IG, "GMA3150", NULL, &intel_g33_driver }, - { PCI_DEVICE_ID_INTEL_GM45_HB, PCI_DEVICE_ID_INTEL_GM45_IG, 0, + { PCI_DEVICE_ID_INTEL_GM45_HB, PCI_DEVICE_ID_INTEL_GM45_IG, "GM45", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_EAGLELAKE_HB, PCI_DEVICE_ID_INTEL_EAGLELAKE_IG, 0, + { PCI_DEVICE_ID_INTEL_EAGLELAKE_HB, PCI_DEVICE_ID_INTEL_EAGLELAKE_IG, "Eaglelake", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_Q45_HB, PCI_DEVICE_ID_INTEL_Q45_IG, 0, + { PCI_DEVICE_ID_INTEL_Q45_HB, PCI_DEVICE_ID_INTEL_Q45_IG, "Q45/Q43", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_G45_HB, PCI_DEVICE_ID_INTEL_G45_IG, 0, + { PCI_DEVICE_ID_INTEL_G45_HB, PCI_DEVICE_ID_INTEL_G45_IG, "G45/G43", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_B43_HB, PCI_DEVICE_ID_INTEL_B43_IG, 0, + { PCI_DEVICE_ID_INTEL_B43_HB, PCI_DEVICE_ID_INTEL_B43_IG, "B43", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_G41_HB, PCI_DEVICE_ID_INTEL_G41_IG, 0, + { PCI_DEVICE_ID_INTEL_G41_HB, PCI_DEVICE_ID_INTEL_G41_IG, "G41", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_D_IG, 0, + { PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_D_IG, "HD Graphics", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, 0, + { PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, "HD Graphics", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, 0, + { PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, "HD Graphics", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, 0, + { PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, "HD Graphics", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB, PCI_DEVICE_ID_INTEL_SANDYBRIDGE_IG, 0, + { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB, PCI_DEVICE_ID_INTEL_SANDYBRIDGE_IG, "Sandybridge", NULL, &intel_i965_driver }, - { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB, PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_IG, 0, + { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB, PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_IG, "Sandybridge", NULL, &intel_i965_driver }, - { 0, 0, 0, NULL, NULL, NULL } + { 0, 0, NULL, NULL, NULL } }; +static int __devinit intel_gmch_probe(struct pci_dev *pdev, + struct agp_bridge_data *bridge) +{ + int i; + bridge->driver = NULL; + + for (i = 0; intel_agp_chipsets[i].name != NULL; i++) { + if ((intel_agp_chipsets[i].gmch_chip_id != 0) && + find_gmch(intel_agp_chipsets[i].gmch_chip_id)) { + bridge->driver = + intel_agp_chipsets[i].gmch_driver; + break; + } + } + + if (!bridge->driver) + return 0; + + bridge->dev_private_data = &intel_private; + bridge->dev = pdev; + + dev_info(&pdev->dev, "Intel %s Chipset\n", intel_agp_chipsets[i].name); + + if (bridge->driver->mask_memory == intel_i965_mask_memory) { + if (pci_set_dma_mask(intel_private.pcidev, DMA_BIT_MASK(36))) + dev_err(&intel_private.pcidev->dev, + "set gfx device dma mask 36bit failed!\n"); + else + pci_set_consistent_dma_mask(intel_private.pcidev, + DMA_BIT_MASK(36)); + } + + return 1; +} + static int __devinit agp_intel_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -2503,22 +871,18 @@ static int __devinit agp_intel_probe(struct pci_dev *pdev, if (!bridge) return -ENOMEM; + bridge->capndx = cap_ptr; + + if (intel_gmch_probe(pdev, bridge)) + goto found_gmch; + for (i = 0; intel_agp_chipsets[i].name != NULL; i++) { /* In case that multiple models of gfx chip may stand on same host bridge type, this can be sure we detect the right IGD. */ if (pdev->device == intel_agp_chipsets[i].chip_id) { - if ((intel_agp_chipsets[i].gmch_chip_id != 0) && - find_gmch(intel_agp_chipsets[i].gmch_chip_id)) { - bridge->driver = - intel_agp_chipsets[i].gmch_driver; - break; - } else if (intel_agp_chipsets[i].multi_gmch_chip) { - continue; - } else { - bridge->driver = intel_agp_chipsets[i].driver; - break; - } + bridge->driver = intel_agp_chipsets[i].driver; + break; } } @@ -2530,18 +894,16 @@ static int __devinit agp_intel_probe(struct pci_dev *pdev, return -ENODEV; } - if (bridge->driver == NULL) { - /* bridge has no AGP and no IGD detected */ + if (!bridge->driver) { if (cap_ptr) dev_warn(&pdev->dev, "can't find bridge device (chip_id: %04x)\n", - intel_agp_chipsets[i].gmch_chip_id); + intel_agp_chipsets[i].gmch_chip_id); agp_put_bridge(bridge); return -ENODEV; } bridge->dev = pdev; - bridge->capndx = cap_ptr; - bridge->dev_private_data = &intel_private; + bridge->dev_private_data = NULL; dev_info(&pdev->dev, "Intel %s Chipset\n", intel_agp_chipsets[i].name); @@ -2577,15 +939,7 @@ static int __devinit agp_intel_probe(struct pci_dev *pdev, &bridge->mode); } - if (bridge->driver->mask_memory == intel_i965_mask_memory) { - if (pci_set_dma_mask(intel_private.pcidev, DMA_BIT_MASK(36))) - dev_err(&intel_private.pcidev->dev, - "set gfx device dma mask 36bit failed!\n"); - else - pci_set_consistent_dma_mask(intel_private.pcidev, - DMA_BIT_MASK(36)); - } - +found_gmch: pci_set_drvdata(pdev, bridge); err = agp_add_bridge(bridge); if (!err) @@ -2611,22 +965,7 @@ static int agp_intel_resume(struct pci_dev *pdev) struct agp_bridge_data *bridge = pci_get_drvdata(pdev); int ret_val; - if (bridge->driver == &intel_generic_driver) - intel_configure(); - else if (bridge->driver == &intel_850_driver) - intel_850_configure(); - else if (bridge->driver == &intel_845_driver) - intel_845_configure(); - else if (bridge->driver == &intel_830mp_driver) - intel_830mp_configure(); - else if (bridge->driver == &intel_915_driver) - intel_i915_configure(); - else if (bridge->driver == &intel_830_driver) - intel_i830_configure(); - else if (bridge->driver == &intel_810_driver) - intel_i810_configure(); - else if (bridge->driver == &intel_i965_driver) - intel_i915_configure(); + bridge->driver->configure(); ret_val = agp_rebind_memory(); if (ret_val != 0) diff --git a/drivers/char/agp/intel-agp.h b/drivers/char/agp/intel-agp.h new file mode 100644 index 00000000000..2547465d465 --- /dev/null +++ b/drivers/char/agp/intel-agp.h @@ -0,0 +1,239 @@ +/* + * Common Intel AGPGART and GTT definitions. + */ + +/* Intel registers */ +#define INTEL_APSIZE 0xb4 +#define INTEL_ATTBASE 0xb8 +#define INTEL_AGPCTRL 0xb0 +#define INTEL_NBXCFG 0x50 +#define INTEL_ERRSTS 0x91 + +/* Intel i830 registers */ +#define I830_GMCH_CTRL 0x52 +#define I830_GMCH_ENABLED 0x4 +#define I830_GMCH_MEM_MASK 0x1 +#define I830_GMCH_MEM_64M 0x1 +#define I830_GMCH_MEM_128M 0 +#define I830_GMCH_GMS_MASK 0x70 +#define I830_GMCH_GMS_DISABLED 0x00 +#define I830_GMCH_GMS_LOCAL 0x10 +#define I830_GMCH_GMS_STOLEN_512 0x20 +#define I830_GMCH_GMS_STOLEN_1024 0x30 +#define I830_GMCH_GMS_STOLEN_8192 0x40 +#define I830_RDRAM_CHANNEL_TYPE 0x03010 +#define I830_RDRAM_ND(x) (((x) & 0x20) >> 5) +#define I830_RDRAM_DDT(x) (((x) & 0x18) >> 3) + +/* This one is for I830MP w. an external graphic card */ +#define INTEL_I830_ERRSTS 0x92 + +/* Intel 855GM/852GM registers */ +#define I855_GMCH_GMS_MASK 0xF0 +#define I855_GMCH_GMS_STOLEN_0M 0x0 +#define I855_GMCH_GMS_STOLEN_1M (0x1 << 4) +#define I855_GMCH_GMS_STOLEN_4M (0x2 << 4) +#define I855_GMCH_GMS_STOLEN_8M (0x3 << 4) +#define I855_GMCH_GMS_STOLEN_16M (0x4 << 4) +#define I855_GMCH_GMS_STOLEN_32M (0x5 << 4) +#define I85X_CAPID 0x44 +#define I85X_VARIANT_MASK 0x7 +#define I85X_VARIANT_SHIFT 5 +#define I855_GME 0x0 +#define I855_GM 0x4 +#define I852_GME 0x2 +#define I852_GM 0x5 + +/* Intel i845 registers */ +#define INTEL_I845_AGPM 0x51 +#define INTEL_I845_ERRSTS 0xc8 + +/* Intel i860 registers */ +#define INTEL_I860_MCHCFG 0x50 +#define INTEL_I860_ERRSTS 0xc8 + +/* Intel i810 registers */ +#define I810_GMADDR 0x10 +#define I810_MMADDR 0x14 +#define I810_PTE_BASE 0x10000 +#define I810_PTE_MAIN_UNCACHED 0x00000000 +#define I810_PTE_LOCAL 0x00000002 +#define I810_PTE_VALID 0x00000001 +#define I830_PTE_SYSTEM_CACHED 0x00000006 +#define I810_SMRAM_MISCC 0x70 +#define I810_GFX_MEM_WIN_SIZE 0x00010000 +#define I810_GFX_MEM_WIN_32M 0x00010000 +#define I810_GMS 0x000000c0 +#define I810_GMS_DISABLE 0x00000000 +#define I810_PGETBL_CTL 0x2020 +#define I810_PGETBL_ENABLED 0x00000001 +#define I965_PGETBL_SIZE_MASK 0x0000000e +#define I965_PGETBL_SIZE_512KB (0 << 1) +#define I965_PGETBL_SIZE_256KB (1 << 1) +#define I965_PGETBL_SIZE_128KB (2 << 1) +#define I965_PGETBL_SIZE_1MB (3 << 1) +#define I965_PGETBL_SIZE_2MB (4 << 1) +#define I965_PGETBL_SIZE_1_5MB (5 << 1) +#define G33_PGETBL_SIZE_MASK (3 << 8) +#define G33_PGETBL_SIZE_1M (1 << 8) +#define G33_PGETBL_SIZE_2M (2 << 8) + +#define I810_DRAM_CTL 0x3000 +#define I810_DRAM_ROW_0 0x00000001 +#define I810_DRAM_ROW_0_SDRAM 0x00000001 + +/* Intel 815 register */ +#define INTEL_815_APCONT 0x51 +#define INTEL_815_ATTBASE_MASK ~0x1FFFFFFF + +/* Intel i820 registers */ +#define INTEL_I820_RDCR 0x51 +#define INTEL_I820_ERRSTS 0xc8 + +/* Intel i840 registers */ +#define INTEL_I840_MCHCFG 0x50 +#define INTEL_I840_ERRSTS 0xc8 + +/* Intel i850 registers */ +#define INTEL_I850_MCHCFG 0x50 +#define INTEL_I850_ERRSTS 0xc8 + +/* intel 915G registers */ +#define I915_GMADDR 0x18 +#define I915_MMADDR 0x10 +#define I915_PTEADDR 0x1C +#define I915_GMCH_GMS_STOLEN_48M (0x6 << 4) +#define I915_GMCH_GMS_STOLEN_64M (0x7 << 4) +#define G33_GMCH_GMS_STOLEN_128M (0x8 << 4) +#define G33_GMCH_GMS_STOLEN_256M (0x9 << 4) +#define INTEL_GMCH_GMS_STOLEN_96M (0xa << 4) +#define INTEL_GMCH_GMS_STOLEN_160M (0xb << 4) +#define INTEL_GMCH_GMS_STOLEN_224M (0xc << 4) +#define INTEL_GMCH_GMS_STOLEN_352M (0xd << 4) + +#define I915_IFPADDR 0x60 + +/* Intel 965G registers */ +#define I965_MSAC 0x62 +#define I965_IFPADDR 0x70 + +/* Intel 7505 registers */ +#define INTEL_I7505_APSIZE 0x74 +#define INTEL_I7505_NCAPID 0x60 +#define INTEL_I7505_NISTAT 0x6c +#define INTEL_I7505_ATTBASE 0x78 +#define INTEL_I7505_ERRSTS 0x42 +#define INTEL_I7505_AGPCTRL 0x70 +#define INTEL_I7505_MCHCFG 0x50 + +#define SNB_GMCH_CTRL 0x50 +#define SNB_GMCH_GMS_STOLEN_MASK 0xF8 +#define SNB_GMCH_GMS_STOLEN_32M (1 << 3) +#define SNB_GMCH_GMS_STOLEN_64M (2 << 3) +#define SNB_GMCH_GMS_STOLEN_96M (3 << 3) +#define SNB_GMCH_GMS_STOLEN_128M (4 << 3) +#define SNB_GMCH_GMS_STOLEN_160M (5 << 3) +#define SNB_GMCH_GMS_STOLEN_192M (6 << 3) +#define SNB_GMCH_GMS_STOLEN_224M (7 << 3) +#define SNB_GMCH_GMS_STOLEN_256M (8 << 3) +#define SNB_GMCH_GMS_STOLEN_288M (9 << 3) +#define SNB_GMCH_GMS_STOLEN_320M (0xa << 3) +#define SNB_GMCH_GMS_STOLEN_352M (0xb << 3) +#define SNB_GMCH_GMS_STOLEN_384M (0xc << 3) +#define SNB_GMCH_GMS_STOLEN_416M (0xd << 3) +#define SNB_GMCH_GMS_STOLEN_448M (0xe << 3) +#define SNB_GMCH_GMS_STOLEN_480M (0xf << 3) +#define SNB_GMCH_GMS_STOLEN_512M (0x10 << 3) +#define SNB_GTT_SIZE_0M (0 << 8) +#define SNB_GTT_SIZE_1M (1 << 8) +#define SNB_GTT_SIZE_2M (2 << 8) +#define SNB_GTT_SIZE_MASK (3 << 8) + +/* pci devices ids */ +#define PCI_DEVICE_ID_INTEL_E7221_HB 0x2588 +#define PCI_DEVICE_ID_INTEL_E7221_IG 0x258a +#define PCI_DEVICE_ID_INTEL_82946GZ_HB 0x2970 +#define PCI_DEVICE_ID_INTEL_82946GZ_IG 0x2972 +#define PCI_DEVICE_ID_INTEL_82G35_HB 0x2980 +#define PCI_DEVICE_ID_INTEL_82G35_IG 0x2982 +#define PCI_DEVICE_ID_INTEL_82965Q_HB 0x2990 +#define PCI_DEVICE_ID_INTEL_82965Q_IG 0x2992 +#define PCI_DEVICE_ID_INTEL_82965G_HB 0x29A0 +#define PCI_DEVICE_ID_INTEL_82965G_IG 0x29A2 +#define PCI_DEVICE_ID_INTEL_82965GM_HB 0x2A00 +#define PCI_DEVICE_ID_INTEL_82965GM_IG 0x2A02 +#define PCI_DEVICE_ID_INTEL_82965GME_HB 0x2A10 +#define PCI_DEVICE_ID_INTEL_82965GME_IG 0x2A12 +#define PCI_DEVICE_ID_INTEL_82945GME_HB 0x27AC +#define PCI_DEVICE_ID_INTEL_82945GME_IG 0x27AE +#define PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB 0xA010 +#define PCI_DEVICE_ID_INTEL_PINEVIEW_M_IG 0xA011 +#define PCI_DEVICE_ID_INTEL_PINEVIEW_HB 0xA000 +#define PCI_DEVICE_ID_INTEL_PINEVIEW_IG 0xA001 +#define PCI_DEVICE_ID_INTEL_G33_HB 0x29C0 +#define PCI_DEVICE_ID_INTEL_G33_IG 0x29C2 +#define PCI_DEVICE_ID_INTEL_Q35_HB 0x29B0 +#define PCI_DEVICE_ID_INTEL_Q35_IG 0x29B2 +#define PCI_DEVICE_ID_INTEL_Q33_HB 0x29D0 +#define PCI_DEVICE_ID_INTEL_Q33_IG 0x29D2 +#define PCI_DEVICE_ID_INTEL_B43_HB 0x2E40 +#define PCI_DEVICE_ID_INTEL_B43_IG 0x2E42 +#define PCI_DEVICE_ID_INTEL_GM45_HB 0x2A40 +#define PCI_DEVICE_ID_INTEL_GM45_IG 0x2A42 +#define PCI_DEVICE_ID_INTEL_EAGLELAKE_HB 0x2E00 +#define PCI_DEVICE_ID_INTEL_EAGLELAKE_IG 0x2E02 +#define PCI_DEVICE_ID_INTEL_Q45_HB 0x2E10 +#define PCI_DEVICE_ID_INTEL_Q45_IG 0x2E12 +#define PCI_DEVICE_ID_INTEL_G45_HB 0x2E20 +#define PCI_DEVICE_ID_INTEL_G45_IG 0x2E22 +#define PCI_DEVICE_ID_INTEL_G41_HB 0x2E30 +#define PCI_DEVICE_ID_INTEL_G41_IG 0x2E32 +#define PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB 0x0040 +#define PCI_DEVICE_ID_INTEL_IRONLAKE_D_IG 0x0042 +#define PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB 0x0044 +#define PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB 0x0062 +#define PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB 0x006a +#define PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG 0x0046 +#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB 0x0100 +#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_IG 0x0102 +#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB 0x0104 +#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_IG 0x0106 + +/* cover 915 and 945 variants */ +#define IS_I915 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_E7221_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82915G_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82915GM_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82945G_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82945GM_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82945GME_HB) + +#define IS_I965 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82946GZ_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82G35_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965Q_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965G_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965GM_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965GME_HB) + +#define IS_G33 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G33_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q35_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q33_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_HB) + +#define IS_PINEVIEW (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_HB) + +#define IS_SNB (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB) + +#define IS_G4X (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_EAGLELAKE_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q45_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G45_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_GM45_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G41_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_B43_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB || \ + IS_SNB) diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c new file mode 100644 index 00000000000..e8ea6825822 --- /dev/null +++ b/drivers/char/agp/intel-gtt.c @@ -0,0 +1,1516 @@ +/* + * Intel GTT (Graphics Translation Table) routines + * + * Caveat: This driver implements the linux agp interface, but this is far from + * a agp driver! GTT support ended up here for purely historical reasons: The + * old userspace intel graphics drivers needed an interface to map memory into + * the GTT. And the drm provides a default interface for graphic devices sitting + * on an agp port. So it made sense to fake the GTT support as an agp port to + * avoid having to create a new api. + * + * With gem this does not make much sense anymore, just needlessly complicates + * the code. But as long as the old graphics stack is still support, it's stuck + * here. + * + * /fairy-tale-mode off + */ + +/* + * If we have Intel graphics, we're not going to have anything other than + * an Intel IOMMU. So make the correct use of the PCI DMA API contingent + * on the Intel IOMMU support (CONFIG_DMAR). + * Only newer chipsets need to bother with this, of course. + */ +#ifdef CONFIG_DMAR +#define USE_PCI_DMA_API 1 +#endif + +static const struct aper_size_info_fixed intel_i810_sizes[] = +{ + {64, 16384, 4}, + /* The 32M mode still requires a 64k gatt */ + {32, 8192, 4} +}; + +#define AGP_DCACHE_MEMORY 1 +#define AGP_PHYS_MEMORY 2 +#define INTEL_AGP_CACHED_MEMORY 3 + +static struct gatt_mask intel_i810_masks[] = +{ + {.mask = I810_PTE_VALID, .type = 0}, + {.mask = (I810_PTE_VALID | I810_PTE_LOCAL), .type = AGP_DCACHE_MEMORY}, + {.mask = I810_PTE_VALID, .type = 0}, + {.mask = I810_PTE_VALID | I830_PTE_SYSTEM_CACHED, + .type = INTEL_AGP_CACHED_MEMORY} +}; + +static struct _intel_private { + struct pci_dev *pcidev; /* device one */ + u8 __iomem *registers; + u32 __iomem *gtt; /* I915G */ + int num_dcache_entries; + /* gtt_entries is the number of gtt entries that are already mapped + * to stolen memory. Stolen memory is larger than the memory mapped + * through gtt_entries, as it includes some reserved space for the BIOS + * popup and for the GTT. + */ + int gtt_entries; /* i830+ */ + int gtt_total_size; + union { + void __iomem *i9xx_flush_page; + void *i8xx_flush_page; + }; + struct page *i8xx_page; + struct resource ifp_resource; + int resource_valid; +} intel_private; + +#ifdef USE_PCI_DMA_API +static int intel_agp_map_page(struct page *page, dma_addr_t *ret) +{ + *ret = pci_map_page(intel_private.pcidev, page, 0, + PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); + if (pci_dma_mapping_error(intel_private.pcidev, *ret)) + return -EINVAL; + return 0; +} + +static void intel_agp_unmap_page(struct page *page, dma_addr_t dma) +{ + pci_unmap_page(intel_private.pcidev, dma, + PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); +} + +static void intel_agp_free_sglist(struct agp_memory *mem) +{ + struct sg_table st; + + st.sgl = mem->sg_list; + st.orig_nents = st.nents = mem->page_count; + + sg_free_table(&st); + + mem->sg_list = NULL; + mem->num_sg = 0; +} + +static int intel_agp_map_memory(struct agp_memory *mem) +{ + struct sg_table st; + struct scatterlist *sg; + int i; + + DBG("try mapping %lu pages\n", (unsigned long)mem->page_count); + + if (sg_alloc_table(&st, mem->page_count, GFP_KERNEL)) + return -ENOMEM; + + mem->sg_list = sg = st.sgl; + + for (i = 0 ; i < mem->page_count; i++, sg = sg_next(sg)) + sg_set_page(sg, mem->pages[i], PAGE_SIZE, 0); + + mem->num_sg = pci_map_sg(intel_private.pcidev, mem->sg_list, + mem->page_count, PCI_DMA_BIDIRECTIONAL); + if (unlikely(!mem->num_sg)) { + intel_agp_free_sglist(mem); + return -ENOMEM; + } + return 0; +} + +static void intel_agp_unmap_memory(struct agp_memory *mem) +{ + DBG("try unmapping %lu pages\n", (unsigned long)mem->page_count); + + pci_unmap_sg(intel_private.pcidev, mem->sg_list, + mem->page_count, PCI_DMA_BIDIRECTIONAL); + intel_agp_free_sglist(mem); +} + +static void intel_agp_insert_sg_entries(struct agp_memory *mem, + off_t pg_start, int mask_type) +{ + struct scatterlist *sg; + int i, j; + + j = pg_start; + + WARN_ON(!mem->num_sg); + + if (mem->num_sg == mem->page_count) { + for_each_sg(mem->sg_list, sg, mem->page_count, i) { + writel(agp_bridge->driver->mask_memory(agp_bridge, + sg_dma_address(sg), mask_type), + intel_private.gtt+j); + j++; + } + } else { + /* sg may merge pages, but we have to separate + * per-page addr for GTT */ + unsigned int len, m; + + for_each_sg(mem->sg_list, sg, mem->num_sg, i) { + len = sg_dma_len(sg) / PAGE_SIZE; + for (m = 0; m < len; m++) { + writel(agp_bridge->driver->mask_memory(agp_bridge, + sg_dma_address(sg) + m * PAGE_SIZE, + mask_type), + intel_private.gtt+j); + j++; + } + } + } + readl(intel_private.gtt+j-1); +} + +#else + +static void intel_agp_insert_sg_entries(struct agp_memory *mem, + off_t pg_start, int mask_type) +{ + int i, j; + u32 cache_bits = 0; + + if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB || + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB) + { + cache_bits = I830_PTE_SYSTEM_CACHED; + } + + for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { + writel(agp_bridge->driver->mask_memory(agp_bridge, + page_to_phys(mem->pages[i]), mask_type), + intel_private.gtt+j); + } + + readl(intel_private.gtt+j-1); +} + +#endif + +static int intel_i810_fetch_size(void) +{ + u32 smram_miscc; + struct aper_size_info_fixed *values; + + pci_read_config_dword(agp_bridge->dev, I810_SMRAM_MISCC, &smram_miscc); + values = A_SIZE_FIX(agp_bridge->driver->aperture_sizes); + + if ((smram_miscc & I810_GMS) == I810_GMS_DISABLE) { + dev_warn(&agp_bridge->dev->dev, "i810 is disabled\n"); + return 0; + } + if ((smram_miscc & I810_GFX_MEM_WIN_SIZE) == I810_GFX_MEM_WIN_32M) { + agp_bridge->current_size = (void *) (values + 1); + agp_bridge->aperture_size_idx = 1; + return values[1].size; + } else { + agp_bridge->current_size = (void *) (values); + agp_bridge->aperture_size_idx = 0; + return values[0].size; + } + + return 0; +} + +static int intel_i810_configure(void) +{ + struct aper_size_info_fixed *current_size; + u32 temp; + int i; + + current_size = A_SIZE_FIX(agp_bridge->current_size); + + if (!intel_private.registers) { + pci_read_config_dword(intel_private.pcidev, I810_MMADDR, &temp); + temp &= 0xfff80000; + + intel_private.registers = ioremap(temp, 128 * 4096); + if (!intel_private.registers) { + dev_err(&intel_private.pcidev->dev, + "can't remap memory\n"); + return -ENOMEM; + } + } + + if ((readl(intel_private.registers+I810_DRAM_CTL) + & I810_DRAM_ROW_0) == I810_DRAM_ROW_0_SDRAM) { + /* This will need to be dynamically assigned */ + dev_info(&intel_private.pcidev->dev, + "detected 4MB dedicated video ram\n"); + intel_private.num_dcache_entries = 1024; + } + pci_read_config_dword(intel_private.pcidev, I810_GMADDR, &temp); + agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + writel(agp_bridge->gatt_bus_addr | I810_PGETBL_ENABLED, intel_private.registers+I810_PGETBL_CTL); + readl(intel_private.registers+I810_PGETBL_CTL); /* PCI Posting. */ + + if (agp_bridge->driver->needs_scratch_page) { + for (i = 0; i < current_size->num_entries; i++) { + writel(agp_bridge->scratch_page, intel_private.registers+I810_PTE_BASE+(i*4)); + } + readl(intel_private.registers+I810_PTE_BASE+((i-1)*4)); /* PCI posting. */ + } + global_cache_flush(); + return 0; +} + +static void intel_i810_cleanup(void) +{ + writel(0, intel_private.registers+I810_PGETBL_CTL); + readl(intel_private.registers); /* PCI Posting. */ + iounmap(intel_private.registers); +} + +static void intel_i810_agp_enable(struct agp_bridge_data *bridge, u32 mode) +{ + return; +} + +/* Exists to support ARGB cursors */ +static struct page *i8xx_alloc_pages(void) +{ + struct page *page; + + page = alloc_pages(GFP_KERNEL | GFP_DMA32, 2); + if (page == NULL) + return NULL; + + if (set_pages_uc(page, 4) < 0) { + set_pages_wb(page, 4); + __free_pages(page, 2); + return NULL; + } + get_page(page); + atomic_inc(&agp_bridge->current_memory_agp); + return page; +} + +static void i8xx_destroy_pages(struct page *page) +{ + if (page == NULL) + return; + + set_pages_wb(page, 4); + put_page(page); + __free_pages(page, 2); + atomic_dec(&agp_bridge->current_memory_agp); +} + +static int intel_i830_type_to_mask_type(struct agp_bridge_data *bridge, + int type) +{ + if (type < AGP_USER_TYPES) + return type; + else if (type == AGP_USER_CACHED_MEMORY) + return INTEL_AGP_CACHED_MEMORY; + else + return 0; +} + +static int intel_i810_insert_entries(struct agp_memory *mem, off_t pg_start, + int type) +{ + int i, j, num_entries; + void *temp; + int ret = -EINVAL; + int mask_type; + + if (mem->page_count == 0) + goto out; + + temp = agp_bridge->current_size; + num_entries = A_SIZE_FIX(temp)->num_entries; + + if ((pg_start + mem->page_count) > num_entries) + goto out_err; + + + for (j = pg_start; j < (pg_start + mem->page_count); j++) { + if (!PGE_EMPTY(agp_bridge, readl(agp_bridge->gatt_table+j))) { + ret = -EBUSY; + goto out_err; + } + } + + if (type != mem->type) + goto out_err; + + mask_type = agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type); + + switch (mask_type) { + case AGP_DCACHE_MEMORY: + if (!mem->is_flushed) + global_cache_flush(); + for (i = pg_start; i < (pg_start + mem->page_count); i++) { + writel((i*4096)|I810_PTE_LOCAL|I810_PTE_VALID, + intel_private.registers+I810_PTE_BASE+(i*4)); + } + readl(intel_private.registers+I810_PTE_BASE+((i-1)*4)); + break; + case AGP_PHYS_MEMORY: + case AGP_NORMAL_MEMORY: + if (!mem->is_flushed) + global_cache_flush(); + for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { + writel(agp_bridge->driver->mask_memory(agp_bridge, + page_to_phys(mem->pages[i]), mask_type), + intel_private.registers+I810_PTE_BASE+(j*4)); + } + readl(intel_private.registers+I810_PTE_BASE+((j-1)*4)); + break; + default: + goto out_err; + } + +out: + ret = 0; +out_err: + mem->is_flushed = true; + return ret; +} + +static int intel_i810_remove_entries(struct agp_memory *mem, off_t pg_start, + int type) +{ + int i; + + if (mem->page_count == 0) + return 0; + + for (i = pg_start; i < (mem->page_count + pg_start); i++) { + writel(agp_bridge->scratch_page, intel_private.registers+I810_PTE_BASE+(i*4)); + } + readl(intel_private.registers+I810_PTE_BASE+((i-1)*4)); + + return 0; +} + +/* + * The i810/i830 requires a physical address to program its mouse + * pointer into hardware. + * However the Xserver still writes to it through the agp aperture. + */ +static struct agp_memory *alloc_agpphysmem_i8xx(size_t pg_count, int type) +{ + struct agp_memory *new; + struct page *page; + + switch (pg_count) { + case 1: page = agp_bridge->driver->agp_alloc_page(agp_bridge); + break; + case 4: + /* kludge to get 4 physical pages for ARGB cursor */ + page = i8xx_alloc_pages(); + break; + default: + return NULL; + } + + if (page == NULL) + return NULL; + + new = agp_create_memory(pg_count); + if (new == NULL) + return NULL; + + new->pages[0] = page; + if (pg_count == 4) { + /* kludge to get 4 physical pages for ARGB cursor */ + new->pages[1] = new->pages[0] + 1; + new->pages[2] = new->pages[1] + 1; + new->pages[3] = new->pages[2] + 1; + } + new->page_count = pg_count; + new->num_scratch_pages = pg_count; + new->type = AGP_PHYS_MEMORY; + new->physical = page_to_phys(new->pages[0]); + return new; +} + +static struct agp_memory *intel_i810_alloc_by_type(size_t pg_count, int type) +{ + struct agp_memory *new; + + if (type == AGP_DCACHE_MEMORY) { + if (pg_count != intel_private.num_dcache_entries) + return NULL; + + new = agp_create_memory(1); + if (new == NULL) + return NULL; + + new->type = AGP_DCACHE_MEMORY; + new->page_count = pg_count; + new->num_scratch_pages = 0; + agp_free_page_array(new); + return new; + } + if (type == AGP_PHYS_MEMORY) + return alloc_agpphysmem_i8xx(pg_count, type); + return NULL; +} + +static void intel_i810_free_by_type(struct agp_memory *curr) +{ + agp_free_key(curr->key); + if (curr->type == AGP_PHYS_MEMORY) { + if (curr->page_count == 4) + i8xx_destroy_pages(curr->pages[0]); + else { + agp_bridge->driver->agp_destroy_page(curr->pages[0], + AGP_PAGE_DESTROY_UNMAP); + agp_bridge->driver->agp_destroy_page(curr->pages[0], + AGP_PAGE_DESTROY_FREE); + } + agp_free_page_array(curr); + } + kfree(curr); +} + +static unsigned long intel_i810_mask_memory(struct agp_bridge_data *bridge, + dma_addr_t addr, int type) +{ + /* Type checking must be done elsewhere */ + return addr | bridge->driver->masks[type].mask; +} + +static struct aper_size_info_fixed intel_i830_sizes[] = +{ + {128, 32768, 5}, + /* The 64M mode still requires a 128k gatt */ + {64, 16384, 5}, + {256, 65536, 6}, + {512, 131072, 7}, +}; + +static void intel_i830_init_gtt_entries(void) +{ + u16 gmch_ctrl; + int gtt_entries = 0; + u8 rdct; + int local = 0; + static const int ddt[4] = { 0, 16, 32, 64 }; + int size; /* reserved space (in kb) at the top of stolen memory */ + + pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl); + + if (IS_I965) { + u32 pgetbl_ctl; + pgetbl_ctl = readl(intel_private.registers+I810_PGETBL_CTL); + + /* The 965 has a field telling us the size of the GTT, + * which may be larger than what is necessary to map the + * aperture. + */ + switch (pgetbl_ctl & I965_PGETBL_SIZE_MASK) { + case I965_PGETBL_SIZE_128KB: + size = 128; + break; + case I965_PGETBL_SIZE_256KB: + size = 256; + break; + case I965_PGETBL_SIZE_512KB: + size = 512; + break; + case I965_PGETBL_SIZE_1MB: + size = 1024; + break; + case I965_PGETBL_SIZE_2MB: + size = 2048; + break; + case I965_PGETBL_SIZE_1_5MB: + size = 1024 + 512; + break; + default: + dev_info(&intel_private.pcidev->dev, + "unknown page table size, assuming 512KB\n"); + size = 512; + } + size += 4; /* add in BIOS popup space */ + } else if (IS_G33 && !IS_PINEVIEW) { + /* G33's GTT size defined in gmch_ctrl */ + switch (gmch_ctrl & G33_PGETBL_SIZE_MASK) { + case G33_PGETBL_SIZE_1M: + size = 1024; + break; + case G33_PGETBL_SIZE_2M: + size = 2048; + break; + default: + dev_info(&agp_bridge->dev->dev, + "unknown page table size 0x%x, assuming 512KB\n", + (gmch_ctrl & G33_PGETBL_SIZE_MASK)); + size = 512; + } + size += 4; + } else if (IS_G4X || IS_PINEVIEW) { + /* On 4 series hardware, GTT stolen is separate from graphics + * stolen, ignore it in stolen gtt entries counting. However, + * 4KB of the stolen memory doesn't get mapped to the GTT. + */ + size = 4; + } else { + /* On previous hardware, the GTT size was just what was + * required to map the aperture. + */ + size = agp_bridge->driver->fetch_size() + 4; + } + + if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82830_HB || + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82845G_HB) { + switch (gmch_ctrl & I830_GMCH_GMS_MASK) { + case I830_GMCH_GMS_STOLEN_512: + gtt_entries = KB(512) - KB(size); + break; + case I830_GMCH_GMS_STOLEN_1024: + gtt_entries = MB(1) - KB(size); + break; + case I830_GMCH_GMS_STOLEN_8192: + gtt_entries = MB(8) - KB(size); + break; + case I830_GMCH_GMS_LOCAL: + rdct = readb(intel_private.registers+I830_RDRAM_CHANNEL_TYPE); + gtt_entries = (I830_RDRAM_ND(rdct) + 1) * + MB(ddt[I830_RDRAM_DDT(rdct)]); + local = 1; + break; + default: + gtt_entries = 0; + break; + } + } else if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB || + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB) { + /* + * SandyBridge has new memory control reg at 0x50.w + */ + u16 snb_gmch_ctl; + pci_read_config_word(intel_private.pcidev, SNB_GMCH_CTRL, &snb_gmch_ctl); + switch (snb_gmch_ctl & SNB_GMCH_GMS_STOLEN_MASK) { + case SNB_GMCH_GMS_STOLEN_32M: + gtt_entries = MB(32) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_64M: + gtt_entries = MB(64) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_96M: + gtt_entries = MB(96) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_128M: + gtt_entries = MB(128) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_160M: + gtt_entries = MB(160) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_192M: + gtt_entries = MB(192) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_224M: + gtt_entries = MB(224) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_256M: + gtt_entries = MB(256) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_288M: + gtt_entries = MB(288) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_320M: + gtt_entries = MB(320) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_352M: + gtt_entries = MB(352) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_384M: + gtt_entries = MB(384) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_416M: + gtt_entries = MB(416) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_448M: + gtt_entries = MB(448) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_480M: + gtt_entries = MB(480) - KB(size); + break; + case SNB_GMCH_GMS_STOLEN_512M: + gtt_entries = MB(512) - KB(size); + break; + } + } else { + switch (gmch_ctrl & I855_GMCH_GMS_MASK) { + case I855_GMCH_GMS_STOLEN_1M: + gtt_entries = MB(1) - KB(size); + break; + case I855_GMCH_GMS_STOLEN_4M: + gtt_entries = MB(4) - KB(size); + break; + case I855_GMCH_GMS_STOLEN_8M: + gtt_entries = MB(8) - KB(size); + break; + case I855_GMCH_GMS_STOLEN_16M: + gtt_entries = MB(16) - KB(size); + break; + case I855_GMCH_GMS_STOLEN_32M: + gtt_entries = MB(32) - KB(size); + break; + case I915_GMCH_GMS_STOLEN_48M: + /* Check it's really I915G */ + if (IS_I915 || IS_I965 || IS_G33 || IS_G4X) + gtt_entries = MB(48) - KB(size); + else + gtt_entries = 0; + break; + case I915_GMCH_GMS_STOLEN_64M: + /* Check it's really I915G */ + if (IS_I915 || IS_I965 || IS_G33 || IS_G4X) + gtt_entries = MB(64) - KB(size); + else + gtt_entries = 0; + break; + case G33_GMCH_GMS_STOLEN_128M: + if (IS_G33 || IS_I965 || IS_G4X) + gtt_entries = MB(128) - KB(size); + else + gtt_entries = 0; + break; + case G33_GMCH_GMS_STOLEN_256M: + if (IS_G33 || IS_I965 || IS_G4X) + gtt_entries = MB(256) - KB(size); + else + gtt_entries = 0; + break; + case INTEL_GMCH_GMS_STOLEN_96M: + if (IS_I965 || IS_G4X) + gtt_entries = MB(96) - KB(size); + else + gtt_entries = 0; + break; + case INTEL_GMCH_GMS_STOLEN_160M: + if (IS_I965 || IS_G4X) + gtt_entries = MB(160) - KB(size); + else + gtt_entries = 0; + break; + case INTEL_GMCH_GMS_STOLEN_224M: + if (IS_I965 || IS_G4X) + gtt_entries = MB(224) - KB(size); + else + gtt_entries = 0; + break; + case INTEL_GMCH_GMS_STOLEN_352M: + if (IS_I965 || IS_G4X) + gtt_entries = MB(352) - KB(size); + else + gtt_entries = 0; + break; + default: + gtt_entries = 0; + break; + } + } + if (gtt_entries > 0) { + dev_info(&agp_bridge->dev->dev, "detected %dK %s memory\n", + gtt_entries / KB(1), local ? "local" : "stolen"); + gtt_entries /= KB(4); + } else { + dev_info(&agp_bridge->dev->dev, + "no pre-allocated video memory detected\n"); + gtt_entries = 0; + } + + intel_private.gtt_entries = gtt_entries; +} + +static void intel_i830_fini_flush(void) +{ + kunmap(intel_private.i8xx_page); + intel_private.i8xx_flush_page = NULL; + unmap_page_from_agp(intel_private.i8xx_page); + + __free_page(intel_private.i8xx_page); + intel_private.i8xx_page = NULL; +} + +static void intel_i830_setup_flush(void) +{ + /* return if we've already set the flush mechanism up */ + if (intel_private.i8xx_page) + return; + + intel_private.i8xx_page = alloc_page(GFP_KERNEL | __GFP_ZERO | GFP_DMA32); + if (!intel_private.i8xx_page) + return; + + intel_private.i8xx_flush_page = kmap(intel_private.i8xx_page); + if (!intel_private.i8xx_flush_page) + intel_i830_fini_flush(); +} + +/* The chipset_flush interface needs to get data that has already been + * flushed out of the CPU all the way out to main memory, because the GPU + * doesn't snoop those buffers. + * + * The 8xx series doesn't have the same lovely interface for flushing the + * chipset write buffers that the later chips do. According to the 865 + * specs, it's 64 octwords, or 1KB. So, to get those previous things in + * that buffer out, we just fill 1KB and clflush it out, on the assumption + * that it'll push whatever was in there out. It appears to work. + */ +static void intel_i830_chipset_flush(struct agp_bridge_data *bridge) +{ + unsigned int *pg = intel_private.i8xx_flush_page; + + memset(pg, 0, 1024); + + if (cpu_has_clflush) + clflush_cache_range(pg, 1024); + else if (wbinvd_on_all_cpus() != 0) + printk(KERN_ERR "Timed out waiting for cache flush.\n"); +} + +/* The intel i830 automatically initializes the agp aperture during POST. + * Use the memory already set aside for in the GTT. + */ +static int intel_i830_create_gatt_table(struct agp_bridge_data *bridge) +{ + int page_order; + struct aper_size_info_fixed *size; + int num_entries; + u32 temp; + + size = agp_bridge->current_size; + page_order = size->page_order; + num_entries = size->num_entries; + agp_bridge->gatt_table_real = NULL; + + pci_read_config_dword(intel_private.pcidev, I810_MMADDR, &temp); + temp &= 0xfff80000; + + intel_private.registers = ioremap(temp, 128 * 4096); + if (!intel_private.registers) + return -ENOMEM; + + temp = readl(intel_private.registers+I810_PGETBL_CTL) & 0xfffff000; + global_cache_flush(); /* FIXME: ?? */ + + /* we have to call this as early as possible after the MMIO base address is known */ + intel_i830_init_gtt_entries(); + + agp_bridge->gatt_table = NULL; + + agp_bridge->gatt_bus_addr = temp; + + return 0; +} + +/* Return the gatt table to a sane state. Use the top of stolen + * memory for the GTT. + */ +static int intel_i830_free_gatt_table(struct agp_bridge_data *bridge) +{ + return 0; +} + +static int intel_i830_fetch_size(void) +{ + u16 gmch_ctrl; + struct aper_size_info_fixed *values; + + values = A_SIZE_FIX(agp_bridge->driver->aperture_sizes); + + if (agp_bridge->dev->device != PCI_DEVICE_ID_INTEL_82830_HB && + agp_bridge->dev->device != PCI_DEVICE_ID_INTEL_82845G_HB) { + /* 855GM/852GM/865G has 128MB aperture size */ + agp_bridge->current_size = (void *) values; + agp_bridge->aperture_size_idx = 0; + return values[0].size; + } + + pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl); + + if ((gmch_ctrl & I830_GMCH_MEM_MASK) == I830_GMCH_MEM_128M) { + agp_bridge->current_size = (void *) values; + agp_bridge->aperture_size_idx = 0; + return values[0].size; + } else { + agp_bridge->current_size = (void *) (values + 1); + agp_bridge->aperture_size_idx = 1; + return values[1].size; + } + + return 0; +} + +static int intel_i830_configure(void) +{ + struct aper_size_info_fixed *current_size; + u32 temp; + u16 gmch_ctrl; + int i; + + current_size = A_SIZE_FIX(agp_bridge->current_size); + + pci_read_config_dword(intel_private.pcidev, I810_GMADDR, &temp); + agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + + pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl); + gmch_ctrl |= I830_GMCH_ENABLED; + pci_write_config_word(agp_bridge->dev, I830_GMCH_CTRL, gmch_ctrl); + + writel(agp_bridge->gatt_bus_addr|I810_PGETBL_ENABLED, intel_private.registers+I810_PGETBL_CTL); + readl(intel_private.registers+I810_PGETBL_CTL); /* PCI Posting. */ + + if (agp_bridge->driver->needs_scratch_page) { + for (i = intel_private.gtt_entries; i < current_size->num_entries; i++) { + writel(agp_bridge->scratch_page, intel_private.registers+I810_PTE_BASE+(i*4)); + } + readl(intel_private.registers+I810_PTE_BASE+((i-1)*4)); /* PCI Posting. */ + } + + global_cache_flush(); + + intel_i830_setup_flush(); + return 0; +} + +static void intel_i830_cleanup(void) +{ + iounmap(intel_private.registers); +} + +static int intel_i830_insert_entries(struct agp_memory *mem, off_t pg_start, + int type) +{ + int i, j, num_entries; + void *temp; + int ret = -EINVAL; + int mask_type; + + if (mem->page_count == 0) + goto out; + + temp = agp_bridge->current_size; + num_entries = A_SIZE_FIX(temp)->num_entries; + + if (pg_start < intel_private.gtt_entries) { + dev_printk(KERN_DEBUG, &intel_private.pcidev->dev, + "pg_start == 0x%.8lx, intel_private.gtt_entries == 0x%.8x\n", + pg_start, intel_private.gtt_entries); + + dev_info(&intel_private.pcidev->dev, + "trying to insert into local/stolen memory\n"); + goto out_err; + } + + if ((pg_start + mem->page_count) > num_entries) + goto out_err; + + /* The i830 can't check the GTT for entries since its read only, + * depend on the caller to make the correct offset decisions. + */ + + if (type != mem->type) + goto out_err; + + mask_type = agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type); + + if (mask_type != 0 && mask_type != AGP_PHYS_MEMORY && + mask_type != INTEL_AGP_CACHED_MEMORY) + goto out_err; + + if (!mem->is_flushed) + global_cache_flush(); + + for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { + writel(agp_bridge->driver->mask_memory(agp_bridge, + page_to_phys(mem->pages[i]), mask_type), + intel_private.registers+I810_PTE_BASE+(j*4)); + } + readl(intel_private.registers+I810_PTE_BASE+((j-1)*4)); + +out: + ret = 0; +out_err: + mem->is_flushed = true; + return ret; +} + +static int intel_i830_remove_entries(struct agp_memory *mem, off_t pg_start, + int type) +{ + int i; + + if (mem->page_count == 0) + return 0; + + if (pg_start < intel_private.gtt_entries) { + dev_info(&intel_private.pcidev->dev, + "trying to disable local/stolen memory\n"); + return -EINVAL; + } + + for (i = pg_start; i < (mem->page_count + pg_start); i++) { + writel(agp_bridge->scratch_page, intel_private.registers+I810_PTE_BASE+(i*4)); + } + readl(intel_private.registers+I810_PTE_BASE+((i-1)*4)); + + return 0; +} + +static struct agp_memory *intel_i830_alloc_by_type(size_t pg_count, int type) +{ + if (type == AGP_PHYS_MEMORY) + return alloc_agpphysmem_i8xx(pg_count, type); + /* always return NULL for other allocation types for now */ + return NULL; +} + +static int intel_alloc_chipset_flush_resource(void) +{ + int ret; + ret = pci_bus_alloc_resource(agp_bridge->dev->bus, &intel_private.ifp_resource, PAGE_SIZE, + PAGE_SIZE, PCIBIOS_MIN_MEM, 0, + pcibios_align_resource, agp_bridge->dev); + + return ret; +} + +static void intel_i915_setup_chipset_flush(void) +{ + int ret; + u32 temp; + + pci_read_config_dword(agp_bridge->dev, I915_IFPADDR, &temp); + if (!(temp & 0x1)) { + intel_alloc_chipset_flush_resource(); + intel_private.resource_valid = 1; + pci_write_config_dword(agp_bridge->dev, I915_IFPADDR, (intel_private.ifp_resource.start & 0xffffffff) | 0x1); + } else { + temp &= ~1; + + intel_private.resource_valid = 1; + intel_private.ifp_resource.start = temp; + intel_private.ifp_resource.end = temp + PAGE_SIZE; + ret = request_resource(&iomem_resource, &intel_private.ifp_resource); + /* some BIOSes reserve this area in a pnp some don't */ + if (ret) + intel_private.resource_valid = 0; + } +} + +static void intel_i965_g33_setup_chipset_flush(void) +{ + u32 temp_hi, temp_lo; + int ret; + + pci_read_config_dword(agp_bridge->dev, I965_IFPADDR + 4, &temp_hi); + pci_read_config_dword(agp_bridge->dev, I965_IFPADDR, &temp_lo); + + if (!(temp_lo & 0x1)) { + + intel_alloc_chipset_flush_resource(); + + intel_private.resource_valid = 1; + pci_write_config_dword(agp_bridge->dev, I965_IFPADDR + 4, + upper_32_bits(intel_private.ifp_resource.start)); + pci_write_config_dword(agp_bridge->dev, I965_IFPADDR, (intel_private.ifp_resource.start & 0xffffffff) | 0x1); + } else { + u64 l64; + + temp_lo &= ~0x1; + l64 = ((u64)temp_hi << 32) | temp_lo; + + intel_private.resource_valid = 1; + intel_private.ifp_resource.start = l64; + intel_private.ifp_resource.end = l64 + PAGE_SIZE; + ret = request_resource(&iomem_resource, &intel_private.ifp_resource); + /* some BIOSes reserve this area in a pnp some don't */ + if (ret) + intel_private.resource_valid = 0; + } +} + +static void intel_i9xx_setup_flush(void) +{ + /* return if already configured */ + if (intel_private.ifp_resource.start) + return; + + if (IS_SNB) + return; + + /* setup a resource for this object */ + intel_private.ifp_resource.name = "Intel Flush Page"; + intel_private.ifp_resource.flags = IORESOURCE_MEM; + + /* Setup chipset flush for 915 */ + if (IS_I965 || IS_G33 || IS_G4X) { + intel_i965_g33_setup_chipset_flush(); + } else { + intel_i915_setup_chipset_flush(); + } + + if (intel_private.ifp_resource.start) { + intel_private.i9xx_flush_page = ioremap_nocache(intel_private.ifp_resource.start, PAGE_SIZE); + if (!intel_private.i9xx_flush_page) + dev_info(&intel_private.pcidev->dev, "can't ioremap flush page - no chipset flushing"); + } +} + +static int intel_i915_configure(void) +{ + struct aper_size_info_fixed *current_size; + u32 temp; + u16 gmch_ctrl; + int i; + + current_size = A_SIZE_FIX(agp_bridge->current_size); + + pci_read_config_dword(intel_private.pcidev, I915_GMADDR, &temp); + + agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + + pci_read_config_word(agp_bridge->dev, I830_GMCH_CTRL, &gmch_ctrl); + gmch_ctrl |= I830_GMCH_ENABLED; + pci_write_config_word(agp_bridge->dev, I830_GMCH_CTRL, gmch_ctrl); + + writel(agp_bridge->gatt_bus_addr|I810_PGETBL_ENABLED, intel_private.registers+I810_PGETBL_CTL); + readl(intel_private.registers+I810_PGETBL_CTL); /* PCI Posting. */ + + if (agp_bridge->driver->needs_scratch_page) { + for (i = intel_private.gtt_entries; i < intel_private.gtt_total_size; i++) { + writel(agp_bridge->scratch_page, intel_private.gtt+i); + } + readl(intel_private.gtt+i-1); /* PCI Posting. */ + } + + global_cache_flush(); + + intel_i9xx_setup_flush(); + + return 0; +} + +static void intel_i915_cleanup(void) +{ + if (intel_private.i9xx_flush_page) + iounmap(intel_private.i9xx_flush_page); + if (intel_private.resource_valid) + release_resource(&intel_private.ifp_resource); + intel_private.ifp_resource.start = 0; + intel_private.resource_valid = 0; + iounmap(intel_private.gtt); + iounmap(intel_private.registers); +} + +static void intel_i915_chipset_flush(struct agp_bridge_data *bridge) +{ + if (intel_private.i9xx_flush_page) + writel(1, intel_private.i9xx_flush_page); +} + +static int intel_i915_insert_entries(struct agp_memory *mem, off_t pg_start, + int type) +{ + int num_entries; + void *temp; + int ret = -EINVAL; + int mask_type; + + if (mem->page_count == 0) + goto out; + + temp = agp_bridge->current_size; + num_entries = A_SIZE_FIX(temp)->num_entries; + + if (pg_start < intel_private.gtt_entries) { + dev_printk(KERN_DEBUG, &intel_private.pcidev->dev, + "pg_start == 0x%.8lx, intel_private.gtt_entries == 0x%.8x\n", + pg_start, intel_private.gtt_entries); + + dev_info(&intel_private.pcidev->dev, + "trying to insert into local/stolen memory\n"); + goto out_err; + } + + if ((pg_start + mem->page_count) > num_entries) + goto out_err; + + /* The i915 can't check the GTT for entries since it's read only; + * depend on the caller to make the correct offset decisions. + */ + + if (type != mem->type) + goto out_err; + + mask_type = agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type); + + if (mask_type != 0 && mask_type != AGP_PHYS_MEMORY && + mask_type != INTEL_AGP_CACHED_MEMORY) + goto out_err; + + if (!mem->is_flushed) + global_cache_flush(); + + intel_agp_insert_sg_entries(mem, pg_start, mask_type); + + out: + ret = 0; + out_err: + mem->is_flushed = true; + return ret; +} + +static int intel_i915_remove_entries(struct agp_memory *mem, off_t pg_start, + int type) +{ + int i; + + if (mem->page_count == 0) + return 0; + + if (pg_start < intel_private.gtt_entries) { + dev_info(&intel_private.pcidev->dev, + "trying to disable local/stolen memory\n"); + return -EINVAL; + } + + for (i = pg_start; i < (mem->page_count + pg_start); i++) + writel(agp_bridge->scratch_page, intel_private.gtt+i); + + readl(intel_private.gtt+i-1); + + return 0; +} + +/* Return the aperture size by just checking the resource length. The effect + * described in the spec of the MSAC registers is just changing of the + * resource size. + */ +static int intel_i9xx_fetch_size(void) +{ + int num_sizes = ARRAY_SIZE(intel_i830_sizes); + int aper_size; /* size in megabytes */ + int i; + + aper_size = pci_resource_len(intel_private.pcidev, 2) / MB(1); + + for (i = 0; i < num_sizes; i++) { + if (aper_size == intel_i830_sizes[i].size) { + agp_bridge->current_size = intel_i830_sizes + i; + return aper_size; + } + } + + return 0; +} + +/* The intel i915 automatically initializes the agp aperture during POST. + * Use the memory already set aside for in the GTT. + */ +static int intel_i915_create_gatt_table(struct agp_bridge_data *bridge) +{ + int page_order; + struct aper_size_info_fixed *size; + int num_entries; + u32 temp, temp2; + int gtt_map_size = 256 * 1024; + + size = agp_bridge->current_size; + page_order = size->page_order; + num_entries = size->num_entries; + agp_bridge->gatt_table_real = NULL; + + pci_read_config_dword(intel_private.pcidev, I915_MMADDR, &temp); + pci_read_config_dword(intel_private.pcidev, I915_PTEADDR, &temp2); + + if (IS_G33) + gtt_map_size = 1024 * 1024; /* 1M on G33 */ + intel_private.gtt = ioremap(temp2, gtt_map_size); + if (!intel_private.gtt) + return -ENOMEM; + + intel_private.gtt_total_size = gtt_map_size / 4; + + temp &= 0xfff80000; + + intel_private.registers = ioremap(temp, 128 * 4096); + if (!intel_private.registers) { + iounmap(intel_private.gtt); + return -ENOMEM; + } + + temp = readl(intel_private.registers+I810_PGETBL_CTL) & 0xfffff000; + global_cache_flush(); /* FIXME: ? */ + + /* we have to call this as early as possible after the MMIO base address is known */ + intel_i830_init_gtt_entries(); + + agp_bridge->gatt_table = NULL; + + agp_bridge->gatt_bus_addr = temp; + + return 0; +} + +/* + * The i965 supports 36-bit physical addresses, but to keep + * the format of the GTT the same, the bits that don't fit + * in a 32-bit word are shifted down to bits 4..7. + * + * Gcc is smart enough to notice that "(addr >> 28) & 0xf0" + * is always zero on 32-bit architectures, so no need to make + * this conditional. + */ +static unsigned long intel_i965_mask_memory(struct agp_bridge_data *bridge, + dma_addr_t addr, int type) +{ + /* Shift high bits down */ + addr |= (addr >> 28) & 0xf0; + + /* Type checking must be done elsewhere */ + return addr | bridge->driver->masks[type].mask; +} + +static void intel_i965_get_gtt_range(int *gtt_offset, int *gtt_size) +{ + u16 snb_gmch_ctl; + + switch (agp_bridge->dev->device) { + case PCI_DEVICE_ID_INTEL_GM45_HB: + case PCI_DEVICE_ID_INTEL_EAGLELAKE_HB: + case PCI_DEVICE_ID_INTEL_Q45_HB: + case PCI_DEVICE_ID_INTEL_G45_HB: + case PCI_DEVICE_ID_INTEL_G41_HB: + case PCI_DEVICE_ID_INTEL_B43_HB: + case PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB: + case PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB: + case PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB: + case PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB: + *gtt_offset = *gtt_size = MB(2); + break; + case PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB: + case PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB: + *gtt_offset = MB(2); + + pci_read_config_word(intel_private.pcidev, SNB_GMCH_CTRL, &snb_gmch_ctl); + switch (snb_gmch_ctl & SNB_GTT_SIZE_MASK) { + default: + case SNB_GTT_SIZE_0M: + printk(KERN_ERR "Bad GTT size mask: 0x%04x.\n", snb_gmch_ctl); + *gtt_size = MB(0); + break; + case SNB_GTT_SIZE_1M: + *gtt_size = MB(1); + break; + case SNB_GTT_SIZE_2M: + *gtt_size = MB(2); + break; + } + break; + default: + *gtt_offset = *gtt_size = KB(512); + } +} + +/* The intel i965 automatically initializes the agp aperture during POST. + * Use the memory already set aside for in the GTT. + */ +static int intel_i965_create_gatt_table(struct agp_bridge_data *bridge) +{ + int page_order; + struct aper_size_info_fixed *size; + int num_entries; + u32 temp; + int gtt_offset, gtt_size; + + size = agp_bridge->current_size; + page_order = size->page_order; + num_entries = size->num_entries; + agp_bridge->gatt_table_real = NULL; + + pci_read_config_dword(intel_private.pcidev, I915_MMADDR, &temp); + + temp &= 0xfff00000; + + intel_i965_get_gtt_range(>t_offset, >t_size); + + intel_private.gtt = ioremap((temp + gtt_offset) , gtt_size); + + if (!intel_private.gtt) + return -ENOMEM; + + intel_private.gtt_total_size = gtt_size / 4; + + intel_private.registers = ioremap(temp, 128 * 4096); + if (!intel_private.registers) { + iounmap(intel_private.gtt); + return -ENOMEM; + } + + temp = readl(intel_private.registers+I810_PGETBL_CTL) & 0xfffff000; + global_cache_flush(); /* FIXME: ? */ + + /* we have to call this as early as possible after the MMIO base address is known */ + intel_i830_init_gtt_entries(); + + agp_bridge->gatt_table = NULL; + + agp_bridge->gatt_bus_addr = temp; + + return 0; +} + +static const struct agp_bridge_driver intel_810_driver = { + .owner = THIS_MODULE, + .aperture_sizes = intel_i810_sizes, + .size_type = FIXED_APER_SIZE, + .num_aperture_sizes = 2, + .needs_scratch_page = true, + .configure = intel_i810_configure, + .fetch_size = intel_i810_fetch_size, + .cleanup = intel_i810_cleanup, + .mask_memory = intel_i810_mask_memory, + .masks = intel_i810_masks, + .agp_enable = intel_i810_agp_enable, + .cache_flush = global_cache_flush, + .create_gatt_table = agp_generic_create_gatt_table, + .free_gatt_table = agp_generic_free_gatt_table, + .insert_memory = intel_i810_insert_entries, + .remove_memory = intel_i810_remove_entries, + .alloc_by_type = intel_i810_alloc_by_type, + .free_by_type = intel_i810_free_by_type, + .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, + .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, + .agp_type_to_mask_type = agp_generic_type_to_mask_type, +}; + +static const struct agp_bridge_driver intel_830_driver = { + .owner = THIS_MODULE, + .aperture_sizes = intel_i830_sizes, + .size_type = FIXED_APER_SIZE, + .num_aperture_sizes = 4, + .needs_scratch_page = true, + .configure = intel_i830_configure, + .fetch_size = intel_i830_fetch_size, + .cleanup = intel_i830_cleanup, + .mask_memory = intel_i810_mask_memory, + .masks = intel_i810_masks, + .agp_enable = intel_i810_agp_enable, + .cache_flush = global_cache_flush, + .create_gatt_table = intel_i830_create_gatt_table, + .free_gatt_table = intel_i830_free_gatt_table, + .insert_memory = intel_i830_insert_entries, + .remove_memory = intel_i830_remove_entries, + .alloc_by_type = intel_i830_alloc_by_type, + .free_by_type = intel_i810_free_by_type, + .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, + .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, + .agp_type_to_mask_type = intel_i830_type_to_mask_type, + .chipset_flush = intel_i830_chipset_flush, +}; + +static const struct agp_bridge_driver intel_915_driver = { + .owner = THIS_MODULE, + .aperture_sizes = intel_i830_sizes, + .size_type = FIXED_APER_SIZE, + .num_aperture_sizes = 4, + .needs_scratch_page = true, + .configure = intel_i915_configure, + .fetch_size = intel_i9xx_fetch_size, + .cleanup = intel_i915_cleanup, + .mask_memory = intel_i810_mask_memory, + .masks = intel_i810_masks, + .agp_enable = intel_i810_agp_enable, + .cache_flush = global_cache_flush, + .create_gatt_table = intel_i915_create_gatt_table, + .free_gatt_table = intel_i830_free_gatt_table, + .insert_memory = intel_i915_insert_entries, + .remove_memory = intel_i915_remove_entries, + .alloc_by_type = intel_i830_alloc_by_type, + .free_by_type = intel_i810_free_by_type, + .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, + .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, + .agp_type_to_mask_type = intel_i830_type_to_mask_type, + .chipset_flush = intel_i915_chipset_flush, +#ifdef USE_PCI_DMA_API + .agp_map_page = intel_agp_map_page, + .agp_unmap_page = intel_agp_unmap_page, + .agp_map_memory = intel_agp_map_memory, + .agp_unmap_memory = intel_agp_unmap_memory, +#endif +}; + +static const struct agp_bridge_driver intel_i965_driver = { + .owner = THIS_MODULE, + .aperture_sizes = intel_i830_sizes, + .size_type = FIXED_APER_SIZE, + .num_aperture_sizes = 4, + .needs_scratch_page = true, + .configure = intel_i915_configure, + .fetch_size = intel_i9xx_fetch_size, + .cleanup = intel_i915_cleanup, + .mask_memory = intel_i965_mask_memory, + .masks = intel_i810_masks, + .agp_enable = intel_i810_agp_enable, + .cache_flush = global_cache_flush, + .create_gatt_table = intel_i965_create_gatt_table, + .free_gatt_table = intel_i830_free_gatt_table, + .insert_memory = intel_i915_insert_entries, + .remove_memory = intel_i915_remove_entries, + .alloc_by_type = intel_i830_alloc_by_type, + .free_by_type = intel_i810_free_by_type, + .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, + .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, + .agp_type_to_mask_type = intel_i830_type_to_mask_type, + .chipset_flush = intel_i915_chipset_flush, +#ifdef USE_PCI_DMA_API + .agp_map_page = intel_agp_map_page, + .agp_unmap_page = intel_agp_unmap_page, + .agp_map_memory = intel_agp_map_memory, + .agp_unmap_memory = intel_agp_unmap_memory, +#endif +}; + +static const struct agp_bridge_driver intel_g33_driver = { + .owner = THIS_MODULE, + .aperture_sizes = intel_i830_sizes, + .size_type = FIXED_APER_SIZE, + .num_aperture_sizes = 4, + .needs_scratch_page = true, + .configure = intel_i915_configure, + .fetch_size = intel_i9xx_fetch_size, + .cleanup = intel_i915_cleanup, + .mask_memory = intel_i965_mask_memory, + .masks = intel_i810_masks, + .agp_enable = intel_i810_agp_enable, + .cache_flush = global_cache_flush, + .create_gatt_table = intel_i915_create_gatt_table, + .free_gatt_table = intel_i830_free_gatt_table, + .insert_memory = intel_i915_insert_entries, + .remove_memory = intel_i915_remove_entries, + .alloc_by_type = intel_i830_alloc_by_type, + .free_by_type = intel_i810_free_by_type, + .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, + .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, + .agp_type_to_mask_type = intel_i830_type_to_mask_type, + .chipset_flush = intel_i915_chipset_flush, +#ifdef USE_PCI_DMA_API + .agp_map_page = intel_agp_map_page, + .agp_unmap_page = intel_agp_unmap_page, + .agp_map_memory = intel_agp_map_memory, + .agp_unmap_memory = intel_agp_unmap_memory, +#endif +}; diff --git a/drivers/char/agp/nvidia-agp.c b/drivers/char/agp/nvidia-agp.c index 10f24e349a2..b9734a97818 100644 --- a/drivers/char/agp/nvidia-agp.c +++ b/drivers/char/agp/nvidia-agp.c @@ -310,6 +310,7 @@ static const struct agp_bridge_driver nvidia_driver = { .aperture_sizes = nvidia_generic_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 5, + .needs_scratch_page = true, .configure = nvidia_configure, .fetch_size = nvidia_fetch_size, .cleanup = nvidia_cleanup, diff --git a/drivers/char/agp/sis-agp.c b/drivers/char/agp/sis-agp.c index 6c3837a0184..29aacd81de7 100644 --- a/drivers/char/agp/sis-agp.c +++ b/drivers/char/agp/sis-agp.c @@ -125,6 +125,7 @@ static struct agp_bridge_driver sis_driver = { .aperture_sizes = sis_generic_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 7, + .needs_scratch_page = true, .configure = sis_configure, .fetch_size = sis_fetch_size, .cleanup = sis_cleanup, @@ -415,14 +416,6 @@ static struct pci_device_id agp_sis_pci_table[] = { .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, }, - { - .class = (PCI_CLASS_BRIDGE_HOST << 8), - .class_mask = ~0, - .vendor = PCI_VENDOR_ID_SI, - .device = PCI_DEVICE_ID_SI_760, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - }, { } }; diff --git a/drivers/char/agp/uninorth-agp.c b/drivers/char/agp/uninorth-agp.c index 6f48931ac1c..95db71360d2 100644 --- a/drivers/char/agp/uninorth-agp.c +++ b/drivers/char/agp/uninorth-agp.c @@ -28,6 +28,7 @@ */ static int uninorth_rev; static int is_u3; +static u32 scratch_value; #define DEFAULT_APERTURE_SIZE 256 #define DEFAULT_APERTURE_STRING "256" @@ -172,7 +173,7 @@ static int uninorth_insert_memory(struct agp_memory *mem, off_t pg_start, int ty gp = (u32 *) &agp_bridge->gatt_table[pg_start]; for (i = 0; i < mem->page_count; ++i) { - if (gp[i]) { + if (gp[i] != scratch_value) { dev_info(&agp_bridge->dev->dev, "uninorth_insert_memory: entry 0x%x occupied (%x)\n", i, gp[i]); @@ -214,8 +215,9 @@ int uninorth_remove_memory(struct agp_memory *mem, off_t pg_start, int type) return 0; gp = (u32 *) &agp_bridge->gatt_table[pg_start]; - for (i = 0; i < mem->page_count; ++i) - gp[i] = 0; + for (i = 0; i < mem->page_count; ++i) { + gp[i] = scratch_value; + } mb(); uninorth_tlbflush(mem); @@ -421,8 +423,13 @@ static int uninorth_create_gatt_table(struct agp_bridge_data *bridge) bridge->gatt_bus_addr = virt_to_phys(table); + if (is_u3) + scratch_value = (page_to_phys(agp_bridge->scratch_page_page) >> PAGE_SHIFT) | 0x80000000UL; + else + scratch_value = cpu_to_le32((page_to_phys(agp_bridge->scratch_page_page) & 0xFFFFF000UL) | + 0x1UL); for (i = 0; i < num_entries; i++) - bridge->gatt_table[i] = 0; + bridge->gatt_table[i] = scratch_value; return 0; @@ -519,6 +526,7 @@ const struct agp_bridge_driver uninorth_agp_driver = { .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = agp_generic_type_to_mask_type, .cant_use_aperture = true, + .needs_scratch_page = true, }; const struct agp_bridge_driver u3_agp_driver = { diff --git a/drivers/char/agp/via-agp.c b/drivers/char/agp/via-agp.c index d3bd243867f..df67e80019d 100644 --- a/drivers/char/agp/via-agp.c +++ b/drivers/char/agp/via-agp.c @@ -175,6 +175,7 @@ static const struct agp_bridge_driver via_agp3_driver = { .aperture_sizes = agp3_generic_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 10, + .needs_scratch_page = true, .configure = via_configure_agp3, .fetch_size = via_fetch_size_agp3, .cleanup = via_cleanup_agp3, @@ -201,6 +202,7 @@ static const struct agp_bridge_driver via_driver = { .aperture_sizes = via_generic_sizes, .size_type = U8_APER_SIZE, .num_aperture_sizes = 9, + .needs_scratch_page = true, .configure = via_configure, .fetch_size = via_fetch_size, .cleanup = via_cleanup, diff --git a/drivers/char/apm-emulation.c b/drivers/char/apm-emulation.c index 4f568cb9af3..033e1505fca 100644 --- a/drivers/char/apm-emulation.c +++ b/drivers/char/apm-emulation.c @@ -265,8 +265,8 @@ static unsigned int apm_poll(struct file *fp, poll_table * wait) * Only when everyone who has opened /dev/apm_bios with write permission * has acknowledge does the actual suspend happen. */ -static int -apm_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg) +static long +apm_ioctl(struct file *filp, u_int cmd, u_long arg) { struct apm_user *as = filp->private_data; int err = -EINVAL; @@ -274,6 +274,7 @@ apm_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg) if (!as->suser || !as->writer) return -EPERM; + lock_kernel(); switch (cmd) { case APM_IOC_SUSPEND: mutex_lock(&state_lock); @@ -334,6 +335,7 @@ apm_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg) mutex_unlock(&state_lock); break; } + unlock_kernel(); return err; } @@ -397,7 +399,7 @@ static const struct file_operations apm_bios_fops = { .owner = THIS_MODULE, .read = apm_read, .poll = apm_poll, - .ioctl = apm_ioctl, + .unlocked_ioctl = apm_ioctl, .open = apm_open, .release = apm_release, }; diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c index a7424bf7eac..63313a33ba5 100644 --- a/drivers/char/applicom.c +++ b/drivers/char/applicom.c @@ -26,6 +26,7 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/errno.h> +#include <linux/smp_lock.h> #include <linux/miscdevice.h> #include <linux/pci.h> #include <linux/wait.h> @@ -106,8 +107,7 @@ static unsigned int DeviceErrorCount; /* number of device error */ static ssize_t ac_read (struct file *, char __user *, size_t, loff_t *); static ssize_t ac_write (struct file *, const char __user *, size_t, loff_t *); -static int ac_ioctl(struct inode *, struct file *, unsigned int, - unsigned long); +static long ac_ioctl(struct file *, unsigned int, unsigned long); static irqreturn_t ac_interrupt(int, void *); static const struct file_operations ac_fops = { @@ -115,7 +115,7 @@ static const struct file_operations ac_fops = { .llseek = no_llseek, .read = ac_read, .write = ac_write, - .ioctl = ac_ioctl, + .unlocked_ioctl = ac_ioctl, }; static struct miscdevice ac_miscdev = { @@ -689,7 +689,7 @@ static irqreturn_t ac_interrupt(int vec, void *dev_instance) -static int ac_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +static long ac_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { /* @ ADG ou ATO selon le cas */ int i; @@ -711,7 +711,8 @@ static int ac_ioctl(struct inode *inode, struct file *file, unsigned int cmd, un kfree(adgl); return -EFAULT; } - + + lock_kernel(); IndexCard = adgl->num_card-1; if(cmd != 6 && ((IndexCard >= MAX_BOARD) || !apbs[IndexCard].RamIO)) { @@ -721,6 +722,7 @@ static int ac_ioctl(struct inode *inode, struct file *file, unsigned int cmd, un warncount--; } kfree(adgl); + unlock_kernel(); return -EINVAL; } @@ -838,6 +840,7 @@ static int ac_ioctl(struct inode *inode, struct file *file, unsigned int cmd, un } Dummy = readb(apbs[IndexCard].RamIO + VERS); kfree(adgl); + unlock_kernel(); return 0; } diff --git a/drivers/char/bsr.c b/drivers/char/bsr.c index 7fef305774d..89d871ef8c2 100644 --- a/drivers/char/bsr.c +++ b/drivers/char/bsr.c @@ -253,7 +253,7 @@ static int bsr_add_node(struct device_node *bn) cur->bsr_device = device_create(bsr_class, NULL, cur->bsr_dev, cur, cur->bsr_name); - if (!cur->bsr_device) { + if (IS_ERR(cur->bsr_device)) { printk(KERN_ERR "device_create failed for %s\n", cur->bsr_name); cdev_del(&cur->bsr_cdev); diff --git a/drivers/char/ds1620.c b/drivers/char/ds1620.c index 61f0146e215..dbee8688f75 100644 --- a/drivers/char/ds1620.c +++ b/drivers/char/ds1620.c @@ -232,7 +232,7 @@ ds1620_read(struct file *file, char __user *buf, size_t count, loff_t *ptr) } static int -ds1620_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +ds1620_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct therm therm; union { @@ -316,6 +316,18 @@ ds1620_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned return 0; } +static long +ds1620_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret; + + lock_kernel(); + ret = ds1620_ioctl(file, cmd, arg); + unlock_kernel(); + + return ret; +} + #ifdef THERM_USE_PROC static int proc_therm_ds1620_read(char *buf, char **start, off_t offset, @@ -344,7 +356,7 @@ static const struct file_operations ds1620_fops = { .owner = THIS_MODULE, .open = ds1620_open, .read = ds1620_read, - .ioctl = ds1620_ioctl, + .unlocked_ioctl = ds1620_unlocked_ioctl, }; static struct miscdevice ds1620_miscdev = { diff --git a/drivers/char/dtlk.c b/drivers/char/dtlk.c index 045c930e632..e3859d4eaea 100644 --- a/drivers/char/dtlk.c +++ b/drivers/char/dtlk.c @@ -93,8 +93,8 @@ static ssize_t dtlk_write(struct file *, const char __user *, static unsigned int dtlk_poll(struct file *, poll_table *); static int dtlk_open(struct inode *, struct file *); static int dtlk_release(struct inode *, struct file *); -static int dtlk_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); +static long dtlk_ioctl(struct file *file, + unsigned int cmd, unsigned long arg); static const struct file_operations dtlk_fops = { @@ -102,7 +102,7 @@ static const struct file_operations dtlk_fops = .read = dtlk_read, .write = dtlk_write, .poll = dtlk_poll, - .ioctl = dtlk_ioctl, + .unlocked_ioctl = dtlk_ioctl, .open = dtlk_open, .release = dtlk_release, }; @@ -263,10 +263,9 @@ static void dtlk_timer_tick(unsigned long data) wake_up_interruptible(&dtlk_process_list); } -static int dtlk_ioctl(struct inode *inode, - struct file *file, - unsigned int cmd, - unsigned long arg) +static long dtlk_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg) { char __user *argp = (char __user *)arg; struct dtlk_settings *sp; @@ -276,7 +275,9 @@ static int dtlk_ioctl(struct inode *inode, switch (cmd) { case DTLK_INTERROGATE: + lock_kernel(); sp = dtlk_interrogate(); + unlock_kernel(); if (copy_to_user(argp, sp, sizeof(struct dtlk_settings))) return -EINVAL; return 0; diff --git a/drivers/char/generic_nvram.c b/drivers/char/generic_nvram.c index fda4181b5e6..82b5a88a82d 100644 --- a/drivers/char/generic_nvram.c +++ b/drivers/char/generic_nvram.c @@ -19,6 +19,7 @@ #include <linux/miscdevice.h> #include <linux/fcntl.h> #include <linux/init.h> +#include <linux/smp_lock.h> #include <asm/uaccess.h> #include <asm/nvram.h> #ifdef CONFIG_PPC_PMAC @@ -84,8 +85,7 @@ static ssize_t write_nvram(struct file *file, const char __user *buf, return p - buf; } -static int nvram_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int nvram_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { switch(cmd) { #ifdef CONFIG_PPC_PMAC @@ -116,12 +116,23 @@ static int nvram_ioctl(struct inode *inode, struct file *file, return 0; } +static long nvram_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret; + + lock_kernel(); + ret = nvram_ioctl(file, cmd, arg); + unlock_kernel(); + + return ret; +} + const struct file_operations nvram_fops = { .owner = THIS_MODULE, .llseek = nvram_llseek, .read = read_nvram, .write = write_nvram, - .ioctl = nvram_ioctl, + .unlocked_ioctl = nvram_unlocked_ioctl, }; static struct miscdevice nvram_dev = { diff --git a/drivers/char/genrtc.c b/drivers/char/genrtc.c index 31e7c91c2d9..b6c2cc167c1 100644 --- a/drivers/char/genrtc.c +++ b/drivers/char/genrtc.c @@ -262,7 +262,7 @@ static inline int gen_set_rtc_irq_bit(unsigned char bit) #endif } -static int gen_rtc_ioctl(struct inode *inode, struct file *file, +static int gen_rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct rtc_time wtime; @@ -332,6 +332,18 @@ static int gen_rtc_ioctl(struct inode *inode, struct file *file, return -EINVAL; } +static long gen_rtc_unlocked_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret; + + lock_kernel(); + ret = gen_rtc_ioctl(file, cmd, arg); + unlock_kernel(); + + return ret; +} + /* * We enforce only one user at a time here with the open/close. * Also clear the previous interrupt data on an open, and clean @@ -482,7 +494,7 @@ static const struct file_operations gen_rtc_fops = { .read = gen_rtc_read, .poll = gen_rtc_poll, #endif - .ioctl = gen_rtc_ioctl, + .unlocked_ioctl = gen_rtc_unlocked_ioctl, .open = gen_rtc_open, .release = gen_rtc_release, }; diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index 9ded667625a..a0a1829d319 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -431,14 +431,18 @@ static int hpet_release(struct inode *inode, struct file *file) static int hpet_ioctl_common(struct hpet_dev *, int, unsigned long, int); -static int -hpet_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) +static long hpet_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { struct hpet_dev *devp; + int ret; devp = file->private_data; - return hpet_ioctl_common(devp, cmd, arg, 0); + lock_kernel(); + ret = hpet_ioctl_common(devp, cmd, arg, 0); + unlock_kernel(); + + return ret; } static int hpet_ioctl_ieon(struct hpet_dev *devp) @@ -654,7 +658,7 @@ static const struct file_operations hpet_fops = { .llseek = no_llseek, .read = hpet_read, .poll = hpet_poll, - .ioctl = hpet_ioctl, + .unlocked_ioctl = hpet_ioctl, .open = hpet_open, .release = hpet_release, .fasync = hpet_fasync, diff --git a/drivers/char/hw_random/n2-drv.c b/drivers/char/hw_random/n2-drv.c index 10f868eefaa..0f9cbf1aaf1 100644 --- a/drivers/char/hw_random/n2-drv.c +++ b/drivers/char/hw_random/n2-drv.c @@ -660,7 +660,7 @@ static int __devinit n2rng_probe(struct of_device *op, np->hvapi_major); goto out_hvapi_unregister; } - np->num_units = of_getintprop_default(op->node, + np->num_units = of_getintprop_default(op->dev.of_node, "rng-#units", 0); if (!np->num_units) { dev_err(&op->dev, "VF RNG lacks rng-#units property\n"); @@ -751,8 +751,11 @@ static const struct of_device_id n2rng_match[] = { MODULE_DEVICE_TABLE(of, n2rng_match); static struct of_platform_driver n2rng_driver = { - .name = "n2rng", - .match_table = n2rng_match, + .driver = { + .name = "n2rng", + .owner = THIS_MODULE, + .of_match_table = n2rng_match, + }, .probe = n2rng_probe, .remove = __devexit_p(n2rng_remove), }; diff --git a/drivers/char/hw_random/pasemi-rng.c b/drivers/char/hw_random/pasemi-rng.c index 7fa61dd1d9d..261ba8f22b8 100644 --- a/drivers/char/hw_random/pasemi-rng.c +++ b/drivers/char/hw_random/pasemi-rng.c @@ -98,7 +98,7 @@ static int __devinit rng_probe(struct of_device *ofdev, const struct of_device_id *match) { void __iomem *rng_regs; - struct device_node *rng_np = ofdev->node; + struct device_node *rng_np = ofdev->dev.of_node; struct resource res; int err = 0; @@ -140,8 +140,11 @@ static struct of_device_id rng_match[] = { }; static struct of_platform_driver rng_driver = { - .name = "pasemi-rng", - .match_table = rng_match, + .driver = { + .name = "pasemi-rng", + .owner = THIS_MODULE, + .of_match_table = rng_match, + }, .probe = rng_probe, .remove = rng_remove, }; diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index 64fe0a793ef..75f1cbd61c1 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -32,7 +32,7 @@ static bool busy; static void random_recv_done(struct virtqueue *vq) { /* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */ - if (!vq->vq_ops->get_buf(vq, &data_avail)) + if (!virtqueue_get_buf(vq, &data_avail)) return; complete(&have_data); @@ -46,10 +46,10 @@ static void register_buffer(u8 *buf, size_t size) sg_init_one(&sg, buf, size); /* There should always be room for one buffer. */ - if (vq->vq_ops->add_buf(vq, &sg, 0, 1, buf) < 0) + if (virtqueue_add_buf(vq, &sg, 0, 1, buf) < 0) BUG(); - vq->vq_ops->kick(vq); + virtqueue_kick(vq); } static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait) diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c index fc8cf7ac7f2..4cd8b227c11 100644 --- a/drivers/char/i8k.c +++ b/drivers/char/i8k.c @@ -23,6 +23,7 @@ #include <linux/seq_file.h> #include <linux/dmi.h> #include <linux/capability.h> +#include <linux/smp_lock.h> #include <asm/uaccess.h> #include <asm/io.h> @@ -82,8 +83,7 @@ module_param(fan_mult, int, 0); MODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with"); static int i8k_open_fs(struct inode *inode, struct file *file); -static int i8k_ioctl(struct inode *, struct file *, unsigned int, - unsigned long); +static long i8k_ioctl(struct file *, unsigned int, unsigned long); static const struct file_operations i8k_fops = { .owner = THIS_MODULE, @@ -91,7 +91,7 @@ static const struct file_operations i8k_fops = { .read = seq_read, .llseek = seq_lseek, .release = single_release, - .ioctl = i8k_ioctl, + .unlocked_ioctl = i8k_ioctl, }; struct smm_regs { @@ -307,8 +307,8 @@ static int i8k_get_dell_signature(int req_fn) return regs.eax == 1145651527 && regs.edx == 1145392204 ? 0 : -1; } -static int i8k_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, - unsigned long arg) +static int +i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg) { int val = 0; int speed; @@ -395,6 +395,17 @@ static int i8k_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, return 0; } +static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + long ret; + + lock_kernel(); + ret = i8k_ioctl_unlocked(fp, cmd, arg); + unlock_kernel(); + + return ret; +} + /* * Print the information for /proc/i8k. */ diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c index 65545de3dbf..d8ec92a3898 100644 --- a/drivers/char/ipmi/ipmi_devintf.c +++ b/drivers/char/ipmi/ipmi_devintf.c @@ -228,8 +228,7 @@ static int handle_send_req(ipmi_user_t user, return rv; } -static int ipmi_ioctl(struct inode *inode, - struct file *file, +static int ipmi_ioctl(struct file *file, unsigned int cmd, unsigned long data) { @@ -630,6 +629,23 @@ static int ipmi_ioctl(struct inode *inode, return rv; } +/* + * Note: it doesn't make sense to take the BKL here but + * not in compat_ipmi_ioctl. -arnd + */ +static long ipmi_unlocked_ioctl(struct file *file, + unsigned int cmd, + unsigned long data) +{ + int ret; + + lock_kernel(); + ret = ipmi_ioctl(file, cmd, data); + unlock_kernel(); + + return ret; +} + #ifdef CONFIG_COMPAT /* @@ -802,7 +818,7 @@ static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd, if (copy_to_user(precv64, &recv64, sizeof(recv64))) return -EFAULT; - rc = ipmi_ioctl(filep->f_path.dentry->d_inode, filep, + rc = ipmi_ioctl(filep, ((cmd == COMPAT_IPMICTL_RECEIVE_MSG) ? IPMICTL_RECEIVE_MSG : IPMICTL_RECEIVE_MSG_TRUNC), @@ -819,14 +835,14 @@ static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd, return rc; } default: - return ipmi_ioctl(filep->f_path.dentry->d_inode, filep, cmd, arg); + return ipmi_ioctl(filep, cmd, arg); } } #endif static const struct file_operations ipmi_fops = { .owner = THIS_MODULE, - .ioctl = ipmi_ioctl, + .unlocked_ioctl = ipmi_unlocked_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = compat_ipmi_ioctl, #endif diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 4462b113ba3..47ffe4a90a9 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -2469,7 +2469,7 @@ static int __devinit ipmi_of_probe(struct of_device *dev, struct smi_info *info; struct resource resource; const int *regsize, *regspacing, *regshift; - struct device_node *np = dev->node; + struct device_node *np = dev->dev.of_node; int ret; int proplen; @@ -2525,7 +2525,7 @@ static int __devinit ipmi_of_probe(struct of_device *dev, info->io.regspacing = regspacing ? *regspacing : DEFAULT_REGSPACING; info->io.regshift = regshift ? *regshift : 0; - info->irq = irq_of_parse_and_map(dev->node, 0); + info->irq = irq_of_parse_and_map(dev->dev.of_node, 0); info->dev = &dev->dev; dev_dbg(&dev->dev, "addr 0x%lx regsize %d spacing %d irq %x\n", @@ -2555,8 +2555,11 @@ static struct of_device_id ipmi_match[] = }; static struct of_platform_driver ipmi_of_platform_driver = { - .name = "ipmi", - .match_table = ipmi_match, + .driver = { + .name = "ipmi", + .owner = THIS_MODULE, + .of_match_table = ipmi_match, + }, .probe = ipmi_of_probe, .remove = __devexit_p(ipmi_of_remove), }; diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index a4d57e31f71..82bcdb262a3 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -659,7 +659,7 @@ static struct watchdog_info ident = { .identity = "IPMI" }; -static int ipmi_ioctl(struct inode *inode, struct file *file, +static int ipmi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; @@ -730,6 +730,19 @@ static int ipmi_ioctl(struct inode *inode, struct file *file, } } +static long ipmi_unlocked_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + int ret; + + lock_kernel(); + ret = ipmi_ioctl(file, cmd, arg); + unlock_kernel(); + + return ret; +} + static ssize_t ipmi_write(struct file *file, const char __user *buf, size_t len, @@ -880,7 +893,7 @@ static const struct file_operations ipmi_wdog_fops = { .read = ipmi_read, .poll = ipmi_poll, .write = ipmi_write, - .ioctl = ipmi_ioctl, + .unlocked_ioctl = ipmi_unlocked_ioctl, .open = ipmi_open, .release = ipmi_close, .fasync = ipmi_fasync, diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index c1ab303455c..98310e1aae3 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -1573,11 +1573,16 @@ static int __devinit isicom_probe(struct pci_dev *pdev, dev_info(&pdev->dev, "ISI PCI Card(Device ID 0x%x)\n", ent->device); /* allot the first empty slot in the array */ - for (index = 0; index < BOARD_COUNT; index++) + for (index = 0; index < BOARD_COUNT; index++) { if (isi_card[index].base == 0) { board = &isi_card[index]; break; } + } + if (index == BOARD_COUNT) { + retval = -ENODEV; + goto err_disable; + } board->index = index; board->base = pci_resource_start(pdev, 3); @@ -1624,6 +1629,7 @@ errunrr: errdec: board->base = 0; card_count--; +err_disable: pci_disable_device(pdev); err: return retval; diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index ada25bb8941..54109dc9240 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -24,6 +24,8 @@ * 21-08-02: Converted to input API, major cleanup. (Vojtech Pavlik) */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/consolemap.h> #include <linux/module.h> #include <linux/sched.h> @@ -38,7 +40,6 @@ #include <linux/kbd_kern.h> #include <linux/kbd_diacr.h> #include <linux/vt_kern.h> -#include <linux/sysrq.h> #include <linux/input.h> #include <linux/reboot.h> #include <linux/notifier.h> @@ -82,8 +83,7 @@ void compute_shiftstate(void); typedef void (k_handler_fn)(struct vc_data *vc, unsigned char value, char up_flag); static k_handler_fn K_HANDLERS; -k_handler_fn *k_handler[16] = { K_HANDLERS }; -EXPORT_SYMBOL_GPL(k_handler); +static k_handler_fn *k_handler[16] = { K_HANDLERS }; #define FN_HANDLERS\ fn_null, fn_enter, fn_show_ptregs, fn_show_mem,\ @@ -133,7 +133,7 @@ static struct input_handler kbd_handler; static DEFINE_SPINLOCK(kbd_event_lock); static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)]; /* keyboard key bitmap */ static unsigned char shift_down[NR_SHIFT]; /* shift state counters.. */ -static int dead_key_next; +static bool dead_key_next; static int npadch = -1; /* -1 or number assembled on pad */ static unsigned int diacr; static char rep; /* flag telling character repeat */ @@ -147,22 +147,6 @@ static struct ledptr { unsigned char valid:1; } ledptrs[3]; -/* Simple translation table for the SysRq keys */ - -#ifdef CONFIG_MAGIC_SYSRQ -unsigned char kbd_sysrq_xlate[KEY_MAX + 1] = - "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ - "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ - "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ - "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ - "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ - "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ - "\r\000/"; /* 0x60 - 0x6f */ -static int sysrq_down; -static int sysrq_alt_use; -#endif -static int sysrq_alt; - /* * Notifier list for console keyboard events */ @@ -361,8 +345,8 @@ static void to_utf8(struct vc_data *vc, uint c) /* 110***** 10****** */ put_queue(vc, 0xc0 | (c >> 6)); put_queue(vc, 0x80 | (c & 0x3f)); - } else if (c < 0x10000) { - if (c >= 0xD800 && c < 0xE000) + } else if (c < 0x10000) { + if (c >= 0xD800 && c < 0xE000) return; if (c == 0xFFFF) return; @@ -370,7 +354,7 @@ static void to_utf8(struct vc_data *vc, uint c) put_queue(vc, 0xe0 | (c >> 12)); put_queue(vc, 0x80 | ((c >> 6) & 0x3f)); put_queue(vc, 0x80 | (c & 0x3f)); - } else if (c < 0x110000) { + } else if (c < 0x110000) { /* 11110*** 10****** 10****** 10****** */ put_queue(vc, 0xf0 | (c >> 18)); put_queue(vc, 0x80 | ((c >> 12) & 0x3f)); @@ -469,6 +453,7 @@ static void fn_enter(struct vc_data *vc) } diacr = 0; } + put_queue(vc, 13); if (vc_kbd_mode(kbd, VC_CRLF)) put_queue(vc, 10); @@ -478,6 +463,7 @@ static void fn_caps_toggle(struct vc_data *vc) { if (rep) return; + chg_vc_kbd_led(kbd, VC_CAPSLOCK); } @@ -485,12 +471,14 @@ static void fn_caps_on(struct vc_data *vc) { if (rep) return; + set_vc_kbd_led(kbd, VC_CAPSLOCK); } static void fn_show_ptregs(struct vc_data *vc) { struct pt_regs *regs = get_irq_regs(); + if (regs) show_regs(regs); } @@ -515,7 +503,7 @@ static void fn_hold(struct vc_data *vc) static void fn_num(struct vc_data *vc) { - if (vc_kbd_mode(kbd,VC_APPLIC)) + if (vc_kbd_mode(kbd, VC_APPLIC)) applkey(vc, 'P', 1); else fn_bare_num(vc); @@ -610,7 +598,7 @@ static void fn_boot_it(struct vc_data *vc) static void fn_compose(struct vc_data *vc) { - dead_key_next = 1; + dead_key_next = true; } static void fn_spawn_con(struct vc_data *vc) @@ -657,7 +645,7 @@ static void k_spec(struct vc_data *vc, unsigned char value, char up_flag) static void k_lowercase(struct vc_data *vc, unsigned char value, char up_flag) { - printk(KERN_ERR "keyboard.c: k_lowercase was called - impossible\n"); + pr_err("k_lowercase was called - impossible\n"); } static void k_unicode(struct vc_data *vc, unsigned int value, char up_flag) @@ -669,7 +657,7 @@ static void k_unicode(struct vc_data *vc, unsigned int value, char up_flag) value = handle_diacr(vc, value); if (dead_key_next) { - dead_key_next = 0; + dead_key_next = false; diacr = value; return; } @@ -691,6 +679,7 @@ static void k_deadunicode(struct vc_data *vc, unsigned int value, char up_flag) { if (up_flag) return; + diacr = (diacr ? handle_diacr(vc, value) : value); } @@ -710,29 +699,28 @@ static void k_dead2(struct vc_data *vc, unsigned char value, char up_flag) static void k_dead(struct vc_data *vc, unsigned char value, char up_flag) { static const unsigned char ret_diacr[NR_DEAD] = {'`', '\'', '^', '~', '"', ',' }; - value = ret_diacr[value]; - k_deadunicode(vc, value, up_flag); + + k_deadunicode(vc, ret_diacr[value], up_flag); } static void k_cons(struct vc_data *vc, unsigned char value, char up_flag) { if (up_flag) return; + set_console(value); } static void k_fn(struct vc_data *vc, unsigned char value, char up_flag) { - unsigned v; - if (up_flag) return; - v = value; - if (v < ARRAY_SIZE(func_table)) { + + if ((unsigned)value < ARRAY_SIZE(func_table)) { if (func_table[value]) puts_queue(vc, func_table[value]); } else - printk(KERN_ERR "k_fn called with value=%d\n", value); + pr_err("k_fn called with value=%d\n", value); } static void k_cur(struct vc_data *vc, unsigned char value, char up_flag) @@ -741,6 +729,7 @@ static void k_cur(struct vc_data *vc, unsigned char value, char up_flag) if (up_flag) return; + applkey(vc, cur_chars[value], vc_kbd_mode(kbd, VC_CKMODE)); } @@ -758,43 +747,45 @@ static void k_pad(struct vc_data *vc, unsigned char value, char up_flag) return; } - if (!vc_kbd_led(kbd, VC_NUMLOCK)) + if (!vc_kbd_led(kbd, VC_NUMLOCK)) { + switch (value) { - case KVAL(K_PCOMMA): - case KVAL(K_PDOT): - k_fn(vc, KVAL(K_REMOVE), 0); - return; - case KVAL(K_P0): - k_fn(vc, KVAL(K_INSERT), 0); - return; - case KVAL(K_P1): - k_fn(vc, KVAL(K_SELECT), 0); - return; - case KVAL(K_P2): - k_cur(vc, KVAL(K_DOWN), 0); - return; - case KVAL(K_P3): - k_fn(vc, KVAL(K_PGDN), 0); - return; - case KVAL(K_P4): - k_cur(vc, KVAL(K_LEFT), 0); - return; - case KVAL(K_P6): - k_cur(vc, KVAL(K_RIGHT), 0); - return; - case KVAL(K_P7): - k_fn(vc, KVAL(K_FIND), 0); - return; - case KVAL(K_P8): - k_cur(vc, KVAL(K_UP), 0); - return; - case KVAL(K_P9): - k_fn(vc, KVAL(K_PGUP), 0); - return; - case KVAL(K_P5): - applkey(vc, 'G', vc_kbd_mode(kbd, VC_APPLIC)); - return; + case KVAL(K_PCOMMA): + case KVAL(K_PDOT): + k_fn(vc, KVAL(K_REMOVE), 0); + return; + case KVAL(K_P0): + k_fn(vc, KVAL(K_INSERT), 0); + return; + case KVAL(K_P1): + k_fn(vc, KVAL(K_SELECT), 0); + return; + case KVAL(K_P2): + k_cur(vc, KVAL(K_DOWN), 0); + return; + case KVAL(K_P3): + k_fn(vc, KVAL(K_PGDN), 0); + return; + case KVAL(K_P4): + k_cur(vc, KVAL(K_LEFT), 0); + return; + case KVAL(K_P6): + k_cur(vc, KVAL(K_RIGHT), 0); + return; + case KVAL(K_P7): + k_fn(vc, KVAL(K_FIND), 0); + return; + case KVAL(K_P8): + k_cur(vc, KVAL(K_UP), 0); + return; + case KVAL(K_P9): + k_fn(vc, KVAL(K_PGUP), 0); + return; + case KVAL(K_P5): + applkey(vc, 'G', vc_kbd_mode(kbd, VC_APPLIC)); + return; } + } put_queue(vc, pad_chars[value]); if (value == KVAL(K_PENTER) && vc_kbd_mode(kbd, VC_CRLF)) @@ -880,6 +871,7 @@ static void k_lock(struct vc_data *vc, unsigned char value, char up_flag) { if (up_flag || rep) return; + chg_vc_kbd_lock(kbd, value); } @@ -888,6 +880,7 @@ static void k_slock(struct vc_data *vc, unsigned char value, char up_flag) k_shift(vc, value, up_flag); if (up_flag || rep) return; + chg_vc_kbd_slock(kbd, value); /* try to make Alt, oops, AltGr and such work */ if (!key_maps[kbd->lockstate ^ kbd->slockstate]) { @@ -925,12 +918,12 @@ static void k_brlcommit(struct vc_data *vc, unsigned int pattern, char up_flag) static void k_brl(struct vc_data *vc, unsigned char value, char up_flag) { - static unsigned pressed,committing; + static unsigned pressed, committing; static unsigned long releasestart; if (kbd->kbdmode != VC_UNICODE) { if (!up_flag) - printk("keyboard mode must be unicode for braille patterns\n"); + pr_warning("keyboard mode must be unicode for braille patterns\n"); return; } @@ -942,32 +935,28 @@ static void k_brl(struct vc_data *vc, unsigned char value, char up_flag) if (value > 8) return; - if (up_flag) { - if (brl_timeout) { - if (!committing || - time_after(jiffies, - releasestart + msecs_to_jiffies(brl_timeout))) { - committing = pressed; - releasestart = jiffies; - } - pressed &= ~(1 << (value - 1)); - if (!pressed) { - if (committing) { - k_brlcommit(vc, committing, 0); - committing = 0; - } - } - } else { - if (committing) { - k_brlcommit(vc, committing, 0); - committing = 0; - } - pressed &= ~(1 << (value - 1)); - } - } else { + if (!up_flag) { pressed |= 1 << (value - 1); if (!brl_timeout) committing = pressed; + } else if (brl_timeout) { + if (!committing || + time_after(jiffies, + releasestart + msecs_to_jiffies(brl_timeout))) { + committing = pressed; + releasestart = jiffies; + } + pressed &= ~(1 << (value - 1)); + if (!pressed && committing) { + k_brlcommit(vc, committing, 0); + committing = 0; + } + } else { + if (committing) { + k_brlcommit(vc, committing, 0); + committing = 0; + } + pressed &= ~(1 << (value - 1)); } } @@ -988,6 +977,7 @@ void setledstate(struct kbd_struct *kbd, unsigned int led) kbd->ledmode = LED_SHOW_IOCTL; } else kbd->ledmode = LED_SHOW_FLAGS; + set_leds(); } @@ -1075,7 +1065,7 @@ static const unsigned short x86_keycodes[256] = 332,340,365,342,343,344,345,346,356,270,341,368,369,370,371,372 }; #ifdef CONFIG_SPARC -static int sparc_l1_a_state = 0; +static int sparc_l1_a_state; extern void sun_do_break(void); #endif @@ -1085,52 +1075,54 @@ static int emulate_raw(struct vc_data *vc, unsigned int keycode, int code; switch (keycode) { - case KEY_PAUSE: - put_queue(vc, 0xe1); - put_queue(vc, 0x1d | up_flag); - put_queue(vc, 0x45 | up_flag); - break; - case KEY_HANGEUL: - if (!up_flag) - put_queue(vc, 0xf2); - break; + case KEY_PAUSE: + put_queue(vc, 0xe1); + put_queue(vc, 0x1d | up_flag); + put_queue(vc, 0x45 | up_flag); + break; - case KEY_HANJA: - if (!up_flag) - put_queue(vc, 0xf1); - break; + case KEY_HANGEUL: + if (!up_flag) + put_queue(vc, 0xf2); + break; - case KEY_SYSRQ: - /* - * Real AT keyboards (that's what we're trying - * to emulate here emit 0xe0 0x2a 0xe0 0x37 when - * pressing PrtSc/SysRq alone, but simply 0x54 - * when pressing Alt+PrtSc/SysRq. - */ - if (sysrq_alt) { - put_queue(vc, 0x54 | up_flag); - } else { - put_queue(vc, 0xe0); - put_queue(vc, 0x2a | up_flag); - put_queue(vc, 0xe0); - put_queue(vc, 0x37 | up_flag); - } - break; + case KEY_HANJA: + if (!up_flag) + put_queue(vc, 0xf1); + break; - default: - if (keycode > 255) - return -1; + case KEY_SYSRQ: + /* + * Real AT keyboards (that's what we're trying + * to emulate here emit 0xe0 0x2a 0xe0 0x37 when + * pressing PrtSc/SysRq alone, but simply 0x54 + * when pressing Alt+PrtSc/SysRq. + */ + if (test_bit(KEY_LEFTALT, key_down) || + test_bit(KEY_RIGHTALT, key_down)) { + put_queue(vc, 0x54 | up_flag); + } else { + put_queue(vc, 0xe0); + put_queue(vc, 0x2a | up_flag); + put_queue(vc, 0xe0); + put_queue(vc, 0x37 | up_flag); + } + break; - code = x86_keycodes[keycode]; - if (!code) - return -1; + default: + if (keycode > 255) + return -1; - if (code & 0x100) - put_queue(vc, 0xe0); - put_queue(vc, (code & 0x7f) | up_flag); + code = x86_keycodes[keycode]; + if (!code) + return -1; - break; + if (code & 0x100) + put_queue(vc, 0xe0); + put_queue(vc, (code & 0x7f) | up_flag); + + break; } return 0; @@ -1153,6 +1145,7 @@ static int emulate_raw(struct vc_data *vc, unsigned int keycode, unsigned char u static void kbd_rawcode(unsigned char data) { struct vc_data *vc = vc_cons[fg_console].d; + kbd = kbd_table + vc->vc_num; if (kbd->kbdmode == VC_RAW) put_queue(vc, data); @@ -1162,10 +1155,12 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw) { struct vc_data *vc = vc_cons[fg_console].d; unsigned short keysym, *key_map; - unsigned char type, raw_mode; + unsigned char type; + bool raw_mode; struct tty_struct *tty; int shift_final; struct keyboard_notifier_param param = { .vc = vc, .value = keycode, .down = down }; + int rc; tty = vc->vc_tty; @@ -1176,8 +1171,6 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw) kbd = kbd_table + vc->vc_num; - if (keycode == KEY_LEFTALT || keycode == KEY_RIGHTALT) - sysrq_alt = down ? keycode : 0; #ifdef CONFIG_SPARC if (keycode == KEY_STOP) sparc_l1_a_state = down; @@ -1185,29 +1178,16 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw) rep = (down == 2); - if ((raw_mode = (kbd->kbdmode == VC_RAW)) && !hw_raw) + raw_mode = (kbd->kbdmode == VC_RAW); + if (raw_mode && !hw_raw) if (emulate_raw(vc, keycode, !down << 7)) if (keycode < BTN_MISC && printk_ratelimit()) - printk(KERN_WARNING "keyboard.c: can't emulate rawmode for keycode %d\n", keycode); + pr_warning("can't emulate rawmode for keycode %d\n", + keycode); -#ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq Hack */ - if (keycode == KEY_SYSRQ && (sysrq_down || (down == 1 && sysrq_alt))) { - if (!sysrq_down) { - sysrq_down = down; - sysrq_alt_use = sysrq_alt; - } - return; - } - if (sysrq_down && !down && keycode == sysrq_alt_use) - sysrq_down = 0; - if (sysrq_down && down && !rep) { - handle_sysrq(kbd_sysrq_xlate[keycode], tty); - return; - } -#endif #ifdef CONFIG_SPARC if (keycode == KEY_A && sparc_l1_a_state) { - sparc_l1_a_state = 0; + sparc_l1_a_state = false; sun_do_break(); } #endif @@ -1229,7 +1209,7 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw) put_queue(vc, (keycode >> 7) | 0x80); put_queue(vc, keycode | 0x80); } - raw_mode = 1; + raw_mode = true; } if (down) @@ -1252,29 +1232,32 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw) param.ledstate = kbd->ledflagstate; key_map = key_maps[shift_final]; - if (atomic_notifier_call_chain(&keyboard_notifier_list, KBD_KEYCODE, ¶m) == NOTIFY_STOP || !key_map) { - atomic_notifier_call_chain(&keyboard_notifier_list, KBD_UNBOUND_KEYCODE, ¶m); + rc = atomic_notifier_call_chain(&keyboard_notifier_list, + KBD_KEYCODE, ¶m); + if (rc == NOTIFY_STOP || !key_map) { + atomic_notifier_call_chain(&keyboard_notifier_list, + KBD_UNBOUND_KEYCODE, ¶m); compute_shiftstate(); kbd->slockstate = 0; return; } - if (keycode >= NR_KEYS) - if (keycode >= KEY_BRL_DOT1 && keycode <= KEY_BRL_DOT8) - keysym = U(K(KT_BRL, keycode - KEY_BRL_DOT1 + 1)); - else - return; - else + if (keycode < NR_KEYS) keysym = key_map[keycode]; + else if (keycode >= KEY_BRL_DOT1 && keycode <= KEY_BRL_DOT8) + keysym = U(K(KT_BRL, keycode - KEY_BRL_DOT1 + 1)); + else + return; type = KTYP(keysym); if (type < 0xf0) { param.value = keysym; - if (atomic_notifier_call_chain(&keyboard_notifier_list, KBD_UNICODE, ¶m) == NOTIFY_STOP) - return; - if (down && !raw_mode) - to_utf8(vc, keysym); + rc = atomic_notifier_call_chain(&keyboard_notifier_list, + KBD_UNICODE, ¶m); + if (rc != NOTIFY_STOP) + if (down && !raw_mode) + to_utf8(vc, keysym); return; } @@ -1288,9 +1271,11 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw) keysym = key_map[keycode]; } } - param.value = keysym; - if (atomic_notifier_call_chain(&keyboard_notifier_list, KBD_KEYSYM, ¶m) == NOTIFY_STOP) + param.value = keysym; + rc = atomic_notifier_call_chain(&keyboard_notifier_list, + KBD_KEYSYM, ¶m); + if (rc == NOTIFY_STOP) return; if (raw_mode && type != KT_SPEC && type != KT_SHIFT) diff --git a/drivers/char/n_gsm.c b/drivers/char/n_gsm.c new file mode 100644 index 00000000000..c4161d5e053 --- /dev/null +++ b/drivers/char/n_gsm.c @@ -0,0 +1,2763 @@ +/* + * n_gsm.c GSM 0710 tty multiplexor + * Copyright (c) 2009/10 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. + * + * 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 IS A DEVELOPMENT SNAPSHOT IT IS NOT A FINAL RELEASE * + * + * TO DO: + * Mostly done: ioctls for setting modes/timing + * Partly done: hooks so you can pull off frames to non tty devs + * Restart DLCI 0 when it closes ? + * Test basic encoding + * Improve the tx engine + * Resolve tx side locking by adding a queue_head and routing + * all control traffic via it + * General tidy/document + * Review the locking/move to refcounts more (mux now moved to an + * alloc/free model ready) + * Use newest tty open/close port helpers and install hooks + * What to do about power functions ? + * Termios setting and negotiation + * Do we need a 'which mux are you' ioctl to correlate mux and tty sets + * + */ + +#include <linux/types.h> +#include <linux/major.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/fcntl.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/tty.h> +#include <linux/timer.h> +#include <linux/ctype.h> +#include <linux/mm.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/poll.h> +#include <linux/bitops.h> +#include <linux/file.h> +#include <linux/uaccess.h> +#include <linux/module.h> +#include <linux/timer.h> +#include <linux/tty_flip.h> +#include <linux/tty_driver.h> +#include <linux/serial.h> +#include <linux/kfifo.h> +#include <linux/skbuff.h> +#include <linux/gsmmux.h> + +static int debug; +module_param(debug, int, 0600); + +#define T1 (HZ/10) +#define T2 (HZ/3) +#define N2 3 + +/* Use long timers for testing at low speed with debug on */ +#ifdef DEBUG_TIMING +#define T1 HZ +#define T2 (2 * HZ) +#endif + +/* Semi-arbitary buffer size limits. 0710 is normally run with 32-64 byte + limits so this is plenty */ +#define MAX_MRU 512 +#define MAX_MTU 512 + +/* + * Each block of data we have queued to go out is in the form of + * a gsm_msg which holds everything we need in a link layer independant + * format + */ + +struct gsm_msg { + struct gsm_msg *next; + u8 addr; /* DLCI address + flags */ + u8 ctrl; /* Control byte + flags */ + unsigned int len; /* Length of data block (can be zero) */ + unsigned char *data; /* Points into buffer but not at the start */ + unsigned char buffer[0]; +}; + +/* + * Each active data link has a gsm_dlci structure associated which ties + * the link layer to an optional tty (if the tty side is open). To avoid + * complexity right now these are only ever freed up when the mux is + * shut down. + * + * At the moment we don't free DLCI objects until the mux is torn down + * this avoid object life time issues but might be worth review later. + */ + +struct gsm_dlci { + struct gsm_mux *gsm; + int addr; + int state; +#define DLCI_CLOSED 0 +#define DLCI_OPENING 1 /* Sending SABM not seen UA */ +#define DLCI_OPEN 2 /* SABM/UA complete */ +#define DLCI_CLOSING 3 /* Sending DISC not seen UA/DM */ + + /* Link layer */ + spinlock_t lock; /* Protects the internal state */ + struct timer_list t1; /* Retransmit timer for SABM and UA */ + int retries; + /* Uplink tty if active */ + struct tty_port port; /* The tty bound to this DLCI if there is one */ + struct kfifo *fifo; /* Queue fifo for the DLCI */ + struct kfifo _fifo; /* For new fifo API porting only */ + int adaption; /* Adaption layer in use */ + u32 modem_rx; /* Our incoming virtual modem lines */ + u32 modem_tx; /* Our outgoing modem lines */ + int dead; /* Refuse re-open */ + /* Flow control */ + int throttled; /* Private copy of throttle state */ + int constipated; /* Throttle status for outgoing */ + /* Packetised I/O */ + struct sk_buff *skb; /* Frame being sent */ + struct sk_buff_head skb_list; /* Queued frames */ + /* Data handling callback */ + void (*data)(struct gsm_dlci *dlci, u8 *data, int len); +}; + +/* DLCI 0, 62/63 are special or reseved see gsmtty_open */ + +#define NUM_DLCI 64 + +/* + * DLCI 0 is used to pass control blocks out of band of the data + * flow (and with a higher link priority). One command can be outstanding + * at a time and we use this structure to manage them. They are created + * and destroyed by the user context, and updated by the receive paths + * and timers + */ + +struct gsm_control { + u8 cmd; /* Command we are issuing */ + u8 *data; /* Data for the command in case we retransmit */ + int len; /* Length of block for retransmission */ + int done; /* Done flag */ + int error; /* Error if any */ +}; + +/* + * Each GSM mux we have is represented by this structure. If we are + * operating as an ldisc then we use this structure as our ldisc + * state. We need to sort out lifetimes and locking with respect + * to the gsm mux array. For now we don't free DLCI objects that + * have been instantiated until the mux itself is terminated. + * + * To consider further: tty open versus mux shutdown. + */ + +struct gsm_mux { + struct tty_struct *tty; /* The tty our ldisc is bound to */ + spinlock_t lock; + + /* Events on the GSM channel */ + wait_queue_head_t event; + + /* Bits for GSM mode decoding */ + + /* Framing Layer */ + unsigned char *buf; + int state; +#define GSM_SEARCH 0 +#define GSM_START 1 +#define GSM_ADDRESS 2 +#define GSM_CONTROL 3 +#define GSM_LEN 4 +#define GSM_DATA 5 +#define GSM_FCS 6 +#define GSM_OVERRUN 7 + unsigned int len; + unsigned int address; + unsigned int count; + int escape; + int encoding; + u8 control; + u8 fcs; + u8 *txframe; /* TX framing buffer */ + + /* Methods for the receiver side */ + void (*receive)(struct gsm_mux *gsm, u8 ch); + void (*error)(struct gsm_mux *gsm, u8 ch, u8 flag); + /* And transmit side */ + int (*output)(struct gsm_mux *mux, u8 *data, int len); + + /* Link Layer */ + unsigned int mru; + unsigned int mtu; + int initiator; /* Did we initiate connection */ + int dead; /* Has the mux been shut down */ + struct gsm_dlci *dlci[NUM_DLCI]; + int constipated; /* Asked by remote to shut up */ + + spinlock_t tx_lock; + unsigned int tx_bytes; /* TX data outstanding */ +#define TX_THRESH_HI 8192 +#define TX_THRESH_LO 2048 + struct gsm_msg *tx_head; /* Pending data packets */ + struct gsm_msg *tx_tail; + + /* Control messages */ + struct timer_list t2_timer; /* Retransmit timer for commands */ + int cretries; /* Command retry counter */ + struct gsm_control *pending_cmd;/* Our current pending command */ + spinlock_t control_lock; /* Protects the pending command */ + + /* Configuration */ + int adaption; /* 1 or 2 supported */ + u8 ftype; /* UI or UIH */ + int t1, t2; /* Timers in 1/100th of a sec */ + int n2; /* Retry count */ + + /* Statistics (not currently exposed) */ + unsigned long bad_fcs; + unsigned long malformed; + unsigned long io_error; + unsigned long bad_size; + unsigned long unsupported; +}; + + +/* + * Mux objects - needed so that we can translate a tty index into the + * relevant mux and DLCI. + */ + +#define MAX_MUX 4 /* 256 minors */ +static struct gsm_mux *gsm_mux[MAX_MUX]; /* GSM muxes */ +static spinlock_t gsm_mux_lock; + +/* + * This section of the driver logic implements the GSM encodings + * both the basic and the 'advanced'. Reliable transport is not + * supported. + */ + +#define CR 0x02 +#define EA 0x01 +#define PF 0x10 + +/* I is special: the rest are ..*/ +#define RR 0x01 +#define UI 0x03 +#define RNR 0x05 +#define REJ 0x09 +#define DM 0x0F +#define SABM 0x2F +#define DISC 0x43 +#define UA 0x63 +#define UIH 0xEF + +/* Channel commands */ +#define CMD_NSC 0x09 +#define CMD_TEST 0x11 +#define CMD_PSC 0x21 +#define CMD_RLS 0x29 +#define CMD_FCOFF 0x31 +#define CMD_PN 0x41 +#define CMD_RPN 0x49 +#define CMD_FCON 0x51 +#define CMD_CLD 0x61 +#define CMD_SNC 0x69 +#define CMD_MSC 0x71 + +/* Virtual modem bits */ +#define MDM_FC 0x01 +#define MDM_RTC 0x02 +#define MDM_RTR 0x04 +#define MDM_IC 0x20 +#define MDM_DV 0x40 + +#define GSM0_SOF 0xF9 +#define GSM1_SOF 0x7E +#define GSM1_ESCAPE 0x7D +#define GSM1_ESCAPE_BITS 0x20 +#define XON 0x11 +#define XOFF 0x13 + +static const struct tty_port_operations gsm_port_ops; + +/* + * CRC table for GSM 0710 + */ + +static const u8 gsm_fcs8[256] = { + 0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, + 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B, + 0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, + 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67, + 0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, + 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43, + 0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, + 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F, + 0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, + 0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B, + 0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, + 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17, + 0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, + 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33, + 0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, + 0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F, + 0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95, + 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B, + 0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, + 0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87, + 0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD, + 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3, + 0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, + 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF, + 0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, + 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB, + 0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, + 0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7, + 0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, + 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3, + 0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, + 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF +}; + +#define INIT_FCS 0xFF +#define GOOD_FCS 0xCF + +/** + * gsm_fcs_add - update FCS + * @fcs: Current FCS + * @c: Next data + * + * Update the FCS to include c. Uses the algorithm in the specification + * notes. + */ + +static inline u8 gsm_fcs_add(u8 fcs, u8 c) +{ + return gsm_fcs8[fcs ^ c]; +} + +/** + * gsm_fcs_add_block - update FCS for a block + * @fcs: Current FCS + * @c: buffer of data + * @len: length of buffer + * + * Update the FCS to include c. Uses the algorithm in the specification + * notes. + */ + +static inline u8 gsm_fcs_add_block(u8 fcs, u8 *c, int len) +{ + while (len--) + fcs = gsm_fcs8[fcs ^ *c++]; + return fcs; +} + +/** + * gsm_read_ea - read a byte into an EA + * @val: variable holding value + * c: byte going into the EA + * + * Processes one byte of an EA. Updates the passed variable + * and returns 1 if the EA is now completely read + */ + +static int gsm_read_ea(unsigned int *val, u8 c) +{ + /* Add the next 7 bits into the value */ + *val <<= 7; + *val |= c >> 1; + /* Was this the last byte of the EA 1 = yes*/ + return c & EA; +} + +/** + * gsm_encode_modem - encode modem data bits + * @dlci: DLCI to encode from + * + * Returns the correct GSM encoded modem status bits (6 bit field) for + * the current status of the DLCI and attached tty object + */ + +static u8 gsm_encode_modem(const struct gsm_dlci *dlci) +{ + u8 modembits = 0; + /* FC is true flow control not modem bits */ + if (dlci->throttled) + modembits |= MDM_FC; + if (dlci->modem_tx & TIOCM_DTR) + modembits |= MDM_RTC; + if (dlci->modem_tx & TIOCM_RTS) + modembits |= MDM_RTR; + if (dlci->modem_tx & TIOCM_RI) + modembits |= MDM_IC; + if (dlci->modem_tx & TIOCM_CD) + modembits |= MDM_DV; + return modembits; +} + +/** + * gsm_print_packet - display a frame for debug + * @hdr: header to print before decode + * @addr: address EA from the frame + * @cr: C/R bit from the frame + * @control: control including PF bit + * @data: following data bytes + * @dlen: length of data + * + * Displays a packet in human readable format for debugging purposes. The + * style is based on amateur radio LAP-B dump display. + */ + +static void gsm_print_packet(const char *hdr, int addr, int cr, + u8 control, const u8 *data, int dlen) +{ + if (!(debug & 1)) + return; + + printk(KERN_INFO "%s %d) %c: ", hdr, addr, "RC"[cr]); + + switch (control & ~PF) { + case SABM: + printk(KERN_CONT "SABM"); + break; + case UA: + printk(KERN_CONT "UA"); + break; + case DISC: + printk(KERN_CONT "DISC"); + break; + case DM: + printk(KERN_CONT "DM"); + break; + case UI: + printk(KERN_CONT "UI"); + break; + case UIH: + printk(KERN_CONT "UIH"); + break; + default: + if (!(control & 0x01)) { + printk(KERN_CONT "I N(S)%d N(R)%d", + (control & 0x0E) >> 1, (control & 0xE)>> 5); + } else switch (control & 0x0F) { + case RR: + printk("RR(%d)", (control & 0xE0) >> 5); + break; + case RNR: + printk("RNR(%d)", (control & 0xE0) >> 5); + break; + case REJ: + printk("REJ(%d)", (control & 0xE0) >> 5); + break; + default: + printk(KERN_CONT "[%02X]", control); + } + } + + if (control & PF) + printk(KERN_CONT "(P)"); + else + printk(KERN_CONT "(F)"); + + if (dlen) { + int ct = 0; + while (dlen--) { + if (ct % 8 == 0) + printk(KERN_CONT "\n "); + printk(KERN_CONT "%02X ", *data++); + ct++; + } + } + printk(KERN_CONT "\n"); +} + + +/* + * Link level transmission side + */ + +/** + * gsm_stuff_packet - bytestuff a packet + * @ibuf: input + * @obuf: output + * @len: length of input + * + * Expand a buffer by bytestuffing it. The worst case size change + * is doubling and the caller is responsible for handing out + * suitable sized buffers. + */ + +static int gsm_stuff_frame(const u8 *input, u8 *output, int len) +{ + int olen = 0; + while (len--) { + if (*input == GSM1_SOF || *input == GSM1_ESCAPE + || *input == XON || *input == XOFF) { + *output++ = GSM1_ESCAPE; + *output++ = *input++ ^ GSM1_ESCAPE_BITS; + olen++; + } else + *output++ = *input++; + olen++; + } + return olen; +} + +static void hex_packet(const unsigned char *p, int len) +{ + int i; + for (i = 0; i < len; i++) { + if (i && (i % 16) == 0) + printk("\n"); + printk("%02X ", *p++); + } + printk("\n"); +} + +/** + * gsm_send - send a control frame + * @gsm: our GSM mux + * @addr: address for control frame + * @cr: command/response bit + * @control: control byte including PF bit + * + * Format up and transmit a control frame. These do not go via the + * queueing logic as they should be transmitted ahead of data when + * they are needed. + * + * FIXME: Lock versus data TX path + */ + +static void gsm_send(struct gsm_mux *gsm, int addr, int cr, int control) +{ + int len; + u8 cbuf[10]; + u8 ibuf[3]; + + switch (gsm->encoding) { + case 0: + cbuf[0] = GSM0_SOF; + cbuf[1] = (addr << 2) | (cr << 1) | EA; + cbuf[2] = control; + cbuf[3] = EA; /* Length of data = 0 */ + cbuf[4] = 0xFF - gsm_fcs_add_block(INIT_FCS, cbuf + 1, 3); + cbuf[5] = GSM0_SOF; + len = 6; + break; + case 1: + case 2: + /* Control frame + packing (but not frame stuffing) in mode 1 */ + ibuf[0] = (addr << 2) | (cr << 1) | EA; + ibuf[1] = control; + ibuf[2] = 0xFF - gsm_fcs_add_block(INIT_FCS, ibuf, 2); + /* Stuffing may double the size worst case */ + len = gsm_stuff_frame(ibuf, cbuf + 1, 3); + /* Now add the SOF markers */ + cbuf[0] = GSM1_SOF; + cbuf[len + 1] = GSM1_SOF; + /* FIXME: we can omit the lead one in many cases */ + len += 2; + break; + default: + WARN_ON(1); + return; + } + gsm->output(gsm, cbuf, len); + gsm_print_packet("-->", addr, cr, control, NULL, 0); +} + +/** + * gsm_response - send a control response + * @gsm: our GSM mux + * @addr: address for control frame + * @control: control byte including PF bit + * + * Format up and transmit a link level response frame. + */ + +static inline void gsm_response(struct gsm_mux *gsm, int addr, int control) +{ + gsm_send(gsm, addr, 0, control); +} + +/** + * gsm_command - send a control command + * @gsm: our GSM mux + * @addr: address for control frame + * @control: control byte including PF bit + * + * Format up and transmit a link level command frame. + */ + +static inline void gsm_command(struct gsm_mux *gsm, int addr, int control) +{ + gsm_send(gsm, addr, 1, control); +} + +/* Data transmission */ + +#define HDR_LEN 6 /* ADDR CTRL [LEN.2] DATA FCS */ + +/** + * gsm_data_alloc - allocate data frame + * @gsm: GSM mux + * @addr: DLCI address + * @len: length excluding header and FCS + * @ctrl: control byte + * + * Allocate a new data buffer for sending frames with data. Space is left + * at the front for header bytes but that is treated as an implementation + * detail and not for the high level code to use + */ + +static struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len, + u8 ctrl) +{ + struct gsm_msg *m = kmalloc(sizeof(struct gsm_msg) + len + HDR_LEN, + GFP_ATOMIC); + if (m == NULL) + return NULL; + m->data = m->buffer + HDR_LEN - 1; /* Allow for FCS */ + m->len = len; + m->addr = addr; + m->ctrl = ctrl; + m->next = NULL; + return m; +} + +/** + * gsm_data_kick - poke the queue + * @gsm: GSM Mux + * + * The tty device has called us to indicate that room has appeared in + * the transmit queue. Ram more data into the pipe if we have any + * + * FIXME: lock against link layer control transmissions + */ + +static void gsm_data_kick(struct gsm_mux *gsm) +{ + struct gsm_msg *msg = gsm->tx_head; + int len; + int skip_sof = 0; + + /* FIXME: We need to apply this solely to data messages */ + if (gsm->constipated) + return; + + while (gsm->tx_head != NULL) { + msg = gsm->tx_head; + if (gsm->encoding != 0) { + gsm->txframe[0] = GSM1_SOF; + len = gsm_stuff_frame(msg->data, + gsm->txframe + 1, msg->len); + gsm->txframe[len + 1] = GSM1_SOF; + len += 2; + } else { + gsm->txframe[0] = GSM0_SOF; + memcpy(gsm->txframe + 1 , msg->data, msg->len); + gsm->txframe[msg->len + 1] = GSM0_SOF; + len = msg->len + 2; + } + + if (debug & 4) { + printk("gsm_data_kick: \n"); + hex_packet(gsm->txframe, len); + } + + if (gsm->output(gsm, gsm->txframe + skip_sof, + len - skip_sof) < 0) + break; + /* FIXME: Can eliminate one SOF in many more cases */ + gsm->tx_head = msg->next; + if (gsm->tx_head == NULL) + gsm->tx_tail = NULL; + gsm->tx_bytes -= msg->len; + kfree(msg); + /* For a burst of frames skip the extra SOF within the + burst */ + skip_sof = 1; + } +} + +/** + * __gsm_data_queue - queue a UI or UIH frame + * @dlci: DLCI sending the data + * @msg: message queued + * + * Add data to the transmit queue and try and get stuff moving + * out of the mux tty if not already doing so. The Caller must hold + * the gsm tx lock. + */ + +static void __gsm_data_queue(struct gsm_dlci *dlci, struct gsm_msg *msg) +{ + struct gsm_mux *gsm = dlci->gsm; + u8 *dp = msg->data; + u8 *fcs = dp + msg->len; + + /* Fill in the header */ + if (gsm->encoding == 0) { + if (msg->len < 128) + *--dp = (msg->len << 1) | EA; + else { + *--dp = (msg->len >> 6) | EA; + *--dp = (msg->len & 127) << 1; + } + } + + *--dp = msg->ctrl; + if (gsm->initiator) + *--dp = (msg->addr << 2) | 2 | EA; + else + *--dp = (msg->addr << 2) | EA; + *fcs = gsm_fcs_add_block(INIT_FCS, dp , msg->data - dp); + /* Ugly protocol layering violation */ + if (msg->ctrl == UI || msg->ctrl == (UI|PF)) + *fcs = gsm_fcs_add_block(*fcs, msg->data, msg->len); + *fcs = 0xFF - *fcs; + + gsm_print_packet("Q> ", msg->addr, gsm->initiator, msg->ctrl, + msg->data, msg->len); + + /* Move the header back and adjust the length, also allow for the FCS + now tacked on the end */ + msg->len += (msg->data - dp) + 1; + msg->data = dp; + + /* Add to the actual output queue */ + if (gsm->tx_tail) + gsm->tx_tail->next = msg; + else + gsm->tx_head = msg; + gsm->tx_tail = msg; + gsm->tx_bytes += msg->len; + gsm_data_kick(gsm); +} + +/** + * gsm_data_queue - queue a UI or UIH frame + * @dlci: DLCI sending the data + * @msg: message queued + * + * Add data to the transmit queue and try and get stuff moving + * out of the mux tty if not already doing so. Take the + * the gsm tx lock and dlci lock. + */ + +static void gsm_data_queue(struct gsm_dlci *dlci, struct gsm_msg *msg) +{ + unsigned long flags; + spin_lock_irqsave(&dlci->gsm->tx_lock, flags); + __gsm_data_queue(dlci, msg); + spin_unlock_irqrestore(&dlci->gsm->tx_lock, flags); +} + +/** + * gsm_dlci_data_output - try and push data out of a DLCI + * @gsm: mux + * @dlci: the DLCI to pull data from + * + * Pull data from a DLCI and send it into the transmit queue if there + * is data. Keep to the MRU of the mux. This path handles the usual tty + * interface which is a byte stream with optional modem data. + * + * Caller must hold the tx_lock of the mux. + */ + +static int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci) +{ + struct gsm_msg *msg; + u8 *dp; + int len, size; + int h = dlci->adaption - 1; + + len = kfifo_len(dlci->fifo); + if (len == 0) + return 0; + + /* MTU/MRU count only the data bits */ + if (len > gsm->mtu) + len = gsm->mtu; + + size = len + h; + + msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype); + /* FIXME: need a timer or something to kick this so it can't + get stuck with no work outstanding and no buffer free */ + if (msg == NULL) + return -ENOMEM; + dp = msg->data; + switch (dlci->adaption) { + case 1: /* Unstructured */ + break; + case 2: /* Unstructed with modem bits. Always one byte as we never + send inline break data */ + *dp += gsm_encode_modem(dlci); + len--; + break; + } + WARN_ON(kfifo_out_locked(dlci->fifo, dp , len, &dlci->lock) != len); + __gsm_data_queue(dlci, msg); + /* Bytes of data we used up */ + return size; +} + +/** + * gsm_dlci_data_output_framed - try and push data out of a DLCI + * @gsm: mux + * @dlci: the DLCI to pull data from + * + * Pull data from a DLCI and send it into the transmit queue if there + * is data. Keep to the MRU of the mux. This path handles framed data + * queued as skbuffs to the DLCI. + * + * Caller must hold the tx_lock of the mux. + */ + +static int gsm_dlci_data_output_framed(struct gsm_mux *gsm, + struct gsm_dlci *dlci) +{ + struct gsm_msg *msg; + u8 *dp; + int len, size; + int last = 0, first = 0; + int overhead = 0; + + /* One byte per frame is used for B/F flags */ + if (dlci->adaption == 4) + overhead = 1; + + /* dlci->skb is locked by tx_lock */ + if (dlci->skb == NULL) { + dlci->skb = skb_dequeue(&dlci->skb_list); + if (dlci->skb == NULL) + return 0; + first = 1; + } + len = dlci->skb->len + overhead; + + /* MTU/MRU count only the data bits */ + if (len > gsm->mtu) { + if (dlci->adaption == 3) { + /* Over long frame, bin it */ + kfree_skb(dlci->skb); + dlci->skb = NULL; + return 0; + } + len = gsm->mtu; + } else + last = 1; + + size = len + overhead; + msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype); + + /* FIXME: need a timer or something to kick this so it can't + get stuck with no work outstanding and no buffer free */ + if (msg == NULL) + return -ENOMEM; + dp = msg->data; + + if (dlci->adaption == 4) { /* Interruptible framed (Packetised Data) */ + /* Flag byte to carry the start/end info */ + *dp++ = last << 7 | first << 6 | 1; /* EA */ + len--; + } + memcpy(dp, skb_pull(dlci->skb, len), len); + __gsm_data_queue(dlci, msg); + if (last) + dlci->skb = NULL; + return size; +} + +/** + * gsm_dlci_data_sweep - look for data to send + * @gsm: the GSM mux + * + * Sweep the GSM mux channels in priority order looking for ones with + * data to send. We could do with optimising this scan a bit. We aim + * to fill the queue totally or up to TX_THRESH_HI bytes. Once we hit + * TX_THRESH_LO we get called again + * + * FIXME: We should round robin between groups and in theory you can + * renegotiate DLCI priorities with optional stuff. Needs optimising. + */ + +static void gsm_dlci_data_sweep(struct gsm_mux *gsm) +{ + int len; + /* Priority ordering: We should do priority with RR of the groups */ + int i = 1; + unsigned long flags; + + spin_lock_irqsave(&gsm->tx_lock, flags); + while (i < NUM_DLCI) { + struct gsm_dlci *dlci; + + if (gsm->tx_bytes > TX_THRESH_HI) + break; + dlci = gsm->dlci[i]; + if (dlci == NULL || dlci->constipated) { + i++; + continue; + } + if (dlci->adaption < 3) + len = gsm_dlci_data_output(gsm, dlci); + else + len = gsm_dlci_data_output_framed(gsm, dlci); + if (len < 0) + return; + /* DLCI empty - try the next */ + if (len == 0) + i++; + } + spin_unlock_irqrestore(&gsm->tx_lock, flags); +} + +/** + * gsm_dlci_data_kick - transmit if possible + * @dlci: DLCI to kick + * + * Transmit data from this DLCI if the queue is empty. We can't rely on + * a tty wakeup except when we filled the pipe so we need to fire off + * new data ourselves in other cases. + */ + +static void gsm_dlci_data_kick(struct gsm_dlci *dlci) +{ + unsigned long flags; + + spin_lock_irqsave(&dlci->gsm->tx_lock, flags); + /* If we have nothing running then we need to fire up */ + if (dlci->gsm->tx_bytes == 0) + gsm_dlci_data_output(dlci->gsm, dlci); + else if (dlci->gsm->tx_bytes < TX_THRESH_LO) + gsm_dlci_data_sweep(dlci->gsm); + spin_unlock_irqrestore(&dlci->gsm->tx_lock, flags); +} + +/* + * Control message processing + */ + + +/** + * gsm_control_reply - send a response frame to a control + * @gsm: gsm channel + * @cmd: the command to use + * @data: data to follow encoded info + * @dlen: length of data + * + * Encode up and queue a UI/UIH frame containing our response. + */ + +static void gsm_control_reply(struct gsm_mux *gsm, int cmd, u8 *data, + int dlen) +{ + struct gsm_msg *msg; + msg = gsm_data_alloc(gsm, 0, dlen + 2, gsm->ftype); + msg->data[0] = (cmd & 0xFE) << 1 | EA; /* Clear C/R */ + msg->data[1] = (dlen << 1) | EA; + memcpy(msg->data + 2, data, dlen); + gsm_data_queue(gsm->dlci[0], msg); +} + +/** + * gsm_process_modem - process received modem status + * @tty: virtual tty bound to the DLCI + * @dlci: DLCI to affect + * @modem: modem bits (full EA) + * + * Used when a modem control message or line state inline in adaption + * layer 2 is processed. Sort out the local modem state and throttles + */ + +static void gsm_process_modem(struct tty_struct *tty, struct gsm_dlci *dlci, + u32 modem) +{ + int mlines = 0; + u8 brk = modem >> 6; + + /* Flow control/ready to communicate */ + if (modem & MDM_FC) { + /* Need to throttle our output on this device */ + dlci->constipated = 1; + } + if (modem & MDM_RTC) { + mlines |= TIOCM_DSR | TIOCM_DTR; + dlci->constipated = 0; + gsm_dlci_data_kick(dlci); + } + /* Map modem bits */ + if (modem & MDM_RTR) + mlines |= TIOCM_RTS | TIOCM_CTS; + if (modem & MDM_IC) + mlines |= TIOCM_RI; + if (modem & MDM_DV) + mlines |= TIOCM_CD; + + /* Carrier drop -> hangup */ + if (tty) { + if ((mlines & TIOCM_CD) == 0 && (dlci->modem_rx & TIOCM_CD)) + if (!(tty->termios->c_cflag & CLOCAL)) + tty_hangup(tty); + if (brk & 0x01) + tty_insert_flip_char(tty, 0, TTY_BREAK); + } + dlci->modem_rx = mlines; +} + +/** + * gsm_control_modem - modem status received + * @gsm: GSM channel + * @data: data following command + * @clen: command length + * + * We have received a modem status control message. This is used by + * the GSM mux protocol to pass virtual modem line status and optionally + * to indicate break signals. Unpack it, convert to Linux representation + * and if need be stuff a break message down the tty. + */ + +static void gsm_control_modem(struct gsm_mux *gsm, u8 *data, int clen) +{ + unsigned int addr = 0; + unsigned int modem = 0; + struct gsm_dlci *dlci; + int len = clen; + u8 *dp = data; + struct tty_struct *tty; + + while (gsm_read_ea(&addr, *dp++) == 0) { + len--; + if (len == 0) + return; + } + /* Must be at least one byte following the EA */ + len--; + if (len <= 0) + return; + + addr >>= 1; + /* Closed port, or invalid ? */ + if (addr == 0 || addr >= NUM_DLCI || gsm->dlci[addr] == NULL) + return; + dlci = gsm->dlci[addr]; + + while (gsm_read_ea(&modem, *dp++) == 0) { + len--; + if (len == 0) + return; + } + tty = tty_port_tty_get(&dlci->port); + gsm_process_modem(tty, dlci, modem); + if (tty) { + tty_wakeup(tty); + tty_kref_put(tty); + } + gsm_control_reply(gsm, CMD_MSC, data, clen); +} + +/** + * gsm_control_rls - remote line status + * @gsm: GSM channel + * @data: data bytes + * @clen: data length + * + * The modem sends us a two byte message on the control channel whenever + * it wishes to send us an error state from the virtual link. Stuff + * this into the uplink tty if present + */ + +static void gsm_control_rls(struct gsm_mux *gsm, u8 *data, int clen) +{ + struct tty_struct *tty; + unsigned int addr = 0 ; + u8 bits; + int len = clen; + u8 *dp = data; + + while (gsm_read_ea(&addr, *dp++) == 0) { + len--; + if (len == 0) + return; + } + /* Must be at least one byte following ea */ + len--; + if (len <= 0) + return; + addr >>= 1; + /* Closed port, or invalid ? */ + if (addr == 0 || addr >= NUM_DLCI || gsm->dlci[addr] == NULL) + return; + /* No error ? */ + bits = *dp; + if ((bits & 1) == 0) + return; + /* See if we have an uplink tty */ + tty = tty_port_tty_get(&gsm->dlci[addr]->port); + + if (tty) { + if (bits & 2) + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + if (bits & 4) + tty_insert_flip_char(tty, 0, TTY_PARITY); + if (bits & 8) + tty_insert_flip_char(tty, 0, TTY_FRAME); + tty_flip_buffer_push(tty); + tty_kref_put(tty); + } + gsm_control_reply(gsm, CMD_RLS, data, clen); +} + +static void gsm_dlci_begin_close(struct gsm_dlci *dlci); + +/** + * gsm_control_message - DLCI 0 control processing + * @gsm: our GSM mux + * @command: the command EA + * @data: data beyond the command/length EAs + * @clen: length + * + * Input processor for control messages from the other end of the link. + * Processes the incoming request and queues a response frame or an + * NSC response if not supported + */ + +static void gsm_control_message(struct gsm_mux *gsm, unsigned int command, + u8 *data, int clen) +{ + u8 buf[1]; + switch (command) { + case CMD_CLD: { + struct gsm_dlci *dlci = gsm->dlci[0]; + /* Modem wishes to close down */ + if (dlci) { + dlci->dead = 1; + gsm->dead = 1; + gsm_dlci_begin_close(dlci); + } + } + break; + case CMD_TEST: + /* Modem wishes to test, reply with the data */ + gsm_control_reply(gsm, CMD_TEST, data, clen); + break; + case CMD_FCON: + /* Modem wants us to STFU */ + gsm->constipated = 1; + gsm_control_reply(gsm, CMD_FCON, NULL, 0); + break; + case CMD_FCOFF: + /* Modem can accept data again */ + gsm->constipated = 0; + gsm_control_reply(gsm, CMD_FCOFF, NULL, 0); + /* Kick the link in case it is idling */ + gsm_data_kick(gsm); + break; + case CMD_MSC: + /* Out of band modem line change indicator for a DLCI */ + gsm_control_modem(gsm, data, clen); + break; + case CMD_RLS: + /* Out of band error reception for a DLCI */ + gsm_control_rls(gsm, data, clen); + break; + case CMD_PSC: + /* Modem wishes to enter power saving state */ + gsm_control_reply(gsm, CMD_PSC, NULL, 0); + break; + /* Optional unsupported commands */ + case CMD_PN: /* Parameter negotiation */ + case CMD_RPN: /* Remote port negotation */ + case CMD_SNC: /* Service negotation command */ + default: + /* Reply to bad commands with an NSC */ + buf[0] = command; + gsm_control_reply(gsm, CMD_NSC, buf, 1); + break; + } +} + +/** + * gsm_control_response - process a response to our control + * @gsm: our GSM mux + * @command: the command (response) EA + * @data: data beyond the command/length EA + * @clen: length + * + * Process a response to an outstanding command. We only allow a single + * control message in flight so this is fairly easy. All the clean up + * is done by the caller, we just update the fields, flag it as done + * and return + */ + +static void gsm_control_response(struct gsm_mux *gsm, unsigned int command, + u8 *data, int clen) +{ + struct gsm_control *ctrl; + unsigned long flags; + + spin_lock_irqsave(&gsm->control_lock, flags); + + ctrl = gsm->pending_cmd; + /* Does the reply match our command */ + command |= 1; + if (ctrl != NULL && (command == ctrl->cmd || command == CMD_NSC)) { + /* Our command was replied to, kill the retry timer */ + del_timer(&gsm->t2_timer); + gsm->pending_cmd = NULL; + /* Rejected by the other end */ + if (command == CMD_NSC) + ctrl->error = -EOPNOTSUPP; + ctrl->done = 1; + wake_up(&gsm->event); + } + spin_unlock_irqrestore(&gsm->control_lock, flags); +} + +/** + * gsm_control_transmit - send control packet + * @gsm: gsm mux + * @ctrl: frame to send + * + * Send out a pending control command (called under control lock) + */ + +static void gsm_control_transmit(struct gsm_mux *gsm, struct gsm_control *ctrl) +{ + struct gsm_msg *msg = gsm_data_alloc(gsm, 0, ctrl->len + 1, + gsm->ftype|PF); + if (msg == NULL) + return; + msg->data[0] = (ctrl->cmd << 1) | 2 | EA; /* command */ + memcpy(msg->data + 1, ctrl->data, ctrl->len); + gsm_data_queue(gsm->dlci[0], msg); +} + +/** + * gsm_control_retransmit - retransmit a control frame + * @data: pointer to our gsm object + * + * Called off the T2 timer expiry in order to retransmit control frames + * that have been lost in the system somewhere. The control_lock protects + * us from colliding with another sender or a receive completion event. + * In that situation the timer may still occur in a small window but + * gsm->pending_cmd will be NULL and we just let the timer expire. + */ + +static void gsm_control_retransmit(unsigned long data) +{ + struct gsm_mux *gsm = (struct gsm_mux *)data; + struct gsm_control *ctrl; + unsigned long flags; + spin_lock_irqsave(&gsm->control_lock, flags); + ctrl = gsm->pending_cmd; + if (ctrl) { + gsm->cretries--; + if (gsm->cretries == 0) { + gsm->pending_cmd = NULL; + ctrl->error = -ETIMEDOUT; + ctrl->done = 1; + spin_unlock_irqrestore(&gsm->control_lock, flags); + wake_up(&gsm->event); + return; + } + gsm_control_transmit(gsm, ctrl); + mod_timer(&gsm->t2_timer, jiffies + gsm->t2 * HZ / 100); + } + spin_unlock_irqrestore(&gsm->control_lock, flags); +} + +/** + * gsm_control_send - send a control frame on DLCI 0 + * @gsm: the GSM channel + * @command: command to send including CR bit + * @data: bytes of data (must be kmalloced) + * @len: length of the block to send + * + * Queue and dispatch a control command. Only one command can be + * active at a time. In theory more can be outstanding but the matching + * gets really complicated so for now stick to one outstanding. + */ + +static struct gsm_control *gsm_control_send(struct gsm_mux *gsm, + unsigned int command, u8 *data, int clen) +{ + struct gsm_control *ctrl = kzalloc(sizeof(struct gsm_control), + GFP_KERNEL); + unsigned long flags; + if (ctrl == NULL) + return NULL; +retry: + wait_event(gsm->event, gsm->pending_cmd == NULL); + spin_lock_irqsave(&gsm->control_lock, flags); + if (gsm->pending_cmd != NULL) { + spin_unlock_irqrestore(&gsm->control_lock, flags); + goto retry; + } + ctrl->cmd = command; + ctrl->data = data; + ctrl->len = clen; + gsm->pending_cmd = ctrl; + gsm->cretries = gsm->n2; + mod_timer(&gsm->t2_timer, jiffies + gsm->t2 * HZ / 100); + gsm_control_transmit(gsm, ctrl); + spin_unlock_irqrestore(&gsm->control_lock, flags); + return ctrl; +} + +/** + * gsm_control_wait - wait for a control to finish + * @gsm: GSM mux + * @control: control we are waiting on + * + * Waits for the control to complete or time out. Frees any used + * resources and returns 0 for success, or an error if the remote + * rejected or ignored the request. + */ + +static int gsm_control_wait(struct gsm_mux *gsm, struct gsm_control *control) +{ + int err; + wait_event(gsm->event, control->done == 1); + err = control->error; + kfree(control); + return err; +} + + +/* + * DLCI level handling: Needs krefs + */ + +/* + * State transitions and timers + */ + +/** + * gsm_dlci_close - a DLCI has closed + * @dlci: DLCI that closed + * + * Perform processing when moving a DLCI into closed state. If there + * is an attached tty this is hung up + */ + +static void gsm_dlci_close(struct gsm_dlci *dlci) +{ + del_timer(&dlci->t1); + if (debug & 8) + printk("DLCI %d goes closed.\n", dlci->addr); + dlci->state = DLCI_CLOSED; + if (dlci->addr != 0) { + struct tty_struct *tty = tty_port_tty_get(&dlci->port); + if (tty) { + tty_hangup(tty); + tty_kref_put(tty); + } + kfifo_reset(dlci->fifo); + } else + dlci->gsm->dead = 1; + wake_up(&dlci->gsm->event); + /* A DLCI 0 close is a MUX termination so we need to kick that + back to userspace somehow */ +} + +/** + * gsm_dlci_open - a DLCI has opened + * @dlci: DLCI that opened + * + * Perform processing when moving a DLCI into open state. + */ + +static void gsm_dlci_open(struct gsm_dlci *dlci) +{ + /* Note that SABM UA .. SABM UA first UA lost can mean that we go + open -> open */ + del_timer(&dlci->t1); + /* This will let a tty open continue */ + dlci->state = DLCI_OPEN; + if (debug & 8) + printk("DLCI %d goes open.\n", dlci->addr); + wake_up(&dlci->gsm->event); +} + +/** + * gsm_dlci_t1 - T1 timer expiry + * @dlci: DLCI that opened + * + * The T1 timer handles retransmits of control frames (essentially of + * SABM and DISC). We resend the command until the retry count runs out + * in which case an opening port goes back to closed and a closing port + * is simply put into closed state (any further frames from the other + * end will get a DM response) + */ + +static void gsm_dlci_t1(unsigned long data) +{ + struct gsm_dlci *dlci = (struct gsm_dlci *)data; + struct gsm_mux *gsm = dlci->gsm; + + switch (dlci->state) { + case DLCI_OPENING: + dlci->retries--; + if (dlci->retries) { + gsm_command(dlci->gsm, dlci->addr, SABM|PF); + mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100); + } else + gsm_dlci_close(dlci); + break; + case DLCI_CLOSING: + dlci->retries--; + if (dlci->retries) { + gsm_command(dlci->gsm, dlci->addr, DISC|PF); + mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100); + } else + gsm_dlci_close(dlci); + break; + } +} + +/** + * gsm_dlci_begin_open - start channel open procedure + * @dlci: DLCI to open + * + * Commence opening a DLCI from the Linux side. We issue SABM messages + * to the modem which should then reply with a UA, at which point we + * will move into open state. Opening is done asynchronously with retry + * running off timers and the responses. + */ + +static void gsm_dlci_begin_open(struct gsm_dlci *dlci) +{ + struct gsm_mux *gsm = dlci->gsm; + if (dlci->state == DLCI_OPEN || dlci->state == DLCI_OPENING) + return; + dlci->retries = gsm->n2; + dlci->state = DLCI_OPENING; + gsm_command(dlci->gsm, dlci->addr, SABM|PF); + mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100); +} + +/** + * gsm_dlci_begin_close - start channel open procedure + * @dlci: DLCI to open + * + * Commence closing a DLCI from the Linux side. We issue DISC messages + * to the modem which should then reply with a UA, at which point we + * will move into closed state. Closing is done asynchronously with retry + * off timers. We may also receive a DM reply from the other end which + * indicates the channel was already closed. + */ + +static void gsm_dlci_begin_close(struct gsm_dlci *dlci) +{ + struct gsm_mux *gsm = dlci->gsm; + if (dlci->state == DLCI_CLOSED || dlci->state == DLCI_CLOSING) + return; + dlci->retries = gsm->n2; + dlci->state = DLCI_CLOSING; + gsm_command(dlci->gsm, dlci->addr, DISC|PF); + mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100); +} + +/** + * gsm_dlci_data - data arrived + * @dlci: channel + * @data: block of bytes received + * @len: length of received block + * + * A UI or UIH frame has arrived which contains data for a channel + * other than the control channel. If the relevant virtual tty is + * open we shovel the bits down it, if not we drop them. + */ + +static void gsm_dlci_data(struct gsm_dlci *dlci, u8 *data, int len) +{ + /* krefs .. */ + struct tty_port *port = &dlci->port; + struct tty_struct *tty = tty_port_tty_get(port); + unsigned int modem = 0; + + if (debug & 16) + printk("%d bytes for tty %p\n", len, tty); + if (tty) { + switch (dlci->adaption) { + /* Unsupported types */ + /* Packetised interruptible data */ + case 4: + break; + /* Packetised uininterruptible voice/data */ + case 3: + break; + /* Asynchronous serial with line state in each frame */ + case 2: + while (gsm_read_ea(&modem, *data++) == 0) { + len--; + if (len == 0) + return; + } + gsm_process_modem(tty, dlci, modem); + /* Line state will go via DLCI 0 controls only */ + case 1: + default: + tty_insert_flip_string(tty, data, len); + tty_flip_buffer_push(tty); + } + tty_kref_put(tty); + } +} + +/** + * gsm_dlci_control - data arrived on control channel + * @dlci: channel + * @data: block of bytes received + * @len: length of received block + * + * A UI or UIH frame has arrived which contains data for DLCI 0 the + * control channel. This should contain a command EA followed by + * control data bytes. The command EA contains a command/response bit + * and we divide up the work accordingly. + */ + +static void gsm_dlci_command(struct gsm_dlci *dlci, u8 *data, int len) +{ + /* See what command is involved */ + unsigned int command = 0; + while (len-- > 0) { + if (gsm_read_ea(&command, *data++) == 1) { + int clen = *data++; + len--; + /* FIXME: this is properly an EA */ + clen >>= 1; + /* Malformed command ? */ + if (clen > len) + return; + if (command & 1) + gsm_control_message(dlci->gsm, command, + data, clen); + else + gsm_control_response(dlci->gsm, command, + data, clen); + return; + } + } +} + +/* + * Allocate/Free DLCI channels + */ + +/** + * gsm_dlci_alloc - allocate a DLCI + * @gsm: GSM mux + * @addr: address of the DLCI + * + * Allocate and install a new DLCI object into the GSM mux. + * + * FIXME: review locking races + */ + +static struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux *gsm, int addr) +{ + struct gsm_dlci *dlci = kzalloc(sizeof(struct gsm_dlci), GFP_ATOMIC); + if (dlci == NULL) + return NULL; + spin_lock_init(&dlci->lock); + dlci->fifo = &dlci->_fifo; + if (kfifo_alloc(&dlci->_fifo, 4096, GFP_KERNEL) < 0) { + kfree(dlci); + return NULL; + } + + skb_queue_head_init(&dlci->skb_list); + init_timer(&dlci->t1); + dlci->t1.function = gsm_dlci_t1; + dlci->t1.data = (unsigned long)dlci; + tty_port_init(&dlci->port); + dlci->port.ops = &gsm_port_ops; + dlci->gsm = gsm; + dlci->addr = addr; + dlci->adaption = gsm->adaption; + dlci->state = DLCI_CLOSED; + if (addr) + dlci->data = gsm_dlci_data; + else + dlci->data = gsm_dlci_command; + gsm->dlci[addr] = dlci; + return dlci; +} + +/** + * gsm_dlci_free - release DLCI + * @dlci: DLCI to destroy + * + * Free up a DLCI. Currently to keep the lifetime rules sane we only + * clean up DLCI objects when the MUX closes rather than as the port + * is closed down on both the tty and mux levels. + * + * Can sleep. + */ +static void gsm_dlci_free(struct gsm_dlci *dlci) +{ + struct tty_struct *tty = tty_port_tty_get(&dlci->port); + if (tty) { + tty_vhangup(tty); + tty_kref_put(tty); + } + del_timer_sync(&dlci->t1); + dlci->gsm->dlci[dlci->addr] = NULL; + kfifo_free(dlci->fifo); + kfree(dlci); +} + + +/* + * LAPBish link layer logic + */ + +/** + * gsm_queue - a GSM frame is ready to process + * @gsm: pointer to our gsm mux + * + * At this point in time a frame has arrived and been demangled from + * the line encoding. All the differences between the encodings have + * been handled below us and the frame is unpacked into the structures. + * The fcs holds the header FCS but any data FCS must be added here. + */ + +static void gsm_queue(struct gsm_mux *gsm) +{ + struct gsm_dlci *dlci; + u8 cr; + int address; + /* We have to sneak a look at the packet body to do the FCS. + A somewhat layering violation in the spec */ + + if ((gsm->control & ~PF) == UI) + gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf, gsm->len); + if (gsm->fcs != GOOD_FCS) { + gsm->bad_fcs++; + if (debug & 4) + printk("BAD FCS %02x\n", gsm->fcs); + return; + } + address = gsm->address >> 1; + if (address >= NUM_DLCI) + goto invalid; + + cr = gsm->address & 1; /* C/R bit */ + + gsm_print_packet("<--", address, cr, gsm->control, gsm->buf, gsm->len); + + cr ^= 1 - gsm->initiator; /* Flip so 1 always means command */ + dlci = gsm->dlci[address]; + + switch (gsm->control) { + case SABM|PF: + if (cr == 0) + goto invalid; + if (dlci == NULL) + dlci = gsm_dlci_alloc(gsm, address); + if (dlci == NULL) + return; + if (dlci->dead) + gsm_response(gsm, address, DM); + else { + gsm_response(gsm, address, UA); + gsm_dlci_open(dlci); + } + break; + case DISC|PF: + if (cr == 0) + goto invalid; + if (dlci == NULL || dlci->state == DLCI_CLOSED) { + gsm_response(gsm, address, DM); + return; + } + /* Real close complete */ + gsm_response(gsm, address, UA); + gsm_dlci_close(dlci); + break; + case UA: + case UA|PF: + if (cr == 0 || dlci == NULL) + break; + switch (dlci->state) { + case DLCI_CLOSING: + gsm_dlci_close(dlci); + break; + case DLCI_OPENING: + gsm_dlci_open(dlci); + break; + } + break; + case DM: /* DM can be valid unsolicited */ + case DM|PF: + if (cr) + goto invalid; + if (dlci == NULL) + return; + gsm_dlci_close(dlci); + break; + case UI: + case UI|PF: + case UIH: + case UIH|PF: +#if 0 + if (cr) + goto invalid; +#endif + if (dlci == NULL || dlci->state != DLCI_OPEN) { + gsm_command(gsm, address, DM|PF); + return; + } + dlci->data(dlci, gsm->buf, gsm->len); + break; + default: + goto invalid; + } + return; +invalid: + gsm->malformed++; + return; +} + + +/** + * gsm0_receive - perform processing for non-transparency + * @gsm: gsm data for this ldisc instance + * @c: character + * + * Receive bytes in gsm mode 0 + */ + +static void gsm0_receive(struct gsm_mux *gsm, unsigned char c) +{ + switch (gsm->state) { + case GSM_SEARCH: /* SOF marker */ + if (c == GSM0_SOF) { + gsm->state = GSM_ADDRESS; + gsm->address = 0; + gsm->len = 0; + gsm->fcs = INIT_FCS; + } + break; /* Address EA */ + case GSM_ADDRESS: + gsm->fcs = gsm_fcs_add(gsm->fcs, c); + if (gsm_read_ea(&gsm->address, c)) + gsm->state = GSM_CONTROL; + break; + case GSM_CONTROL: /* Control Byte */ + gsm->fcs = gsm_fcs_add(gsm->fcs, c); + gsm->control = c; + gsm->state = GSM_LEN; + break; + case GSM_LEN: /* Length EA */ + gsm->fcs = gsm_fcs_add(gsm->fcs, c); + if (gsm_read_ea(&gsm->len, c)) { + if (gsm->len > gsm->mru) { + gsm->bad_size++; + gsm->state = GSM_SEARCH; + break; + } + gsm->count = 0; + gsm->state = GSM_DATA; + } + break; + case GSM_DATA: /* Data */ + gsm->buf[gsm->count++] = c; + if (gsm->count == gsm->len) + gsm->state = GSM_FCS; + break; + case GSM_FCS: /* FCS follows the packet */ + gsm->fcs = c; + gsm_queue(gsm); + /* And then back for the next frame */ + gsm->state = GSM_SEARCH; + break; + } +} + +/** + * gsm0_receive - perform processing for non-transparency + * @gsm: gsm data for this ldisc instance + * @c: character + * + * Receive bytes in mode 1 (Advanced option) + */ + +static void gsm1_receive(struct gsm_mux *gsm, unsigned char c) +{ + if (c == GSM1_SOF) { + /* EOF is only valid in frame if we have got to the data state + and received at least one byte (the FCS) */ + if (gsm->state == GSM_DATA && gsm->count) { + /* Extract the FCS */ + gsm->count--; + gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->buf[gsm->count]); + gsm->len = gsm->count; + gsm_queue(gsm); + gsm->state = GSM_START; + return; + } + /* Any partial frame was a runt so go back to start */ + if (gsm->state != GSM_START) { + gsm->malformed++; + gsm->state = GSM_START; + } + /* A SOF in GSM_START means we are still reading idling or + framing bytes */ + return; + } + + if (c == GSM1_ESCAPE) { + gsm->escape = 1; + return; + } + + /* Only an unescaped SOF gets us out of GSM search */ + if (gsm->state == GSM_SEARCH) + return; + + if (gsm->escape) { + c ^= GSM1_ESCAPE_BITS; + gsm->escape = 0; + } + switch (gsm->state) { + case GSM_START: /* First byte after SOF */ + gsm->address = 0; + gsm->state = GSM_ADDRESS; + gsm->fcs = INIT_FCS; + /* Drop through */ + case GSM_ADDRESS: /* Address continuation */ + gsm->fcs = gsm_fcs_add(gsm->fcs, c); + if (gsm_read_ea(&gsm->address, c)) + gsm->state = GSM_CONTROL; + break; + case GSM_CONTROL: /* Control Byte */ + gsm->fcs = gsm_fcs_add(gsm->fcs, c); + gsm->control = c; + gsm->count = 0; + gsm->state = GSM_DATA; + break; + case GSM_DATA: /* Data */ + if (gsm->count > gsm->mru ) { /* Allow one for the FCS */ + gsm->state = GSM_OVERRUN; + gsm->bad_size++; + } else + gsm->buf[gsm->count++] = c; + break; + case GSM_OVERRUN: /* Over-long - eg a dropped SOF */ + break; + } +} + +/** + * gsm_error - handle tty error + * @gsm: ldisc data + * @data: byte received (may be invalid) + * @flag: error received + * + * Handle an error in the receipt of data for a frame. Currently we just + * go back to hunting for a SOF. + * + * FIXME: better diagnostics ? + */ + +static void gsm_error(struct gsm_mux *gsm, + unsigned char data, unsigned char flag) +{ + gsm->state = GSM_SEARCH; + gsm->io_error++; +} + +/** + * gsm_cleanup_mux - generic GSM protocol cleanup + * @gsm: our mux + * + * Clean up the bits of the mux which are the same for all framing + * protocols. Remove the mux from the mux table, stop all the timers + * and then shut down each device hanging up the channels as we go. + */ + +void gsm_cleanup_mux(struct gsm_mux *gsm) +{ + int i; + struct gsm_dlci *dlci = gsm->dlci[0]; + struct gsm_msg *txq; + + gsm->dead = 1; + + spin_lock(&gsm_mux_lock); + for (i = 0; i < MAX_MUX; i++) { + if (gsm_mux[i] == gsm) { + gsm_mux[i] = NULL; + break; + } + } + spin_unlock(&gsm_mux_lock); + WARN_ON(i == MAX_MUX); + + del_timer_sync(&gsm->t2_timer); + /* Now we are sure T2 has stopped */ + if (dlci) { + dlci->dead = 1; + gsm_dlci_begin_close(dlci); + wait_event_interruptible(gsm->event, + dlci->state == DLCI_CLOSED); + } + /* Free up any link layer users */ + for (i = 0; i < NUM_DLCI; i++) + if (gsm->dlci[i]) + gsm_dlci_free(gsm->dlci[i]); + /* Now wipe the queues */ + for (txq = gsm->tx_head; txq != NULL; txq = gsm->tx_head) { + gsm->tx_head = txq->next; + kfree(txq); + } + gsm->tx_tail = NULL; +} +EXPORT_SYMBOL_GPL(gsm_cleanup_mux); + +/** + * gsm_activate_mux - generic GSM setup + * @gsm: our mux + * + * Set up the bits of the mux which are the same for all framing + * protocols. Add the mux to the mux table so it can be opened and + * finally kick off connecting to DLCI 0 on the modem. + */ + +int gsm_activate_mux(struct gsm_mux *gsm) +{ + struct gsm_dlci *dlci; + int i = 0; + + init_timer(&gsm->t2_timer); + gsm->t2_timer.function = gsm_control_retransmit; + gsm->t2_timer.data = (unsigned long)gsm; + init_waitqueue_head(&gsm->event); + spin_lock_init(&gsm->control_lock); + spin_lock_init(&gsm->tx_lock); + + if (gsm->encoding == 0) + gsm->receive = gsm0_receive; + else + gsm->receive = gsm1_receive; + gsm->error = gsm_error; + + spin_lock(&gsm_mux_lock); + for (i = 0; i < MAX_MUX; i++) { + if (gsm_mux[i] == NULL) { + gsm_mux[i] = gsm; + break; + } + } + spin_unlock(&gsm_mux_lock); + if (i == MAX_MUX) + return -EBUSY; + + dlci = gsm_dlci_alloc(gsm, 0); + if (dlci == NULL) + return -ENOMEM; + gsm->dead = 0; /* Tty opens are now permissible */ + return 0; +} +EXPORT_SYMBOL_GPL(gsm_activate_mux); + +/** + * gsm_free_mux - free up a mux + * @mux: mux to free + * + * Dispose of allocated resources for a dead mux. No refcounting + * at present so the mux must be truely dead. + */ +void gsm_free_mux(struct gsm_mux *gsm) +{ + kfree(gsm->txframe); + kfree(gsm->buf); + kfree(gsm); +} +EXPORT_SYMBOL_GPL(gsm_free_mux); + +/** + * gsm_alloc_mux - allocate a mux + * + * Creates a new mux ready for activation. + */ + +struct gsm_mux *gsm_alloc_mux(void) +{ + struct gsm_mux *gsm = kzalloc(sizeof(struct gsm_mux), GFP_KERNEL); + if (gsm == NULL) + return NULL; + gsm->buf = kmalloc(MAX_MRU + 1, GFP_KERNEL); + if (gsm->buf == NULL) { + kfree(gsm); + return NULL; + } + gsm->txframe = kmalloc(2 * MAX_MRU + 2, GFP_KERNEL); + if (gsm->txframe == NULL) { + kfree(gsm->buf); + kfree(gsm); + return NULL; + } + spin_lock_init(&gsm->lock); + + gsm->t1 = T1; + gsm->t2 = T2; + gsm->n2 = N2; + gsm->ftype = UIH; + gsm->initiator = 0; + gsm->adaption = 1; + gsm->encoding = 1; + gsm->mru = 64; /* Default to encoding 1 so these should be 64 */ + gsm->mtu = 64; + gsm->dead = 1; /* Avoid early tty opens */ + + return gsm; +} +EXPORT_SYMBOL_GPL(gsm_alloc_mux); + + + + +/** + * gsmld_output - write to link + * @gsm: our mux + * @data: bytes to output + * @len: size + * + * Write a block of data from the GSM mux to the data channel. This + * will eventually be serialized from above but at the moment isn't. + */ + +static int gsmld_output(struct gsm_mux *gsm, u8 *data, int len) +{ + if (tty_write_room(gsm->tty) < len) { + set_bit(TTY_DO_WRITE_WAKEUP, &gsm->tty->flags); + return -ENOSPC; + } + if (debug & 4) { + printk("-->%d bytes out\n", len); + hex_packet(data, len); + } + gsm->tty->ops->write(gsm->tty, data, len); + return len; +} + +/** + * gsmld_attach_gsm - mode set up + * @tty: our tty structure + * @gsm: our mux + * + * Set up the MUX for basic mode and commence connecting to the + * modem. Currently called from the line discipline set up but + * will need moving to an ioctl path. + */ + +static int gsmld_attach_gsm(struct tty_struct *tty, struct gsm_mux *gsm) +{ + int ret; + + gsm->tty = tty_kref_get(tty); + gsm->output = gsmld_output; + ret = gsm_activate_mux(gsm); + if (ret != 0) + tty_kref_put(gsm->tty); + return ret; +} + + +/** + * gsmld_detach_gsm - stop doing 0710 mux + * @tty: tty atttached to the mux + * @gsm: mux + * + * Shutdown and then clean up the resources used by the line discipline + */ + +static void gsmld_detach_gsm(struct tty_struct *tty, struct gsm_mux *gsm) +{ + WARN_ON(tty != gsm->tty); + gsm_cleanup_mux(gsm); + tty_kref_put(gsm->tty); + gsm->tty = NULL; +} + +static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + struct gsm_mux *gsm = tty->disc_data; + const unsigned char *dp; + char *f; + int i; + char buf[64]; + char flags; + + if (debug & 4) { + printk("Inbytes %dd\n", count); + hex_packet(cp, count); + } + + for (i = count, dp = cp, f = fp; i; i--, dp++) { + flags = *f++; + switch (flags) { + case TTY_NORMAL: + gsm->receive(gsm, *dp); + break; + case TTY_OVERRUN: + case TTY_BREAK: + case TTY_PARITY: + case TTY_FRAME: + gsm->error(gsm, *dp, flags); + break; + default: + printk(KERN_ERR "%s: unknown flag %d\n", + tty_name(tty, buf), flags); + break; + } + } + /* FASYNC if needed ? */ + /* If clogged call tty_throttle(tty); */ +} + +/** + * gsmld_chars_in_buffer - report available bytes + * @tty: tty device + * + * Report the number of characters buffered to be delivered to user + * at this instant in time. + * + * Locking: gsm lock + */ + +static ssize_t gsmld_chars_in_buffer(struct tty_struct *tty) +{ + return 0; +} + +/** + * gsmld_flush_buffer - clean input queue + * @tty: terminal device + * + * Flush the input buffer. Called when the line discipline is + * being closed, when the tty layer wants the buffer flushed (eg + * at hangup). + */ + +static void gsmld_flush_buffer(struct tty_struct *tty) +{ +} + +/** + * gsmld_close - close the ldisc for this tty + * @tty: device + * + * Called from the terminal layer when this line discipline is + * being shut down, either because of a close or becsuse of a + * discipline change. The function will not be called while other + * ldisc methods are in progress. + */ + +static void gsmld_close(struct tty_struct *tty) +{ + struct gsm_mux *gsm = tty->disc_data; + + gsmld_detach_gsm(tty, gsm); + + gsmld_flush_buffer(tty); + /* Do other clean up here */ + gsm_free_mux(gsm); +} + +/** + * gsmld_open - open an ldisc + * @tty: terminal to open + * + * Called when this line discipline is being attached to the + * terminal device. Can sleep. Called serialized so that no + * other events will occur in parallel. No further open will occur + * until a close. + */ + +static int gsmld_open(struct tty_struct *tty) +{ + struct gsm_mux *gsm; + + if (tty->ops->write == NULL) + return -EINVAL; + + /* Attach our ldisc data */ + gsm = gsm_alloc_mux(); + if (gsm == NULL) + return -ENOMEM; + + tty->disc_data = gsm; + tty->receive_room = 65536; + + /* Attach the initial passive connection */ + gsm->encoding = 1; + return gsmld_attach_gsm(tty, gsm); +} + +/** + * gsmld_write_wakeup - asynchronous I/O notifier + * @tty: tty device + * + * Required for the ptys, serial driver etc. since processes + * that attach themselves to the master and rely on ASYNC + * IO must be woken up + */ + +static void gsmld_write_wakeup(struct tty_struct *tty) +{ + struct gsm_mux *gsm = tty->disc_data; + + /* Queue poll */ + clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + gsm_data_kick(gsm); + if (gsm->tx_bytes < TX_THRESH_LO) + gsm_dlci_data_sweep(gsm); +} + +/** + * gsmld_read - read function for tty + * @tty: tty device + * @file: file object + * @buf: userspace buffer pointer + * @nr: size of I/O + * + * Perform reads for the line discipline. We are guaranteed that the + * line discipline will not be closed under us but we may get multiple + * parallel readers and must handle this ourselves. We may also get + * a hangup. Always called in user context, may sleep. + * + * This code must be sure never to sleep through a hangup. + */ + +static ssize_t gsmld_read(struct tty_struct *tty, struct file *file, + unsigned char __user *buf, size_t nr) +{ + return -EOPNOTSUPP; +} + +/** + * gsmld_write - write function for tty + * @tty: tty device + * @file: file object + * @buf: userspace buffer pointer + * @nr: size of I/O + * + * Called when the owner of the device wants to send a frame + * itself (or some other control data). The data is transferred + * as-is and must be properly framed and checksummed as appropriate + * by userspace. Frames are either sent whole or not at all as this + * avoids pain user side. + */ + +static ssize_t gsmld_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) +{ + int space = tty_write_room(tty); + if (space >= nr) + return tty->ops->write(tty, buf, nr); + set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + return -ENOBUFS; +} + +/** + * gsmld_poll - poll method for N_GSM0710 + * @tty: terminal device + * @file: file accessing it + * @wait: poll table + * + * Called when the line discipline is asked to poll() for data or + * for special events. This code is not serialized with respect to + * other events save open/close. + * + * This code must be sure never to sleep through a hangup. + * Called without the kernel lock held - fine + */ + +static unsigned int gsmld_poll(struct tty_struct *tty, struct file *file, + poll_table *wait) +{ + unsigned int mask = 0; + struct gsm_mux *gsm = tty->disc_data; + + poll_wait(file, &tty->read_wait, wait); + poll_wait(file, &tty->write_wait, wait); + if (tty_hung_up_p(file)) + mask |= POLLHUP; + if (!tty_is_writelocked(tty) && tty_write_room(tty) > 0) + mask |= POLLOUT | POLLWRNORM; + if (gsm->dead) + mask |= POLLHUP; + return mask; +} + +static int gsmld_config(struct tty_struct *tty, struct gsm_mux *gsm, + struct gsm_config *c) +{ + int need_close = 0; + int need_restart = 0; + + /* Stuff we don't support yet - UI or I frame transport, windowing */ + if ((c->adaption !=1 && c->adaption != 2) || c->k) + return -EOPNOTSUPP; + /* Check the MRU/MTU range looks sane */ + if (c->mru > MAX_MRU || c->mtu > MAX_MTU || c->mru < 8 || c->mtu < 8) + return -EINVAL; + if (c->n2 < 3) + return -EINVAL; + if (c->encapsulation > 1) /* Basic, advanced, no I */ + return -EINVAL; + if (c->initiator > 1) + return -EINVAL; + if (c->i == 0 || c->i > 2) /* UIH and UI only */ + return -EINVAL; + /* + * See what is needed for reconfiguration + */ + + /* Timing fields */ + if (c->t1 != 0 && c->t1 != gsm->t1) + need_restart = 1; + if (c->t2 != 0 && c->t2 != gsm->t2) + need_restart = 1; + if (c->encapsulation != gsm->encoding) + need_restart = 1; + if (c->adaption != gsm->adaption) + need_restart = 1; + /* Requires care */ + if (c->initiator != gsm->initiator) + need_close = 1; + if (c->mru != gsm->mru) + need_restart = 1; + if (c->mtu != gsm->mtu) + need_restart = 1; + + /* + * Close down what is needed, restart and initiate the new + * configuration + */ + + if (need_close || need_restart) { + gsm_dlci_begin_close(gsm->dlci[0]); + /* This will timeout if the link is down due to N2 expiring */ + wait_event_interruptible(gsm->event, + gsm->dlci[0]->state == DLCI_CLOSED); + if (signal_pending(current)) + return -EINTR; + } + if (need_restart) + gsm_cleanup_mux(gsm); + + gsm->initiator = c->initiator; + gsm->mru = c->mru; + gsm->encoding = c->encapsulation; + gsm->adaption = c->adaption; + + if (c->i == 1) + gsm->ftype = UIH; + else if (c->i == 2) + gsm->ftype = UI; + + if (c->t1) + gsm->t1 = c->t1; + if (c->t2) + gsm->t2 = c->t2; + + /* FIXME: We need to separate activation/deactivation from adding + and removing from the mux array */ + if (need_restart) + gsm_activate_mux(gsm); + if (gsm->initiator && need_close) + gsm_dlci_begin_open(gsm->dlci[0]); + return 0; +} + +static int gsmld_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct gsm_config c; + struct gsm_mux *gsm = tty->disc_data; + + switch (cmd) { + case GSMIOC_GETCONF: + memset(&c, 0, sizeof(c)); + c.adaption = gsm->adaption; + c.encapsulation = gsm->encoding; + c.initiator = gsm->initiator; + c.t1 = gsm->t1; + c.t2 = gsm->t2; + c.t3 = 0; /* Not supported */ + c.n2 = gsm->n2; + if (gsm->ftype == UIH) + c.i = 1; + else + c.i = 2; + printk("Ftype %d i %d\n", gsm->ftype, c.i); + c.mru = gsm->mru; + c.mtu = gsm->mtu; + c.k = 0; + if (copy_to_user((void *)arg, &c, sizeof(c))) + return -EFAULT; + return 0; + case GSMIOC_SETCONF: + if (copy_from_user(&c, (void *)arg, sizeof(c))) + return -EFAULT; + return gsmld_config(tty, gsm, &c); + default: + return n_tty_ioctl_helper(tty, file, cmd, arg); + } +} + + +/* Line discipline for real tty */ +struct tty_ldisc_ops tty_ldisc_packet = { + .owner = THIS_MODULE, + .magic = TTY_LDISC_MAGIC, + .name = "n_gsm", + .open = gsmld_open, + .close = gsmld_close, + .flush_buffer = gsmld_flush_buffer, + .chars_in_buffer = gsmld_chars_in_buffer, + .read = gsmld_read, + .write = gsmld_write, + .ioctl = gsmld_ioctl, + .poll = gsmld_poll, + .receive_buf = gsmld_receive_buf, + .write_wakeup = gsmld_write_wakeup +}; + +/* + * Virtual tty side + */ + +#define TX_SIZE 512 + +static int gsmtty_modem_update(struct gsm_dlci *dlci, u8 brk) +{ + u8 modembits[5]; + struct gsm_control *ctrl; + int len = 2; + + if (brk) + len++; + + modembits[0] = len << 1 | EA; /* Data bytes */ + modembits[1] = dlci->addr << 2 | 3; /* DLCI, EA, 1 */ + modembits[2] = gsm_encode_modem(dlci) << 1 | EA; + if (brk) + modembits[3] = brk << 4 | 2 | EA; /* Valid, EA */ + ctrl = gsm_control_send(dlci->gsm, CMD_MSC, modembits, len + 1); + if (ctrl == NULL) + return -ENOMEM; + return gsm_control_wait(dlci->gsm, ctrl); +} + +static int gsm_carrier_raised(struct tty_port *port) +{ + struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port); + /* Not yet open so no carrier info */ + if (dlci->state != DLCI_OPEN) + return 0; + if (debug & 2) + return 1; + return dlci->modem_rx & TIOCM_CD; +} + +static void gsm_dtr_rts(struct tty_port *port, int onoff) +{ + struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port); + unsigned int modem_tx = dlci->modem_tx; + if (onoff) + modem_tx |= TIOCM_DTR | TIOCM_RTS; + else + modem_tx &= ~(TIOCM_DTR | TIOCM_RTS); + if (modem_tx != dlci->modem_tx) { + dlci->modem_tx = modem_tx; + gsmtty_modem_update(dlci, 0); + } +} + +static const struct tty_port_operations gsm_port_ops = { + .carrier_raised = gsm_carrier_raised, + .dtr_rts = gsm_dtr_rts, +}; + + +static int gsmtty_open(struct tty_struct *tty, struct file *filp) +{ + struct gsm_mux *gsm; + struct gsm_dlci *dlci; + struct tty_port *port; + unsigned int line = tty->index; + unsigned int mux = line >> 6; + + line = line & 0x3F; + + if (mux >= MAX_MUX) + return -ENXIO; + /* FIXME: we need to lock gsm_mux for lifetimes of ttys eventually */ + if (gsm_mux[mux] == NULL) + return -EUNATCH; + if (line == 0 || line > 61) /* 62/63 reserved */ + return -ECHRNG; + gsm = gsm_mux[mux]; + if (gsm->dead) + return -EL2HLT; + dlci = gsm->dlci[line]; + if (dlci == NULL) + dlci = gsm_dlci_alloc(gsm, line); + if (dlci == NULL) + return -ENOMEM; + port = &dlci->port; + port->count++; + tty->driver_data = dlci; + tty_port_tty_set(port, tty); + + dlci->modem_rx = 0; + /* We could in theory open and close before we wait - eg if we get + a DM straight back. This is ok as that will have caused a hangup */ + set_bit(ASYNCB_INITIALIZED, &port->flags); + /* Start sending off SABM messages */ + gsm_dlci_begin_open(dlci); + /* And wait for virtual carrier */ + return tty_port_block_til_ready(port, tty, filp); +} + +static void gsmtty_close(struct tty_struct *tty, struct file *filp) +{ + struct gsm_dlci *dlci = tty->driver_data; + if (dlci == NULL) + return; + if (tty_port_close_start(&dlci->port, tty, filp) == 0) + return; + gsm_dlci_begin_close(dlci); + tty_port_close_end(&dlci->port, tty); + tty_port_tty_set(&dlci->port, NULL); +} + +static void gsmtty_hangup(struct tty_struct *tty) +{ + struct gsm_dlci *dlci = tty->driver_data; + tty_port_hangup(&dlci->port); + gsm_dlci_begin_close(dlci); +} + +static int gsmtty_write(struct tty_struct *tty, const unsigned char *buf, + int len) +{ + struct gsm_dlci *dlci = tty->driver_data; + /* Stuff the bytes into the fifo queue */ + int sent = kfifo_in_locked(dlci->fifo, buf, len, &dlci->lock); + /* Need to kick the channel */ + gsm_dlci_data_kick(dlci); + return sent; +} + +static int gsmtty_write_room(struct tty_struct *tty) +{ + struct gsm_dlci *dlci = tty->driver_data; + return TX_SIZE - kfifo_len(dlci->fifo); +} + +static int gsmtty_chars_in_buffer(struct tty_struct *tty) +{ + struct gsm_dlci *dlci = tty->driver_data; + return kfifo_len(dlci->fifo); +} + +static void gsmtty_flush_buffer(struct tty_struct *tty) +{ + struct gsm_dlci *dlci = tty->driver_data; + /* Caution needed: If we implement reliable transport classes + then the data being transmitted can't simply be junked once + it has first hit the stack. Until then we can just blow it + away */ + kfifo_reset(dlci->fifo); + /* Need to unhook this DLCI from the transmit queue logic */ +} + +static void gsmtty_wait_until_sent(struct tty_struct *tty, int timeout) +{ + /* The FIFO handles the queue so the kernel will do the right + thing waiting on chars_in_buffer before calling us. No work + to do here */ +} + +static int gsmtty_tiocmget(struct tty_struct *tty, struct file *filp) +{ + struct gsm_dlci *dlci = tty->driver_data; + return dlci->modem_rx; +} + +static int gsmtty_tiocmset(struct tty_struct *tty, struct file *filp, + unsigned int set, unsigned int clear) +{ + struct gsm_dlci *dlci = tty->driver_data; + unsigned int modem_tx = dlci->modem_tx; + + modem_tx &= clear; + modem_tx |= set; + + if (modem_tx != dlci->modem_tx) { + dlci->modem_tx = modem_tx; + return gsmtty_modem_update(dlci, 0); + } + return 0; +} + + +static int gsmtty_ioctl(struct tty_struct *tty, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + return -ENOIOCTLCMD; +} + +static void gsmtty_set_termios(struct tty_struct *tty, struct ktermios *old) +{ + /* For the moment its fixed. In actual fact the speed information + for the virtual channel can be propogated in both directions by + the RPN control message. This however rapidly gets nasty as we + then have to remap modem signals each way according to whether + our virtual cable is null modem etc .. */ + tty_termios_copy_hw(tty->termios, old); +} + +static void gsmtty_throttle(struct tty_struct *tty) +{ + struct gsm_dlci *dlci = tty->driver_data; + if (tty->termios->c_cflag & CRTSCTS) + dlci->modem_tx &= ~TIOCM_DTR; + dlci->throttled = 1; + /* Send an MSC with DTR cleared */ + gsmtty_modem_update(dlci, 0); +} + +static void gsmtty_unthrottle(struct tty_struct *tty) +{ + struct gsm_dlci *dlci = tty->driver_data; + if (tty->termios->c_cflag & CRTSCTS) + dlci->modem_tx |= TIOCM_DTR; + dlci->throttled = 0; + /* Send an MSC with DTR set */ + gsmtty_modem_update(dlci, 0); +} + +static int gsmtty_break_ctl(struct tty_struct *tty, int state) +{ + struct gsm_dlci *dlci = tty->driver_data; + int encode = 0; /* Off */ + + if (state == -1) /* "On indefinitely" - we can't encode this + properly */ + encode = 0x0F; + else if (state > 0) { + encode = state / 200; /* mS to encoding */ + if (encode > 0x0F) + encode = 0x0F; /* Best effort */ + } + return gsmtty_modem_update(dlci, encode); +} + +static struct tty_driver *gsm_tty_driver; + +/* Virtual ttys for the demux */ +static const struct tty_operations gsmtty_ops = { + .open = gsmtty_open, + .close = gsmtty_close, + .write = gsmtty_write, + .write_room = gsmtty_write_room, + .chars_in_buffer = gsmtty_chars_in_buffer, + .flush_buffer = gsmtty_flush_buffer, + .ioctl = gsmtty_ioctl, + .throttle = gsmtty_throttle, + .unthrottle = gsmtty_unthrottle, + .set_termios = gsmtty_set_termios, + .hangup = gsmtty_hangup, + .wait_until_sent = gsmtty_wait_until_sent, + .tiocmget = gsmtty_tiocmget, + .tiocmset = gsmtty_tiocmset, + .break_ctl = gsmtty_break_ctl, +}; + + + +static int __init gsm_init(void) +{ + /* Fill in our line protocol discipline, and register it */ + int status = tty_register_ldisc(N_GSM0710, &tty_ldisc_packet); + if (status != 0) { + printk(KERN_ERR "n_gsm: can't register line discipline (err = %d)\n", status); + return status; + } + + gsm_tty_driver = alloc_tty_driver(256); + if (!gsm_tty_driver) { + tty_unregister_ldisc(N_GSM0710); + printk(KERN_ERR "gsm_init: tty allocation failed.\n"); + return -EINVAL; + } + gsm_tty_driver->owner = THIS_MODULE; + gsm_tty_driver->driver_name = "gsmtty"; + gsm_tty_driver->name = "gsmtty"; + gsm_tty_driver->major = 0; /* Dynamic */ + gsm_tty_driver->minor_start = 0; + gsm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + gsm_tty_driver->subtype = SERIAL_TYPE_NORMAL; + gsm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV + | TTY_DRIVER_HARDWARE_BREAK; + gsm_tty_driver->init_termios = tty_std_termios; + /* Fixme */ + gsm_tty_driver->init_termios.c_lflag &= ~ECHO; + tty_set_operations(gsm_tty_driver, &gsmtty_ops); + + spin_lock_init(&gsm_mux_lock); + + if (tty_register_driver(gsm_tty_driver)) { + put_tty_driver(gsm_tty_driver); + tty_unregister_ldisc(N_GSM0710); + printk(KERN_ERR "gsm_init: tty registration failed.\n"); + return -EBUSY; + } + printk(KERN_INFO "gsm_init: loaded as %d,%d.\n", gsm_tty_driver->major, gsm_tty_driver->minor_start); + return 0; +} + +static void __exit gsm_exit(void) +{ + int status = tty_unregister_ldisc(N_GSM0710); + if (status != 0) + printk(KERN_ERR "n_gsm: can't unregister line discipline (err = %d)\n", status); + tty_unregister_driver(gsm_tty_driver); + put_tty_driver(gsm_tty_driver); + printk(KERN_INFO "gsm_init: unloaded.\n"); +} + +module_init(gsm_init); +module_exit(gsm_exit); + + +MODULE_LICENSE("GPL"); +MODULE_ALIAS_LDISC(N_GSM0710); diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c index 47e8f7b0e4c..66d2917b003 100644 --- a/drivers/char/nvram.c +++ b/drivers/char/nvram.c @@ -296,8 +296,8 @@ checksum_err: return -EIO; } -static int nvram_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long nvram_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { int i; @@ -308,6 +308,7 @@ static int nvram_ioctl(struct inode *inode, struct file *file, if (!capable(CAP_SYS_ADMIN)) return -EACCES; + lock_kernel(); spin_lock_irq(&rtc_lock); for (i = 0; i < NVRAM_BYTES; ++i) @@ -315,6 +316,7 @@ static int nvram_ioctl(struct inode *inode, struct file *file, __nvram_set_checksum(); spin_unlock_irq(&rtc_lock); + unlock_kernel(); return 0; case NVRAM_SETCKS: @@ -323,9 +325,11 @@ static int nvram_ioctl(struct inode *inode, struct file *file, if (!capable(CAP_SYS_ADMIN)) return -EACCES; + lock_kernel(); spin_lock_irq(&rtc_lock); __nvram_set_checksum(); spin_unlock_irq(&rtc_lock); + unlock_kernel(); return 0; default: @@ -422,7 +426,7 @@ static const struct file_operations nvram_fops = { .llseek = nvram_llseek, .read = nvram_read, .write = nvram_write, - .ioctl = nvram_ioctl, + .unlocked_ioctl = nvram_ioctl, .open = nvram_open, .release = nvram_release, }; diff --git a/drivers/char/nwflash.c b/drivers/char/nwflash.c index f80810901db..043a1c7b86b 100644 --- a/drivers/char/nwflash.c +++ b/drivers/char/nwflash.c @@ -94,8 +94,9 @@ static int get_flash_id(void) return c2; } -static int flash_ioctl(struct inode *inodep, struct file *filep, unsigned int cmd, unsigned long arg) +static long flash_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { + lock_kernel(); switch (cmd) { case CMD_WRITE_DISABLE: gbWriteBase64Enable = 0; @@ -113,8 +114,10 @@ static int flash_ioctl(struct inode *inodep, struct file *filep, unsigned int cm default: gbWriteBase64Enable = 0; gbWriteEnable = 0; + unlock_kernel(); return -EINVAL; } + unlock_kernel(); return 0; } @@ -631,7 +634,7 @@ static const struct file_operations flash_fops = .llseek = flash_llseek, .read = flash_read, .write = flash_write, - .ioctl = flash_ioctl, + .unlocked_ioctl = flash_ioctl, }; static struct miscdevice flash_miscdev = diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c index 90b199f97be..e7956acf2ad 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -106,7 +106,6 @@ static int major; /* major number we get from the kernel */ struct cm4000_dev { struct pcmcia_device *p_dev; - dev_node_t node; /* OS node (major,minor) */ unsigned char atr[MAX_ATR]; unsigned char rbuf[512]; @@ -884,8 +883,7 @@ static void monitor_card(unsigned long p) /* slow down warning, but prompt immediately after insertion */ if (dev->cwarn == 0 || dev->cwarn == 10) { set_bit(IS_BAD_CARD, &dev->flags); - printk(KERN_WARNING MODULE_NAME ": device %s: ", - dev->node.dev_name); + dev_warn(&dev->p_dev->dev, MODULE_NAME ": "); if (test_bit(IS_BAD_CSUM, &dev->flags)) { DEBUGP(4, dev, "ATR checksum (0x%.2x, should " "be zero) failed\n", dev->atr_csum); @@ -1781,11 +1779,6 @@ static int cm4000_config(struct pcmcia_device * link, int devno) goto cs_release; dev = link->priv; - sprintf(dev->node.dev_name, DEVICE_NAME "%d", devno); - dev->node.major = major; - dev->node.minor = devno; - dev->node.next = NULL; - link->dev_node = &dev->node; return 0; diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c index a6a70e476be..c0775c844e0 100644 --- a/drivers/char/pcmcia/cm4040_cs.c +++ b/drivers/char/pcmcia/cm4040_cs.c @@ -72,7 +72,6 @@ static struct class *cmx_class; struct reader_dev { struct pcmcia_device *p_dev; - dev_node_t node; wait_queue_head_t devq; wait_queue_head_t poll_wait; wait_queue_head_t read_wait; @@ -568,10 +567,6 @@ static int reader_config(struct pcmcia_device *link, int devno) } dev = link->priv; - sprintf(dev->node.dev_name, DEVICE_NAME "%d", devno); - dev->node.major = major; - dev->node.minor = devno; - dev->node.next = &dev->node; DEBUGP(2, dev, "device " DEVICE_NAME "%d at 0x%.4x-0x%.4x\n", devno, link->io.BasePort1, link->io.BasePort1+link->io.NumPorts1); diff --git a/drivers/char/pcmcia/ipwireless/main.c b/drivers/char/pcmcia/ipwireless/main.c index dff24dae148..63c32e3f23b 100644 --- a/drivers/char/pcmcia/ipwireless/main.c +++ b/drivers/char/pcmcia/ipwireless/main.c @@ -195,9 +195,6 @@ static int config_ipwireless(struct ipw_dev *ipw) link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - link->irq.Handler = ipwireless_interrupt; - INIT_WORK(&ipw->work_reboot, signalled_reboot_work); ipwireless_init_hardware_v1(ipw->hardware, link->io.BasePort1, @@ -205,8 +202,7 @@ static int config_ipwireless(struct ipw_dev *ipw) ipw->is_v2_card, signalled_reboot_callback, ipw); - ret = pcmcia_request_irq(link, &link->irq); - + ret = pcmcia_request_irq(link, ipwireless_interrupt); if (ret != 0) goto exit; @@ -217,7 +213,7 @@ static int config_ipwireless(struct ipw_dev *ipw) (unsigned int) link->io.BasePort1, (unsigned int) (link->io.BasePort1 + link->io.NumPorts1 - 1), - (unsigned int) link->irq.AssignedIRQ); + (unsigned int) link->irq); if (ipw->attr_memory && ipw->common_memory) printk(KERN_INFO IPWIRELESS_PCCARD_NAME ": attr memory 0x%08lx-0x%08lx, common memory 0x%08lx-0x%08lx\n", @@ -232,8 +228,7 @@ static int config_ipwireless(struct ipw_dev *ipw) if (!ipw->network) goto exit; - ipw->tty = ipwireless_tty_create(ipw->hardware, ipw->network, - ipw->nodes); + ipw->tty = ipwireless_tty_create(ipw->hardware, ipw->network); if (!ipw->tty) goto exit; @@ -248,8 +243,6 @@ static int config_ipwireless(struct ipw_dev *ipw) if (ret != 0) goto exit; - link->dev_node = &ipw->nodes[0]; - return 0; exit: @@ -271,8 +264,6 @@ exit: static void release_ipwireless(struct ipw_dev *ipw) { - pcmcia_disable_device(ipw->link); - if (ipw->common_memory) { release_mem_region(ipw->request_common_memory.Base, ipw->request_common_memory.Size); @@ -288,7 +279,6 @@ static void release_ipwireless(struct ipw_dev *ipw) if (ipw->attr_memory) pcmcia_release_window(ipw->link, ipw->handle_attr_memory); - /* Break the link with Card Services */ pcmcia_disable_device(ipw->link); } @@ -313,9 +303,6 @@ static int ipwireless_attach(struct pcmcia_device *link) ipw->link = link; link->priv = ipw; - /* Link this device into our device list. */ - link->dev_node = &ipw->nodes[0]; - ipw->hardware = ipwireless_hardware_create(); if (!ipw->hardware) { kfree(ipw); diff --git a/drivers/char/pcmcia/ipwireless/main.h b/drivers/char/pcmcia/ipwireless/main.h index 0e0363af9ab..96d0ef31b17 100644 --- a/drivers/char/pcmcia/ipwireless/main.h +++ b/drivers/char/pcmcia/ipwireless/main.h @@ -54,7 +54,6 @@ struct ipw_dev { void __iomem *common_memory; win_req_t request_common_memory; - dev_node_t nodes[2]; /* Reference to attribute memory, containing CIS data */ void *attribute_memory; diff --git a/drivers/char/pcmcia/ipwireless/tty.c b/drivers/char/pcmcia/ipwireless/tty.c index 2bb7874a689..1a2c2c3b068 100644 --- a/drivers/char/pcmcia/ipwireless/tty.c +++ b/drivers/char/pcmcia/ipwireless/tty.c @@ -487,7 +487,7 @@ static int ipw_ioctl(struct tty_struct *linux_tty, struct file *file, return tty_mode_ioctl(linux_tty, file, cmd , arg); } -static int add_tty(dev_node_t *nodesp, int j, +static int add_tty(int j, struct ipw_hardware *hardware, struct ipw_network *network, int channel_idx, int secondary_channel_idx, int tty_type) @@ -510,19 +510,13 @@ static int add_tty(dev_node_t *nodesp, int j, ipwireless_associate_network_tty(network, secondary_channel_idx, ttys[j]); - if (nodesp != NULL) { - sprintf(nodesp->dev_name, "ttyIPWp%d", j); - nodesp->major = ipw_tty_driver->major; - nodesp->minor = j + ipw_tty_driver->minor_start; - } if (get_tty(j + ipw_tty_driver->minor_start) == ttys[j]) report_registering(ttys[j]); return 0; } struct ipw_tty *ipwireless_tty_create(struct ipw_hardware *hardware, - struct ipw_network *network, - dev_node_t *nodes) + struct ipw_network *network) { int i, j; @@ -539,26 +533,23 @@ struct ipw_tty *ipwireless_tty_create(struct ipw_hardware *hardware, if (allfree) { j = i; - if (add_tty(&nodes[0], j, hardware, network, + if (add_tty(j, hardware, network, IPW_CHANNEL_DIALLER, IPW_CHANNEL_RAS, TTYTYPE_MODEM)) return NULL; j += IPWIRELESS_PCMCIA_MINOR_RANGE; - if (add_tty(&nodes[1], j, hardware, network, + if (add_tty(j, hardware, network, IPW_CHANNEL_DIALLER, -1, TTYTYPE_MONITOR)) return NULL; j += IPWIRELESS_PCMCIA_MINOR_RANGE; - if (add_tty(NULL, j, hardware, network, + if (add_tty(j, hardware, network, IPW_CHANNEL_RAS, -1, TTYTYPE_RAS_RAW)) return NULL; - nodes[0].next = &nodes[1]; - nodes[1].next = NULL; - return ttys[i]; } } diff --git a/drivers/char/pcmcia/ipwireless/tty.h b/drivers/char/pcmcia/ipwireless/tty.h index b0deb9168b6..4da6c201f72 100644 --- a/drivers/char/pcmcia/ipwireless/tty.h +++ b/drivers/char/pcmcia/ipwireless/tty.h @@ -34,8 +34,7 @@ int ipwireless_tty_init(void); void ipwireless_tty_release(void); struct ipw_tty *ipwireless_tty_create(struct ipw_hardware *hw, - struct ipw_network *net, - dev_node_t *nodes); + struct ipw_network *net); void ipwireless_tty_free(struct ipw_tty *tty); void ipwireless_tty_received(struct ipw_tty *tty, unsigned char *data, unsigned int length); diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index c31a0d913d3..308903ec8bf 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -220,7 +220,6 @@ typedef struct _mgslpc_info { /* PCMCIA support */ struct pcmcia_device *p_dev; - dev_node_t node; int stop; /* SPPP/Cisco HDLC device parts */ @@ -552,10 +551,6 @@ static int mgslpc_probe(struct pcmcia_device *link) /* Initialize the struct pcmcia_device structure */ - /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - link->irq.Handler = NULL; - link->conf.Attributes = 0; link->conf.IntType = INT_MEMORY_AND_IO; @@ -608,9 +603,7 @@ static int mgslpc_config(struct pcmcia_device *link) link->conf.ConfigIndex = 8; link->conf.Present = PRESENT_OPTION; - link->irq.Handler = mgslpc_isr; - - ret = pcmcia_request_irq(link, &link->irq); + ret = pcmcia_request_irq(link, mgslpc_isr); if (ret) goto failed; ret = pcmcia_request_configuration(link, &link->conf); @@ -618,17 +611,12 @@ static int mgslpc_config(struct pcmcia_device *link) goto failed; info->io_base = link->io.BasePort1; - info->irq_level = link->irq.AssignedIRQ; - - /* add to linked list of devices */ - sprintf(info->node.dev_name, "mgslpc0"); - info->node.major = info->node.minor = 0; - link->dev_node = &info->node; + info->irq_level = link->irq; - printk(KERN_INFO "%s: index 0x%02x:", - info->node.dev_name, link->conf.ConfigIndex); + dev_info(&link->dev, "index 0x%02x:", + link->conf.ConfigIndex); if (link->conf.Attributes & CONF_ENABLE_IRQ) - printk(", irq %d", link->irq.AssignedIRQ); + printk(", irq %d", link->irq); if (link->io.NumPorts1) printk(", io 0x%04x-0x%04x", link->io.BasePort1, link->io.BasePort1+link->io.NumPorts1-1); diff --git a/drivers/char/random.c b/drivers/char/random.c index 2fd3d39995d..8d85587b6d4 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -257,6 +257,7 @@ #define INPUT_POOL_WORDS 128 #define OUTPUT_POOL_WORDS 32 #define SEC_XFER_SIZE 512 +#define EXTRACT_SIZE 10 /* * The minimum number of bits of entropy before we wake up a read on @@ -414,7 +415,7 @@ struct entropy_store { unsigned add_ptr; int entropy_count; int input_rotate; - __u8 *last_data; + __u8 last_data[EXTRACT_SIZE]; }; static __u32 input_pool_data[INPUT_POOL_WORDS]; @@ -714,8 +715,6 @@ void add_disk_randomness(struct gendisk *disk) } #endif -#define EXTRACT_SIZE 10 - /********************************************************************* * * Entropy extraction routines @@ -862,7 +861,7 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf, while (nbytes) { extract_buf(r, tmp); - if (r->last_data) { + if (fips_enabled) { spin_lock_irqsave(&r->lock, flags); if (!memcmp(tmp, r->last_data, EXTRACT_SIZE)) panic("Hardware RNG duplicated output!\n"); @@ -951,9 +950,6 @@ static void init_std_data(struct entropy_store *r) now = ktime_get_real(); mix_pool_bytes(r, &now, sizeof(now)); mix_pool_bytes(r, utsname(), sizeof(*(utsname()))); - /* Enable continuous test in fips mode */ - if (fips_enabled) - r->last_data = kmalloc(EXTRACT_SIZE, GFP_KERNEL); } static int rand_initialize(void) diff --git a/drivers/char/raw.c b/drivers/char/raw.c index 8756ab0daa8..b38942f6bf3 100644 --- a/drivers/char/raw.c +++ b/drivers/char/raw.c @@ -121,13 +121,17 @@ static int raw_release(struct inode *inode, struct file *filp) /* * Forward ioctls to the underlying block device. */ -static int -raw_ioctl(struct inode *inode, struct file *filp, - unsigned int command, unsigned long arg) +static long +raw_ioctl(struct file *filp, unsigned int command, unsigned long arg) { struct block_device *bdev = filp->private_data; + int ret; + + lock_kernel(); + ret = blkdev_ioctl(bdev, 0, command, arg); + unlock_kernel(); - return blkdev_ioctl(bdev, 0, command, arg); + return ret; } static void bind_device(struct raw_config_request *rq) @@ -141,13 +145,14 @@ static void bind_device(struct raw_config_request *rq) * Deal with ioctls against the raw-device control interface, to bind * and unbind other raw devices. */ -static int raw_ctl_ioctl(struct inode *inode, struct file *filp, - unsigned int command, unsigned long arg) +static long raw_ctl_ioctl(struct file *filp, unsigned int command, + unsigned long arg) { struct raw_config_request rq; struct raw_device_data *rawdev; int err = 0; + lock_kernel(); switch (command) { case RAW_SETBIND: case RAW_GETBIND: @@ -240,25 +245,26 @@ static int raw_ctl_ioctl(struct inode *inode, struct file *filp, break; } out: + unlock_kernel(); return err; } static const struct file_operations raw_fops = { - .read = do_sync_read, - .aio_read = generic_file_aio_read, - .write = do_sync_write, - .aio_write = blkdev_aio_write, - .fsync = blkdev_fsync, - .open = raw_open, - .release= raw_release, - .ioctl = raw_ioctl, - .owner = THIS_MODULE, + .read = do_sync_read, + .aio_read = generic_file_aio_read, + .write = do_sync_write, + .aio_write = blkdev_aio_write, + .fsync = blkdev_fsync, + .open = raw_open, + .release = raw_release, + .unlocked_ioctl = raw_ioctl, + .owner = THIS_MODULE, }; static const struct file_operations raw_ctl_fops = { - .ioctl = raw_ctl_ioctl, - .open = raw_open, - .owner = THIS_MODULE, + .unlocked_ioctl = raw_ctl_ioctl, + .open = raw_open, + .owner = THIS_MODULE, }; static struct cdev raw_cdev; diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c index 8dfd24721a8..ecbe479c7d6 100644 --- a/drivers/char/serial167.c +++ b/drivers/char/serial167.c @@ -176,23 +176,6 @@ static void config_setup(struct cyclades_port *); static void show_status(int); #endif -#ifdef CONFIG_REMOTE_DEBUG -static void debug_setup(void); -void queueDebugChar(int c); -int getDebugChar(void); - -#define DEBUG_PORT 1 -#define DEBUG_LEN 256 - -typedef struct { - int in; - int out; - unsigned char buf[DEBUG_LEN]; -} debugq; - -debugq debugiq; -#endif - /* * I have my own version of udelay(), as it is needed when initialising * the chip, before the delay loop has been calibrated. Should probably @@ -515,11 +498,6 @@ static irqreturn_t cd2401_tx_interrupt(int irq, void *dev_id) /* determine the channel and change to that context */ channel = (u_short) (base_addr[CyLICR] >> 2); -#ifdef CONFIG_REMOTE_DEBUG - if (channel == DEBUG_PORT) { - panic("TxInt on debug port!!!"); - } -#endif /* validate the port number (as configured and open) */ if ((channel < 0) || (NR_PORTS <= channel)) { base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy); @@ -627,7 +605,6 @@ static irqreturn_t cd2401_rx_interrupt(int irq, void *dev_id) char data; int char_count; int save_cnt; - int len; /* determine the channel and change to that context */ channel = (u_short) (base_addr[CyLICR] >> 2); @@ -635,14 +612,6 @@ static irqreturn_t cd2401_rx_interrupt(int irq, void *dev_id) info->last_active = jiffies; save_cnt = char_count = base_addr[CyRFOC]; -#ifdef CONFIG_REMOTE_DEBUG - if (channel == DEBUG_PORT) { - while (char_count--) { - data = base_addr[CyRDR]; - queueDebugChar(data); - } - } else -#endif /* if there is nowhere to put the data, discard it */ if (info->tty == 0) { while (char_count--) { @@ -1528,7 +1497,6 @@ static int cy_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { - unsigned long val; struct cyclades_port *info = tty->driver_data; int ret_val = 0; void __user *argp = (void __user *)arg; @@ -2197,9 +2165,7 @@ static int __init serial167_init(void) port_num++; info++; } -#ifdef CONFIG_REMOTE_DEBUG - debug_setup(); -#endif + ret = request_irq(MVME167_IRQ_SER_ERR, cd2401_rxerr_interrupt, 0, "cd2401_errors", cd2401_rxerr_interrupt); if (ret) { @@ -2520,193 +2486,4 @@ static int __init serial167_console_init(void) console_initcall(serial167_console_init); -#ifdef CONFIG_REMOTE_DEBUG -void putDebugChar(int c) -{ - volatile unsigned char *base_addr = (u_char *) BASE_ADDR; - unsigned long flags; - volatile u_char sink; - u_char ier; - int port; - - local_irq_save(flags); - - /* Ensure transmitter is enabled! */ - - port = DEBUG_PORT; - base_addr[CyCAR] = (u_char) port; - while (base_addr[CyCCR]) - ; - base_addr[CyCCR] = CyENB_XMTR; - - ier = base_addr[CyIER]; - base_addr[CyIER] = CyTxMpty; - - while (1) { - if (pcc2chip[PccSCCTICR] & 0x20) { - /* We have a Tx int. Acknowledge it */ - sink = pcc2chip[PccTPIACKR]; - if ((base_addr[CyLICR] >> 2) == port) { - base_addr[CyTDR] = c; - base_addr[CyTEOIR] = 0; - break; - } else - base_addr[CyTEOIR] = CyNOTRANS; - } - } - - base_addr[CyIER] = ier; - - local_irq_restore(flags); -} - -int getDebugChar() -{ - volatile unsigned char *base_addr = (u_char *) BASE_ADDR; - unsigned long flags; - volatile u_char sink; - u_char ier; - int port; - int i, c; - - i = debugiq.out; - if (i != debugiq.in) { - c = debugiq.buf[i]; - if (++i == DEBUG_LEN) - i = 0; - debugiq.out = i; - return c; - } - /* OK, nothing in queue, wait in poll loop */ - - local_irq_save(flags); - - /* Ensure receiver is enabled! */ - - port = DEBUG_PORT; - base_addr[CyCAR] = (u_char) port; -#if 0 - while (base_addr[CyCCR]) - ; - base_addr[CyCCR] = CyENB_RCVR; -#endif - ier = base_addr[CyIER]; - base_addr[CyIER] = CyRxData; - - while (1) { - if (pcc2chip[PccSCCRICR] & 0x20) { - /* We have a Rx int. Acknowledge it */ - sink = pcc2chip[PccRPIACKR]; - if ((base_addr[CyLICR] >> 2) == port) { - int cnt = base_addr[CyRFOC]; - while (cnt-- > 0) { - c = base_addr[CyRDR]; - if (c == 0) - printk - ("!! debug char is null (cnt=%d) !!", - cnt); - else - queueDebugChar(c); - } - base_addr[CyREOIR] = 0; - i = debugiq.out; - if (i == debugiq.in) - panic("Debug input queue empty!"); - c = debugiq.buf[i]; - if (++i == DEBUG_LEN) - i = 0; - debugiq.out = i; - break; - } else - base_addr[CyREOIR] = CyNOTRANS; - } - } - - base_addr[CyIER] = ier; - - local_irq_restore(flags); - - return (c); -} - -void queueDebugChar(int c) -{ - int i; - - i = debugiq.in; - debugiq.buf[i] = c; - if (++i == DEBUG_LEN) - i = 0; - if (i != debugiq.out) - debugiq.in = i; -} - -static void debug_setup() -{ - unsigned long flags; - volatile unsigned char *base_addr = (u_char *) BASE_ADDR; - int i, cflag; - - cflag = B19200; - - local_irq_save(flags); - - for (i = 0; i < 4; i++) { - base_addr[CyCAR] = i; - base_addr[CyLICR] = i << 2; - } - - debugiq.in = debugiq.out = 0; - - base_addr[CyCAR] = DEBUG_PORT; - - /* baud rate */ - i = cflag & CBAUD; - - base_addr[CyIER] = 0; - - base_addr[CyCMR] = CyASYNC; - base_addr[CyLICR] = DEBUG_PORT << 2; - base_addr[CyLIVR] = 0x5c; - - /* tx and rx baud rate */ - - base_addr[CyTCOR] = baud_co[i]; - base_addr[CyTBPR] = baud_bpr[i]; - base_addr[CyRCOR] = baud_co[i] >> 5; - base_addr[CyRBPR] = baud_bpr[i]; - - /* set line characteristics according configuration */ - - base_addr[CySCHR1] = 0; - base_addr[CySCHR2] = 0; - base_addr[CySCRL] = 0; - base_addr[CySCRH] = 0; - base_addr[CyCOR1] = Cy_8_BITS | CyPARITY_NONE; - base_addr[CyCOR2] = 0; - base_addr[CyCOR3] = Cy_1_STOP; - base_addr[CyCOR4] = baud_cor4[i]; - base_addr[CyCOR5] = 0; - base_addr[CyCOR6] = 0; - base_addr[CyCOR7] = 0; - - write_cy_cmd(base_addr, CyINIT_CHAN); - write_cy_cmd(base_addr, CyENB_RCVR); - - base_addr[CyCAR] = DEBUG_PORT; /* !!! Is this needed? */ - - base_addr[CyRTPRL] = 2; - base_addr[CyRTPRH] = 0; - - base_addr[CyMSVR1] = CyRTS; - base_addr[CyMSVR2] = CyDTR; - - base_addr[CyIER] = CyRxData; - - local_irq_restore(flags); - -} /* debug_setup */ - -#endif - MODULE_LICENSE("GPL"); diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index 59de2525d30..5d15630a583 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -1,7 +1,4 @@ -/* -*- linux-c -*- - * - * $Id: sysrq.c,v 1.15 1998/08/23 14:56:41 mj Exp $ - * +/* * Linux Magic System Request Key Hacks * * (c) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz> @@ -10,8 +7,13 @@ * (c) 2000 Crutcher Dunnavant <crutcher+kernel@datastacks.com> * overhauled to use key registration * based upon discusions in irc://irc.openprojects.net/#kernelnewbies + * + * Copyright (c) 2010 Dmitry Torokhov + * Input handler conversion */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/mm.h> @@ -39,33 +41,34 @@ #include <linux/hrtimer.h> #include <linux/oom.h> #include <linux/slab.h> +#include <linux/input.h> #include <asm/ptrace.h> #include <asm/irq_regs.h> /* Whether we react on sysrq keys or just ignore them */ -int __read_mostly __sysrq_enabled = 1; - -static int __read_mostly sysrq_always_enabled; +static int __read_mostly sysrq_enabled = 1; +static bool __read_mostly sysrq_always_enabled; -int sysrq_on(void) +static bool sysrq_on(void) { - return __sysrq_enabled || sysrq_always_enabled; + return sysrq_enabled || sysrq_always_enabled; } /* * A value of 1 means 'all', other nonzero values are an op mask: */ -static inline int sysrq_on_mask(int mask) +static bool sysrq_on_mask(int mask) { - return sysrq_always_enabled || __sysrq_enabled == 1 || - (__sysrq_enabled & mask); + return sysrq_always_enabled || + sysrq_enabled == 1 || + (sysrq_enabled & mask); } static int __init sysrq_always_enabled_setup(char *str) { - sysrq_always_enabled = 1; - printk(KERN_INFO "debug: sysrq always enabled.\n"); + sysrq_always_enabled = true; + pr_info("sysrq always enabled.\n"); return 1; } @@ -76,6 +79,7 @@ __setup("sysrq_always_enabled", sysrq_always_enabled_setup); static void sysrq_handle_loglevel(int key, struct tty_struct *tty) { int i; + i = key - '0'; console_loglevel = 7; printk("Loglevel set to %d\n", i); @@ -101,7 +105,7 @@ static struct sysrq_key_op sysrq_SAK_op = { .enable_mask = SYSRQ_ENABLE_KEYBOARD, }; #else -#define sysrq_SAK_op (*(struct sysrq_key_op *)0) +#define sysrq_SAK_op (*(struct sysrq_key_op *)NULL) #endif #ifdef CONFIG_VT @@ -119,7 +123,7 @@ static struct sysrq_key_op sysrq_unraw_op = { .enable_mask = SYSRQ_ENABLE_KEYBOARD, }; #else -#define sysrq_unraw_op (*(struct sysrq_key_op *)0) +#define sysrq_unraw_op (*(struct sysrq_key_op *)NULL) #endif /* CONFIG_VT */ static void sysrq_handle_crash(int key, struct tty_struct *tty) @@ -195,7 +199,7 @@ static struct sysrq_key_op sysrq_showlocks_op = { .action_msg = "Show Locks Held", }; #else -#define sysrq_showlocks_op (*(struct sysrq_key_op *)0) +#define sysrq_showlocks_op (*(struct sysrq_key_op *)NULL) #endif #ifdef CONFIG_SMP @@ -289,7 +293,7 @@ static struct sysrq_key_op sysrq_showstate_blocked_op = { static void sysrq_ftrace_dump(int key, struct tty_struct *tty) { - ftrace_dump(); + ftrace_dump(DUMP_ALL); } static struct sysrq_key_op sysrq_ftrace_dump_op = { .handler = sysrq_ftrace_dump, @@ -298,7 +302,7 @@ static struct sysrq_key_op sysrq_ftrace_dump_op = { .enable_mask = SYSRQ_ENABLE_DUMP, }; #else -#define sysrq_ftrace_dump_op (*(struct sysrq_key_op *)0) +#define sysrq_ftrace_dump_op (*(struct sysrq_key_op *)NULL) #endif static void sysrq_handle_showmem(int key, struct tty_struct *tty) @@ -477,6 +481,7 @@ struct sysrq_key_op *__sysrq_get_key_op(int key) i = sysrq_key_table_key2index(key); if (i != -1) op_p = sysrq_key_table[i]; + return op_p; } @@ -488,11 +493,7 @@ static void __sysrq_put_key_op(int key, struct sysrq_key_op *op_p) sysrq_key_table[i] = op_p; } -/* - * This is the non-locking version of handle_sysrq. It must/can only be called - * by sysrq key handlers, as they are inside of the lock - */ -void __handle_sysrq(int key, struct tty_struct *tty, int check_mask) +static void __handle_sysrq(int key, struct tty_struct *tty, int check_mask) { struct sysrq_key_op *op_p; int orig_log_level; @@ -544,10 +545,6 @@ void __handle_sysrq(int key, struct tty_struct *tty, int check_mask) spin_unlock_irqrestore(&sysrq_key_table_lock, flags); } -/* - * This function is called by the keyboard handler when SysRq is pressed - * and any other keycode arrives. - */ void handle_sysrq(int key, struct tty_struct *tty) { if (sysrq_on()) @@ -555,10 +552,177 @@ void handle_sysrq(int key, struct tty_struct *tty) } EXPORT_SYMBOL(handle_sysrq); +#ifdef CONFIG_INPUT + +/* Simple translation table for the SysRq keys */ +static const unsigned char sysrq_xlate[KEY_MAX + 1] = + "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ + "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ + "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ + "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ + "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ + "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ + "\r\000/"; /* 0x60 - 0x6f */ + +static bool sysrq_down; +static int sysrq_alt_use; +static int sysrq_alt; + +static bool sysrq_filter(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ + if (type != EV_KEY) + goto out; + + switch (code) { + + case KEY_LEFTALT: + case KEY_RIGHTALT: + if (value) + sysrq_alt = code; + else if (sysrq_down && code == sysrq_alt_use) + sysrq_down = false; + break; + + case KEY_SYSRQ: + if (value == 1 && sysrq_alt) { + sysrq_down = true; + sysrq_alt_use = sysrq_alt; + } + break; + + default: + if (sysrq_down && value && value != 2) + __handle_sysrq(sysrq_xlate[code], NULL, 1); + break; + } + +out: + return sysrq_down; +} + +static int sysrq_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + struct input_handle *handle; + int error; + + sysrq_down = false; + sysrq_alt = 0; + + handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = "sysrq"; + + error = input_register_handle(handle); + if (error) { + pr_err("Failed to register input sysrq handler, error %d\n", + error); + goto err_free; + } + + error = input_open_device(handle); + if (error) { + pr_err("Failed to open input device, error %d\n", error); + goto err_unregister; + } + + return 0; + + err_unregister: + input_unregister_handle(handle); + err_free: + kfree(handle); + return error; +} + +static void sysrq_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +/* + * We are matching on KEY_LEFTALT insteard of KEY_SYSRQ because not all + * keyboards have SysRq ikey predefined and so user may add it to keymap + * later, but we expect all such keyboards to have left alt. + */ +static const struct input_device_id sysrq_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_KEYBIT, + .evbit = { BIT_MASK(EV_KEY) }, + .keybit = { BIT_MASK(KEY_LEFTALT) }, + }, + { }, +}; + +static struct input_handler sysrq_handler = { + .filter = sysrq_filter, + .connect = sysrq_connect, + .disconnect = sysrq_disconnect, + .name = "sysrq", + .id_table = sysrq_ids, +}; + +static bool sysrq_handler_registered; + +static inline void sysrq_register_handler(void) +{ + int error; + + error = input_register_handler(&sysrq_handler); + if (error) + pr_err("Failed to register input handler, error %d", error); + else + sysrq_handler_registered = true; +} + +static inline void sysrq_unregister_handler(void) +{ + if (sysrq_handler_registered) { + input_unregister_handler(&sysrq_handler); + sysrq_handler_registered = false; + } +} + +#else + +static inline void sysrq_register_handler(void) +{ +} + +static inline void sysrq_unregister_handler(void) +{ +} + +#endif /* CONFIG_INPUT */ + +int sysrq_toggle_support(int enable_mask) +{ + bool was_enabled = sysrq_on(); + + sysrq_enabled = enable_mask; + + if (was_enabled != sysrq_on()) { + if (sysrq_on()) + sysrq_register_handler(); + else + sysrq_unregister_handler(); + } + + return 0; +} + static int __sysrq_swap_key_ops(int key, struct sysrq_key_op *insert_op_p, struct sysrq_key_op *remove_op_p) { - int retval; unsigned long flags; @@ -599,6 +763,7 @@ static ssize_t write_sysrq_trigger(struct file *file, const char __user *buf, return -EFAULT; __handle_sysrq(c, NULL, 0); } + return count; } @@ -606,10 +771,28 @@ static const struct file_operations proc_sysrq_trigger_operations = { .write = write_sysrq_trigger, }; +static void sysrq_init_procfs(void) +{ + if (!proc_create("sysrq-trigger", S_IWUSR, NULL, + &proc_sysrq_trigger_operations)) + pr_err("Failed to register proc interface\n"); +} + +#else + +static inline void sysrq_init_procfs(void) +{ +} + +#endif /* CONFIG_PROC_FS */ + static int __init sysrq_init(void) { - proc_create("sysrq-trigger", S_IWUSR, NULL, &proc_sysrq_trigger_operations); + sysrq_init_procfs(); + + if (sysrq_on()) + sysrq_register_handler(); + return 0; } module_init(sysrq_init); -#endif diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index f5fc64f89c5..4dc338f3d1a 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -17,14 +17,16 @@ menuconfig TCG_TPM obtained at: <http://sourceforge.net/projects/trousers>. To compile this driver as a module, choose M here; the module will be called tpm. If unsure, say N. - Note: For more TPM drivers enable CONFIG_PNP, CONFIG_ACPI + Notes: + 1) For more TPM drivers enable CONFIG_PNP, CONFIG_ACPI and CONFIG_PNPACPI. + 2) Without ACPI enabled, the BIOS event log won't be accessible, + which is required to validate the PCR 0-7 values. if TCG_TPM config TCG_TIS tristate "TPM Interface Specification 1.2 Interface" - depends on PNP ---help--- If you have a TPM security chip that is compliant with the TCG TIS 1.2 TPM specification say Yes and it will be accessible diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 068c816e694..05ad4a17a28 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -1068,6 +1068,27 @@ void tpm_remove_hardware(struct device *dev) } EXPORT_SYMBOL_GPL(tpm_remove_hardware); +#define TPM_ORD_SAVESTATE cpu_to_be32(152) +#define SAVESTATE_RESULT_SIZE 10 + +static struct tpm_input_header savestate_header = { + .tag = TPM_TAG_RQU_COMMAND, + .length = cpu_to_be32(10), + .ordinal = TPM_ORD_SAVESTATE +}; + +/* Bug workaround - some TPM's don't flush the most + * recently changed pcr on suspend, so force the flush + * with an extend to the selected _unused_ non-volatile pcr. + */ +static int tpm_suspend_pcr; +static int __init tpm_suspend_setup(char *str) +{ + get_option(&str, &tpm_suspend_pcr); + return 1; +} +__setup("tpm_suspend_pcr=", tpm_suspend_setup); + /* * We are about to suspend. Save the TPM state * so that it can be restored. @@ -1075,17 +1096,29 @@ EXPORT_SYMBOL_GPL(tpm_remove_hardware); int tpm_pm_suspend(struct device *dev, pm_message_t pm_state) { struct tpm_chip *chip = dev_get_drvdata(dev); - u8 savestate[] = { - 0, 193, /* TPM_TAG_RQU_COMMAND */ - 0, 0, 0, 10, /* blob length (in bytes) */ - 0, 0, 0, 152 /* TPM_ORD_SaveState */ - }; + struct tpm_cmd_t cmd; + int rc; + + u8 dummy_hash[TPM_DIGEST_SIZE] = { 0 }; if (chip == NULL) return -ENODEV; - tpm_transmit(chip, savestate, sizeof(savestate)); - return 0; + /* for buggy tpm, flush pcrs with extend to selected dummy */ + if (tpm_suspend_pcr) { + cmd.header.in = pcrextend_header; + cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(tpm_suspend_pcr); + memcpy(cmd.params.pcrextend_in.hash, dummy_hash, + TPM_DIGEST_SIZE); + rc = transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, + "extending dummy pcr before suspend"); + } + + /* now do the actual savestate */ + cmd.header.in = savestate_header; + rc = transmit_cmd(chip, &cmd, SAVESTATE_RESULT_SIZE, + "sending savestate before suspend"); + return rc; } EXPORT_SYMBOL_GPL(tpm_pm_suspend); diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 94345994f8a..24314a9cffe 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -598,7 +598,7 @@ out_err: tpm_remove_hardware(chip->dev); return rc; } - +#ifdef CONFIG_PNP static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev, const struct pnp_device_id *pnp_id) { @@ -663,7 +663,7 @@ static struct pnp_driver tis_pnp_driver = { module_param_string(hid, tpm_pnp_tbl[TIS_HID_USR_IDX].id, sizeof(tpm_pnp_tbl[TIS_HID_USR_IDX].id), 0444); MODULE_PARM_DESC(hid, "Set additional specific HID for this driver to probe"); - +#endif static int tpm_tis_suspend(struct platform_device *dev, pm_message_t msg) { return tpm_pm_suspend(&dev->dev, msg); @@ -690,21 +690,21 @@ MODULE_PARM_DESC(force, "Force device probe rather than using ACPI entry"); static int __init init_tis(void) { int rc; +#ifdef CONFIG_PNP + if (!force) + return pnp_register_driver(&tis_pnp_driver); +#endif - if (force) { - rc = platform_driver_register(&tis_drv); - if (rc < 0) - return rc; - if (IS_ERR(pdev=platform_device_register_simple("tpm_tis", -1, NULL, 0))) - return PTR_ERR(pdev); - if((rc=tpm_tis_init(&pdev->dev, TIS_MEM_BASE, TIS_MEM_LEN, 0)) != 0) { - platform_device_unregister(pdev); - platform_driver_unregister(&tis_drv); - } + rc = platform_driver_register(&tis_drv); + if (rc < 0) return rc; + if (IS_ERR(pdev=platform_device_register_simple("tpm_tis", -1, NULL, 0))) + return PTR_ERR(pdev); + if((rc=tpm_tis_init(&pdev->dev, TIS_MEM_BASE, TIS_MEM_LEN, 0)) != 0) { + platform_device_unregister(pdev); + platform_driver_unregister(&tis_drv); } - - return pnp_register_driver(&tis_pnp_driver); + return rc; } static void __exit cleanup_tis(void) @@ -728,12 +728,14 @@ static void __exit cleanup_tis(void) list_del(&i->list); } spin_unlock(&tis_lock); - - if (force) { - platform_device_unregister(pdev); - platform_driver_unregister(&tis_drv); - } else +#ifdef CONFIG_PNP + if (!force) { pnp_unregister_driver(&tis_pnp_driver); + return; + } +#endif + platform_device_unregister(pdev); + platform_driver_unregister(&tis_drv); } module_init(init_tis); diff --git a/drivers/char/tty_buffer.c b/drivers/char/tty_buffer.c index 7ee52164d47..cc1e9850d65 100644 --- a/drivers/char/tty_buffer.c +++ b/drivers/char/tty_buffer.c @@ -238,7 +238,7 @@ EXPORT_SYMBOL_GPL(tty_buffer_request_room); * @size: size * * Queue a series of bytes to the tty buffering. All the characters - * passed are marked as without error. Returns the number added. + * passed are marked with the supplied flag. Returns the number added. * * Locking: Called functions may take tty->buf.lock */ diff --git a/drivers/char/viotape.c b/drivers/char/viotape.c index 1144a04cda6..42f7fa442ff 100644 --- a/drivers/char/viotape.c +++ b/drivers/char/viotape.c @@ -866,7 +866,7 @@ static int viotape_probe(struct vio_dev *vdev, const struct vio_device_id *id) { int i = vdev->unit_address; int j; - struct device_node *node = vdev->dev.archdata.of_node; + struct device_node *node = vdev->dev.of_node; if (i >= VIOTAPE_MAX_TAPE) return -ENODEV; diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 196428c2287..8c99bf1b5e9 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -33,35 +33,6 @@ #include <linux/workqueue.h> #include "hvc_console.h" -/* Moved here from .h file in order to disable MULTIPORT. */ -#define VIRTIO_CONSOLE_F_MULTIPORT 1 /* Does host provide multiple ports? */ - -struct virtio_console_multiport_conf { - struct virtio_console_config config; - /* max. number of ports this device can hold */ - __u32 max_nr_ports; - /* number of ports added so far */ - __u32 nr_ports; -} __attribute__((packed)); - -/* - * A message that's passed between the Host and the Guest for a - * particular port. - */ -struct virtio_console_control { - __u32 id; /* Port number */ - __u16 event; /* The kind of control event (see below) */ - __u16 value; /* Extra information for the key */ -}; - -/* Some events for control messages */ -#define VIRTIO_CONSOLE_PORT_READY 0 -#define VIRTIO_CONSOLE_CONSOLE_PORT 1 -#define VIRTIO_CONSOLE_RESIZE 2 -#define VIRTIO_CONSOLE_PORT_OPEN 3 -#define VIRTIO_CONSOLE_PORT_NAME 4 -#define VIRTIO_CONSOLE_PORT_REMOVE 5 - /* * This is a global struct for storing common data for all the devices * this driver handles. @@ -107,6 +78,9 @@ struct console { /* The hvc device associated with this console port */ struct hvc_struct *hvc; + /* The size of the console */ + struct winsize ws; + /* * This number identifies the number that we used to register * with hvc in hvc_instantiate() and hvc_alloc(); this is the @@ -139,7 +113,6 @@ struct ports_device { * notification */ struct work_struct control_work; - struct work_struct config_work; struct list_head ports; @@ -150,7 +123,7 @@ struct ports_device { spinlock_t cvq_lock; /* The current config space is stored here */ - struct virtio_console_multiport_conf config; + struct virtio_console_config config; /* The virtio device we're associated with */ struct virtio_device *vdev; @@ -189,6 +162,9 @@ struct port { */ spinlock_t inbuf_lock; + /* Protect the operations on the out_vq. */ + spinlock_t outvq_lock; + /* The IO vqs for this port */ struct virtqueue *in_vq, *out_vq; @@ -214,6 +190,8 @@ struct port { /* The 'id' to identify the port with the Host */ u32 id; + bool outvq_full; + /* Is the host device open */ bool host_connected; @@ -328,7 +306,7 @@ static void *get_inbuf(struct port *port) unsigned int len; vq = port->in_vq; - buf = vq->vq_ops->get_buf(vq, &len); + buf = virtqueue_get_buf(vq, &len); if (buf) { buf->len = len; buf->offset = 0; @@ -349,8 +327,8 @@ static int add_inbuf(struct virtqueue *vq, struct port_buffer *buf) sg_init_one(sg, buf->buf, buf->size); - ret = vq->vq_ops->add_buf(vq, sg, 0, 1, buf); - vq->vq_ops->kick(vq); + ret = virtqueue_add_buf(vq, sg, 0, 1, buf); + virtqueue_kick(vq); return ret; } @@ -366,7 +344,7 @@ static void discard_port_data(struct port *port) if (port->inbuf) buf = port->inbuf; else - buf = vq->vq_ops->get_buf(vq, &len); + buf = virtqueue_get_buf(vq, &len); ret = 0; while (buf) { @@ -374,7 +352,7 @@ static void discard_port_data(struct port *port) ret++; free_buf(buf); } - buf = vq->vq_ops->get_buf(vq, &len); + buf = virtqueue_get_buf(vq, &len); } port->inbuf = NULL; if (ret) @@ -403,57 +381,96 @@ out: return ret; } -static ssize_t send_control_msg(struct port *port, unsigned int event, - unsigned int value) +static ssize_t __send_control_msg(struct ports_device *portdev, u32 port_id, + unsigned int event, unsigned int value) { struct scatterlist sg[1]; struct virtio_console_control cpkt; struct virtqueue *vq; unsigned int len; - if (!use_multiport(port->portdev)) + if (!use_multiport(portdev)) return 0; - cpkt.id = port->id; + cpkt.id = port_id; cpkt.event = event; cpkt.value = value; - vq = port->portdev->c_ovq; + vq = portdev->c_ovq; sg_init_one(sg, &cpkt, sizeof(cpkt)); - if (vq->vq_ops->add_buf(vq, sg, 1, 0, &cpkt) >= 0) { - vq->vq_ops->kick(vq); - while (!vq->vq_ops->get_buf(vq, &len)) + if (virtqueue_add_buf(vq, sg, 1, 0, &cpkt) >= 0) { + virtqueue_kick(vq); + while (!virtqueue_get_buf(vq, &len)) cpu_relax(); } return 0; } -static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count) +static ssize_t send_control_msg(struct port *port, unsigned int event, + unsigned int value) +{ + return __send_control_msg(port->portdev, port->id, event, value); +} + +/* Callers must take the port->outvq_lock */ +static void reclaim_consumed_buffers(struct port *port) +{ + void *buf; + unsigned int len; + + while ((buf = virtqueue_get_buf(port->out_vq, &len))) { + kfree(buf); + port->outvq_full = false; + } +} + +static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count, + bool nonblock) { struct scatterlist sg[1]; struct virtqueue *out_vq; ssize_t ret; + unsigned long flags; unsigned int len; out_vq = port->out_vq; + spin_lock_irqsave(&port->outvq_lock, flags); + + reclaim_consumed_buffers(port); + sg_init_one(sg, in_buf, in_count); - ret = out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, in_buf); + ret = virtqueue_add_buf(out_vq, sg, 1, 0, in_buf); /* Tell Host to go! */ - out_vq->vq_ops->kick(out_vq); + virtqueue_kick(out_vq); if (ret < 0) { in_count = 0; - goto fail; + goto done; } - /* Wait till the host acknowledges it pushed out the data we sent. */ - while (!out_vq->vq_ops->get_buf(out_vq, &len)) + if (ret == 0) + port->outvq_full = true; + + if (nonblock) + goto done; + + /* + * Wait till the host acknowledges it pushed out the data we + * sent. This is done for ports in blocking mode or for data + * from the hvc_console; the tty operations are performed with + * spinlocks held so we can't sleep here. + */ + while (!virtqueue_get_buf(out_vq, &len)) cpu_relax(); -fail: - /* We're expected to return the amount of data we wrote */ +done: + spin_unlock_irqrestore(&port->outvq_lock, flags); + /* + * We're expected to return the amount of data we wrote -- all + * of it + */ return in_count; } @@ -503,9 +520,28 @@ static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count, } /* The condition that must be true for polling to end */ -static bool wait_is_over(struct port *port) +static bool will_read_block(struct port *port) +{ + return !port_has_data(port) && port->host_connected; +} + +static bool will_write_block(struct port *port) { - return port_has_data(port) || !port->host_connected; + bool ret; + + if (!port->host_connected) + return true; + + spin_lock_irq(&port->outvq_lock); + /* + * Check if the Host has consumed any buffers since we last + * sent data (this is only applicable for nonblocking ports). + */ + reclaim_consumed_buffers(port); + ret = port->outvq_full; + spin_unlock_irq(&port->outvq_lock); + + return ret; } static ssize_t port_fops_read(struct file *filp, char __user *ubuf, @@ -528,7 +564,7 @@ static ssize_t port_fops_read(struct file *filp, char __user *ubuf, return -EAGAIN; ret = wait_event_interruptible(port->waitqueue, - wait_is_over(port)); + !will_read_block(port)); if (ret < 0) return ret; } @@ -554,9 +590,22 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, struct port *port; char *buf; ssize_t ret; + bool nonblock; port = filp->private_data; + nonblock = filp->f_flags & O_NONBLOCK; + + if (will_write_block(port)) { + if (nonblock) + return -EAGAIN; + + ret = wait_event_interruptible(port->waitqueue, + !will_write_block(port)); + if (ret < 0) + return ret; + } + count = min((size_t)(32 * 1024), count); buf = kmalloc(count, GFP_KERNEL); @@ -569,9 +618,14 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, goto free_buf; } - ret = send_buf(port, buf, count); + ret = send_buf(port, buf, count, nonblock); + + if (nonblock && ret > 0) + goto out; + free_buf: kfree(buf); +out: return ret; } @@ -586,7 +640,7 @@ static unsigned int port_fops_poll(struct file *filp, poll_table *wait) ret = 0; if (port->inbuf) ret |= POLLIN | POLLRDNORM; - if (port->host_connected) + if (!will_write_block(port)) ret |= POLLOUT; if (!port->host_connected) ret |= POLLHUP; @@ -610,6 +664,10 @@ static int port_fops_release(struct inode *inode, struct file *filp) spin_unlock_irq(&port->inbuf_lock); + spin_lock_irq(&port->outvq_lock); + reclaim_consumed_buffers(port); + spin_unlock_irq(&port->outvq_lock); + return 0; } @@ -638,6 +696,15 @@ static int port_fops_open(struct inode *inode, struct file *filp) port->guest_connected = true; spin_unlock_irq(&port->inbuf_lock); + spin_lock_irq(&port->outvq_lock); + /* + * There might be a chance that we missed reclaiming a few + * buffers in the window of the port getting previously closed + * and opening now. + */ + reclaim_consumed_buffers(port); + spin_unlock_irq(&port->outvq_lock); + /* Notify host of port being opened */ send_control_msg(filp->private_data, VIRTIO_CONSOLE_PORT_OPEN, 1); @@ -676,9 +743,9 @@ static int put_chars(u32 vtermno, const char *buf, int count) port = find_port_by_vtermno(vtermno); if (!port) - return 0; + return -EPIPE; - return send_buf(port, (void *)buf, count); + return send_buf(port, (void *)buf, count, false); } /* @@ -692,9 +759,13 @@ static int get_chars(u32 vtermno, char *buf, int count) { struct port *port; + /* If we've not set up the port yet, we have no input to give. */ + if (unlikely(early_put_chars)) + return 0; + port = find_port_by_vtermno(vtermno); if (!port) - return 0; + return -EPIPE; /* If we don't have an input queue yet, we can't get input. */ BUG_ON(!port->in_vq); @@ -705,22 +776,14 @@ static int get_chars(u32 vtermno, char *buf, int count) static void resize_console(struct port *port) { struct virtio_device *vdev; - struct winsize ws; /* The port could have been hot-unplugged */ - if (!port) + if (!port || !is_console_port(port)) return; vdev = port->portdev->vdev; - if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE)) { - vdev->config->get(vdev, - offsetof(struct virtio_console_config, cols), - &ws.ws_col, sizeof(u16)); - vdev->config->get(vdev, - offsetof(struct virtio_console_config, rows), - &ws.ws_row, sizeof(u16)); - hvc_resize(port->cons.hvc, ws); - } + if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE)) + hvc_resize(port->cons.hvc, port->cons.ws); } /* We set the configuration at this point, since we now have a tty */ @@ -804,6 +867,13 @@ int init_port_console(struct port *port) spin_unlock_irq(&pdrvdata_lock); port->guest_connected = true; + /* + * Start using the new console output if this is the first + * console to come up. + */ + if (early_put_chars) + early_put_chars = NULL; + /* Notify host of port being opened */ send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 1); @@ -859,6 +929,8 @@ static ssize_t debugfs_read(struct file *filp, char __user *ubuf, out_offset += snprintf(buf + out_offset, out_count - out_offset, "host_connected: %d\n", port->host_connected); out_offset += snprintf(buf + out_offset, out_count - out_offset, + "outvq_full: %d\n", port->outvq_full); + out_offset += snprintf(buf + out_offset, out_count - out_offset, "is_console: %s\n", is_console_port(port) ? "yes" : "no"); out_offset += snprintf(buf + out_offset, out_count - out_offset, @@ -875,6 +947,153 @@ static const struct file_operations port_debugfs_ops = { .read = debugfs_read, }; +static void set_console_size(struct port *port, u16 rows, u16 cols) +{ + if (!port || !is_console_port(port)) + return; + + port->cons.ws.ws_row = rows; + port->cons.ws.ws_col = cols; +} + +static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock) +{ + struct port_buffer *buf; + unsigned int nr_added_bufs; + int ret; + + nr_added_bufs = 0; + do { + buf = alloc_buf(PAGE_SIZE); + if (!buf) + break; + + spin_lock_irq(lock); + ret = add_inbuf(vq, buf); + if (ret < 0) { + spin_unlock_irq(lock); + free_buf(buf); + break; + } + nr_added_bufs++; + spin_unlock_irq(lock); + } while (ret > 0); + + return nr_added_bufs; +} + +static int add_port(struct ports_device *portdev, u32 id) +{ + char debugfs_name[16]; + struct port *port; + struct port_buffer *buf; + dev_t devt; + unsigned int nr_added_bufs; + int err; + + port = kmalloc(sizeof(*port), GFP_KERNEL); + if (!port) { + err = -ENOMEM; + goto fail; + } + + port->portdev = portdev; + port->id = id; + + port->name = NULL; + port->inbuf = NULL; + port->cons.hvc = NULL; + + port->cons.ws.ws_row = port->cons.ws.ws_col = 0; + + port->host_connected = port->guest_connected = false; + + port->outvq_full = false; + + port->in_vq = portdev->in_vqs[port->id]; + port->out_vq = portdev->out_vqs[port->id]; + + cdev_init(&port->cdev, &port_fops); + + devt = MKDEV(portdev->chr_major, id); + err = cdev_add(&port->cdev, devt, 1); + if (err < 0) { + dev_err(&port->portdev->vdev->dev, + "Error %d adding cdev for port %u\n", err, id); + goto free_port; + } + port->dev = device_create(pdrvdata.class, &port->portdev->vdev->dev, + devt, port, "vport%up%u", + port->portdev->drv_index, id); + if (IS_ERR(port->dev)) { + err = PTR_ERR(port->dev); + dev_err(&port->portdev->vdev->dev, + "Error %d creating device for port %u\n", + err, id); + goto free_cdev; + } + + spin_lock_init(&port->inbuf_lock); + spin_lock_init(&port->outvq_lock); + init_waitqueue_head(&port->waitqueue); + + /* Fill the in_vq with buffers so the host can send us data. */ + nr_added_bufs = fill_queue(port->in_vq, &port->inbuf_lock); + if (!nr_added_bufs) { + dev_err(port->dev, "Error allocating inbufs\n"); + err = -ENOMEM; + goto free_device; + } + + /* + * If we're not using multiport support, this has to be a console port + */ + if (!use_multiport(port->portdev)) { + err = init_port_console(port); + if (err) + goto free_inbufs; + } + + spin_lock_irq(&portdev->ports_lock); + list_add_tail(&port->list, &port->portdev->ports); + spin_unlock_irq(&portdev->ports_lock); + + /* + * Tell the Host we're set so that it can send us various + * configuration parameters for this port (eg, port name, + * caching, whether this is a console port, etc.) + */ + send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); + + if (pdrvdata.debugfs_dir) { + /* + * Finally, create the debugfs file that we can use to + * inspect a port's state at any time + */ + sprintf(debugfs_name, "vport%up%u", + port->portdev->drv_index, id); + port->debugfs_file = debugfs_create_file(debugfs_name, 0444, + pdrvdata.debugfs_dir, + port, + &port_debugfs_ops); + } + return 0; + +free_inbufs: + while ((buf = virtqueue_detach_unused_buf(port->in_vq))) + free_buf(buf); +free_device: + device_destroy(pdrvdata.class, port->dev->devt); +free_cdev: + cdev_del(&port->cdev); +free_port: + kfree(port); +fail: + /* The host might want to notify management sw about port add failure */ + __send_control_msg(portdev, id, VIRTIO_CONSOLE_PORT_READY, 0); + return err; +} + /* Remove all port-specific data. */ static int remove_port(struct port *port) { @@ -888,7 +1107,18 @@ static int remove_port(struct port *port) spin_lock_irq(&pdrvdata_lock); list_del(&port->cons.list); spin_unlock_irq(&pdrvdata_lock); +#if 0 + /* + * hvc_remove() not called as removing one hvc port + * results in other hvc ports getting frozen. + * + * Once this is resolved in hvc, this functionality + * will be enabled. Till that is done, the -EPIPE + * return from get_chars() above will help + * hvc_console.c to clean up on ports we remove here. + */ hvc_remove(port->cons.hvc); +#endif } if (port->guest_connected) send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 0); @@ -900,8 +1130,10 @@ static int remove_port(struct port *port) /* Remove unused data this port might have received. */ discard_port_data(port); + reclaim_consumed_buffers(port); + /* Remove buffers we queued up for the Host to send us data in. */ - while ((buf = port->in_vq->vq_ops->detach_unused_buf(port->in_vq))) + while ((buf = virtqueue_detach_unused_buf(port->in_vq))) free_buf(buf); kfree(port->name); @@ -924,7 +1156,7 @@ static void handle_control_message(struct ports_device *portdev, cpkt = (struct virtio_console_control *)(buf->buf + buf->offset); port = find_port_by_id(portdev, cpkt->id); - if (!port) { + if (!port && cpkt->event != VIRTIO_CONSOLE_PORT_ADD) { /* No valid header at start of buffer. Drop it. */ dev_dbg(&portdev->vdev->dev, "Invalid index %u in control packet\n", cpkt->id); @@ -932,6 +1164,24 @@ static void handle_control_message(struct ports_device *portdev, } switch (cpkt->event) { + case VIRTIO_CONSOLE_PORT_ADD: + if (port) { + dev_dbg(&portdev->vdev->dev, + "Port %u already added\n", port->id); + send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); + break; + } + if (cpkt->id >= portdev->config.max_nr_ports) { + dev_warn(&portdev->vdev->dev, + "Request for adding port with out-of-bound id %u, max. supported id: %u\n", + cpkt->id, portdev->config.max_nr_ports - 1); + break; + } + add_port(portdev, cpkt->id); + break; + case VIRTIO_CONSOLE_PORT_REMOVE: + remove_port(port); + break; case VIRTIO_CONSOLE_CONSOLE_PORT: if (!cpkt->value) break; @@ -944,15 +1194,34 @@ static void handle_control_message(struct ports_device *portdev, * have to notify the host first. */ break; - case VIRTIO_CONSOLE_RESIZE: + case VIRTIO_CONSOLE_RESIZE: { + struct { + __u16 rows; + __u16 cols; + } size; + if (!is_console_port(port)) break; + + memcpy(&size, buf->buf + buf->offset + sizeof(*cpkt), + sizeof(size)); + set_console_size(port, size.rows, size.cols); + port->cons.hvc->irq_requested = 1; resize_console(port); break; + } case VIRTIO_CONSOLE_PORT_OPEN: port->host_connected = cpkt->value; wake_up_interruptible(&port->waitqueue); + /* + * If the host port got closed and the host had any + * unconsumed buffers, we'll be able to reclaim them + * now. + */ + spin_lock_irq(&port->outvq_lock); + reclaim_consumed_buffers(port); + spin_unlock_irq(&port->outvq_lock); break; case VIRTIO_CONSOLE_PORT_NAME: /* @@ -990,32 +1259,6 @@ static void handle_control_message(struct ports_device *portdev, kobject_uevent(&port->dev->kobj, KOBJ_CHANGE); } break; - case VIRTIO_CONSOLE_PORT_REMOVE: - /* - * Hot unplug the port. We don't decrement nr_ports - * since we don't want to deal with extra complexities - * of using the lowest-available port id: We can just - * pick up the nr_ports number as the id and not have - * userspace send it to us. This helps us in two - * ways: - * - * - We don't need to have a 'port_id' field in the - * config space when a port is hot-added. This is a - * good thing as we might queue up multiple hotplug - * requests issued in our workqueue. - * - * - Another way to deal with this would have been to - * use a bitmap of the active ports and select the - * lowest non-active port from that map. That - * bloats the already tight config space and we - * would end up artificially limiting the - * max. number of ports to sizeof(bitmap). Right - * now we can support 2^32 ports (as the port id is - * stored in a u32 type). - * - */ - remove_port(port); - break; } } @@ -1030,7 +1273,7 @@ static void control_work_handler(struct work_struct *work) vq = portdev->c_ivq; spin_lock(&portdev->cvq_lock); - while ((buf = vq->vq_ops->get_buf(vq, &len))) { + while ((buf = virtqueue_get_buf(vq, &len))) { spin_unlock(&portdev->cvq_lock); buf->len = len; @@ -1092,204 +1335,29 @@ static void config_intr(struct virtio_device *vdev) struct ports_device *portdev; portdev = vdev->priv; - if (use_multiport(portdev)) { - /* Handle port hot-add */ - schedule_work(&portdev->config_work); - } - /* - * We'll use this way of resizing only for legacy support. - * For newer userspace (VIRTIO_CONSOLE_F_MULTPORT+), use - * control messages to indicate console size changes so that - * it can be done per-port - */ - resize_console(find_port_by_id(portdev, 0)); -} - -static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock) -{ - struct port_buffer *buf; - unsigned int nr_added_bufs; - int ret; - - nr_added_bufs = 0; - do { - buf = alloc_buf(PAGE_SIZE); - if (!buf) - break; - - spin_lock_irq(lock); - ret = add_inbuf(vq, buf); - if (ret < 0) { - spin_unlock_irq(lock); - free_buf(buf); - break; - } - nr_added_bufs++; - spin_unlock_irq(lock); - } while (ret > 0); - - return nr_added_bufs; -} - -static int add_port(struct ports_device *portdev, u32 id) -{ - char debugfs_name[16]; - struct port *port; - struct port_buffer *buf; - dev_t devt; - unsigned int nr_added_bufs; - int err; - - port = kmalloc(sizeof(*port), GFP_KERNEL); - if (!port) { - err = -ENOMEM; - goto fail; - } - - port->portdev = portdev; - port->id = id; - - port->name = NULL; - port->inbuf = NULL; - port->cons.hvc = NULL; - - port->host_connected = port->guest_connected = false; - - port->in_vq = portdev->in_vqs[port->id]; - port->out_vq = portdev->out_vqs[port->id]; - - cdev_init(&port->cdev, &port_fops); - - devt = MKDEV(portdev->chr_major, id); - err = cdev_add(&port->cdev, devt, 1); - if (err < 0) { - dev_err(&port->portdev->vdev->dev, - "Error %d adding cdev for port %u\n", err, id); - goto free_port; - } - port->dev = device_create(pdrvdata.class, &port->portdev->vdev->dev, - devt, port, "vport%up%u", - port->portdev->drv_index, id); - if (IS_ERR(port->dev)) { - err = PTR_ERR(port->dev); - dev_err(&port->portdev->vdev->dev, - "Error %d creating device for port %u\n", - err, id); - goto free_cdev; - } - - spin_lock_init(&port->inbuf_lock); - init_waitqueue_head(&port->waitqueue); - - /* Fill the in_vq with buffers so the host can send us data. */ - nr_added_bufs = fill_queue(port->in_vq, &port->inbuf_lock); - if (!nr_added_bufs) { - dev_err(port->dev, "Error allocating inbufs\n"); - err = -ENOMEM; - goto free_device; - } - - /* - * If we're not using multiport support, this has to be a console port - */ - if (!use_multiport(port->portdev)) { - err = init_port_console(port); - if (err) - goto free_inbufs; - } - - spin_lock_irq(&portdev->ports_lock); - list_add_tail(&port->list, &port->portdev->ports); - spin_unlock_irq(&portdev->ports_lock); - - /* - * Tell the Host we're set so that it can send us various - * configuration parameters for this port (eg, port name, - * caching, whether this is a console port, etc.) - */ - send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); - - if (pdrvdata.debugfs_dir) { - /* - * Finally, create the debugfs file that we can use to - * inspect a port's state at any time - */ - sprintf(debugfs_name, "vport%up%u", - port->portdev->drv_index, id); - port->debugfs_file = debugfs_create_file(debugfs_name, 0444, - pdrvdata.debugfs_dir, - port, - &port_debugfs_ops); - } - return 0; - -free_inbufs: - while ((buf = port->in_vq->vq_ops->detach_unused_buf(port->in_vq))) - free_buf(buf); -free_device: - device_destroy(pdrvdata.class, port->dev->devt); -free_cdev: - cdev_del(&port->cdev); -free_port: - kfree(port); -fail: - return err; -} -/* - * The workhandler for config-space updates. - * - * This is called when ports are hot-added. - */ -static void config_work_handler(struct work_struct *work) -{ - struct virtio_console_multiport_conf virtconconf; - struct ports_device *portdev; - struct virtio_device *vdev; - int err; + if (!use_multiport(portdev)) { + struct port *port; + u16 rows, cols; - portdev = container_of(work, struct ports_device, config_work); + vdev->config->get(vdev, + offsetof(struct virtio_console_config, cols), + &cols, sizeof(u16)); + vdev->config->get(vdev, + offsetof(struct virtio_console_config, rows), + &rows, sizeof(u16)); - vdev = portdev->vdev; - vdev->config->get(vdev, - offsetof(struct virtio_console_multiport_conf, - nr_ports), - &virtconconf.nr_ports, - sizeof(virtconconf.nr_ports)); + port = find_port_by_id(portdev, 0); + set_console_size(port, rows, cols); - if (portdev->config.nr_ports == virtconconf.nr_ports) { /* - * Port 0 got hot-added. Since we already did all the - * other initialisation for it, just tell the Host - * that the port is ready if we find the port. In - * case the port was hot-removed earlier, we call - * add_port to add the port. + * We'll use this way of resizing only for legacy + * support. For newer userspace + * (VIRTIO_CONSOLE_F_MULTPORT+), use control messages + * to indicate console size changes so that it can be + * done per-port. */ - struct port *port; - - port = find_port_by_id(portdev, 0); - if (!port) - add_port(portdev, 0); - else - send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); - return; - } - if (virtconconf.nr_ports > portdev->config.max_nr_ports) { - dev_warn(&vdev->dev, - "More ports specified (%u) than allowed (%u)", - portdev->config.nr_ports + 1, - portdev->config.max_nr_ports); - return; - } - if (virtconconf.nr_ports < portdev->config.nr_ports) - return; - - /* Hot-add ports */ - while (virtconconf.nr_ports - portdev->config.nr_ports) { - err = add_port(portdev, portdev->config.nr_ports); - if (err) - break; - portdev->config.nr_ports++; + resize_console(port); } } @@ -1414,7 +1482,6 @@ static const struct file_operations portdev_fops = { static int __devinit virtcons_probe(struct virtio_device *vdev) { struct ports_device *portdev; - u32 i; int err; bool multiport; @@ -1443,37 +1510,19 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) } multiport = false; - portdev->config.nr_ports = 1; portdev->config.max_nr_ports = 1; -#if 0 /* Multiport is not quite ready yet --RR */ if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT)) { multiport = true; vdev->features[0] |= 1 << VIRTIO_CONSOLE_F_MULTIPORT; - vdev->config->get(vdev, - offsetof(struct virtio_console_multiport_conf, - nr_ports), - &portdev->config.nr_ports, - sizeof(portdev->config.nr_ports)); - vdev->config->get(vdev, - offsetof(struct virtio_console_multiport_conf, - max_nr_ports), + vdev->config->get(vdev, offsetof(struct virtio_console_config, + max_nr_ports), &portdev->config.max_nr_ports, sizeof(portdev->config.max_nr_ports)); - if (portdev->config.nr_ports > portdev->config.max_nr_ports) { - dev_warn(&vdev->dev, - "More ports (%u) specified than allowed (%u). Will init %u ports.", - portdev->config.nr_ports, - portdev->config.max_nr_ports, - portdev->config.max_nr_ports); - - portdev->config.nr_ports = portdev->config.max_nr_ports; - } } /* Let the Host know we support multiple ports.*/ vdev->config->finalize_features(vdev); -#endif err = init_vqs(portdev); if (err < 0) { @@ -1489,7 +1538,6 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) spin_lock_init(&portdev->cvq_lock); INIT_WORK(&portdev->control_work, &control_work_handler); - INIT_WORK(&portdev->config_work, &config_work_handler); nr_added_bufs = fill_queue(portdev->c_ivq, &portdev->cvq_lock); if (!nr_added_bufs) { @@ -1498,16 +1546,22 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) err = -ENOMEM; goto free_vqs; } + } else { + /* + * For backward compatibility: Create a console port + * if we're running on older host. + */ + add_port(portdev, 0); } - for (i = 0; i < portdev->config.nr_ports; i++) - add_port(portdev, i); - - /* Start using the new console output. */ - early_put_chars = NULL; + __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, + VIRTIO_CONSOLE_DEVICE_READY, 1); return 0; free_vqs: + /* The host might want to notify mgmt sw about device add failure */ + __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, + VIRTIO_CONSOLE_DEVICE_READY, 0); vdev->config->del_vqs(vdev); kfree(portdev->in_vqs); kfree(portdev->out_vqs); @@ -1529,17 +1583,16 @@ static void virtcons_remove(struct virtio_device *vdev) portdev = vdev->priv; cancel_work_sync(&portdev->control_work); - cancel_work_sync(&portdev->config_work); list_for_each_entry_safe(port, port2, &portdev->ports, list) remove_port(port); unregister_chrdev(portdev->chr_major, "virtio-portsdev"); - while ((buf = portdev->c_ivq->vq_ops->get_buf(portdev->c_ivq, &len))) + while ((buf = virtqueue_get_buf(portdev->c_ivq, &len))) free_buf(buf); - while ((buf = portdev->c_ivq->vq_ops->detach_unused_buf(portdev->c_ivq))) + while ((buf = virtqueue_detach_unused_buf(portdev->c_ivq))) free_buf(buf); vdev->config->del_vqs(vdev); @@ -1556,6 +1609,7 @@ static struct virtio_device_id id_table[] = { static unsigned int features[] = { VIRTIO_CONSOLE_F_SIZE, + VIRTIO_CONSOLE_F_MULTIPORT, }; static struct virtio_driver virtio_console = { diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c index 7261b8d9087..ed8a9cec2a0 100644 --- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c @@ -772,18 +772,18 @@ hwicap_of_probe(struct of_device *op, const struct of_device_id *match) dev_dbg(&op->dev, "hwicap_of_probe(%p, %p)\n", op, match); - rc = of_address_to_resource(op->node, 0, &res); + rc = of_address_to_resource(op->dev.of_node, 0, &res); if (rc) { dev_err(&op->dev, "invalid address\n"); return rc; } - id = of_get_property(op->node, "port-number", NULL); + id = of_get_property(op->dev.of_node, "port-number", NULL); /* It's most likely that we're using V4, if the family is not specified */ regs = &v4_config_registers; - family = of_get_property(op->node, "xlnx,family", NULL); + family = of_get_property(op->dev.of_node, "xlnx,family", NULL); if (family) { if (!strcmp(family, "virtex2p")) { @@ -812,13 +812,12 @@ static const struct of_device_id __devinitconst hwicap_of_match[] = { MODULE_DEVICE_TABLE(of, hwicap_of_match); static struct of_platform_driver hwicap_of_driver = { - .owner = THIS_MODULE, - .name = DRIVER_NAME, - .match_table = hwicap_of_match, .probe = hwicap_of_probe, .remove = __devexit_p(hwicap_of_remove), .driver = { .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = hwicap_of_match, }, }; |