From af358dcd0bc3c7d460485950c662dafa159ef047 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 11 Oct 2002 01:02:46 -0700 Subject: [PATCH] "nousb" for in-kernel USB Here's the updated "nousb" patch for vanilla 2.5.41. It applies with 2 small offsets to 2.5.41-bk3. --- include/linux/usb.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/usb.h b/include/linux/usb.h index 5dbc3cb115f7..89ecca734172 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -670,6 +670,7 @@ extern void usb_deregister_dev(int num_minors, int start_minor); extern int usb_device_probe(struct device *dev); extern int usb_device_remove(struct device *dev); +extern int usb_disabled(void); /* -------------------------------------------------------------------------- */ -- cgit v1.2.3 From 8ead40f5496dcff30dd355159ca2ad10b7105390 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 12 Oct 2002 19:32:50 -0700 Subject: [PATCH] page freeing function for swsusp Software suspend needs a way of forcing page reclaim, up to the point where 50% of memory is free. This patch implements a function to do that: int shrink_all_memory(int nr_pages); Will attempt to reclaim `nr_pages' pages and return them to the free pages pool. It returns the number of pages which it actually freed. If called with a "large" number of pages it will only free up to a few hundred, so the caller needs to loop on it. If it returns zero then there is no point in calling it again. --- include/linux/swap.h | 1 + mm/vmscan.c | 48 +++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 42 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/swap.h b/include/linux/swap.h index 4ec8559245c6..e0302ec1662d 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -163,6 +163,7 @@ extern void swap_setup(void); /* linux/mm/vmscan.c */ extern int try_to_free_pages(struct zone *, unsigned int, unsigned int); +int shrink_all_memory(int nr_pages); /* linux/mm/page_io.c */ int swap_readpage(struct file *file, struct page *page); diff --git a/mm/vmscan.c b/mm/vmscan.c index 486f04e6b5a1..84cdccedeeab 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -695,12 +695,19 @@ try_to_free_pages(struct zone *classzone, } /* - * kswapd will work across all this node's zones until they are all at - * pages_high. + * For kswapd, balance_pgdat() will work across all this node's zones until + * they are all at pages_high. + * + * If `nr_pages' is non-zero then it is the number of pages which are to be + * reclaimed, regardless of the zone occupancies. This is a software suspend + * special. + * + * Returns the number of pages which were actually freed. */ -static void kswapd_balance_pgdat(pg_data_t *pgdat) +static int balance_pgdat(pg_data_t *pgdat, int nr_pages) { - int priority = DEF_PRIORITY; + int to_free = nr_pages; + int priority; int i; for (priority = DEF_PRIORITY; priority; priority--) { @@ -713,20 +720,23 @@ static void kswapd_balance_pgdat(pg_data_t *pgdat) int to_reclaim; to_reclaim = zone->pages_high - zone->free_pages; + if (nr_pages && to_free > 0) + to_reclaim = min(to_free, SWAP_CLUSTER_MAX*8); if (to_reclaim <= 0) continue; success = 0; max_scan = zone->nr_inactive >> priority; if (max_scan < to_reclaim * 2) max_scan = to_reclaim * 2; - shrink_zone(zone, max_scan, GFP_KSWAPD, + to_free -= shrink_zone(zone, max_scan, GFP_KSWAPD, to_reclaim, &nr_mapped); shrink_slab(max_scan + nr_mapped, GFP_KSWAPD); } if (success) - break; /* All zones are at pages_high */ + break; blk_congestion_wait(WRITE, HZ/4); } + return nr_pages - to_free; } /* @@ -773,11 +783,35 @@ int kswapd(void *p) prepare_to_wait(&pgdat->kswapd_wait, &wait, TASK_INTERRUPTIBLE); schedule(); finish_wait(&pgdat->kswapd_wait, &wait); - kswapd_balance_pgdat(pgdat); + balance_pgdat(pgdat, 0); blk_run_queues(); } } +#ifdef CONFIG_SOFTWARE_SUSPEND +/* + * Try to free `nr_pages' of memory, system-wide. Returns the number of freed + * pages. + */ +int shrink_all_memory(int nr_pages) +{ + pg_data_t *pgdat; + int nr_to_free = nr_pages; + int ret = 0; + + for_each_pgdat(pgdat) { + int freed; + + freed = balance_pgdat(pgdat, nr_to_free); + ret += freed; + nr_to_free -= freed; + if (nr_to_free <= 0) + break; + } + return ret; +} +#endif + static int __init kswapd_init(void) { pg_data_t *pgdat; -- cgit v1.2.3 From 8f7a14042e1f5fc814fa60956e3a2dcf5744a0ed Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 12 Oct 2002 19:33:06 -0700 Subject: [PATCH] reduced and tunable swappiness /proc/sys/vm/swappiness controls the VM's tendency to unmap pages and to swap things out. 100 -> basically current 2.5 behaviour 0 -> not very swappy at all The mechanism which is used to control swappiness is: to be reluctant to bring mapped pages onto the inactive list. Prefer to reclaim pagecache instead. The control for that mechanism is as follows: - If there is a large amount of mapped memory in the machine, we prefer to bring mapped pages onto the inactive list. - If page reclaim is under distress (more scanning is happening) then prefer to bring mapped pages onto the inactive list. This is basically the 2.4 algorithm, really. - If the /proc/sys/vm/swappiness control is high then prefer to bring mapped pages onto the inactive list. The implementation is simple: calculate the above three things as percentages and add them up. If that's over 100% then start reclaiming mapped pages. The `proportion of mapped memory' is downgraded so that we don't swap just because a lot of memory is mapped into pagetables - we still need some VM distress before starting to swap that memory out. For a while I was adding a little bias so that we prefer to unmap file-backed memory before swapping out anon memory. Because usually file backed memory can be evicted and reestablished with one I/O, not two. It was unmapping executable text too easily, so here I just treat them equally. --- include/linux/swap.h | 1 + include/linux/sysctl.h | 1 + kernel/sysctl.c | 3 ++ mm/vmscan.c | 103 ++++++++++++++++++++++++++++++++++++++++--------- 4 files changed, 89 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/linux/swap.h b/include/linux/swap.h index e0302ec1662d..4457c1dec40b 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -164,6 +164,7 @@ extern void swap_setup(void); /* linux/mm/vmscan.c */ extern int try_to_free_pages(struct zone *, unsigned int, unsigned int); int shrink_all_memory(int nr_pages); +extern int vm_swappiness; /* linux/mm/page_io.c */ int swap_readpage(struct file *file, struct page *page); diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 8139ec747979..f1a5aaa234ad 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -153,6 +153,7 @@ enum VM_OVERCOMMIT_RATIO=16, /* percent of RAM to allow overcommit in */ VM_PAGEBUF=17, /* struct: Control pagebuf parameters */ VM_HUGETLB_PAGES=18, /* int: Number of available Huge Pages */ + VM_SWAPPINESS=19, /* Tendency to steal mapped memory */ }; diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 53d13c96fbf2..3b49efc1c523 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -311,6 +311,9 @@ static ctl_table vm_table[] = { { VM_NR_PDFLUSH_THREADS, "nr_pdflush_threads", &nr_pdflush_threads, sizeof nr_pdflush_threads, 0444 /* read-only*/, NULL, &proc_dointvec}, + {VM_SWAPPINESS, "swappiness", &vm_swappiness, sizeof(vm_swappiness), + 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec, NULL, &zero, + &one_hundred }, #ifdef CONFIG_HUGETLB_PAGE {VM_HUGETLB_PAGES, "nr_hugepages", &htlbpage_max, sizeof(int), 0644, NULL, &proc_dointvec}, diff --git a/mm/vmscan.c b/mm/vmscan.c index 84cdccedeeab..0086407047f6 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -35,13 +35,18 @@ #include /* - * The "priority" of VM scanning is how much of the queues we - * will scan in one go. A value of 12 for DEF_PRIORITY implies - * that we'll scan 1/4096th of the queues ("queue_length >> 12") - * during a normal aging round. + * The "priority" of VM scanning is how much of the queues we will scan in one + * go. A value of 12 for DEF_PRIORITY implies that we will scan 1/4096th of the + * queues ("queue_length >> 12") during an aging round. */ #define DEF_PRIORITY 12 +/* + * From 0 .. 100. Higher means more swappy. + */ +int vm_swappiness = 60; +static long total_memory; + #ifdef ARCH_HAS_PREFETCH #define prefetch_prev_lru_page(_page, _base, _field) \ do { \ @@ -101,7 +106,6 @@ static inline int is_page_cache_freeable(struct page *page) return page_count(page) - !!PagePrivate(page) == 2; } - /* * shrink_list returns the number of reclaimed pages */ @@ -439,7 +443,8 @@ done: * But we had to alter page->flags anyway. */ static /* inline */ void -refill_inactive_zone(struct zone *zone, const int nr_pages_in) +refill_inactive_zone(struct zone *zone, const int nr_pages_in, + struct page_state *ps, int priority) { int pgdeactivate = 0; int nr_pages = nr_pages_in; @@ -448,6 +453,10 @@ refill_inactive_zone(struct zone *zone, const int nr_pages_in) LIST_HEAD(l_active); /* Pages to go onto the active_list */ struct page *page; struct pagevec pvec; + int reclaim_mapped = 0; + long mapped_ratio; + long distress; + long swap_tendency; lru_add_drain(); spin_lock_irq(&zone->lru_lock); @@ -469,6 +478,37 @@ refill_inactive_zone(struct zone *zone, const int nr_pages_in) } spin_unlock_irq(&zone->lru_lock); + /* + * `distress' is a measure of how much trouble we're having reclaiming + * pages. 0 -> no problems. 100 -> great trouble. + */ + distress = 100 >> priority; + + /* + * The point of this algorithm is to decide when to start reclaiming + * mapped memory instead of just pagecache. Work out how much memory + * is mapped. + */ + mapped_ratio = (ps->nr_mapped * 100) / total_memory; + + /* + * Now decide how much we really want to unmap some pages. The mapped + * ratio is downgraded - just because there's a lot of mapped memory + * doesn't necessarily mean that page reclaim isn't succeeding. + * + * The distress ratio is important - we don't want to start going oom. + * + * A 100% value of vm_swappiness overrides this algorithm altogether. + */ + swap_tendency = mapped_ratio / 2 + distress + vm_swappiness; + + /* + * Now use this metric to decide whether to start moving mapped memory + * onto the inactive list. + */ + if (swap_tendency >= 100) + reclaim_mapped = 1; + while (!list_empty(&l_hold)) { page = list_entry(l_hold.prev, struct page, lru); list_del(&page->lru); @@ -480,6 +520,10 @@ refill_inactive_zone(struct zone *zone, const int nr_pages_in) continue; } pte_chain_unlock(page); + if (!reclaim_mapped) { + list_add(&page->lru, &l_active); + continue; + } } /* * FIXME: need to consider page_count(page) here if/when we @@ -546,7 +590,7 @@ refill_inactive_zone(struct zone *zone, const int nr_pages_in) */ static /* inline */ int shrink_zone(struct zone *zone, int max_scan, unsigned int gfp_mask, - const int nr_pages, int *nr_mapped) + const int nr_pages, int *nr_mapped, struct page_state *ps, int priority) { unsigned long ratio; @@ -563,11 +607,23 @@ shrink_zone(struct zone *zone, int max_scan, unsigned int gfp_mask, ratio = (unsigned long)nr_pages * zone->nr_active / ((zone->nr_inactive | 1) * 2); atomic_add(ratio+1, &zone->refill_counter); - while (atomic_read(&zone->refill_counter) > SWAP_CLUSTER_MAX) { - atomic_sub(SWAP_CLUSTER_MAX, &zone->refill_counter); - refill_inactive_zone(zone, SWAP_CLUSTER_MAX); + if (atomic_read(&zone->refill_counter) > SWAP_CLUSTER_MAX) { + int count; + + /* + * Don't try to bring down too many pages in one attempt. + * If this fails, the caller will increase `priority' and + * we'll try again, with an increased chance of reclaiming + * mapped memory. + */ + count = atomic_read(&zone->refill_counter); + if (count > SWAP_CLUSTER_MAX * 4) + count = SWAP_CLUSTER_MAX * 4; + atomic_sub(count, &zone->refill_counter); + refill_inactive_zone(zone, count, ps, priority); } - return shrink_cache(nr_pages, zone, gfp_mask, max_scan, nr_mapped); + return shrink_cache(nr_pages, zone, gfp_mask, + max_scan, nr_mapped); } /* @@ -603,7 +659,8 @@ static void shrink_slab(int total_scanned, int gfp_mask) */ static int shrink_caches(struct zone *classzone, int priority, int *total_scanned, - int gfp_mask, const int nr_pages, int order) + int gfp_mask, const int nr_pages, int order, + struct page_state *ps) { struct zone *first_classzone; struct zone *zone; @@ -630,7 +687,7 @@ shrink_caches(struct zone *classzone, int priority, int *total_scanned, if (max_scan < to_reclaim * 2) max_scan = to_reclaim * 2; ret += shrink_zone(zone, max_scan, gfp_mask, - to_reclaim, &nr_mapped); + to_reclaim, &nr_mapped, ps, priority); *total_scanned += max_scan; *total_scanned += nr_mapped; if (ret >= nr_pages) @@ -666,12 +723,14 @@ try_to_free_pages(struct zone *classzone, inc_page_state(pageoutrun); - for (priority = DEF_PRIORITY; priority; priority--) { + for (priority = DEF_PRIORITY; priority >= 0; priority--) { int total_scanned = 0; + struct page_state ps; + get_page_state(&ps); nr_reclaimed += shrink_caches(classzone, priority, &total_scanned, gfp_mask, - nr_pages, order); + nr_pages, order, &ps); if (nr_reclaimed >= nr_pages) return 1; if (total_scanned == 0) @@ -704,7 +763,7 @@ try_to_free_pages(struct zone *classzone, * * Returns the number of pages which were actually freed. */ -static int balance_pgdat(pg_data_t *pgdat, int nr_pages) +static int balance_pgdat(pg_data_t *pgdat, int nr_pages, struct page_state *ps) { int to_free = nr_pages; int priority; @@ -729,7 +788,7 @@ static int balance_pgdat(pg_data_t *pgdat, int nr_pages) if (max_scan < to_reclaim * 2) max_scan = to_reclaim * 2; to_free -= shrink_zone(zone, max_scan, GFP_KSWAPD, - to_reclaim, &nr_mapped); + to_reclaim, &nr_mapped, ps, priority); shrink_slab(max_scan + nr_mapped, GFP_KSWAPD); } if (success) @@ -778,12 +837,15 @@ int kswapd(void *p) tsk->flags |= PF_MEMALLOC|PF_KSWAPD; for ( ; ; ) { + struct page_state ps; + if (current->flags & PF_FREEZE) refrigerator(PF_IOTHREAD); prepare_to_wait(&pgdat->kswapd_wait, &wait, TASK_INTERRUPTIBLE); schedule(); finish_wait(&pgdat->kswapd_wait, &wait); - balance_pgdat(pgdat, 0); + get_page_state(&ps); + balance_pgdat(pgdat, 0, &ps); blk_run_queues(); } } @@ -801,8 +863,10 @@ int shrink_all_memory(int nr_pages) for_each_pgdat(pgdat) { int freed; + struct page_state ps; - freed = balance_pgdat(pgdat, nr_to_free); + get_page_state(&ps); + freed = balance_pgdat(pgdat, nr_to_free, &ps); ret += freed; nr_to_free -= freed; if (nr_to_free <= 0) @@ -819,6 +883,7 @@ static int __init kswapd_init(void) swap_setup(); for_each_pgdat(pgdat) kernel_thread(kswapd, pgdat, CLONE_KERNEL); + total_memory = nr_free_pagecache_pages(); return 0; } -- cgit v1.2.3 From bf175bc495d14b8776c001252177c8c8a0342727 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 12 Oct 2002 19:33:16 -0700 Subject: [PATCH] rename /proc/sys/vm/dirty_async_ratio to dirty_ratio Since /proc/sys/vm/dirty_sync_ratio went away, the name "dirty_async_ratio" makes no sense. So rename it to just /proc/sys/vm/dirty_ratio. --- Documentation/filesystems/proc.txt | 2 +- Documentation/sysctl/vm.txt | 4 ++-- include/linux/sysctl.h | 2 +- include/linux/writeback.h | 2 +- kernel/sysctl.c | 4 ++-- mm/page-writeback.c | 18 +++++++++--------- 6 files changed, 16 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index 3ad9c3fd7f08..e7f223b0b9df 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -966,7 +966,7 @@ dirty_background_ratio Contains, as a percentage of total system memory, the number of pages at which the pdflush background writeback daemon will start writing out dirty data. -dirty_async_ratio +dirty_ratio ----------------- Contains, as a percentage of total system memory, the number of pages at which diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt index ed6ccff766f4..e6f8c613c879 100644 --- a/Documentation/sysctl/vm.txt +++ b/Documentation/sysctl/vm.txt @@ -18,14 +18,14 @@ files can be found in mm/swap.c. Currently, these files are in /proc/sys/vm: - overcommit_memory - page-cluster -- dirty_async_ratio +- dirty_ratio - dirty_background_ratio - dirty_expire_centisecs - dirty_writeback_centisecs ============================================================== -dirty_async_ratio, dirty_background_ratio, dirty_expire_centisecs, +dirty_ratio, dirty_background_ratio, dirty_expire_centisecs, dirty_writeback_centisecs: See Documentation/filesystems/proc.txt diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index f1a5aaa234ad..58055e1998cd 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -146,7 +146,7 @@ enum VM_UNUSED9=9, /* was: struct: Set page table cache parameters */ VM_PAGE_CLUSTER=10, /* int: set number of pages to swap together */ VM_DIRTY_BACKGROUND=11, /* dirty_background_ratio */ - VM_DIRTY_ASYNC=12, /* dirty_async_ratio */ + VM_DIRTY_RATIO=12, /* dirty_ratio */ VM_DIRTY_WB_CS=13, /* dirty_writeback_centisecs */ VM_DIRTY_EXPIRE_CS=14, /* dirty_expire_centisecs */ VM_NR_PDFLUSH_THREADS=15, /* nr_pdflush_threads */ diff --git a/include/linux/writeback.h b/include/linux/writeback.h index b71468f8f072..687091648a3a 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -67,7 +67,7 @@ int wakeup_bdflush(long nr_pages); /* These 5 are exported to sysctl. */ extern int dirty_background_ratio; -extern int dirty_async_ratio; +extern int vm_dirty_ratio; extern int dirty_writeback_centisecs; extern int dirty_expire_centisecs; diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 3b49efc1c523..0317dd749b2b 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -286,8 +286,8 @@ static ctl_table vm_table[] = { &dirty_background_ratio, sizeof(dirty_background_ratio), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec, NULL, &zero, &one_hundred }, - {VM_DIRTY_ASYNC, "dirty_async_ratio", &dirty_async_ratio, - sizeof(dirty_async_ratio), 0644, NULL, &proc_dointvec_minmax, + {VM_DIRTY_RATIO, "dirty_ratio", &vm_dirty_ratio, + sizeof(vm_dirty_ratio), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec, NULL, &zero, &one_hundred }, {VM_DIRTY_WB_CS, "dirty_writeback_centisecs", &dirty_writeback_centisecs, sizeof(dirty_writeback_centisecs), 0644, diff --git a/mm/page-writeback.c b/mm/page-writeback.c index e2dca9be22fe..f135ed5ab789 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -63,9 +63,9 @@ static inline long sync_writeback_pages(void) int dirty_background_ratio = 10; /* - * The generator of dirty data starts async writeback at this percentage + * The generator of dirty data starts writeback at this percentage */ -int dirty_async_ratio = 40; +int vm_dirty_ratio = 40; /* * The interval between `kupdate'-style writebacks, in centiseconds @@ -86,7 +86,7 @@ static void background_writeout(unsigned long _min_pages); /* * balance_dirty_pages() must be called by processes which are generating dirty * data. It looks at the number of dirty pages in the machine and will force - * the caller to perform writeback if the system is over `async_thresh'. + * the caller to perform writeback if the system is over `vm_dirty_ratio'. * If we're over `background_thresh' then pdflush is woken to perform some * writeout. */ @@ -94,15 +94,15 @@ void balance_dirty_pages(struct address_space *mapping) { struct page_state ps; long background_thresh; - long async_thresh; + long dirty_thresh; struct backing_dev_info *bdi = mapping->backing_dev_info; background_thresh = (dirty_background_ratio * total_pages) / 100; - async_thresh = (dirty_async_ratio * total_pages) / 100; + dirty_thresh = (vm_dirty_ratio * total_pages) / 100; get_page_state(&ps); - while (ps.nr_dirty + ps.nr_writeback > async_thresh) { + while (ps.nr_dirty + ps.nr_writeback > dirty_thresh) { struct writeback_control wbc = { .bdi = bdi, .sync_mode = WB_SYNC_NONE, @@ -116,7 +116,7 @@ void balance_dirty_pages(struct address_space *mapping) writeback_inodes(&wbc); get_page_state(&ps); - if (ps.nr_dirty + ps.nr_writeback <= async_thresh) + if (ps.nr_dirty + ps.nr_writeback <= dirty_thresh) break; blk_congestion_wait(WRITE, HZ/10); } @@ -338,8 +338,8 @@ static int __init page_writeback_init(void) if (correction < 100) { dirty_background_ratio *= correction; dirty_background_ratio /= 100; - dirty_async_ratio *= correction; - dirty_async_ratio /= 100; + vm_dirty_ratio *= correction; + vm_dirty_ratio /= 100; } init_timer(&wb_timer); -- cgit v1.2.3 From f83d142d9a52f9cd6040910879b60d7bd2c4f4fc Mon Sep 17 00:00:00 2001 From: Joe Burks Date: Sat, 12 Oct 2002 23:24:09 -0700 Subject: [PATCH] USB: Vicam driver update/rewrite Updates the vicam driver to the latest version from the sourceforge.net project. Binary files linux-2.5.41/drivers/usb/media/.usbvideo.c.swp and linux-2.5.41-vicam/drivers/usb/media/.usbvideo.c.swp differ diff -urN linux-2.5.41/drivers/usb/media/Makefile linux-2.5.41-vicam/drivers/usb/media/Makefile --- drivers/usb/media/Makefile | 2 +- drivers/usb/media/vicam.c | 1894 +++++++++++++++++++++++++++----------------- drivers/usb/media/vicam.h | 81 -- include/linux/videodev.h | 1 + 4 files changed, 1188 insertions(+), 790 deletions(-) delete mode 100644 drivers/usb/media/vicam.h (limited to 'include') diff --git a/drivers/usb/media/Makefile b/drivers/usb/media/Makefile index b3aa1f558bbe..d30da815bf8f 100644 --- a/drivers/usb/media/Makefile +++ b/drivers/usb/media/Makefile @@ -14,6 +14,6 @@ obj-$(CONFIG_USB_OV511) += ov511.o obj-$(CONFIG_USB_PWC) += pwc.o obj-$(CONFIG_USB_SE401) += se401.o obj-$(CONFIG_USB_STV680) += stv680.o -obj-$(CONFIG_USB_VICAM) += vicam.o +obj-$(CONFIG_USB_VICAM) += vicam.o usbvideo.o include $(TOPDIR)/Rules.make diff --git a/drivers/usb/media/vicam.c b/drivers/usb/media/vicam.c index 5b673019b7e1..709cdf93afa3 100644 --- a/drivers/usb/media/vicam.c +++ b/drivers/usb/media/vicam.c @@ -1,102 +1,356 @@ -/* -*- linux-c -*- - * USB ViCAM driver - * - * Copyright (c) 2001 Christopher L Cheney (ccheney@cheney.cx) - * Copyright (c) 2001 Pavel Machek (pavel@suse.cz) sponsored by SuSE +/* + * USB ViCam WebCam driver + * Copyright (c) 2002 Joe Burks (jburks@wavicle.org) * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. + * Supports 3COM HomeConnect PC Digital WebCam * - * This driver is for the Vista Imaging ViCAM and 3Com HomeConnect USB + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * Thanks to Greg Kroah-Hartman for the USB Skeleton driver + * 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. * - * TODO: - * - find out the ids for the Vista Imaging ViCAM + * 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. * - * History: + * This source code is based heavily on the CPiA webcam driver which was + * written by Peter Pregler, Scott J. Bertin and Johannes Erdfelt * - * 2001_07_07 - 0.1 - christopher: first version - * 2001_08_28 - 0.2 - pavel: messed it up, but for some fun, try - while true; do dd if=/dev/video of=/dev/fb0 bs=$[0x1e480] count=1 2> /dev/null; done - yep, moving pictures. - * 2001_08_29 - 0.3 - pavel: played a little bit more. Experimental mmap support. For some fun, - get gqcam-0.9, compile it and run. Better than dd ;-). - * 2001_08_29 - 0.4 - pavel: added shutter speed control (not much functional) - kill update_params if it does not seem to work for you. - * 2001_08_30 - 0.5 - pavel: fixed stupid bug with update_params & vicam_bulk - + * Portions of this code were also copied from usbvideo.c * - * FIXME: It crashes on rmmod with camera plugged. - */ -#define DEBUG 1 + * Special thanks to the the whole team at Sourceforge for help making + * this driver become a reality. Notably: + * Andy Armstrong who reverse engineered the color encoding and + * Pavel Machek and Chris Cheney who worked on reverse engineering the + * camera controls and wrote the first generation driver. + * */ -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include -#include -#include -#include -#include +#include +#include #include - -#include -#include #include +#include +#include +#include "usbvideo.h" -#include +// #define VICAM_DEBUG -#include "vicam.h" -#include "vicamurbs.h" +#ifndef MODULE_LICENSE +#define MODULE_LICENSE(a) +#endif + +#ifndef bool +#define bool int +#endif + +#ifdef VICAM_DEBUG +#define ADBG(lineno,fmt,args...) printk(fmt, jiffies, __FUNCTION__, lineno, ##args) +#define DBG(fmt,args...) ADBG((__LINE__),KERN_DEBUG __FILE__"(%ld):%s (%d):"fmt,##args) +#else +#define DBG(fmn,args...) do {} while(0) +#endif /* Version Information */ -#define DRIVER_VERSION "v0" -#define DRIVER_AUTHOR "Christopher L Cheney , Pavel Machek " -#define DRIVER_DESC "USB ViCAM Driver" +#define DRIVER_VERSION "v1.0" +#define DRIVER_AUTHOR "Joe Burks, jburks@wavicle.org" +#define DRIVER_DESC "ViCam WebCam Driver" /* Define these values to match your device */ -#define USB_VICAM_VENDOR_ID 0x04C1 -#define USB_VICAM_PRODUCT_ID 0x009D +#define USB_VICAM_VENDOR_ID 0x04c1 +#define USB_VICAM_PRODUCT_ID 0x009d -/* table of devices that work with this driver */ -static struct usb_device_id vicam_table [] = { - { USB_DEVICE(USB_VICAM_VENDOR_ID, USB_VICAM_PRODUCT_ID) }, - { } /* Terminating entry */ -}; +#define VICAM_BYTES_PER_PIXEL 3 +#define VICAM_MAX_READ_SIZE (512*242+128) +#define VICAM_MAX_FRAME_SIZE (VICAM_BYTES_PER_PIXEL*320*240) +#define VICAM_FRAMES 2 -MODULE_DEVICE_TABLE (usb, vicam_table); +/* Not sure what all the bytes in these char + * arrays do, but they're necessary to make + * the camera work. + */ -static int video_nr = -1; /* next avail video device */ -static struct usb_driver vicam_driver; +static unsigned char setup1[] = { + 0xB6, 0xC3, 0x1F, 0x00, 0x02, 0x64, 0xE7, 0x67, + 0xFD, 0xFF, 0x0E, 0xC0, 0xE7, 0x09, 0xDE, 0x00, + 0x8E, 0x00, 0xC0, 0x09, 0x40, 0x03, 0xC0, 0x17, + 0x44, 0x03, 0x4B, 0xAF, 0xC0, 0x07, 0x00, 0x00, + 0x4B, 0xAF, 0x97, 0xCF, 0x00, 0x00 +}; -static char *buf, *buf2; -static volatile int change_pending = 0; +static unsigned char setup2[] = { + 0xB6, 0xC3, 0x03, 0x00, 0x03, 0x64, 0x18, 0x00, + 0x00, 0x00 +}; -static int vicam_parameters(struct usb_vicam *vicam); +static unsigned char setup3[] = { + 0xB6, 0xC3, 0x01, 0x00, 0x06, 0x64, 0x00, 0x00 +}; -/****************************************************************************** - * - * Memory management functions - * - * Taken from bttv-drivers.c 2.4.7-pre3 - * - ******************************************************************************/ +static unsigned char setup4[] = { + 0xB6, 0xC3, 0x8F, 0x06, 0x02, 0x64, 0xE7, 0x07, + 0x00, 0x00, 0x08, 0xC0, 0xE7, 0x07, 0x00, 0x00, + 0x3E, 0xC0, 0xE7, 0x07, 0x54, 0x01, 0xAA, 0x00, + 0xE7, 0x07, 0xC8, 0x05, 0xB6, 0x00, 0xE7, 0x07, + 0x42, 0x01, 0xD2, 0x00, 0xE7, 0x07, 0x7C, 0x00, + 0x16, 0x00, 0xE7, 0x07, 0x56, 0x00, 0x18, 0x00, + 0xE7, 0x07, 0x06, 0x00, 0x92, 0xC0, 0xE7, 0x07, + 0x00, 0x00, 0x1E, 0xC0, 0xE7, 0x07, 0xFF, 0xFF, + 0x22, 0xC0, 0xE7, 0x07, 0x04, 0x00, 0x24, 0xC0, + 0xE7, 0x07, 0xEC, 0x27, 0x28, 0xC0, 0xE7, 0x07, + 0x16, 0x01, 0x8E, 0x00, 0xE7, 0x87, 0x01, 0x00, + 0x0E, 0xC0, 0x97, 0xCF, 0xD7, 0x09, 0x00, 0xC0, + 0xE7, 0x77, 0x01, 0x00, 0x92, 0xC0, 0x09, 0xC1, + 0xE7, 0x09, 0xFE, 0x05, 0x24, 0x01, 0xE7, 0x09, + 0x04, 0x06, 0x26, 0x01, 0xE7, 0x07, 0x07, 0x00, + 0x92, 0xC0, 0xE7, 0x05, 0x00, 0xC0, 0xC0, 0xDF, + 0x97, 0xCF, 0x17, 0x00, 0x57, 0x00, 0x17, 0x02, + 0xD7, 0x09, 0x00, 0xC0, 0xE7, 0x77, 0x01, 0x00, + 0x92, 0xC0, 0x0A, 0xC1, 0xE7, 0x57, 0xFF, 0xFF, + 0xFA, 0x05, 0x0D, 0xC0, 0xE7, 0x57, 0x00, 0x00, + 0xFA, 0x05, 0x0F, 0xC0, 0x9F, 0xAF, 0xC6, 0x00, + 0xE7, 0x05, 0x00, 0xC0, 0xC8, 0x05, 0xC1, 0x05, + 0xC0, 0x05, 0xC0, 0xDF, 0x97, 0xCF, 0x27, 0xDA, + 0xFA, 0x05, 0xEF, 0x07, 0x01, 0x00, 0x0B, 0x06, + 0x73, 0xCF, 0x9F, 0xAF, 0x78, 0x01, 0x9F, 0xAF, + 0x1A, 0x03, 0x6E, 0xCF, 0xE7, 0x09, 0xFC, 0x05, + 0x24, 0x01, 0xE7, 0x09, 0x02, 0x06, 0x26, 0x01, + 0xE7, 0x07, 0x07, 0x00, 0x92, 0xC0, 0xE7, 0x09, + 0xFC, 0x05, 0xFE, 0x05, 0xE7, 0x09, 0x02, 0x06, + 0x04, 0x06, 0xE7, 0x09, 0x00, 0x06, 0xFC, 0x05, + 0xE7, 0x09, 0xFE, 0x05, 0x00, 0x06, 0x27, 0xDA, + 0xFA, 0x05, 0xE7, 0x57, 0x01, 0x00, 0xFA, 0x05, + 0x02, 0xCA, 0x04, 0xC0, 0x97, 0xCF, 0x9F, 0xAF, + 0x66, 0x05, 0x97, 0xCF, 0xE7, 0x07, 0x40, 0x00, + 0x02, 0x06, 0xC8, 0x09, 0xFC, 0x05, 0x9F, 0xAF, + 0xDA, 0x02, 0x97, 0xCF, 0xCF, 0x17, 0x02, 0x00, + 0xEF, 0x57, 0x81, 0x00, 0x09, 0x06, 0x9F, 0xA0, + 0xB6, 0x01, 0xEF, 0x57, 0x80, 0x00, 0x09, 0x06, + 0x9F, 0xA0, 0x40, 0x02, 0xEF, 0x57, 0x01, 0x00, + 0x0B, 0x06, 0x9F, 0xA0, 0x46, 0x03, 0xE7, 0x07, + 0x01, 0x00, 0x0A, 0xC0, 0x46, 0xAF, 0x47, 0xAF, + 0x9F, 0xAF, 0x40, 0x02, 0xE7, 0x07, 0x2E, 0x00, + 0x0A, 0xC0, 0xEF, 0x87, 0x80, 0x00, 0x09, 0x06, + 0x97, 0xCF, 0x00, 0x0E, 0x01, 0x00, 0xC0, 0x57, + 0x51, 0x00, 0x9F, 0xC0, 0x9E, 0x02, 0xC0, 0x57, + 0x50, 0x00, 0x20, 0xC0, 0xC0, 0x57, 0x55, 0x00, + 0x12, 0xC0, 0xC0, 0x57, 0x56, 0x00, 0x9F, 0xC0, + 0x72, 0x02, 0x9F, 0xCF, 0xD6, 0x02, 0xC1, 0x0B, + 0x08, 0x06, 0x01, 0xD0, 0x6F, 0x90, 0x08, 0x06, + 0xC0, 0x07, 0x08, 0x00, 0xC1, 0x0B, 0x08, 0x06, + 0x9F, 0xAF, 0x28, 0x05, 0x97, 0xCF, 0x2F, 0x0E, + 0x02, 0x00, 0x08, 0x06, 0xC0, 0x07, 0x08, 0x00, + 0xC1, 0x0B, 0x08, 0x06, 0x9F, 0xAF, 0x28, 0x05, + 0x9F, 0xCF, 0xD6, 0x02, 0x2F, 0x0E, 0x02, 0x00, + 0x09, 0x06, 0xEF, 0x87, 0x80, 0x00, 0x09, 0x06, + 0x9F, 0xCF, 0xD6, 0x02, 0xEF, 0x67, 0x7F, 0xFF, + 0x09, 0x06, 0xE7, 0x67, 0xFF, 0xFD, 0x22, 0xC0, + 0xE7, 0x67, 0xEF, 0xFF, 0x24, 0xC0, 0xE7, 0x87, + 0x10, 0x00, 0x28, 0xC0, 0x9F, 0xAF, 0xB8, 0x05, + 0xE7, 0x87, 0xE0, 0x21, 0x24, 0xC0, 0x9F, 0xAF, + 0xA8, 0x05, 0xE7, 0x87, 0x08, 0x00, 0x24, 0xC0, + 0xE7, 0x67, 0xDF, 0xFF, 0x24, 0xC0, 0xC8, 0x07, + 0x0A, 0x00, 0xC0, 0x07, 0x00, 0x00, 0xC1, 0x07, + 0x01, 0x00, 0x9F, 0xAF, 0x28, 0x05, 0x9F, 0xAF, + 0xB8, 0x05, 0xC0, 0x07, 0x9E, 0x00, 0x9F, 0xAF, + 0x44, 0x05, 0xE7, 0x67, 0xFF, 0xFE, 0x24, 0xC0, + 0xC0, 0x09, 0x20, 0xC0, 0xE7, 0x87, 0x00, 0x01, + 0x24, 0xC0, 0xC0, 0x77, 0x00, 0x02, 0x0F, 0xC1, + 0xE7, 0x67, 0xF7, 0xFF, 0x24, 0xC0, 0xE7, 0x67, + 0xF7, 0xFF, 0x24, 0xC0, 0xE7, 0x87, 0x08, 0x00, + 0x24, 0xC0, 0x08, 0xDA, 0x5E, 0xC1, 0xEF, 0x07, + 0x80, 0x00, 0x09, 0x06, 0x97, 0xCF, 0xEF, 0x07, + 0x01, 0x00, 0x0A, 0x06, 0x97, 0xCF, 0xEF, 0x07, + 0x00, 0x00, 0x0B, 0x06, 0xEF, 0x07, 0x00, 0x00, + 0x0A, 0x06, 0xEF, 0x67, 0x7F, 0xFF, 0x09, 0x06, + 0xEF, 0x07, 0x00, 0x00, 0x0D, 0x06, 0xE7, 0x67, + 0xEF, 0xFF, 0x28, 0xC0, 0xE7, 0x67, 0x17, 0xD8, + 0x24, 0xC0, 0xE7, 0x07, 0x00, 0x00, 0x1E, 0xC0, + 0xE7, 0x07, 0xFF, 0xFF, 0x22, 0xC0, 0x97, 0xCF, + 0xC8, 0x07, 0x0E, 0x06, 0x9F, 0xAF, 0xDA, 0x02, + 0xE7, 0x07, 0x00, 0x00, 0xF2, 0x05, 0xE7, 0x07, + 0x10, 0x00, 0xF6, 0x05, 0xE7, 0x07, 0x0E, 0x06, + 0xF4, 0x05, 0xE7, 0x07, 0xD6, 0x02, 0xF8, 0x05, + 0xC8, 0x07, 0xF2, 0x05, 0xC1, 0x07, 0x00, 0x80, + 0x50, 0xAF, 0x97, 0xCF, 0x2F, 0x0C, 0x02, 0x00, + 0x07, 0x06, 0x2F, 0x0C, 0x04, 0x00, 0x06, 0x06, + 0xE7, 0x07, 0x00, 0x00, 0xF2, 0x05, 0xE7, 0x07, + 0x10, 0x00, 0xF6, 0x05, 0xE7, 0x07, 0xE2, 0x05, + 0xF4, 0x05, 0xE7, 0x07, 0xCE, 0x02, 0xF8, 0x05, + 0xC8, 0x07, 0xF2, 0x05, 0xC1, 0x07, 0x00, 0x80, + 0x51, 0xAF, 0x97, 0xCF, 0x9F, 0xAF, 0x66, 0x04, + 0x9F, 0xAF, 0x1A, 0x03, 0x59, 0xAF, 0x97, 0xCF, + 0xC0, 0x07, 0x0E, 0x00, 0xC1, 0x0B, 0x0C, 0x06, + 0x41, 0xD1, 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, + 0x3C, 0x00, 0x9F, 0xAF, 0x44, 0x05, 0x68, 0x00, + 0xC0, 0x07, 0x3B, 0x00, 0x9F, 0xAF, 0x44, 0x05, + 0x6F, 0x00, 0x0C, 0x06, 0x68, 0x00, 0xE0, 0x07, + 0x04, 0x01, 0xE8, 0x0B, 0x0A, 0x06, 0xE8, 0x07, + 0x00, 0x00, 0xE0, 0x07, 0x00, 0x02, 0xE0, 0x07, + 0xEC, 0x01, 0xE0, 0x07, 0xFC, 0xFF, 0x97, 0xCF, + 0xE7, 0x07, 0xFF, 0xFF, 0xFA, 0x05, 0xEF, 0x07, + 0x00, 0x00, 0x0B, 0x06, 0xE7, 0x07, 0x0E, 0x06, + 0x24, 0x01, 0xE7, 0x07, 0x0E, 0x06, 0xFE, 0x05, + 0xE7, 0x07, 0x40, 0x00, 0x26, 0x01, 0xE7, 0x07, + 0x40, 0x00, 0x04, 0x06, 0xE7, 0x07, 0x07, 0x00, + 0x92, 0xC0, 0x97, 0xCF, 0xEF, 0x07, 0x02, 0x00, + 0x0B, 0x06, 0x9F, 0xAF, 0x78, 0x01, 0xEF, 0x77, + 0x80, 0x00, 0x07, 0x06, 0x9F, 0xC0, 0x14, 0x04, + 0xEF, 0x77, 0x01, 0x00, 0x07, 0x06, 0x37, 0xC0, + 0xEF, 0x77, 0x01, 0x00, 0x0D, 0x06, 0x0F, 0xC1, + 0xEF, 0x07, 0x01, 0x00, 0x0D, 0x06, 0xC0, 0x07, + 0x02, 0x00, 0xC1, 0x07, 0x30, 0x00, 0x9F, 0xAF, + 0x28, 0x05, 0xC0, 0x07, 0x01, 0x00, 0xC1, 0x07, + 0x02, 0x00, 0x9F, 0xAF, 0x28, 0x05, 0xC8, 0x07, + 0xFF, 0x4F, 0x9F, 0xAF, 0xA8, 0x05, 0xC0, 0x07, + 0x38, 0x00, 0x9F, 0xAF, 0x44, 0x05, 0xC1, 0x77, + 0x03, 0x00, 0x02, 0xC1, 0x08, 0xDA, 0x75, 0xC1, + 0xC1, 0x77, 0x01, 0x00, 0x0A, 0xC1, 0xC0, 0x07, + 0x01, 0x00, 0xC1, 0x07, 0x02, 0x00, 0x9F, 0xAF, + 0x28, 0x05, 0xEF, 0x07, 0x01, 0x00, 0x06, 0x06, + 0x2C, 0xCF, 0xC0, 0x07, 0x01, 0x00, 0xC1, 0x07, + 0x04, 0x00, 0x9F, 0xAF, 0x28, 0x05, 0xEF, 0x07, + 0x00, 0x00, 0x06, 0x06, 0x22, 0xCF, 0xEF, 0x07, + 0x00, 0x00, 0x0D, 0x06, 0xEF, 0x57, 0x01, 0x00, + 0x06, 0x06, 0x1B, 0xC0, 0xC0, 0x07, 0x01, 0x00, + 0xC1, 0x07, 0x01, 0x00, 0x9F, 0xAF, 0x28, 0x05, + 0xC0, 0x07, 0x02, 0x00, 0xC1, 0x07, 0x30, 0x00, + 0x9F, 0xAF, 0x28, 0x05, 0xC8, 0x07, 0xFF, 0x4F, + 0x9F, 0xAF, 0xA8, 0x05, 0xC0, 0x07, 0x38, 0x00, + 0x9F, 0xAF, 0x44, 0x05, 0xC1, 0x67, 0x03, 0x00, + 0xC1, 0x57, 0x03, 0x00, 0x02, 0xC0, 0x08, 0xDA, + 0x73, 0xC1, 0xC0, 0x07, 0x02, 0x00, 0xC1, 0x07, + 0x12, 0x00, 0xEF, 0x57, 0x00, 0x00, 0x06, 0x06, + 0x02, 0xC0, 0xC1, 0x07, 0x23, 0x00, 0x9F, 0xAF, + 0x28, 0x05, 0xC0, 0x07, 0x14, 0x00, 0xC1, 0x0B, + 0xEA, 0x05, 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, + 0x3E, 0x00, 0x9F, 0xAF, 0x0A, 0x05, 0xE7, 0x09, + 0xE4, 0x05, 0xFA, 0x05, 0x27, 0xD8, 0xFA, 0x05, + 0xE7, 0x07, 0x0E, 0x06, 0xFC, 0x05, 0xE7, 0x07, + 0x4E, 0x06, 0x00, 0x06, 0xE7, 0x07, 0x40, 0x00, + 0x02, 0x06, 0x9F, 0xAF, 0x66, 0x05, 0x9F, 0xAF, + 0xC6, 0x00, 0x97, 0xCF, 0xC1, 0x0B, 0xE2, 0x05, + 0x41, 0xD0, 0x01, 0xD2, 0xC1, 0x17, 0x23, 0x00, + 0x9F, 0xAF, 0xDC, 0x04, 0xC0, 0x07, 0x04, 0x00, + 0xC1, 0x0B, 0xE3, 0x05, 0x9F, 0xAF, 0x28, 0x05, + 0xC0, 0x07, 0x06, 0x00, 0xC1, 0x09, 0xE6, 0x05, + 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, 0x07, 0x00, + 0xC1, 0x09, 0xE6, 0x05, 0xC1, 0xD1, 0x9F, 0xAF, + 0x28, 0x05, 0xC0, 0x07, 0x0B, 0x00, 0xC1, 0x09, + 0xE8, 0x05, 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, + 0x0C, 0x00, 0xC1, 0x09, 0xE8, 0x05, 0xC1, 0xD1, + 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, 0x0D, 0x00, + 0xC1, 0x07, 0x09, 0x00, 0x9F, 0xAF, 0x28, 0x05, + 0xC0, 0x07, 0x03, 0x00, 0xC1, 0x07, 0x32, 0x00, + 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, 0x0F, 0x00, + 0xC1, 0x07, 0x00, 0x00, 0x9F, 0xAF, 0x28, 0x05, + 0x97, 0xCF, 0xE7, 0x67, 0xFF, 0xD9, 0x24, 0xC0, + 0xC8, 0x07, 0x0A, 0x00, 0x40, 0x00, 0xC0, 0x67, + 0x00, 0x02, 0x27, 0x80, 0x24, 0xC0, 0xE7, 0x87, + 0x00, 0x04, 0x24, 0xC0, 0xE7, 0x67, 0xFF, 0xF9, + 0x24, 0xC0, 0x01, 0xD2, 0x08, 0xDA, 0x72, 0xC1, + 0xE7, 0x87, 0x00, 0x20, 0x24, 0xC0, 0x97, 0xCF, + 0x27, 0x00, 0x1E, 0xC0, 0xE7, 0x87, 0xFF, 0x00, + 0x22, 0xC0, 0xE7, 0x67, 0x7F, 0xFF, 0x24, 0xC0, + 0xE7, 0x87, 0x80, 0x00, 0x24, 0xC0, 0xE7, 0x87, + 0x80, 0x00, 0x24, 0xC0, 0x97, 0xCF, 0x9F, 0xAF, + 0x0A, 0x05, 0x67, 0x00, 0x1E, 0xC0, 0xE7, 0x67, + 0xBF, 0xFF, 0x24, 0xC0, 0xE7, 0x87, 0x40, 0x00, + 0x24, 0xC0, 0xE7, 0x87, 0x40, 0x00, 0x24, 0xC0, + 0x97, 0xCF, 0x9F, 0xAF, 0x0A, 0x05, 0xE7, 0x67, + 0x00, 0xFF, 0x22, 0xC0, 0xE7, 0x67, 0xFF, 0xFE, + 0x24, 0xC0, 0xE7, 0x67, 0xFF, 0xFE, 0x24, 0xC0, + 0xC1, 0x09, 0x20, 0xC0, 0xE7, 0x87, 0x00, 0x01, + 0x24, 0xC0, 0x97, 0xCF, 0xC0, 0x07, 0x40, 0x00, + 0xC8, 0x09, 0xFC, 0x05, 0xE7, 0x67, 0x00, 0xFF, + 0x22, 0xC0, 0xE7, 0x67, 0xFF, 0xFE, 0x24, 0xC0, + 0xE7, 0x67, 0xBF, 0xFF, 0x24, 0xC0, 0xE7, 0x67, + 0xBF, 0xFF, 0x24, 0xC0, 0x00, 0xDA, 0xE8, 0x09, + 0x20, 0xC0, 0xE7, 0x87, 0x40, 0x00, 0x24, 0xC0, + 0xE7, 0x87, 0x40, 0x00, 0x24, 0xC0, 0x00, 0xDA, + 0xE8, 0x09, 0x20, 0xC0, 0x6D, 0xC1, 0xE7, 0x87, + 0x00, 0x01, 0x24, 0xC0, 0x97, 0xCF, 0xE7, 0x07, + 0x32, 0x00, 0x12, 0xC0, 0xE7, 0x77, 0x00, 0x80, + 0x12, 0xC0, 0x7C, 0xC0, 0x97, 0xCF, 0xE7, 0x07, + 0x20, 0x4E, 0x12, 0xC0, 0xE7, 0x77, 0x00, 0x80, + 0x12, 0xC0, 0x7C, 0xC0, 0x97, 0xCF, 0x09, 0x02, + 0x19, 0x00, 0x01, 0x01, 0x00, 0x80, 0x96, 0x09, + 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x05, 0x81, 0x02, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; -/* Here we want the physical address of the memory. - * This is used when initializing the contents of the area. - */ -static inline unsigned long kvirt_to_pa(unsigned long adr) +static unsigned char setup5[] = { + 0xB6, 0xC3, 0x2F, 0x01, 0x03, 0x64, 0x0E, 0x00, + 0x14, 0x00, 0x1A, 0x00, 0x20, 0x00, 0x26, 0x00, + 0x4A, 0x00, 0x64, 0x00, 0x6A, 0x00, 0x92, 0x00, + 0x9A, 0x00, 0xA0, 0x00, 0xB2, 0x00, 0xB8, 0x00, + 0xBE, 0x00, 0xC2, 0x00, 0xC8, 0x00, 0xCE, 0x00, + 0xDC, 0x00, 0xDA, 0x00, 0xE2, 0x00, 0xE0, 0x00, + 0xE8, 0x00, 0xE6, 0x00, 0xEE, 0x00, 0xEC, 0x00, + 0xF2, 0x00, 0xF8, 0x00, 0x02, 0x01, 0x0A, 0x01, + 0x0E, 0x01, 0x12, 0x01, 0x1E, 0x01, 0x22, 0x01, + 0x28, 0x01, 0x2C, 0x01, 0x32, 0x01, 0x36, 0x01, + 0x44, 0x01, 0x50, 0x01, 0x5E, 0x01, 0x72, 0x01, + 0x76, 0x01, 0x7A, 0x01, 0x80, 0x01, 0x88, 0x01, + 0x8C, 0x01, 0x94, 0x01, 0x9C, 0x01, 0xA0, 0x01, + 0xA4, 0x01, 0xAA, 0x01, 0xB0, 0x01, 0xB4, 0x01, + 0xBA, 0x01, 0xD0, 0x01, 0xDA, 0x01, 0xF6, 0x01, + 0xFA, 0x01, 0x02, 0x02, 0x34, 0x02, 0x3C, 0x02, + 0x44, 0x02, 0x4A, 0x02, 0x50, 0x02, 0x56, 0x02, + 0x74, 0x02, 0x78, 0x02, 0x7E, 0x02, 0x84, 0x02, + 0x8A, 0x02, 0x88, 0x02, 0x90, 0x02, 0x8E, 0x02, + 0x94, 0x02, 0xA2, 0x02, 0xA8, 0x02, 0xAE, 0x02, + 0xB4, 0x02, 0xBA, 0x02, 0xB8, 0x02, 0xC0, 0x02, + 0xBE, 0x02, 0xC4, 0x02, 0xD0, 0x02, 0xD4, 0x02, + 0xE0, 0x02, 0xE6, 0x02, 0xEE, 0x02, 0xF8, 0x02, + 0xFC, 0x02, 0x06, 0x03, 0x1E, 0x03, 0x24, 0x03, + 0x28, 0x03, 0x30, 0x03, 0x2E, 0x03, 0x3C, 0x03, + 0x4A, 0x03, 0x4E, 0x03, 0x54, 0x03, 0x58, 0x03, + 0x5E, 0x03, 0x66, 0x03, 0x6E, 0x03, 0x7A, 0x03, + 0x86, 0x03, 0x8E, 0x03, 0x96, 0x03, 0xB2, 0x03, + 0xB8, 0x03, 0xC6, 0x03, 0xCC, 0x03, 0xD4, 0x03, + 0xDA, 0x03, 0xE8, 0x03, 0xF4, 0x03, 0xFC, 0x03, + 0x04, 0x04, 0x20, 0x04, 0x2A, 0x04, 0x32, 0x04, + 0x36, 0x04, 0x3E, 0x04, 0x44, 0x04, 0x42, 0x04, + 0x48, 0x04, 0x4E, 0x04, 0x4C, 0x04, 0x54, 0x04, + 0x52, 0x04, 0x5A, 0x04, 0x5E, 0x04, 0x62, 0x04, + 0x68, 0x04, 0x74, 0x04, 0x7C, 0x04, 0x80, 0x04, + 0x88, 0x04, 0x8C, 0x04, 0x94, 0x04, 0x9A, 0x04, + 0xA2, 0x04, 0xA6, 0x04, 0xAE, 0x04, 0xB4, 0x04, + 0xC0, 0x04, 0xCC, 0x04, 0xD8, 0x04, 0x2A, 0x05, + 0x46, 0x05, 0x6C, 0x05, 0x00, 0x00 +}; + +static unsigned long kvirt_to_pa(unsigned long adr) { unsigned long kva, ret; @@ -106,526 +360,734 @@ static inline unsigned long kvirt_to_pa(unsigned long adr) return ret; } -static void * rvmalloc(unsigned long size) +/* rvmalloc / rvfree copied from usbvideo.c + * + * Not sure why these are not yet non-statics which I can reference through + * usbvideo.h the same as it is in 2.4.20. I bet this will get fixed sometime + * in the future. + * +*/ +static void *rvmalloc(unsigned long size) { - void * mem; + void *mem; unsigned long adr; - size=PAGE_ALIGN(size); - mem=vmalloc_32(size); - if (mem) - { - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr=(unsigned long) mem; - while (size > 0) - { - mem_map_reserve(vmalloc_to_page((void *)adr)); - adr+=PAGE_SIZE; - size-=PAGE_SIZE; - } + size = PAGE_ALIGN(size); + mem = vmalloc_32(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + mem_map_reserve(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; } + return mem; } -static void rvfree(void * mem, unsigned long size) +static void rvfree(void *mem, unsigned long size) { unsigned long adr; - if (mem) - { - adr=(unsigned long) mem; - while ((long) size > 0) - { - mem_map_unreserve(vmalloc_to_page((void *)adr)); - adr+=PAGE_SIZE; - size-=PAGE_SIZE; - } - vfree(mem); + if (!mem) + return; + + adr = (unsigned long) mem; + while ((long) size > 0) { + mem_map_unreserve(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; } + vfree(mem); } + +struct vicam_camera { + u16 shutter_speed; // capture shutter speed + u16 gain; // capture gain -/****************************************************************************** - * - * Foo Bar - * - ******************************************************************************/ + u8 *raw_image; // raw data captured from the camera + u8 *framebuf; // processed data in RGB24 format -/** - * usb_vicam_debug_data - */ -static inline void usb_vicam_debug_data (const char *function, int size, const unsigned char *data) -{ - int i; + struct video_device vdev; // v4l video device + struct usb_device *udev; // usb device - if (!debug) - return; + struct semaphore busy_lock; // guard against SMP multithreading - printk (KERN_DEBUG __FILE__": %s - length = %d, data = ", - function, size); - for (i = 0; i < size; ++i) { - printk ("%.2x ", data[i]); - } - printk ("\n"); -} + bool is_initialized; + u8 open_count; + u8 bulkEndpoint; + bool needsDummyRead; -/***************************************************************************** - * - * Send command to vicam - * - *****************************************************************************/ + u32 framebuf_size; // # of valid bytes in framebuf + u32 framebuf_read_start; // position in frame buf that a read is happening at. + +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *proc_entry; +#endif -static int vicam_sndctrl(int set, struct usb_vicam *vicam, unsigned short req, - unsigned short value, unsigned char *cp, int size) +}; + +static int vicam_probe( struct usb_interface *intf, const struct usb_device_id *id); +static void vicam_disconnect(struct usb_interface *intf); +static void read_frame(struct vicam_camera *cam, int framenum); + +static int +send_control_msg(struct usb_device *udev, u8 request, u16 value, u16 index, + unsigned char *cp, u16 size) { - int ret; - unsigned char *transfer_buffer = kmalloc (size, GFP_KERNEL); + int status; - /* Needs to return data I think, works for sending though */ + // for reasons not yet known to me, you can't send USB control messages + // with data in the module (if you are compiled as a module). Whatever + // the reason, copying it to memory allocated as kernel memory then + // doing the usb control message fixes the problem. + + unsigned char *transfer_buffer = kmalloc(size, GFP_KERNEL); memcpy(transfer_buffer, cp, size); - - ret = usb_control_msg ( vicam->udev, set ? usb_sndctrlpipe(vicam->udev, 0) : usb_rcvctrlpipe(vicam->udev, 0), req, (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, 0, transfer_buffer, size, HZ); + + status = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + request, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, value, index, + transfer_buffer, size, HZ); kfree(transfer_buffer); - if (ret) - printk("vicam: error: %d\n", ret); - mdelay(100); - return ret; -} + if (status < 0) { + printk(KERN_INFO "Failed sending control message, error %d.\n", + status); + } -/***************************************************************************** - * - * Video4Linux Helpers - * - *****************************************************************************/ + return status; +} -static int vicam_get_capability(struct usb_vicam *vicam, struct video_capability *b) +static int +initialize_camera(struct vicam_camera *cam) { - dbg("vicam_get_capability"); - - strcpy(b->name, vicam->camera_name); - b->type = VID_TYPE_CAPTURE | VID_TYPE_MONOCHROME; - b->channels = 1; - b->audios = 0; - - b->maxwidth = vicam->width[vicam->sizes-1]; - b->maxheight = vicam->height[vicam->sizes-1]; - b->minwidth = vicam->width[0]; - b->minheight = vicam->height[0]; + struct usb_device *udev = cam->udev; + int status; + + if ((status = + send_control_msg(udev, 0xff, 0, 0, setup1, sizeof (setup1))) < 0) + return status; + if ((status = + send_control_msg(udev, 0xff, 0, 0, setup2, sizeof (setup2))) < 0) + return status; + if ((status = + send_control_msg(udev, 0xff, 0, 0, setup3, sizeof (setup3))) < 0) + return status; + if ((status = + send_control_msg(udev, 0xff, 0, 0, setup4, sizeof (setup4))) < 0) + return status; + if ((status = + send_control_msg(udev, 0xff, 0, 0, setup5, sizeof (setup5))) < 0) + return status; + if ((status = + send_control_msg(udev, 0xff, 0, 0, setup3, sizeof (setup3))) < 0) + return status; return 0; } - -static int vicam_get_channel(struct usb_vicam *vicam, struct video_channel *v) + +static int +set_camera_power(struct vicam_camera *cam, int state) { - dbg("vicam_get_channel"); + int status; - if (v->channel != 0) - return -EINVAL; - - v->flags = 0; - v->tuners = 0; - v->type = VIDEO_TYPE_CAMERA; - strcpy(v->name, "Camera"); + if ((status = send_control_msg(cam->udev, 0x50, state, 0, NULL, 0)) < 0) + return status; - return 0; -} - -static int vicam_set_channel(struct usb_vicam *vicam, struct video_channel *v) -{ - dbg("vicam_set_channel"); + if (state) { + send_control_msg(cam->udev, 0x55, 1, 0, NULL, 0); + } - if (v->channel != 0) - return -EINVAL; - return 0; } - -static int vicam_get_mmapbuffer(struct usb_vicam *vicam, struct video_mbuf *vm) + +static int +vicam_ioctl(struct inode *inode, struct file *file, unsigned int ioctlnr, unsigned long ul_arg) { - int i; + void *arg = (void *)ul_arg; + struct vicam_camera *cam = file->private_data; + int retval = 0; - dbg("vicam_get_mmapbuffer"); + if (!cam) + return -ENODEV; - memset(vm, 0, sizeof(vm)); - vm->size = VICAM_NUMFRAMES * vicam->maxframesize; - vm->frames = VICAM_NUMFRAMES; + /* make this _really_ smp-safe */ + if (down_interruptible(&cam->busy_lock)) + return -EINTR; - for (i=0; ioffsets[i] = vicam->maxframesize * i; + switch (ioctlnr) { + /* query capabilites */ + case VIDIOCGCAP: + { + struct video_capability b; + + DBG("VIDIOCGCAP\n"); + strcpy(b.name, "ViCam-based Camera"); + b.type = VID_TYPE_CAPTURE; + b.channels = 1; + b.audios = 0; + b.maxwidth = 320; /* VIDEOSIZE_CIF */ + b.maxheight = 240; + b.minwidth = 320; /* VIDEOSIZE_48_48 */ + b.minheight = 240; + + if (copy_to_user(arg, &b, sizeof (b))) + retval = -EFAULT; + + break; + } + /* get/set video source - we are a camera and nothing else */ + case VIDIOCGCHAN: + { + struct video_channel v; + + DBG("VIDIOCGCHAN\n"); + if (copy_from_user(&v, arg, sizeof (v))) { + retval = -EFAULT; + break; + } + if (v.channel != 0) { + retval = -EINVAL; + break; + } + + v.channel = 0; + strcpy(v.name, "Camera"); + v.tuners = 0; + v.flags = 0; + v.type = VIDEO_TYPE_CAMERA; + v.norm = 0; + + if (copy_to_user(arg, &v, sizeof (v))) + retval = -EFAULT; + break; + } - return 0; -} + case VIDIOCSCHAN: + { + int v; -static int vicam_get_picture(struct usb_vicam *vicam, struct video_picture *p) -{ - dbg("vicam_get_picture"); + if (copy_from_user(&v, arg, sizeof (v))) + retval = -EFAULT; + DBG("VIDIOCSCHAN %d\n", v); - /* This is probably where that weird 0x56 call goes */ - p->brightness = vicam->win.brightness; - p->hue = vicam->win.hue; - p->colour = vicam->win.colour; - p->contrast = vicam->win.contrast; - p->whiteness = vicam->win.whiteness; - p->depth = vicam->win.depth; - p->palette = vicam->win.palette; + if (retval == 0 && v != 0) + retval = -EINVAL; - return 0; -} + break; + } -static void synchronize(struct usb_vicam *vicam) -{ - DECLARE_WAITQUEUE(wait, current); - change_pending = 1; - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&vicam->wait, &wait); - if (change_pending) - schedule(); - remove_wait_queue(&vicam->wait, &wait); - set_current_state(TASK_RUNNING); - vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x00, NULL, 0); - mdelay(10); - vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x00, NULL, 0); - mdelay(10); -} + /* image properties */ + case VIDIOCGPICT: + { + struct video_picture vp; + DBG("VIDIOCGPICT\n"); + memset(&vp, 0, sizeof (struct video_picture)); + vp.brightness = cam->gain << 8; + vp.depth = 24; + vp.palette = VIDEO_PALETTE_RGB24; + if (copy_to_user + (arg, &vp, sizeof (struct video_picture))) + retval = -EFAULT; + break; + } -static void params_changed(struct usb_vicam *vicam) -{ -#if 1 - synchronize(vicam); - mdelay(10); - vicam_parameters(vicam); - printk(KERN_DEBUG "Submiting urb: %d\n", usb_submit_urb(vicam->readurb, GFP_KERNEL)); -#endif + case VIDIOCSPICT: + { + struct video_picture *vp = (struct video_picture *) arg; + + DBG("VIDIOCSPICT depth = %d, pal = %d\n", vp->depth, + vp->palette); + + cam->gain = vp->brightness >> 8; + + if (vp->depth != 24 + || vp->palette != VIDEO_PALETTE_RGB24) + retval = -EINVAL; + + break; + } + + /* get/set capture window */ + case VIDIOCGWIN: + { + struct video_window vw; + vw.x = 0; + vw.y = 0; + vw.width = 320; + vw.height = 240; + vw.chromakey = 0; + vw.flags = 0; + vw.clips = NULL; + vw.clipcount = 0; + + DBG("VIDIOCGWIN\n"); + + if (copy_to_user + ((void *) arg, (void *) &vw, sizeof (vw))) + retval = -EFAULT; + + // I'm not sure what the deal with a capture window is, it is very poorly described + // in the doc. So I won't support it now. + break; + } + + case VIDIOCSWIN: + { + + struct video_window *vw = (struct video_window *) arg; + DBG("VIDIOCSWIN %d x %d\n", vw->width, vw->height); + + if ( vw->width != 320 || vw->height != 240 ) + retval = -EFAULT; + + break; + } + + /* mmap interface */ + case VIDIOCGMBUF: + { + struct video_mbuf vm; + int i; + + DBG("VIDIOCGMBUF\n"); + memset(&vm, 0, sizeof (vm)); + vm.size = + VICAM_MAX_FRAME_SIZE * VICAM_FRAMES; + vm.frames = VICAM_FRAMES; + for (i = 0; i < VICAM_FRAMES; i++) + vm.offsets[i] = VICAM_MAX_FRAME_SIZE * i; + + if (copy_to_user + ((void *) arg, (void *) &vm, sizeof (vm))) + retval = -EFAULT; + + break; + } + + case VIDIOCMCAPTURE: + { + struct video_mmap vm; + // int video_size; + + if (copy_from_user + ((void *) &vm, (void *) arg, sizeof (vm))) { + retval = -EFAULT; + break; + } + + DBG("VIDIOCMCAPTURE frame=%d, height=%d, width=%d, format=%d.\n",vm.frame,vm.width,vm.height,vm.format); + + if ( vm.frame >= VICAM_FRAMES || vm.format != VIDEO_PALETTE_RGB24 ) + retval = -EINVAL; + + // in theory right here we'd start the image capturing + // (fill in a bulk urb and submit it asynchronously) + // + // Instead we're going to do a total hack job for now and + // retrieve the frame in VIDIOCSYNC + + break; + } + + case VIDIOCSYNC: + { + int frame; + + if (copy_from_user((void *) &frame, arg, sizeof (int))) { + retval = -EFAULT; + break; + } + DBG("VIDIOCSYNC: %d\n", frame); + + read_frame(cam, frame); + + break; + } + + /* pointless to implement overlay with this camera */ + case VIDIOCCAPTURE: + case VIDIOCGFBUF: + case VIDIOCSFBUF: + case VIDIOCKEY: + retval = -EINVAL; + break; + + /* tuner interface - we have none */ + case VIDIOCGTUNER: + case VIDIOCSTUNER: + case VIDIOCGFREQ: + case VIDIOCSFREQ: + retval = -EINVAL; + break; + + /* audio interface - we have none */ + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + retval = -EINVAL; + break; + default: + retval = -ENOIOCTLCMD; + break; + } + + up(&cam->busy_lock); + return retval; } -static int vicam_set_picture(struct usb_vicam *vicam, struct video_picture *p) +static int +vicam_open(struct inode *inode, struct file *file) { - int changed = 0; - info("vicam_set_picture (%d)", p->brightness); - - -#define SET(x) \ - if (vicam->win.x != p->x) \ - vicam->win.x = p->x, changed = 1; - SET(brightness); - SET(hue); - SET(colour); - SET(contrast); - SET(whiteness); - SET(depth); - SET(palette); - if (changed) - params_changed(vicam); + struct video_device *dev = video_devdata(file); + struct vicam_camera *cam = + (struct vicam_camera *) dev->priv; + DBG("open\n"); + + if (!cam) { + printk(KERN_ERR + "vicam video_device improperly initialized"); + } - return 0; - /* Investigate what should be done maybe 0x56 type call */ - if (p->depth != 8) return 1; - if (p->palette != VIDEO_PALETTE_GREY) return 1; + down_interruptible(&cam->busy_lock); + + if (cam->open_count > 0) { + printk(KERN_INFO + "vicam_open called on already opened camera"); + up(&cam->busy_lock); + return -EBUSY; + } + + if (!cam->raw_image) { + cam->raw_image = kmalloc(VICAM_MAX_READ_SIZE, GFP_KERNEL); + if (!cam->raw_image) { + up(&cam->busy_lock); + return -ENOMEM; + } + } + + if (!cam->framebuf) { + cam->framebuf = + rvmalloc(VICAM_MAX_FRAME_SIZE * VICAM_FRAMES); + if (!cam->framebuf) { + kfree(cam->raw_image); + up(&cam->busy_lock); + return -ENOMEM; + } + } + // First upload firmware, then turn the camera on + + if (!cam->is_initialized) { + initialize_camera(cam); + + cam->is_initialized = 1; + } + + set_camera_power(cam, 1); + + cam->needsDummyRead = 1; + cam->open_count++; + up(&cam->busy_lock); + + file->private_data = cam; + return 0; } -/* FIXME - vicam_sync_frame - important */ -static int vicam_sync_frame(struct usb_vicam *vicam, int frame) +static int +vicam_close(struct inode *inode, struct file *file) { - dbg("vicam_sync_frame"); + struct vicam_camera *cam = file->private_data; + DBG("close\n"); + set_camera_power(cam, 0); - if(frame <0 || frame >= VICAM_NUMFRAMES) - return -EINVAL; + cam->open_count--; - /* Probably need to handle various cases */ -/* ret=vicam_newframe(vicam, frame); - vicam->frame[frame].grabstate=FRAME_UNUSED; -*/ return 0; } - -static int vicam_get_window(struct usb_vicam *vicam, struct video_window *vw) -{ - dbg("vicam_get_window"); - vw->x = 0; - vw->y = 0; - vw->chromakey = 0; - vw->flags = 0; - vw->clipcount = 0; - vw->width = vicam->win.width; - vw->height = vicam->win.height; +inline int pin(int x) +{ + return((x > 255) ? 255 : ((x < 0) ? 0 : x)); +} - return 0; +inline void writepixel(char *rgb, int Y, int Cr, int Cb) +{ + Y = 1160 * (Y - 16); + + rgb[2] = pin( ( ( Y + ( 1594 * Cr ) ) + 500 ) / 1300 ); + rgb[1] = pin( ( ( Y - ( 392 * Cb ) - ( 813 * Cr ) ) + 500 ) / 1000 ); + rgb[0] = pin( ( ( Y + ( 2017 * Cb ) ) + 500 ) / 900 ); } -static int vicam_set_window(struct usb_vicam *vicam, struct video_window *vw) +#define DATA_HEADER_SIZE 64 + +// -------------------------------------------------------------------------------- +// vicam_decode_color - Convert from Vicam Y-Cr-Cb to RGB +// +// Copyright (C) 2002 Monroe Williams (monroe@pobox.com) +// -------------------------------------------------------------------------------- + +void vicam_decode_color( char *data, char *rgb) { - info("vicam_set_window"); - - if (vw->flags) - return -EINVAL; - if (vw->clipcount) - return -EINVAL; + int x,y; + int Cr, Cb; + int sign; + int prevX, nextX, prevY, nextY; + int skip; + unsigned char *src; + unsigned char *dst; - if (vicam->win.width == vw->width && vicam->win.height == vw->height) - return 0; + prevY = 512; + nextY = 512; - /* Pick largest mode that is smaller than specified res */ - /* If specified res is too small reject */ + src = data + DATA_HEADER_SIZE; + dst = rgb; - /* Add urb send to device... */ + for(y = 1; y < 241; y += 2) + { + // even line + sign = 1; + prevX = 1; + nextX = 1; - vicam->win.width = vw->width; - vicam->win.height = vw->height; - params_changed(vicam); + skip = 0; - return 0; -} + dst = rgb + (y-1)*320*3; + + for(x = 0; x < 512; x++) + { + if(x == 512-1) + nextX = -1; -/* FIXME - vicam_mmap_capture - important */ -static int vicam_mmap_capture(struct usb_vicam *vicam, struct video_mmap *vm) -{ - dbg("vicam_mmap_capture"); + Cr = sign * ((src[prevX] - src[0]) + (src[nextX] - src[0])) >> 1; + Cb = sign * ((src[prevY] - src[prevX + prevY]) + (src[prevY] - src[nextX + prevY]) + (src[nextY] - src[prevX + nextY]) + (src[nextY] - src[nextX + nextY])) >> 2; - /* usbvideo.c looks good for using here */ + writepixel( + dst + ((x*5)>>3)*3, + src[0] + (sign * (Cr >> 1)), + Cr, + Cb); - /* - if (vm->frame >= VICAM_NUMFRAMES) - return -EINVAL; - if (vicam->frame[vm->frame].grabstate != FRAME_UNUSED) - return -EBUSY; - vicam->frame[vm->frame].grabstate=FRAME_READY; - */ + src++; + sign *= -1; + prevX = -1; + } - /* No need to vicam_set_window here according to Alan */ + prevY = -512; - /* - if (!vicam->streaming) - vicam_start_stream(vicam); - */ + if(y == (242 - 2)) + nextY = -512; - /* set frame as ready */ + // odd line + sign = 1; + prevX = 1; + nextX = 1; - return 0; -} + skip = 0; -/***************************************************************************** - * - * Video4Linux - * - *****************************************************************************/ + dst = rgb + (y)*320*3; + + for(x = 0; x < 512; x++) + { + if(x == 512-1) + nextX = -1; + + Cr = sign * ((src[prevX + prevY] - src[prevY]) + (src[nextX + prevY] - src[prevY]) + (src[prevX + nextY] - src[nextY]) + (src[nextX + nextY] - src[nextY])) >> 2; + Cb = sign * ((src[0] - src[prevX]) + (src[0] - src[nextX])) >> 1; + + writepixel( + dst + ((x * 5)>>3)*3, + src[0] - (sign * (Cb >> 1)), + Cr, + Cb); + + src++; + sign *= -1; + prevX = -1; + } + } +} -static int vicam_v4l_open(struct inode *inode, struct file *file) +static void +read_frame(struct vicam_camera *cam, int framenum) { - struct video_device *vdev = video_devdata(file); - struct usb_vicam *vicam = (struct usb_vicam *)vdev; - int err = 0; - - dbg("vicam_v4l_open"); - down(&vicam->sem); - - vicam->fbuf = rvmalloc(vicam->maxframesize * VICAM_NUMFRAMES); - if (vicam->open_count) { - err = -EBUSY; - } else if (!vicam->fbuf) { - err =- ENOMEM; + unsigned char request[16]; + int realShutter; + int n; + int actual_length; + + memset(request, 0, 16); + request[0] = cam->gain; // 0 = 0% gain, FF = 100% gain + + request[1] = 0; // 512x242 capture + + request[2] = 0x90; // the function of these two bytes + request[3] = 0x07; // is not yet understood + + if (cam->shutter_speed > 60) { + // Short exposure + realShutter = + ((-15631900 / cam->shutter_speed) + 260533) / 1000; + request[4] = realShutter & 0xFF; + request[5] = (realShutter >> 8) & 0xFF; + request[6] = 0x03; + request[7] = 0x01; } else { -#ifdef BLINKING - vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x01, NULL, 0); - info ("led on"); - vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x01, NULL, 0); -#endif - vicam->open_count++; - file->private_data = vdev; + // Long exposure + realShutter = 15600 / cam->shutter_speed - 1; + request[4] = 0; + request[5] = 0; + request[6] = realShutter & 0xFF; + request[7] = realShutter >> 8; } - up(&vicam->sem); - return err; -} - -static int vicam_v4l_close(struct inode *inode, struct file *file) -{ - struct video_device *vdev = file->private_data; - struct usb_vicam *vicam = (struct usb_vicam *)vdev; + // Per John Markus Bjørndalen, byte at index 8 causes problems if it isn't 0 + request[8] = 0; + // bytes 9-15 do not seem to affect exposure or image quality - dbg("vicam_v4l_close"); - - down(&vicam->sem); + n = send_control_msg(cam->udev, 0x51, 0x80, 0, request, 16); -#ifdef BLINKING - info ("led off"); - vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x00, NULL, 0); -// vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x00, NULL, 0); Leave it on -#endif + if (n < 0) { + printk(KERN_ERR + " Problem sending frame capture control message"); + return; + } - rvfree(vicam->fbuf, vicam->maxframesize * VICAM_NUMFRAMES); - vicam->fbuf = 0; - vicam->open_count=0; - file->private_data = NULL; + n = usb_bulk_msg(cam->udev, + usb_rcvbulkpipe(cam->udev, cam->bulkEndpoint), + cam->raw_image, + 512 * 242 + 128, &actual_length, HZ*10); - up(&vicam->sem); - /* Why does se401.c have a usbdevice check here? */ - /* If device is unplugged while open, I guess we only may unregister now */ - return 0; -} + if (n < 0) { + printk(KERN_ERR "Problem during bulk read of frame data: %d\n", + n); + } -static int vicam_v4l_read(struct file *file, char *user_buf, - size_t buflen, loff_t *ppos) -{ - struct video_device *vdev = file->private_data; - //struct usb_vicam *vicam = (struct usb_vicam *)vdev; + vicam_decode_color(cam->raw_image, + cam->framebuf + + framenum * VICAM_MAX_FRAME_SIZE ); - dbg("vicam_v4l_read(%d)", buflen); + cam->framebuf_size = + 320 * 240 * VICAM_BYTES_PER_PIXEL; + cam->framebuf_read_start = 0; - if (!vdev || !buf) - return -EFAULT; + return; - if (copy_to_user(user_buf, buf2, buflen)) - return -EFAULT; - return buflen; } -static int vicam_v4l_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int +vicam_read( struct file *file, char *buf, size_t count, loff_t *ppos ) { - struct video_device *vdev = file->private_data; - struct usb_vicam *vicam = (struct usb_vicam *)vdev; - int ret = -EL3RST; + struct vicam_camera *cam = file->private_data; + DBG("read %d bytes.\n", (int) count); - if (!vicam->udev) - return -EIO; + if (!buf) + return -EINVAL; - down(&vicam->sem); + if (!count) + return -EINVAL; - switch (cmd) { - case VIDIOCGCAP: - { - struct video_capability *b = arg; - ret = vicam_get_capability(vicam,b); - dbg("name %s",b->name); - break; - } - case VIDIOCGFBUF: - { - struct video_buffer *vb = arg; - info("vicam_v4l_ioctl - VIDIOCGBUF - query frame buffer param"); - /* frame buffer not supported, not used */ - memset(vb, 0, sizeof(*vb)); - ret = 0; - break; - } - case VIDIOCGWIN: - { - struct video_window *vw = arg; - ret = vicam_get_window(vicam, vw); - break; - } - case VIDIOCSWIN: - { - struct video_window *vw = arg; - ret = vicam_set_window(vicam, vw); - break; + // This is some code that will hopefully allow us to do shell copies from + // the /dev/videoX to a file and have it actually work. + if (cam->framebuf_size != 0) { + if (cam->framebuf_read_start == cam->framebuf_size) { + cam->framebuf_size = cam->framebuf_read_start = 0; + return 0; + } else { + if (cam->framebuf_read_start + count <= + cam->framebuf_size) { + // count does not exceed available bytes + copy_to_user(buf, + (cam->framebuf) + + cam->framebuf_read_start, count); + cam->framebuf_read_start += count; + return count; + } else { + count = + cam->framebuf_size - + cam->framebuf_read_start; + copy_to_user(buf, + (cam->framebuf) + + cam->framebuf_read_start, count); + cam->framebuf_read_start = cam->framebuf_size; + return count; + } + } } - case VIDIOCGCHAN: - { - struct video_channel *v = arg; - ret = vicam_get_channel(vicam,v); - break; - } - case VIDIOCSCHAN: - { - struct video_channel *v = arg; - ret = vicam_set_channel(vicam,v); - break; - } - case VIDIOCGPICT: - { - struct video_picture *p = arg; - ret = vicam_get_picture(vicam,p); - break; - } - case VIDIOCSPICT: - { - struct video_picture *p = arg; - ret = vicam_set_picture(vicam,p); - break; - } - case VIDIOCGMBUF: - { - struct video_mbuf *vm = arg; - ret = vicam_get_mmapbuffer(vicam,vm); - break; - } - case VIDIOCMCAPTURE: - { - struct video_mmap *vm = arg; - ret = vicam_mmap_capture(vicam,vm); - break; - } - case VIDIOCSYNC: - { - int *frame = arg; - ret = vicam_sync_frame(vicam,*frame); - break; + down_interruptible(&cam->busy_lock); + + if (cam->needsDummyRead) { + read_frame(cam, 0); + cam->needsDummyRead = 0; } + // read_frame twice because the camera doesn't seem to take the shutter speed for the first one. - case VIDIOCKEY: - ret = 0; - - case VIDIOCCAPTURE: - case VIDIOCSFBUF: - case VIDIOCGTUNER: - case VIDIOCSTUNER: - case VIDIOCGFREQ: - case VIDIOCSFREQ: - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - case VIDIOCGUNIT: - ret = -EINVAL; + read_frame(cam, 0); - default: - { - info("vicam_v4l_ioctl - %ui",cmd); - ret = -ENOIOCTLCMD; - } - } /* end switch */ + if (count > cam->framebuf_size) + count = cam->framebuf_size; - up(&vicam->sem); - return ret; -} + copy_to_user(buf, cam->framebuf, count); -static int vicam_v4l_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return video_usercopy(inode, file, cmd, arg, vicam_v4l_do_ioctl); + if (count != cam->framebuf_size) + cam->framebuf_read_start = count; + else + cam->framebuf_size = 0; + + up(&cam->busy_lock); + + return count; } -static int vicam_v4l_mmap(struct file *file, struct vm_area_struct *vma) + +static int +vicam_mmap(struct file *file, struct vm_area_struct *vma) { - struct video_device *vdev = file->private_data; - struct usb_vicam *vicam = (struct usb_vicam *)vdev; + // TODO: allocate the raw frame buffer if necessary + unsigned long page, pos; unsigned long start = vma->vm_start; unsigned long size = vma->vm_end-vma->vm_start; - unsigned long page, pos; + struct vicam_camera *cam = file->private_data; - down(&vicam->sem); - - if (vicam->udev == NULL) { - up(&vicam->sem); - return -EIO; - } -#if 0 - if (size > (((VICAM_NUMFRAMES * vicam->maxframesize) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) { - up(&vicam->sem); - return -EINVAL; + if (!cam) + return -ENODEV; + + DBG("vicam_mmap: %ld\n", size); + + /* We let mmap allocate as much as it wants because Linux was adding 2048 bytes + * to the size the application requested for mmap and it was screwing apps up. + if (size > VICAM_FRAMES*VICAM_MAX_FRAME_SIZE) + return -EINVAL; + */ + + /* make this _really_ smp-safe */ + if (down_interruptible(&cam->busy_lock)) + return -EINTR; + + if (!cam->framebuf) { /* we do lazy allocation */ + cam->framebuf = + rvmalloc(VICAM_MAX_FRAME_SIZE * VICAM_FRAMES); + if (!cam->framebuf) { + up(&cam->busy_lock); + return -ENOMEM; + } } -#endif - pos = (unsigned long)vicam->fbuf; + + pos = (unsigned long)cam->framebuf; while (size > 0) { page = kvirt_to_pa(pos); - if (remap_page_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { - up(&vicam->sem); + if (remap_page_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) return -EAGAIN; - } + start += PAGE_SIZE; pos += PAGE_SIZE; if (size > PAGE_SIZE) @@ -633,299 +1095,315 @@ static int vicam_v4l_mmap(struct file *file, struct vm_area_struct *vma) else size = 0; } - up(&vicam->sem); - return 0; + up(&cam->busy_lock); + + return 0; } -/* FIXME - vicam_template - important */ -static struct file_operations vicam_fops = { - .owner = THIS_MODULE, - .open = vicam_v4l_open, - .release = vicam_v4l_close, - .read = vicam_v4l_read, - .mmap = vicam_v4l_mmap, - .ioctl = vicam_v4l_ioctl, - .llseek = no_llseek, -}; -static struct video_device vicam_template = { - .owner = THIS_MODULE, - .name = "vicam USB camera", - .type = VID_TYPE_CAPTURE, - .hardware = VID_HARDWARE_SE401, /* need to ask for own id */ - .fops = &vicam_fops, -}; +#ifdef CONFIG_PROC_FS -/****************************************************************************** - * - * Some Routines - * - ******************************************************************************/ +static struct proc_dir_entry *vicam_proc_root = NULL; -/* -Flash the led -vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x01, NULL, 0); -info ("led on"); -vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x01, NULL, 0); -info ("led off"); -vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x00, NULL, 0); -vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x00, NULL, 0); -*/ +static int +vicam_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *out = page; + int len; + struct vicam_camera *cam = (struct vicam_camera *) data; + + out += + sprintf(out, "Vicam-based WebCam Linux Driver.\n"); + out += sprintf(out, "(c) 2002 Joe Burks (jburks@wavicle.org)\n"); + out += sprintf(out, "vicam stats:\n"); + out += sprintf(out, " Shutter Speed: 1/%d\n", cam->shutter_speed); + out += sprintf(out, " Gain: %d\n", cam->gain); + + len = out - page; + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) + return 0; + } else + len = count; + + *start = page + off; + return len; +} -static void vicam_bulk(struct urb *urb) +static int +vicam_write_proc(struct file *file, const char *buffer, + unsigned long count, void *data) { - struct usb_vicam *vicam = urb->context; + char *in; + char *start; + struct vicam_camera *cam = (struct vicam_camera *) data; - /* if (!vicam || !vicam->dev || !vicam->used) - return; - */ + in = kmalloc(count + 1, GFP_KERNEL); + if (!in) + return -ENOMEM; - if (urb->status) - printk("vicam%d: nonzero read/write bulk status received: %d", - 0, urb->status); + in[count] = 0; // I'm not sure buffer is gauranteed to be null terminated + // so I do this to make sure I have a null in there. - urb->actual_length = 0; - urb->dev = vicam->udev; + strncpy(in, buffer, count); - memcpy(buf2, buf+64, 0x1e480); - if (vicam->fbuf) - memcpy(vicam->fbuf, buf+64, 0x1e480); + start = strstr(in, "gain="); + if (start + && (start == in || *(start - 1) == ' ' || *(start - 1) == ',')) + cam->gain = simple_strtoul(start + 5, NULL, 10); - if (!change_pending) { - if (usb_submit_urb(urb, GFP_ATOMIC)) - dbg("failed resubmitting read urb"); - } else { - change_pending = 0; - wake_up_interruptible(&vicam->wait); - } + start = strstr(in, "shutter="); + if (start + && (start == in || *(start - 1) == ' ' || *(start - 1) == ',')) + cam->shutter_speed = simple_strtoul(start + 8, NULL, 10); + + kfree(in); + return count; } -static int vicam_parameters(struct usb_vicam *vicam) +void +vicam_create_proc_root(void) { - unsigned char req[0x10]; - unsigned int shutter; - shutter = 10; + vicam_proc_root = create_proc_entry("video/vicam", S_IFDIR, 0); - switch (vicam->win.width) { - case 512: - default: - memcpy(req, s512x242bw, 0x10); - break; - case 256: - memcpy(req, s256x242bw, 0x10); - break; - case 128: - memcpy(req, s128x122bw, 0x10); - break; - } + if (vicam_proc_root) + vicam_proc_root->owner = THIS_MODULE; + else + printk(KERN_ERR + "could not create /proc entry for vicam!"); +} +void +vicam_destroy_proc_root(void) +{ + if (vicam_proc_root) + remove_proc_entry("video/vicam", 0); +} - mdelay(10); - vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x01, NULL, 0); - info ("led on"); - vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x01, NULL, 0); +void +vicam_create_proc_entry(void *ptr) +{ + struct vicam_camera *cam = (struct vicam_camera *) ptr; - mdelay(10); + char name[7]; + struct proc_dir_entry *ent; - shutter = vicam->win.contrast / 256; - if (shutter == 0) - shutter = 1; - printk("vicam_parameters: brightness %d, shutter %d\n", vicam->win.brightness, shutter ); - req[0] = vicam->win.brightness /256; - shutter = 15600/shutter - 1; - req[6] = shutter & 0xff; - req[7] = (shutter >> 8) & 0xff; - vicam_sndctrl(1, vicam, VICAM_REQ_CAPTURE, 0x80, req, 0x10); - mdelay(10); - vicam_sndctrl(0, vicam, VICAM_REQ_GET_SOMETHIN, 0, buf, 0x10); - mdelay(10); + DBG(KERN_INFO "vicam: creating proc entry\n"); - return 0; + if (!vicam_proc_root || !cam) { + printk(KERN_INFO + "vicam: could not create proc entry, %s pointer is null.\n", + (!cam ? "camera" : "root")); + return; + } + + sprintf(name, "video%d", cam->vdev.minor); + + ent = + create_proc_entry(name, S_IFREG | S_IRUGO | S_IWUSR, + vicam_proc_root); + if (!ent) + return; + + ent->data = cam; + ent->read_proc = vicam_read_proc; + ent->write_proc = vicam_write_proc; + ent->size = 512; + cam->proc_entry = ent; } -static int vicam_init(struct usb_vicam *vicam) +void +vicam_destroy_proc_entry(void *ptr) { - int width[] = {128, 256, 512}; - int height[] = {122, 242, 242}; - - dbg("vicam_init"); - buf = kmalloc(0x1e480, GFP_KERNEL); - buf2 = kmalloc(0x1e480, GFP_KERNEL); - if ((!buf) || (!buf2)) { - printk("Not enough memory for vicam!\n"); - goto error; - } + struct vicam_camera *cam = (struct vicam_camera *) ptr; + char name[7]; - /* do we do aspect correction in kernel or not? */ - vicam->sizes = 3; - vicam->width = kmalloc(vicam->sizes*sizeof(int), GFP_KERNEL); - vicam->height = kmalloc(vicam->sizes*sizeof(int), GFP_KERNEL); - memcpy(vicam->width, &width, sizeof(width)); - memcpy(vicam->height, &height, sizeof(height)); - vicam->maxframesize = vicam->width[vicam->sizes-1] * vicam->height[vicam->sizes-1]; + if (!cam || !cam->proc_entry) + return; - /* Download firmware to camera */ - vicam_sndctrl(1, vicam, VICAM_REQ_VENDOR, 0, firmware1, sizeof(firmware1)); - vicam_sndctrl(1, vicam, VICAM_REQ_VENDOR, 0, findex1, sizeof(findex1)); - vicam_sndctrl(1, vicam, VICAM_REQ_VENDOR, 0, fsetup, sizeof(fsetup)); - vicam_sndctrl(1, vicam, VICAM_REQ_VENDOR, 0, firmware2, sizeof(firmware2)); - vicam_sndctrl(1, vicam, VICAM_REQ_VENDOR, 0, findex2, sizeof(findex2)); - vicam_sndctrl(1, vicam, VICAM_REQ_VENDOR, 0, fsetup, sizeof(fsetup)); + sprintf(name, "video%d", cam->vdev.minor); + remove_proc_entry(name, vicam_proc_root); + cam->proc_entry = NULL; - vicam_parameters(vicam); +} - FILL_BULK_URB(vicam->readurb, vicam->udev, usb_rcvbulkpipe(vicam->udev, 0x81), - buf, 0x1e480, vicam_bulk, vicam); - printk(KERN_DEBUG "Submiting urb: %d\n", usb_submit_urb(vicam->readurb, GFP_KERNEL)); +#endif +int +vicam_video_init(struct video_device *vdev) +{ + // This would normally create the proc entry for this camera +#ifdef CONFIG_PROC_FS + vicam_create_proc_entry(vdev->priv); +#endif return 0; -error: - if (buf) - kfree(buf); - if (buf2) - kfree(buf2); - return 1; } -static int vicam_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct usb_vicam *vicam; - char *camera_name=NULL; +static struct file_operations vicam_fops = { + .owner = THIS_MODULE, + .open = vicam_open, + .release =vicam_close, + .read = vicam_read, + .mmap = vicam_mmap, + .ioctl = vicam_ioctl, + .llseek = no_llseek, +}; + +static struct video_device vicam_template = { + .owner = THIS_MODULE, + .name = "ViCam-based USB Camera", + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_VICAM, + .fops = &vicam_fops, +// .initialize = vicam_video_init, + .minor = -1, +}; + +/* table of devices that work with this driver */ +static struct usb_device_id vicam_table[] = { + {USB_DEVICE(USB_VICAM_VENDOR_ID, USB_VICAM_PRODUCT_ID)}, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, vicam_table); - dbg("vicam_probe"); +static struct usb_driver vicam_driver = { + name:"vicam", + probe:vicam_probe, + disconnect:vicam_disconnect, + id_table:vicam_table +}; +/** + * vicam_probe + * + * Called by the usb core when a new device is connected that it thinks + * this driver might be interested in. + */ +static int +vicam_probe( struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + int bulkEndpoint = 0; + const struct usb_interface_descriptor *interface; + const struct usb_endpoint_descriptor *endpoint; + struct vicam_camera *cam; + /* See if the device offered us matches what we can accept */ - if ((udev->descriptor.idVendor != USB_VICAM_VENDOR_ID) || - (udev->descriptor.idProduct != USB_VICAM_PRODUCT_ID)) { + if ((dev->descriptor.idVendor != USB_VICAM_VENDOR_ID) || + (dev->descriptor.idProduct != USB_VICAM_PRODUCT_ID)) { return -ENODEV; } - - camera_name="3Com HomeConnect USB"; - info("ViCAM camera found: %s", camera_name); - - vicam = kmalloc (sizeof(struct usb_vicam), GFP_KERNEL); - if (vicam == NULL) { - err ("couldn't kmalloc vicam struct"); - return -ENOMEM; + + printk(KERN_INFO "ViCam based webcam connected\n"); + + interface = &intf->altsetting[0]; + + DBG(KERN_DEBUG "Interface %d. has %u. endpoints!\n", + ifnum, (unsigned) (interface->bNumEndpoints)); + endpoint = &interface->endpoint[0]; + + if ((endpoint->bEndpointAddress & 0x80) && + ((endpoint->bmAttributes & 3) == 0x02)) { + /* we found a bulk in endpoint */ + bulkEndpoint = endpoint->bEndpointAddress; + } else { + printk(KERN_ERR + "No bulk in endpoint was found ?! (this is bad)\n"); } - memset(vicam, 0, sizeof(*vicam)); - vicam->readurb = usb_alloc_urb(0, GFP_KERNEL); - if (!vicam->readurb) { - kfree(vicam); + if ((cam = + kmalloc(sizeof (struct vicam_camera), GFP_KERNEL)) == NULL) { + printk(KERN_WARNING + "could not allocate kernel memory for vicam_camera struct\n"); return -ENOMEM; } - vicam->udev = udev; - vicam->camera_name = camera_name; - vicam->win.brightness = 128; - vicam->win.contrast = 10; + memset(cam, 0, sizeof (struct vicam_camera)); - /* FIXME */ - if (vicam_init(vicam)) { - usb_free_urb(vicam->readurb); - kfree(vicam); - return -ENOMEM; - } - memcpy(&vicam->vdev, &vicam_template, sizeof(vicam_template)); - memcpy(vicam->vdev.name, vicam->camera_name, strlen(vicam->camera_name)); - - if (video_register_device(&vicam->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { - err("video_register_device"); - usb_free_urb(vicam->readurb); - kfree(vicam); + cam->shutter_speed = 15; + + init_MUTEX(&cam->busy_lock); + + memcpy(&cam->vdev, &vicam_template, + sizeof (vicam_template)); + cam->vdev.priv = cam; // sort of a reverse mapping for those functions that get vdev only + + cam->udev = dev; + cam->bulkEndpoint = bulkEndpoint; + + if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1) == -1) { + kfree(cam); + printk(KERN_WARNING "video_register_device failed\n"); return -EIO; } - info("registered new video device: video%d", vicam->vdev.minor); - - init_MUTEX (&vicam->sem); - init_waitqueue_head(&vicam->wait); + printk(KERN_INFO "ViCam webcam driver now controlling video device %d\n",cam->vdev.minor); + + dev_set_drvdata(&intf->dev, cam); - dev_set_drvdata (&intf->dev, vicam); return 0; } - -/* FIXME - vicam_disconnect - important */ -static void vicam_disconnect(struct usb_interface *intf) +static void +vicam_disconnect(struct usb_interface *intf) { - struct usb_vicam *vicam; + struct vicam_camera *cam = dev_get_drvdata(&intf->dev); - vicam = dev_get_drvdata (&intf->dev); + dev_set_drvdata ( &intf->dev, NULL ); + usb_put_dev(cam->udev); + + cam->udev = NULL; + + video_unregister_device(&cam->vdev); - dev_set_drvdata (&intf->dev, NULL); +#ifdef CONFIG_PROC_FS + vicam_destroy_proc_entry(cam); +#endif - if (vicam) { - video_unregister_device(&vicam->vdev); - vicam->udev = NULL; -/* - vicam->frame[0].grabstate = FRAME_ERROR; - vicam->frame[1].grabstate = FRAME_ERROR; -*/ + if (cam->raw_image) + kfree(cam->raw_image); + if (cam->framebuf) + rvfree(cam->framebuf, + VICAM_MAX_FRAME_SIZE * VICAM_FRAMES); - /* Free buffers and shit */ - info("%s disconnected", vicam->camera_name); - synchronize(vicam); + kfree(cam); - if (!vicam->open_count) { - /* Other random junk */ - usb_free_urb(vicam->readurb); - kfree(vicam); - vicam = NULL; - } - } + printk(KERN_DEBUG "ViCam-based WebCam disconnected\n"); } -/* usb specific object needed to register this driver with the usb subsystem */ -static struct usb_driver vicam_driver = { - .owner = THIS_MODULE, - .name = "vicam", - .probe = vicam_probe, - .disconnect = vicam_disconnect, - .id_table = vicam_table, -}; - -/****************************************************************************** - * - * Module Routines - * - ******************************************************************************/ - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); - -/* Module paramaters */ -MODULE_PARM(debug, "i"); -MODULE_PARM_DESC(debug, "Debug enabled or not"); - -static int __init usb_vicam_init(void) +/* + */ +static int __init +usb_vicam_init(void) { - int result; - - printk("VICAM: initializing\n"); - /* register this driver with the USB subsystem */ - result = usb_register(&vicam_driver); - if (result < 0) { - err("usb_register failed for the "__FILE__" driver. Error number %d", - result); - return -1; - } - - info(DRIVER_VERSION " " DRIVER_AUTHOR); - info(DRIVER_DESC); + DBG(KERN_INFO "ViCam-based WebCam driver startup\n"); +#ifdef CONFIG_PROC_FS + vicam_create_proc_root(); +#endif + if (usb_register(&vicam_driver) != 0) + printk(KERN_WARNING "usb_register failed!\n"); return 0; } -static void __exit usb_vicam_exit(void) +static void __exit +usb_vicam_exit(void) { - /* deregister this driver with the USB subsystem */ + DBG(KERN_INFO + "ViCam-based WebCam driver shutdown\n"); + usb_deregister(&vicam_driver); +#ifdef CONFIG_PROC_FS + vicam_destroy_proc_root(); +#endif } module_init(usb_vicam_init); module_exit(usb_vicam_exit); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/media/vicam.h b/drivers/usb/media/vicam.h deleted file mode 100644 index 4527d8e644f7..000000000000 --- a/drivers/usb/media/vicam.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * - * Vista Imaging ViCAM / 3Com HomeConnect Usermode Driver - * Christopher L Cheney (C) 2001 - * - */ - -#ifndef __LINUX_VICAM_H -#define __LINUX_VICAM_H - - -#ifdef CONFIG_USB_DEBUG - static int debug = 1; -#else - static int debug; -#endif - -/* Use our own dbg macro */ -#undef dbg -#define dbg(format, arg...) do { if (debug) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg); } while (0) - -#define VICAM_NUMFRAMES 30 -#define VICAM_NUMSBUF 1 - -/* USB REQUEST NUMBERS */ -#define VICAM_REQ_VENDOR 0xff -#define VICAM_REQ_CAMERA_POWER 0x50 -#define VICAM_REQ_CAPTURE 0x51 -#define VICAM_REQ_LED_CONTROL 0x55 -#define VICAM_REQ_GET_SOMETHIN 0x56 - -/* not required but lets you know camera is on */ -/* camera must be on to turn on led */ -/* 0x01 always on 0x03 on when picture taken (flashes) */ - -struct picture_parm -{ - int width; - int height; - int brightness; - int hue; - int colour; - int contrast; - int whiteness; - int depth; - int palette; -}; - -struct vicam_scratch { - unsigned char *data; - volatile int state; - int offset; - int length; -}; - -/* Structure to hold all of our device specific stuff */ -struct usb_vicam -{ - struct video_device vdev; - struct usb_device *udev; - - int open_count; /* number of times this port has been opened */ - struct semaphore sem; /* locks this structure */ - wait_queue_head_t wait; /* Processes waiting */ - - int streaming; - - /* v4l stuff */ - char *camera_name; - char *fbuf; - struct urb *urb[VICAM_NUMSBUF]; - int sizes; - int *width; - int *height; - int maxframesize; - struct picture_parm win; - struct proc_dir_entry *proc_entry; /* /proc/se401/videoX */ - struct urb *readurb; -}; - -#endif diff --git a/include/linux/videodev.h b/include/linux/videodev.h index 80966ed8e288..a90d968374eb 100644 --- a/include/linux/videodev.h +++ b/include/linux/videodev.h @@ -397,6 +397,7 @@ struct video_code #define VID_HARDWARE_PWC 31 /* Philips webcams */ #define VID_HARDWARE_MEYE 32 /* Sony Vaio MotionEye cameras */ #define VID_HARDWARE_CPIA2 33 +#define VID_HARDWARE_VICAM 34 #endif /* __LINUX_VIDEODEV_H */ -- cgit v1.2.3 From 31d49a6340423315c7f1c8301b4ac2fe14bca408 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Oct 2002 12:55:54 +0100 Subject: [ARM] Update timekeeping functions to use tick_nsec/1000 This updates the ARM time keeping functions to use tick_nsec/1000 instead of tick. --- arch/arm/kernel/time-acorn.c | 33 +++++++++++++-------------------- arch/arm/kernel/time.c | 9 +++++---- arch/arm/mach-iop310/iq80310-time.c | 2 +- include/asm-arm/arch-ebsa285/time.h | 4 ++-- include/asm-arm/arch-sa1100/time.h | 2 +- 5 files changed, 22 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/arch/arm/kernel/time-acorn.c b/arch/arm/kernel/time-acorn.c index 91f9e1a134c2..eb637801c7a5 100644 --- a/arch/arm/kernel/time-acorn.c +++ b/arch/arm/kernel/time-acorn.c @@ -25,44 +25,37 @@ extern unsigned long (*gettimeoffset)(void); static unsigned long ioctime_gettimeoffset(void) { - unsigned int count1, count2, status1, status2; - unsigned long offset = 0; + unsigned int count1, count2, status; + long offset; - status1 = ioc_readb(IOC_IRQREQA); - barrier (); ioc_writeb (0, IOC_T0LATCH); barrier (); count1 = ioc_readb(IOC_T0CNTL) | (ioc_readb(IOC_T0CNTH) << 8); barrier (); - status2 = ioc_readb(IOC_IRQREQA); + status = ioc_readb(IOC_IRQREQA); barrier (); ioc_writeb (0, IOC_T0LATCH); barrier (); count2 = ioc_readb(IOC_T0CNTL) | (ioc_readb(IOC_T0CNTH) << 8); + offset = count2; if (count2 < count1) { /* - * This means that we haven't just had an interrupt - * while reading into status2. + * We have not had an interrupt between reading count1 + * and count2. */ - if (status2 & (1 << 5)) - offset = tick; - count1 = count2; + if (status & (1 << 5)) + offset -= LATCH; } else if (count2 > count1) { /* - * We have just had another interrupt while reading - * status2. + * We have just had another interrupt between reading + * count1 and count2. */ - offset += tick; - count1 = count2; + offset -= LATCH; } - count1 = LATCH - count1; - /* - * count1 = number of clock ticks since last interrupt - */ - offset += count1 * tick / LATCH; - return offset; + offset = (LATCH - offset) * (tick_nsec / 1000); + return (offset + LATCH/2) / LATCH; } void __init ioctime_init(void) diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c index 3d79e5c47367..07df8125c4cb 100644 --- a/arch/arm/kernel/time.c +++ b/arch/arm/kernel/time.c @@ -115,8 +115,8 @@ static inline void do_set_rtc(void) time_before(xtime.tv_sec, next_rtc_update)) return; - if (xtime.tv_usec < 50000 - (tick >> 1) && - xtime.tv_usec >= 50000 + (tick >> 1)) + if (xtime.tv_nsec < 500000000 - ((unsigned) tick_nsec >> 1) && + xtime.tv_nsec >= 500000000 + ((unsigned) tick_nsec >> 1)) return; if (set_rtc()) @@ -166,7 +166,7 @@ void do_gettimeofday(struct timeval *tv) usec += lost * USECS_PER_JIFFY; sec = xtime.tv_sec; - usec += xtime.tv_usec; + usec += xtime.tv_nsec / 1000; read_unlock_irqrestore(&xtime_lock, flags); /* usec may have gone up a lot: be safe */ @@ -196,7 +196,8 @@ void do_settimeofday(struct timeval *tv) tv->tv_sec--; } - xtime = *tv; + xtime.tv_sec = tv->tv_sec; + xtime.tv_nsec = tv->tv_usec * 1000; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; time_maxerror = NTP_PHASE_LIMIT; diff --git a/arch/arm/mach-iop310/iq80310-time.c b/arch/arm/mach-iop310/iq80310-time.c index a986a2346836..1151c6b3db28 100644 --- a/arch/arm/mach-iop310/iq80310-time.c +++ b/arch/arm/mach-iop310/iq80310-time.c @@ -85,7 +85,7 @@ static unsigned long iq80310_gettimeoffset (void) /* * Now convert them to usec. */ - usec = (unsigned long)(elapsed*tick)/LATCH; + usec = (unsigned long)(elapsed * (tick_nsec / 1000))/LATCH; return usec; } diff --git a/include/asm-arm/arch-ebsa285/time.h b/include/asm-arm/arch-ebsa285/time.h index 7b40c671daad..a500b9c08b8b 100644 --- a/include/asm-arm/arch-ebsa285/time.h +++ b/include/asm-arm/arch-ebsa285/time.h @@ -62,7 +62,7 @@ static unsigned long isa_gettimeoffset(void) count_p = count; - count = (((mSEC_10_from_14/6)-1) - count) * tick; + count = (((mSEC_10_from_14/6)-1) - count) * (tick_nsec / 1000); count = (count + (mSEC_10_from_14/6)/2) / (mSEC_10_from_14/6); return count; @@ -182,7 +182,7 @@ static unsigned long timer1_gettimeoffset (void) { unsigned long value = LATCH - *CSR_TIMER1_VALUE; - return (tick * value) / LATCH; + return ((tick_nsec / 1000) * value) / LATCH; } static void timer1_interrupt(int irq, void *dev_id, struct pt_regs *regs) diff --git a/include/asm-arm/arch-sa1100/time.h b/include/asm-arm/arch-sa1100/time.h index 5e459a32e8f8..c52eb9588c3c 100644 --- a/include/asm-arm/arch-sa1100/time.h +++ b/include/asm-arm/arch-sa1100/time.h @@ -58,7 +58,7 @@ static unsigned long sa1100_gettimeoffset (void) elapsed = LATCH - ticks_to_match; /* Now convert them to usec */ - usec = (unsigned long)(elapsed*tick)/LATCH; + usec = (unsigned long)(elapsed * (tick_nsec / 1000))/LATCH; return usec; } -- cgit v1.2.3 From d301001e7e5478cac6d42dc34b4e096ecb540793 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Oct 2002 13:05:47 +0100 Subject: [ARM] Update pcibios_enable_device, supply pci_mmap_page_range() Update pcibios_enable_device to only enable requested resources, mainly for IDE. Supply a pci_mmap_page_range() function to allow user space to mmap PCI regions. --- arch/arm/kernel/bios32.c | 32 +++++++++++++++++++++++++++++++- include/asm-arm/pci.h | 4 ++++ 2 files changed, 35 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index 201f14368eb2..8b359fa51a47 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -599,7 +599,7 @@ void pcibios_align_resource(void *data, struct resource *res, * pcibios_enable_device - Enable I/O and memory. * @dev: PCI device to be enabled */ -int pcibios_enable_device(struct pci_dev *dev) +int pcibios_enable_device(struct pci_dev *dev, int mask) { u16 cmd, old_cmd; int idx; @@ -608,6 +608,10 @@ int pcibios_enable_device(struct pci_dev *dev) pci_read_config_word(dev, PCI_COMMAND, &cmd); old_cmd = cmd; for (idx = 0; idx < 6; idx++) { + /* Only set up the requested stuff */ + if (!(mask & (1 << idx))) + continue; + r = dev->resource + idx; if (!r->start && r->end) { printk(KERN_ERR "PCI: Device %s not available because" @@ -626,3 +630,29 @@ int pcibios_enable_device(struct pci_dev *dev) } return 0; } + +int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, int write_combine) +{ + struct pci_sys_data *root = dev->sysdata; + unsigned long prot, phys; + + if (mmap_state == pci_mmap_io) { + return -EINVAL; + } else { + phys = root->mem_offset + (vma->vm_pgoff << PAGE_SHIFT); + } + + /* + * Mark this as IO + */ + vma->vm_flags |= VM_SHM | VM_LOCKED | VM_IO; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + if (remap_page_range(vma, vma->vm_start, phys, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + return -EAGAIN; + + return 0; +} diff --git a/include/asm-arm/pci.h b/include/asm-arm/pci.h index 3a7310558dd2..cd074e0daf51 100644 --- a/include/asm-arm/pci.h +++ b/include/asm-arm/pci.h @@ -248,6 +248,10 @@ void *pci_pool_alloc (struct pci_pool *pool, int flags, dma_addr_t *handle); void pci_pool_free (struct pci_pool *pool, void *vaddr, dma_addr_t addr); #endif +#define HAVE_PCI_MMAP +extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, int write_combine); + #endif /* __KERNEL__ */ #endif -- cgit v1.2.3 From d6494ca3b17d50b62b4092c39b6f35bfffced466 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Oct 2002 13:22:31 +0100 Subject: [ARM] Update RiscPC decompressor for PIC changes This cset fixes the RiscPC decompressor code for the PIC changes. We use a pointer to a structure rather than a structure to access params. With a PIC decompressor, the address of the structure gets PIC-ified which is not what we want. --- arch/arm/boot/compressed/Makefile | 2 +- arch/arm/boot/compressed/ll_char_wr.S | 216 +++++++++++++++------------------- include/asm-arm/arch-rpc/uncompress.h | 24 ++-- 3 files changed, 107 insertions(+), 135 deletions(-) (limited to 'include') diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile index 67278fa673f1..7078453e2c44 100644 --- a/arch/arm/boot/compressed/Makefile +++ b/arch/arm/boot/compressed/Makefile @@ -18,7 +18,7 @@ ZLDFLAGS = -p -X -T vmlinux.lds # ifeq ($(CONFIG_ARCH_ACORN),y) OBJS += ll_char_wr.o font.o -ZLDFLAGS += -defsym params=$(PARAMS_PHYS) +CFLAGS += -DPARAMS_PHYS=$(PARAMS_PHYS) endif ifeq ($(CONFIG_ARCH_NETWINDER),y) diff --git a/arch/arm/boot/compressed/ll_char_wr.S b/arch/arm/boot/compressed/ll_char_wr.S index c25ce8129aa6..d7bbd9da2fca 100644 --- a/arch/arm/boot/compressed/ll_char_wr.S +++ b/arch/arm/boot/compressed/ll_char_wr.S @@ -19,144 +19,116 @@ #include #include - .text + .text -#define BOLD 0x01 -#define ITALIC 0x02 -#define UNDERLINE 0x04 -#define FLASH 0x08 -#define INVERSE 0x10 - -LC0: .word bytes_per_char_h - .word video_size_row - .word acorndata_8x8 - .word con_charconvtable +LC0: .word LC0 + .word bytes_per_char_h + .word video_size_row + .word acorndata_8x8 + .word con_charconvtable +/* + * r0 = ptr + * r1 = char + * r2 = white + */ ENTRY(ll_write_char) - stmfd sp!, {r4 - r7, lr} + stmfd sp!, {r4 - r7, lr} @ @ Smashable regs: {r0 - r3}, [r4 - r7], (r8 - fp), [ip], (sp), [lr], (pc) @ - eor ip, r1, #UNDERLINE << 9 -/* - * calculate colours - */ - tst r1, #INVERSE << 9 - moveq r2, r1, lsr #16 - moveq r3, r1, lsr #24 - movne r2, r1, lsr #24 - movne r3, r1, lsr #16 - and r3, r3, #255 - and r2, r2, #255 -/* - * calculate offset into character table - */ - mov r1, r1, lsl #23 - mov r1, r1, lsr #20 -/* - * calculate offset required for each row [maybe I should make this an argument to this fn. - * Have to see what the register usage is like in the calling routines. - */ - adr r4, LC0 - ldmia r4, {r4, r5, r6, lr} - ldr r4, [r4] - ldr r5, [r5] -/* - * Go to resolution-dependent routine... - */ - cmp r4, #4 - blt Lrow1bpp - eor r2, r3, r2 @ Create eor mask to change colour from bg - orr r3, r3, r3, lsl #8 @ to fg. - orr r3, r3, r3, lsl #16 - add r0, r0, r5, lsl #3 @ Move to bottom of character - add r1, r1, #7 - ldrb r7, [r6, r1] - tst ip, #UNDERLINE << 9 - eoreq r7, r7, #255 - teq r4, #8 - beq Lrow8bpplp + /* + * calculate offset into character table + */ + mov r1, r1, lsl #3 + /* + * calculate offset required for each row. + */ + adr ip, LC0 + ldmia ip, {r3, r4, r5, r6, lr} + sub ip, ip, r3 + add r6, r6, ip + add lr, lr, ip + ldr r4, [r4, ip] + ldr r5, [r5, ip] + /* + * Go to resolution-dependent routine... + */ + cmp r4, #4 + blt Lrow1bpp + add r0, r0, r5, lsl #3 @ Move to bottom of character + orr r1, r1, #7 + ldrb r7, [r6, r1] + teq r4, #8 + beq Lrow8bpplp @ @ Smashable regs: {r0 - r3}, [r4], {r5 - r7}, (r8 - fp), [ip], (sp), {lr}, (pc) @ - orr r3, r3, r3, lsl #4 -Lrow4bpplp: ldr r7, [lr, r7, lsl #2] - mul r7, r2, r7 - tst r1, #7 @ avoid using r7 directly after - eor ip, r3, r7 - str ip, [r0, -r5]! - LOADREGS(eqfd, sp!, {r4 - r7, pc}) - sub r1, r1, #1 - ldrb r7, [r6, r1] - ldr r7, [lr, r7, lsl #2] - mul r7, r2, r7 - tst r1, #7 @ avoid using r7 directly after - eor ip, r3, r7 - str ip, [r0, -r5]! - subne r1, r1, #1 - ldrneb r7, [r6, r1] - bne Lrow4bpplp - LOADREGS(fd, sp!, {r4 - r7, pc}) +Lrow4bpplp: + ldr r7, [lr, r7, lsl #2] + mul r7, r2, r7 + sub r1, r1, #1 @ avoid using r7 directly after + str r7, [r0, -r5]! + ldrb r7, [r6, r1] + ldr r7, [lr, r7, lsl #2] + mul r7, r2, r7 + tst r1, #7 @ avoid using r7 directly after + str r7, [r0, -r5]! + subne r1, r1, #1 + ldrneb r7, [r6, r1] + bne Lrow4bpplp + LOADREGS(fd, sp!, {r4 - r7, pc}) @ @ Smashable regs: {r0 - r3}, [r4], {r5 - r7}, (r8 - fp), [ip], (sp), {lr}, (pc) @ -Lrow8bpplp: mov ip, r7, lsr #4 - ldr ip, [lr, ip, lsl #2] - mul r4, r2, ip - and ip, r7, #15 @ avoid r4 - ldr ip, [lr, ip, lsl #2] @ avoid r4 - mul ip, r2, ip @ avoid r4 - eor r4, r3, r4 @ avoid ip - tst r1, #7 @ avoid ip - sub r0, r0, r5 @ avoid ip - eor ip, r3, ip - stmia r0, {r4, ip} - LOADREGS(eqfd, sp!, {r4 - r7, pc}) - sub r1, r1, #1 - ldrb r7, [r6, r1] - mov ip, r7, lsr #4 - ldr ip, [lr, ip, lsl #2] - mul r4, r2, ip - and ip, r7, #15 @ avoid r4 - ldr ip, [lr, ip, lsl #2] @ avoid r4 - mul ip, r2, ip @ avoid r4 - eor r4, r3, r4 @ avoid ip - tst r1, #7 @ avoid ip - sub r0, r0, r5 @ avoid ip - eor ip, r3, ip - stmia r0, {r4, ip} - subne r1, r1, #1 - ldrneb r7, [r6, r1] - bne Lrow8bpplp - LOADREGS(fd, sp!, {r4 - r7, pc}) +Lrow8bpplp: + mov ip, r7, lsr #4 + ldr ip, [lr, ip, lsl #2] + mul r4, r2, ip + and ip, r7, #15 @ avoid r4 + ldr ip, [lr, ip, lsl #2] @ avoid r4 + mul ip, r2, ip @ avoid r4 + sub r1, r1, #1 @ avoid ip + sub r0, r0, r5 @ avoid ip + stmia r0, {r4, ip} + ldrb r7, [r6, r1] + mov ip, r7, lsr #4 + ldr ip, [lr, ip, lsl #2] + mul r4, r2, ip + and ip, r7, #15 @ avoid r4 + ldr ip, [lr, ip, lsl #2] @ avoid r4 + mul ip, r2, ip @ avoid r4 + tst r1, #7 @ avoid ip + sub r0, r0, r5 @ avoid ip + stmia r0, {r4, ip} + subne r1, r1, #1 + ldrneb r7, [r6, r1] + bne Lrow8bpplp + LOADREGS(fd, sp!, {r4 - r7, pc}) @ @ Smashable regs: {r0 - r3}, [r4], {r5, r6}, [r7], (r8 - fp), [ip], (sp), [lr], (pc) @ -Lrow1bpp: add r6, r6, r1 - ldmia r6, {r4, r7} - tst ip, #INVERSE << 9 - mvnne r4, r4 - mvnne r7, r7 - strb r4, [r0], r5 - mov r4, r4, lsr #8 - strb r4, [r0], r5 - mov r4, r4, lsr #8 - strb r4, [r0], r5 - mov r4, r4, lsr #8 - strb r4, [r0], r5 - strb r7, [r0], r5 - mov r7, r7, lsr #8 - strb r7, [r0], r5 - mov r7, r7, lsr #8 - strb r7, [r0], r5 - mov r7, r7, lsr #8 - tst ip, #UNDERLINE << 9 - mvneq r7, r7 - strb r7, [r0], r5 - LOADREGS(fd, sp!, {r4 - r7, pc}) +Lrow1bpp: + add r6, r6, r1 + ldmia r6, {r4, r7} + strb r4, [r0], r5 + mov r4, r4, lsr #8 + strb r4, [r0], r5 + mov r4, r4, lsr #8 + strb r4, [r0], r5 + mov r4, r4, lsr #8 + strb r4, [r0], r5 + strb r7, [r0], r5 + mov r7, r7, lsr #8 + strb r7, [r0], r5 + mov r7, r7, lsr #8 + strb r7, [r0], r5 + mov r7, r7, lsr #8 + strb r7, [r0], r5 + LOADREGS(fd, sp!, {r4 - r7, pc}) - .bss + .bss ENTRY(con_charconvtable) - .space 1024 + .space 1024 diff --git a/include/asm-arm/arch-rpc/uncompress.h b/include/asm-arm/arch-rpc/uncompress.h index dde1bfc7af20..4a3036dad1f2 100644 --- a/include/asm-arm/arch-rpc/uncompress.h +++ b/include/asm-arm/arch-rpc/uncompress.h @@ -56,7 +56,7 @@ static const unsigned long palette_4[16] = { #define palette_setpixel(p) *(unsigned long *)(IO_START+0x00400000) = 0x10000000|((p) & 255) #define palette_write(v) *(unsigned long *)(IO_START+0x00400000) = 0x00000000|((v) & 0x00ffffff) -extern struct param_struct params; +static struct param_struct *params = (struct param_struct *)PARAMS_PHYS; #ifndef STANDALONE_DEBUG /* @@ -64,13 +64,13 @@ extern struct param_struct params; */ static void puts(const char *s) { - extern void ll_write_char(char *, unsigned long); + extern void ll_write_char(char *, char c, char white); int x,y; unsigned char c; char *ptr; - x = params.video_x; - y = params.video_y; + x = params->video_x; + y = params->video_y; while ( ( c = *(unsigned char *)s++ ) != '\0' ) { if ( c == '\n' ) { @@ -79,8 +79,8 @@ static void puts(const char *s) y--; } } else { - ptr = VIDMEM + ((y*video_num_columns*params.bytes_per_char_v+x)*bytes_per_char_h); - ll_write_char(ptr, c|(white<<16)); + ptr = VIDMEM + ((y*video_num_columns*params->bytes_per_char_v+x)*bytes_per_char_h); + ll_write_char(ptr, c, white); if ( ++x >= video_num_columns ) { x = 0; if ( ++y >= video_num_lines ) { @@ -90,8 +90,8 @@ static void puts(const char *s) } } - params.video_x = x; - params.video_y = y; + params->video_x = x; + params->video_y = y; } static void error(char *x); @@ -103,9 +103,9 @@ static void arch_decomp_setup(void) { int i; - video_num_lines = params.video_num_rows; - video_num_columns = params.video_num_cols; - bytes_per_char_h = params.bytes_per_char_h; + video_num_lines = params->video_num_rows; + video_num_columns = params->video_num_cols; + bytes_per_char_h = params->bytes_per_char_h; video_size_row = video_num_columns * bytes_per_char_h; if (bytes_per_char_h == 4) for (i = 0; i < 256; i++) @@ -140,7 +140,7 @@ static void arch_decomp_setup(void) white = 7; } - if (params.nr_pages * params.page_size < 4096*1024) error("<4M of mem\n"); + if (params->nr_pages * params->page_size < 4096*1024) error("<4M of mem\n"); } #endif -- cgit v1.2.3 From 9c7fd8c76249652f8cd291bbea7d725aab337f11 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Oct 2002 13:47:15 +0100 Subject: [ARM] Optimise ARM TLB handling Sanitise includes of asm/tlbflush.h, asm/cacheflush.h, asm/proc-fns.h Implement ARM-specific TLB "shootdown" code. It turns out that it is overall more efficient to unconditionally invalidate the whole TLB rather than entry by entry when removing areas. --- arch/arm/kernel/arch.c | 1 + arch/arm/mm/fault-armv.c | 2 + arch/arm/mm/init.c | 6 + arch/arm/mm/minicache.c | 2 + arch/arm/mm/mm-armv.c | 2 + arch/arm/mm/proc-syms.c | 14 ++- arch/arm/mm/tlb-v3.S | 46 +------ arch/arm/mm/tlb-v4.S | 60 +-------- arch/arm/mm/tlb-v4wb.S | 99 +-------------- include/asm-arm/glue.h | 118 ------------------ include/asm-arm/page.h | 64 ++++++++++ include/asm-arm/pgalloc.h | 3 - include/asm-arm/pgtable.h | 1 + include/asm-arm/proc-armo/system.h | 2 - include/asm-arm/proc-armv/pgalloc.h | 1 + include/asm-arm/proc-armv/tlbflush.h | 230 ++++++++++++++++++++++++++++------- include/asm-arm/procinfo.h | 2 - include/asm-arm/tlb.h | 77 ++++++++++-- 18 files changed, 348 insertions(+), 382 deletions(-) (limited to 'include') diff --git a/arch/arm/kernel/arch.c b/arch/arm/kernel/arch.c index e1e97a8b7bce..ba96bea03746 100644 --- a/arch/arm/kernel/arch.c +++ b/arch/arm/kernel/arch.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include diff --git a/arch/arm/mm/fault-armv.c b/arch/arm/mm/fault-armv.c index fb05656bdb6a..f8ad035fdf01 100644 --- a/arch/arm/mm/fault-armv.c +++ b/arch/arm/mm/fault-armv.c @@ -16,8 +16,10 @@ #include #include +#include #include #include +#include #include "fault.h" diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 45b70123774c..ff3347e2669f 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -519,6 +519,10 @@ void __init paging_init(struct meminfo *mi, struct machine_desc *mdesc) bdata->node_boot_start >> PAGE_SHIFT, zhole_size); } +#ifndef CONFIG_DISCONTIGMEM + mem_map = contig_page_data.node_mem_map; +#endif + /* * finish off the bad pages once * the mem_map is initialised @@ -559,7 +563,9 @@ void __init mem_init(void) initpages = &__init_end - &__init_begin; high_memory = (void *)__va(meminfo.end); +#ifndef CONFIG_DISCONTIGMEM max_mapnr = virt_to_page(high_memory) - mem_map; +#endif /* * We may have non-contiguous memory. diff --git a/arch/arm/mm/minicache.c b/arch/arm/mm/minicache.c index 96ae5be2b642..0ef53e8e4ec0 100644 --- a/arch/arm/mm/minicache.c +++ b/arch/arm/mm/minicache.c @@ -15,9 +15,11 @@ */ #include #include + #include #include #include +#include /* * 0xffff8000 to 0xffffffff is reserved for any ARM architecture diff --git a/arch/arm/mm/mm-armv.c b/arch/arm/mm/mm-armv.c index 259f7541a875..d834681634e3 100644 --- a/arch/arm/mm/mm-armv.c +++ b/arch/arm/mm/mm-armv.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -20,6 +21,7 @@ #include #include #include +#include #include diff --git a/arch/arm/mm/proc-syms.c b/arch/arm/mm/proc-syms.c index c21c843ecd21..fe84d27a9681 100644 --- a/arch/arm/mm/proc-syms.c +++ b/arch/arm/mm/proc-syms.c @@ -10,8 +10,10 @@ #include #include +#include #include #include +#include #ifndef MULTI_CPU EXPORT_SYMBOL(cpu_cache_clean_invalidate_all); @@ -29,11 +31,11 @@ EXPORT_SYMBOL(cpu_set_pte); EXPORT_SYMBOL(processor); #endif -#ifndef MULTI_TLB -EXPORT_SYMBOL_NOVERS(__cpu_flush_kern_tlb_all); -EXPORT_SYMBOL_NOVERS(__cpu_flush_user_tlb_mm); -EXPORT_SYMBOL_NOVERS(__cpu_flush_user_tlb_range); -EXPORT_SYMBOL_NOVERS(__cpu_flush_user_tlb_page); -#else +/* + * No module should need to touch the TLB (and currently + * no modules do. We export this for "loadkernel" support + * (booting a new kernel from within a running kernel.) + */ +#ifdef MULTI_TLB EXPORT_SYMBOL(cpu_tlb); #endif diff --git a/arch/arm/mm/tlb-v3.S b/arch/arm/mm/tlb-v3.S index 270108bb40c0..063d50ab88dd 100644 --- a/arch/arm/mm/tlb-v3.S +++ b/arch/arm/mm/tlb-v3.S @@ -13,31 +13,10 @@ */ #include #include +#include #include "proc-macros.S" .align 5 -/* - * v3_flush_user_tlb_mm(mm) - * - * Invalidate all TLB entries in a particular address space - * - * - mm - mm_struct describing address space - */ -ENTRY(v3_flush_user_tlb_mm) - act_mm r1 @ get current->active_mm - teq r0, r1 @ == mm ? - movne pc, lr @ no, we dont do anything - -/* - * v3_flush_kern_tlb_all() - * - * Invalidate the entire TLB - */ -ENTRY(v3_flush_kern_tlb_all) - mov r0, #0 - mcr p15, 0, r0, c5, c0, 0 @ invalidate TLB - mov pc, lr - /* * v3_flush_user_tlb_range(start, end, mm) * @@ -62,32 +41,11 @@ ENTRY(v3_flush_kern_tlb_range) blo 1b mov pc, lr -/* - * v3_flush_user_tlb_page(vaddr,vma) - * - * Invalidate the specified page in the specified address space. - * - * - vaddr - virtual address (may not be aligned) - * - vma - vma_struct describing address range - */ - .align 5 -ENTRY(v3_flush_user_tlb_page) - vma_vm_mm r2, r1 @ get vma->vm_mm - act_mm r3 @ get current->active_mm - teq r2, r3 @ equal - movne pc, lr @ no -ENTRY(v3_flush_kern_tlb_page) - mcr p15, 0, r0, c6, c0, 0 @ invalidate TLB entry - mov pc, lr - .section ".text.init", #alloc, #execinstr .type v3_tlb_fns, #object ENTRY(v3_tlb_fns) - .long v3_flush_kern_tlb_all - .long v3_flush_user_tlb_mm .long v3_flush_user_tlb_range - .long v3_flush_user_tlb_page .long v3_flush_kern_tlb_range - .long v3_flush_kern_tlb_page + .long v3_tlb_flags .size v3_tlb_fns, . - v3_tlb_fns diff --git a/arch/arm/mm/tlb-v4.S b/arch/arm/mm/tlb-v4.S index 2dd035b2281f..2ab22d9f3454 100644 --- a/arch/arm/mm/tlb-v4.S +++ b/arch/arm/mm/tlb-v4.S @@ -14,31 +14,10 @@ */ #include #include +#include #include "proc-macros.S" .align 5 -/* - * v4_flush_user_tlb_mm(mm) - * - * Invalidate all TLB entries in a particular address space - * - * - mm - mm_struct describing address space - */ -ENTRY(v4_flush_user_tlb_mm) - act_mm r1 @ get current->active_mm - teq r0, r1 @ == mm ? - movne pc, lr @ no, we dont do anything - -/* - * v4_flush_kern_tlb_all() - * - * Invalidate the entire TLB - */ -ENTRY(v4_flush_kern_tlb_all) - mov r0, #0 - mcr p15, 0, r0, c8, c7, 0 @ invalidate I + D TLBs - mov pc, lr - /* * v4_flush_user_tlb_range(start, end, mm) * @@ -64,25 +43,6 @@ ENTRY(v4_flush_user_tlb_range) blo 1b mov pc, lr -/* - * v4_flush_user_tlb_page(vaddr,vma) - * - * Invalidate the specified page in the specified address space. - * - * - vaddr - virtual address (may not be aligned) - * - vma - vma_struct describing address range - */ - .align 5 -ENTRY(v4_flush_user_tlb_page) - vma_vm_mm r2, r1 @ get vma->vm_mm - act_mm r3 @ get current->active_mm - teq r2, r3 @ equal - movne pc, lr @ no - vma_vm_flags r2, r1 -.v4_flush_kern_tlb_page: - mcr p15, 0, r0, c8, c7, 1 @ invalidate TLB entry - mov pc, lr - /* * v4_flush_kerm_tlb_range(start, end) * @@ -95,27 +55,11 @@ ENTRY(v4_flush_user_tlb_page) .globl v4_flush_kern_tlb_range .equ v4_flush_kern_tlb_range, .v4_flush_kern_tlb_range - -/* - * v4_flush_kern_tlb_page(kaddr) - * - * Invalidate the TLB entry for the specified page. The address - * will be in the kernels virtual memory space. Current uses - * only require the D-TLB to be invalidated. - * - * - kaddr - Kernel virtual memory address - */ -.globl v4_flush_kern_tlb_page -.equ v4_flush_kern_tlb_page, .v4_flush_kern_tlb_page - .section ".text.init", #alloc, #execinstr .type v4_tlb_fns, #object ENTRY(v4_tlb_fns) - .long v4_flush_kern_tlb_all - .long v4_flush_user_tlb_mm .long v4_flush_user_tlb_range - .long v4_flush_user_tlb_page .long v4_flush_kern_tlb_range - .long v4_flush_kern_tlb_page + .long v4_tlb_flags .size v4_tlb_fns, . - v4_tlb_fns diff --git a/arch/arm/mm/tlb-v4wb.S b/arch/arm/mm/tlb-v4wb.S index 3cdca44fcfb2..e9f8fb16b635 100644 --- a/arch/arm/mm/tlb-v4wb.S +++ b/arch/arm/mm/tlb-v4wb.S @@ -14,34 +14,10 @@ */ #include #include +#include #include "proc-macros.S" .align 5 -/* - * v4wb_flush_user_tlb_mm(mm) - * - * Invalidate all TLB entries in a particular address space - * - * - mm - mm_struct describing address space - */ -ENTRY(v4wb_flush_user_tlb_mm) -ENTRY(v4wbi_flush_user_tlb_mm) - act_mm r1 @ get current->active_mm - teq r0, r1 @ == mm ? - movne pc, lr @ no, we dont do anything - -/* - * v4wb_flush_tlb_all() - * - * Invalidate the entire TLB - */ -ENTRY(v4wb_flush_kern_tlb_all) -ENTRY(v4wbi_flush_kern_tlb_all) - mov r0, #0 - mcr p15, 0, r0, c7, c10, 4 @ drain WB - mcr p15, 0, r0, c8, c7, 0 @ invalidate I + D TLBs - mov pc, lr - /* * v4wb_flush_user_tlb_range(start, end, mm) * @@ -69,28 +45,6 @@ ENTRY(v4wb_flush_user_tlb_range) blo 1b mov pc, lr -/* - * v4wb_flush_user_tlb_page(vaddr,vma) - * - * Invalidate the specified page in the specified address space. - * - * - vaddr - virtual address (may not be aligned) - * - vma - vma_struct describing address range - */ - .align 5 -ENTRY(v4wb_flush_user_tlb_page) - vma_vm_mm r2, r1 @ get vma->vm_mm - act_mm r3 @ get current->active_mm - teq r2, r3 @ equal - movne pc, lr @ no - vma_vm_flags r2, r1 - mov r3, #0 - mcr p15, 0, r3, c7, c10, 4 @ drain WB - tst r2, #VM_EXEC - mcrne p15, 0, r3, c8, c5, 0 @ invalidate I TLB - mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry - mov pc, lr - /* * v4_flush_kerm_tlb_range(start, end) * @@ -112,20 +66,6 @@ ENTRY(v4wb_flush_kern_tlb_range) blo 1b mov pc, lr -/* - * v4_flush_kern_tlb_page(kaddr) - * - * Invalidate the TLB entry for the specified page. The address - * will be in the kernels virtual memory space. Current uses - * only require the D-TLB to be invalidated. - * - * - kaddr - Kernel virtual memory address - */ -ENTRY(v4wb_flush_kern_tlb_page) - mcr p15, 0, r3, c8, c5, 0 @ invalidate I TLB - mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry - mov pc, lr - /* * These two are optimised for ARM920, ARM922, ARM926, Xscale */ @@ -158,28 +98,6 @@ ENTRY(v4wbi_flush_user_tlb_range) blo 1b mov pc, lr -/* - * v4wb_flush_tlb_page(vaddr,vma) - * - * Invalidate the specified page in the specified address space. - * - * - vaddr - virtual address (may not be aligned) - * - vma - vma_struct describing address range - */ - .align 5 -ENTRY(v4wbi_flush_user_tlb_page) - vma_vm_mm r2, r1 @ get vma->vm_mm - act_mm r3 @ get current->active_mm - teq r2, r3 @ equal - movne pc, lr @ no - vma_vm_flags r2, r1 - mov r3, #0 - mcr p15, 0, r3, c7, c10, 4 @ drain WB - tst r2, #VM_EXEC - mcrne p15, 0, r0, c8, c5, 1 @ invalidate I TLB entry - mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry - mov pc, lr - ENTRY(v4wbi_flush_kern_tlb_range) mov r3, #0 mcr p15, 0, r3, c7, c10, 4 @ drain WB @@ -192,29 +110,18 @@ ENTRY(v4wbi_flush_kern_tlb_range) blo 1b mov pc, lr -ENTRY(v4wbi_flush_kern_tlb_page) - mcr p15, 0, r0, c8, c5, 1 @ invalidate I TLB entry - mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry - mov pc, lr - .section ".text.init", #alloc, #execinstr .type v4wb_tlb_fns, #object ENTRY(v4wb_tlb_fns) - .long v4wb_flush_kern_tlb_all - .long v4wb_flush_user_tlb_mm .long v4wb_flush_user_tlb_range - .long v4wb_flush_user_tlb_page .long v4wb_flush_kern_tlb_range - .long v4wb_flush_kern_tlb_page + .long v4wb_tlb_flags .size v4wb_tlb_fns, . - v4wb_tlb_fns .type v4wbi_tlb_fns, #object ENTRY(v4wbi_tlb_fns) - .long v4wbi_flush_kern_tlb_all - .long v4wbi_flush_user_tlb_mm .long v4wbi_flush_user_tlb_range - .long v4wbi_flush_user_tlb_page .long v4wbi_flush_kern_tlb_range - .long v4wbi_flush_kern_tlb_page + .long v4wbi_tlb_flags .size v4wbi_tlb_fns, . - v4wbi_tlb_fns diff --git a/include/asm-arm/glue.h b/include/asm-arm/glue.h index 84b5d77c7562..a29d70176c75 100644 --- a/include/asm-arm/glue.h +++ b/include/asm-arm/glue.h @@ -26,59 +26,6 @@ -/* - * MMU TLB Model - * ============= - * - * We have the following to choose from: - * v3 - ARMv3 - * v4 - ARMv4 without write buffer - * v4wb - ARMv4 with write buffer without I TLB flush entry instruction - * v4wbi - ARMv4 with write buffer with I TLB flush entry instruction - */ -#undef _TLB -#undef MULTI_TLB - -#if defined(CONFIG_CPU_ARM610) || defined(CONFIG_CPU_ARM710) -# ifdef _TLB -# define MULTI_TLB 1 -# else -# define _TLB v3 -# endif -#endif - -#if defined(CONFIG_CPU_ARM720T) -# ifdef _TLB -# define MULTI_TLB 1 -# else -# define _TLB v4 -# endif -#endif - -#if defined(CONFIG_CPU_ARM920T) || defined(CONFIG_CPU_ARM922T) || \ - defined(CONFIG_CPU_ARM926T) || defined(CONFIG_CPU_ARM1020) || \ - defined(CONFIG_CPU_XSCALE) -# ifdef _TLB -# define MULTI_TLB 1 -# else -# define _TLB v4wbi -# endif -#endif - -#if defined(CONFIG_CPU_SA110) || defined(CONFIG_CPU_SA1100) -# ifdef _TLB -# define MULTI_TLB 1 -# else -# define _TLB v4wb -# endif -#endif - -#ifndef _TLB -#error Unknown TLB model -#endif - - - /* * Data Abort Model * ================ @@ -156,69 +103,4 @@ #error Unknown data abort handler type #endif - -/* - * User Space Model - * ================ - * - * This section selects the correct set of functions for dealing with - * page-based copying and clearing for user space for the particular - * processor(s) we're building for. - * - * We have the following to choose from: - * v3 - ARMv3 - * v4wt - ARMv4 with writethrough cache, without minicache - * v4wb - ARMv4 with writeback cache, without minicache - * v4_mc - ARMv4 with minicache - * xscale - Xscale - */ -#undef _USER -#undef MULTI_USER - -#if defined(CONFIG_CPU_ARM610) || defined(CONFIG_CPU_ARM710) -# ifdef _USER -# define MULTI_USER 1 -# else -# define _USER v3 -# endif -#endif - -#if defined(CONFIG_CPU_ARM720T) -# ifdef _USER -# define MULTI_USER 1 -# else -# define _USER v4wt -# endif -#endif - -#if defined(CONFIG_CPU_ARM920T) || defined(CONFIG_CPU_ARM922T) || \ - defined(CONFIG_CPU_ARM926T) || defined(CONFIG_CPU_SA110) || \ - defined(CONFIG_CPU_ARM1020) -# ifdef _USER -# define MULTI_USER 1 -# else -# define _USER v4wb -# endif -#endif - -#if defined(CONFIG_CPU_SA1100) -# ifdef _USER -# define MULTI_USER 1 -# else -# define _USER v4_mc -# endif -#endif - -#if defined(CONFIG_CPU_XSCALE) -# ifdef _USER -# define MULTI_USER 1 -# else -# define _USER xscale_mc -# endif -#endif - -#ifndef _USER -#error Unknown user operations model -#endif - #endif diff --git a/include/asm-arm/page.h b/include/asm-arm/page.h index 84b1f5970586..0b1a26ed991d 100644 --- a/include/asm-arm/page.h +++ b/include/asm-arm/page.h @@ -8,6 +8,70 @@ #include +/* + * User Space Model + * ================ + * + * This section selects the correct set of functions for dealing with + * page-based copying and clearing for user space for the particular + * processor(s) we're building for. + * + * We have the following to choose from: + * v3 - ARMv3 + * v4wt - ARMv4 with writethrough cache, without minicache + * v4wb - ARMv4 with writeback cache, without minicache + * v4_mc - ARMv4 with minicache + * xscale - Xscale + */ +#undef _USER +#undef MULTI_USER + +#if defined(CONFIG_CPU_ARM610) || defined(CONFIG_CPU_ARM710) +# ifdef _USER +# define MULTI_USER 1 +# else +# define _USER v3 +# endif +#endif + +#if defined(CONFIG_CPU_ARM720T) +# ifdef _USER +# define MULTI_USER 1 +# else +# define _USER v4wt +# endif +#endif + +#if defined(CONFIG_CPU_ARM920T) || defined(CONFIG_CPU_ARM922T) || \ + defined(CONFIG_CPU_ARM926T) || defined(CONFIG_CPU_SA110) || \ + defined(CONFIG_CPU_ARM1020) +# ifdef _USER +# define MULTI_USER 1 +# else +# define _USER v4wb +# endif +#endif + +#if defined(CONFIG_CPU_SA1100) +# ifdef _USER +# define MULTI_USER 1 +# else +# define _USER v4_mc +# endif +#endif + +#if defined(CONFIG_CPU_XSCALE) +# ifdef _USER +# define MULTI_USER 1 +# else +# define _USER xscale_mc +# endif +#endif + +#ifndef _USER +#error Unknown user operations model +#endif + struct cpu_user_fns { void (*cpu_clear_user_page)(void *p, unsigned long user); void (*cpu_copy_user_page)(void *to, const void *from, diff --git a/include/asm-arm/pgalloc.h b/include/asm-arm/pgalloc.h index f0e2c9f5393d..81a7eccf2991 100644 --- a/include/asm-arm/pgalloc.h +++ b/include/asm-arm/pgalloc.h @@ -11,9 +11,6 @@ #define _ASMARM_PGALLOC_H #include -#include -#include - #include /* diff --git a/include/asm-arm/pgtable.h b/include/asm-arm/pgtable.h index ca5bef29d81b..da98e2ee55b8 100644 --- a/include/asm-arm/pgtable.h +++ b/include/asm-arm/pgtable.h @@ -12,6 +12,7 @@ #include #include +#include #include /* diff --git a/include/asm-arm/proc-armo/system.h b/include/asm-arm/proc-armo/system.h index 23f2b96cb2d0..9e46e58f56e4 100644 --- a/include/asm-arm/proc-armo/system.h +++ b/include/asm-arm/proc-armo/system.h @@ -10,8 +10,6 @@ #ifndef __ASM_PROC_SYSTEM_H #define __ASM_PROC_SYSTEM_H -#include - #define vectors_base() (0) static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size) diff --git a/include/asm-arm/proc-armv/pgalloc.h b/include/asm-arm/proc-armv/pgalloc.h index 99546c6fc1c8..53e760417601 100644 --- a/include/asm-arm/proc-armv/pgalloc.h +++ b/include/asm-arm/proc-armv/pgalloc.h @@ -5,6 +5,7 @@ * * Page table allocation/freeing primitives for 32-bit ARM processors. */ +#include #include "pgtable.h" /* diff --git a/include/asm-arm/proc-armv/tlbflush.h b/include/asm-arm/proc-armv/tlbflush.h index d465e954ae13..fd758b2e90f2 100644 --- a/include/asm-arm/proc-armv/tlbflush.h +++ b/include/asm-arm/proc-armv/tlbflush.h @@ -7,6 +7,113 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include +#include + +#define TLB_V3_PAGE (1 << 0) +#define TLB_V4_U_PAGE (1 << 1) +#define TLB_V4_D_PAGE (1 << 2) +#define TLB_V4_I_PAGE (1 << 3) + +#define TLB_V3_FULL (1 << 8) +#define TLB_V4_U_FULL (1 << 9) +#define TLB_V4_D_FULL (1 << 10) +#define TLB_V4_I_FULL (1 << 11) + +#define TLB_WB (1 << 31) + +/* + * MMU TLB Model + * ============= + * + * We have the following to choose from: + * v3 - ARMv3 + * v4 - ARMv4 without write buffer + * v4wb - ARMv4 with write buffer without I TLB flush entry instruction + * v4wbi - ARMv4 with write buffer with I TLB flush entry instruction + */ +#undef _TLB +#undef MULTI_TLB + +#define v3_tlb_flags (TLB_V3_FULL | TLB_V3_PAGE) + +#if defined(CONFIG_CPU_ARM610) || defined(CONFIG_CPU_ARM710) +# ifdef _TLB +# define MULTI_TLB 1 +# else +# define _TLB v3 +# endif +#endif + +#define v4_tlb_flags (TLB_V4_U_FULL | TLB_V4_U_PAGE) + +#if defined(CONFIG_CPU_ARM720T) +# ifdef _TLB +# define MULTI_TLB 1 +# else +# define _TLB v4 +# endif +#endif + +#define v4wbi_tlb_flags (TLB_WB | \ + TLB_V4_I_FULL | TLB_V4_D_FULL | \ + TLB_V4_I_PAGE | TLB_V4_D_PAGE) + +#if defined(CONFIG_CPU_ARM920T) || defined(CONFIG_CPU_ARM922T) || \ + defined(CONFIG_CPU_ARM926T) || defined(CONFIG_CPU_ARM1020) || \ + defined(CONFIG_CPU_XSCALE) +# ifdef _TLB +# define MULTI_TLB 1 +# else +# define _TLB v4wbi +# endif +#endif + +#define v4wb_tlb_flags (TLB_WB | \ + TLB_V4_I_FULL | TLB_V4_D_FULL | \ + TLB_V4_D_PAGE) + +#if defined(CONFIG_CPU_SA110) || defined(CONFIG_CPU_SA1100) +# ifdef _TLB +# define MULTI_TLB 1 +# else +# define _TLB v4wb +# endif +#endif + +#ifndef _TLB +#error Unknown TLB model +#endif + +#ifndef __ASSEMBLY__ + +struct cpu_tlb_fns { + void (*flush_user_range)(unsigned long, unsigned long, struct vm_area_struct *); + void (*flush_kern_range)(unsigned long, unsigned long); + unsigned long tlb_flags; +}; + +/* + * Select the calling method + */ +#ifdef MULTI_TLB + +extern struct cpu_tlb_fns cpu_tlb; + +#define __cpu_flush_user_tlb_range cpu_tlb.flush_user_range +#define __cpu_flush_kern_tlb_range cpu_tlb.flush_kern_range +#define __cpu_tlb_flags cpu_tlb.tlb_flags + +#else + +#define __cpu_flush_user_tlb_range __glue(_TLB,_flush_user_tlb_range) +#define __cpu_flush_kern_tlb_range __glue(_TLB,_flush_kern_tlb_range) +#define __cpu_tlb_flags __glue(_TLB,_tlb_flags) + +extern void __cpu_flush_user_tlb_range(unsigned long, unsigned long, struct vm_area_struct *); +extern void __cpu_flush_kern_tlb_range(unsigned long, unsigned long); + +#endif /* * TLB Management @@ -51,56 +158,94 @@ * - kaddr - Kernel virtual memory address */ -struct cpu_tlb_fns { - void (*flush_kern_all)(void); - void (*flush_user_mm)(struct mm_struct *); - void (*flush_user_range)(unsigned long, unsigned long, struct vm_area_struct *); - void (*flush_user_page)(unsigned long, struct vm_area_struct *); - void (*flush_kern_range)(unsigned long, unsigned long); - void (*flush_kern_page)(unsigned long); -}; +#define tlb_flag(f) (__cpu_tlb_flags & (f)) -/* - * Convert calls to our calling convention. - */ -#define flush_tlb_all() __cpu_flush_kern_tlb_all() -#define flush_tlb_mm(mm) __cpu_flush_user_tlb_mm(mm) -#define flush_tlb_range(vma,start,end) __cpu_flush_user_tlb_range(start,end,vma) -#define flush_tlb_page(vma,vaddr) __cpu_flush_user_tlb_page(vaddr,vma) -#define flush_tlb_kernel_range(s,e) __cpu_flush_kern_tlb_range(s,e) -#define flush_tlb_kernel_page(kaddr) __cpu_flush_kern_tlb_page(kaddr) +static inline void flush_tlb_all(void) +{ + const int zero = 0; -/* - * Now select the calling method - */ -#ifdef MULTI_TLB + if (tlb_flag(TLB_WB)) + asm("mcr%? p15, 0, %0, c7, c10, 4" : : "r" (zero)); -extern struct cpu_tlb_fns cpu_tlb; + if (tlb_flag(TLB_V3_FULL)) + asm("mcr%? p15, 0, %0, c6, c0, 0" : : "r" (zero)); + if (tlb_flag(TLB_V4_U_FULL)) + asm("mcr%? p15, 0, %0, c8, c7, 0" : : "r" (zero)); + if (tlb_flag(TLB_V4_D_FULL)) + asm("mcr%? p15, 0, %0, c8, c6, 0" : : "r" (zero)); + if (tlb_flag(TLB_V4_I_FULL)) + asm("mcr%? p15, 0, %0, c8, c5, 0" : : "r" (zero)); +} -#define __cpu_flush_kern_tlb_all cpu_tlb.flush_kern_all -#define __cpu_flush_user_tlb_mm cpu_tlb.flush_user_mm -#define __cpu_flush_user_tlb_range cpu_tlb.flush_user_range -#define __cpu_flush_user_tlb_page cpu_tlb.flush_user_page -#define __cpu_flush_kern_tlb_range cpu_tlb.flush_kern_range -#define __cpu_flush_kern_tlb_page cpu_tlb.flush_kern_page +static inline void flush_tlb_mm(struct mm_struct *mm) +{ + const int zero = 0; -#else + if (tlb_flag(TLB_WB)) + asm("mcr%? p15, 0, %0, c7, c10, 4" : : "r" (zero)); -#define __cpu_flush_kern_tlb_all __glue(_TLB,_flush_kern_tlb_all) -#define __cpu_flush_user_tlb_mm __glue(_TLB,_flush_user_tlb_mm) -#define __cpu_flush_user_tlb_range __glue(_TLB,_flush_user_tlb_range) -#define __cpu_flush_user_tlb_page __glue(_TLB,_flush_user_tlb_page) -#define __cpu_flush_kern_tlb_range __glue(_TLB,_flush_kern_tlb_range) -#define __cpu_flush_kern_tlb_page __glue(_TLB,_flush_kern_tlb_page) + if (mm == current->active_mm) { + if (tlb_flag(TLB_V3_FULL)) + asm("mcr%? p15, 0, %0, c6, c0, 0" : : "r" (zero)); + if (tlb_flag(TLB_V4_U_FULL)) + asm("mcr%? p15, 0, %0, c8, c7, 0" : : "r" (zero)); + if (tlb_flag(TLB_V4_D_FULL)) + asm("mcr%? p15, 0, %0, c8, c6, 0" : : "r" (zero)); + if (tlb_flag(TLB_V4_I_FULL)) + asm("mcr%? p15, 0, %0, c8, c5, 0" : : "r" (zero)); + } +} -extern void __cpu_flush_kern_tlb_all(void); -extern void __cpu_flush_user_tlb_mm(struct mm_struct *); -extern void __cpu_flush_user_tlb_range(unsigned long, unsigned long, struct vm_area_struct *); -extern void __cpu_flush_user_tlb_page(unsigned long, struct vm_area_struct *); -extern void __cpu_flush_kern_tlb_range(unsigned long, unsigned long); -extern void __cpu_flush_kern_tlb_page(unsigned long); +static inline void +flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr) +{ + const int zero = 0; -#endif + uaddr &= PAGE_MASK; + + if (tlb_flag(TLB_WB)) + asm("mcr%? p15, 0, %0, c7, c10, 4" : : "r" (zero)); + + if (vma->vm_mm == current->active_mm) { + if (tlb_flag(TLB_V3_PAGE)) + asm("mcr%? p15, 0, %0, c6, c0, 0" : : "r" (uaddr)); + if (tlb_flag(TLB_V4_U_PAGE)) + asm("mcr%? p15, 0, %0, c8, c7, 1" : : "r" (uaddr)); + if (tlb_flag(TLB_V4_D_PAGE)) + asm("mcr%? p15, 0, %0, c8, c6, 1" : : "r" (uaddr)); + if (tlb_flag(TLB_V4_I_PAGE)) + asm("mcr%? p15, 0, %0, c8, c5, 1" : : "r" (uaddr)); + if (!tlb_flag(TLB_V4_I_PAGE) && tlb_flag(TLB_V4_I_FULL)) + asm("mcr%? p15, 0, %0, c8, c5, 0" : : "r" (zero)); + } +} + +static inline void flush_tlb_kernel_page(unsigned long kaddr) +{ + const int zero = 0; + + kaddr &= PAGE_MASK; + + if (tlb_flag(TLB_WB)) + asm("mcr%? p15, 0, %0, c7, c10, 4" : : "r" (zero)); + + if (tlb_flag(TLB_V3_PAGE)) + asm("mcr%? p15, 0, %0, c6, c0, 0" : : "r" (kaddr)); + if (tlb_flag(TLB_V4_U_PAGE)) + asm("mcr%? p15, 0, %0, c8, c7, 1" : : "r" (kaddr)); + if (tlb_flag(TLB_V4_D_PAGE)) + asm("mcr%? p15, 0, %0, c8, c6, 1" : : "r" (kaddr)); + if (tlb_flag(TLB_V4_I_PAGE)) + asm("mcr%? p15, 0, %0, c8, c5, 1" : : "r" (kaddr)); + if (!tlb_flag(TLB_V4_I_PAGE) && tlb_flag(TLB_V4_I_FULL)) + asm("mcr%? p15, 0, %0, c8, c5, 0" : : "r" (zero)); +} + +/* + * Convert calls to our calling convention. + */ +#define flush_tlb_range(vma,start,end) __cpu_flush_user_tlb_range(start,end,vma) +#define flush_tlb_kernel_range(s,e) __cpu_flush_kern_tlb_range(s,e) /* * if PG_dcache_dirty is set for the page, we need to ensure that any @@ -123,3 +268,4 @@ extern void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, pte #define memc_update_addr(mm,pte,log) do { } while (0) #define memc_clear(mm,physaddr) do { } while (0) +#endif diff --git a/include/asm-arm/procinfo.h b/include/asm-arm/procinfo.h index c143ffa712de..fced718b32be 100644 --- a/include/asm-arm/procinfo.h +++ b/include/asm-arm/procinfo.h @@ -12,8 +12,6 @@ #ifndef __ASSEMBLY__ -#include - struct cpu_tlb_fns; struct cpu_user_fns; struct processor; diff --git a/include/asm-arm/tlb.h b/include/asm-arm/tlb.h index 318357c01183..3aa4ee0feb67 100644 --- a/include/asm-arm/tlb.h +++ b/include/asm-arm/tlb.h @@ -1,21 +1,76 @@ +/* + * linux/include/asm-arm/tlb.h + * + * Copyright (C) 2002 Russell King + * + * 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. + * + * Experimentation shows that on a StrongARM, it appears to be faster + * to use the "invalidate whole tlb" rather than "invalidate single + * tlb" for this. + * + * This appears true for both the process fork+exit case, as well as + * the munmap-large-area case. + */ #ifndef __ASMARM_TLB_H #define __ASMARM_TLB_H -#include #include -#define tlb_flush(tlb) \ - flush_tlb_mm((tlb)->mm) -#define tlb_start_vma(tlb,vma) \ - flush_cache_range(vma, vma->vm_start, vma->vm_end) -#define tlb_end_vma(tlb,vma) \ - flush_tlb_range(vma, vma->vm_start, vma->vm_end) +/* + * TLB handling. This allows us to remove pages from the page + * tables, and efficiently handle the TLB issues. + */ +typedef struct free_pte_ctx { + struct mm_struct *mm; + unsigned int freed; -#define __tlb_remove_tlb_entry(tlb, ptep, address) do { } while (0) + unsigned int flushes; + unsigned int avoided_flushes; +} mmu_gather_t; -#include +extern mmu_gather_t mmu_gathers[NR_CPUS]; -#define __pmd_free_tlb(tlb, pmd) pmd_free(pmd) -#define __pte_free_tlb(tlb, pte) pte_free(pte) +static inline mmu_gather_t *tlb_gather_mmu(struct mm_struct *mm, unsigned int full_mm_flush) +{ + int cpu = smp_processor_id(); + mmu_gather_t *tlb = &mmu_gathers[cpu]; + + tlb->mm = mm; + tlb->freed = 0; + + return tlb; +} + +static inline void tlb_finish_mmu(mmu_gather_t *tlb, unsigned long start, unsigned long end) +{ + struct mm_struct *mm = tlb->mm; + unsigned long freed = tlb->freed; + int rss = mm->rss; + + if (rss < freed) + freed = rss; + mm->rss = rss - freed; + + if (freed) { + flush_tlb_mm(mm); + tlb->flushes++; + } else { + tlb->avoided_flushes++; + } + + /* keep the page table cache within bounds */ + check_pgt_cache(); +} + +#define tlb_remove_tlb_entry(tlb,ptep,address) do { } while (0) +#define tlb_start_vma(tlb,vma) do { } while (0) +#define tlb_end_vma(tlb,vma) do { } while (0) + +#define tlb_remove_page(tlb,page) free_page_and_swap_cache(page) +#define pte_free_tlb(tlb,ptep) pte_free(ptep) +#define pmd_free_tlb(tlb,pmdp) pmd_free(pmdp) #endif -- cgit v1.2.3 From 69bc3279ef04f96ae524305c4491bbf5a4260bf2 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Oct 2002 14:00:14 +0100 Subject: [ARM] Update neponset/sa1111 for Linux device model updates. This updates these neponset and sa1111 support to use the new system device infrastructure in the Linux device model. --- arch/arm/mach-sa1100/neponset.c | 26 ++- arch/arm/mach-sa1100/sa1111.c | 391 +++++++++++++++++++++---------------- include/asm-arm/arch-sa1100/irqs.h | 2 +- include/asm-arm/hardware/sa1111.h | 14 -- 4 files changed, 241 insertions(+), 192 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-sa1100/neponset.c b/arch/arm/mach-sa1100/neponset.c index 9d3a939de4ae..71c60c3c1467 100644 --- a/arch/arm/mach-sa1100/neponset.c +++ b/arch/arm/mach-sa1100/neponset.c @@ -193,20 +193,30 @@ static int neponset_resume(struct device *dev, u32 level) } static struct device_driver neponset_device_driver = { - .suspend = neponset_suspend, - .resume = neponset_resume, + .name = "NEPONSET", + .bus = &system_bus_type, + .suspend = neponset_suspend, + .resume = neponset_resume, }; -static struct device neponset_device = { - .name = "Neponset", - .bus_id = "neponset", - .driver = &neponset_device_driver, +static struct sys_device neponset_device = { + .name = "NEPONSET", + .id = 0, + .root = NULL, + .dev = { + .name = "Neponset", + .bus_id = "neponset", + .bus = &system_bus_type, + .driver = &neponset_device_driver, + }, }; static int __init neponset_init(void) { int ret; + driver_register(&neponset_device_driver); + /* * The Neponset is only present on the Assabet machine type. */ @@ -231,7 +241,7 @@ static int __init neponset_init(void) return -ENODEV; } - ret = register_sys_device(&neponset_device); + ret = sys_device_register(&neponset_device); if (ret) return ret; @@ -256,7 +266,7 @@ static int __init neponset_init(void) return sa1111_init(0x40000000, IRQ_NEPONSET_SA1111); } -arch_initcall(neponset_init); +subsys_initcall(neponset_init); static struct map_desc neponset_io_desc[] __initdata = { /* virtual physical length type */ diff --git a/arch/arm/mach-sa1100/sa1111.c b/arch/arm/mach-sa1100/sa1111.c index 9ded8fe5f98e..54a40462b2dc 100644 --- a/arch/arm/mach-sa1100/sa1111.c +++ b/arch/arm/mach-sa1100/sa1111.c @@ -43,7 +43,7 @@ * anchor point for all the other drivers. */ struct sa1111 { - struct device dev; + struct device *dev; struct resource res; int irq; spinlock_t lock; @@ -64,7 +64,7 @@ static struct sa1111_dev usb_dev = { .devid = SA1111_DEVID_USB, .irq = { IRQ_USBPWR, - IRQ_NHCIM, + IRQ_HCIM, IRQ_HCIBUFFACC, IRQ_HCIRMTWKP, IRQ_NHCIMFCIR, @@ -148,7 +148,6 @@ static struct sa1111_dev *devs[] = { &ssp_dev, &kbd_dev, &mse_dev, - &int_dev, &pcmcia_dev, }; @@ -158,7 +157,6 @@ static unsigned int dev_offset[] = { 0x0800, SA1111_KBD, SA1111_MSE, - SA1111_INTC, 0x1800, }; @@ -372,7 +370,106 @@ static void __init sa1111_init_irq(struct sa1111_dev *sadev) set_irq_chained_handler(sadev->irq[0], sa1111_irq_handler); } -static struct device_driver sa1111_device_driver; +/* + * Bring the SA1111 out of reset. This requires a set procedure: + * 1. nRESET asserted (by hardware) + * 2. CLK turned on from SA1110 + * 3. nRESET deasserted + * 4. VCO turned on, PLL_BYPASS turned off + * 5. Wait lock time, then assert RCLKEn + * 7. PCR set to allow clocking of individual functions + * + * Until we've done this, the only registers we can access are: + * SBI_SKCR + * SBI_SMCR + * SBI_SKID + */ +static void sa1111_wake(struct sa1111 *sachip) +{ + unsigned long flags, r; + + spin_lock_irqsave(&sachip->lock, flags); + + /* + * First, set up the 3.6864MHz clock on GPIO 27 for the SA-1111: + * (SA-1110 Developer's Manual, section 9.1.2.1) + */ + GAFR |= GPIO_32_768kHz; + GPDR |= GPIO_32_768kHz; + TUCR = TUCR_3_6864MHz; + + /* + * Turn VCO on, and disable PLL Bypass. + */ + r = sa1111_readl(sachip->base + SA1111_SKCR); + r &= ~SKCR_VCO_OFF; + sa1111_writel(r, sachip->base + SA1111_SKCR); + r |= SKCR_PLL_BYPASS | SKCR_OE_EN; + sa1111_writel(r, sachip->base + SA1111_SKCR); + + /* + * Wait lock time. SA1111 manual _doesn't_ + * specify a figure for this! We choose 100us. + */ + udelay(100); + + /* + * Enable RCLK. We also ensure that RDYEN is set. + */ + r |= SKCR_RCLKEN | SKCR_RDYEN; + sa1111_writel(r, sachip->base + SA1111_SKCR); + + /* + * Wait 14 RCLK cycles for the chip to finish coming out + * of reset. (RCLK=24MHz). This is 590ns. + */ + udelay(1); + + /* + * Ensure all clocks are initially off. + */ + sa1111_writel(0, sachip->base + SA1111_SKPCR); + + spin_unlock_irqrestore(&sachip->lock, flags); +} + +/* + * Configure the SA1111 shared memory controller. + */ +void +sa1111_configure_smc(struct sa1111 *sachip, int sdram, unsigned int drac, + unsigned int cas_latency) +{ + unsigned int smcr = SMCR_DTIM | SMCR_MBGE | FInsrt(drac, SMCR_DRAC); + + if (cas_latency == 3) + smcr |= SMCR_CLAT; + + sa1111_writel(smcr, sachip->base + SA1111_SMCR); +} + +static void +sa1111_init_one_child(struct sa1111 *sachip, struct sa1111_dev *sadev, unsigned int offset) +{ + snprintf(sadev->dev.bus_id, sizeof(sadev->dev.bus_id), + "%4.4x", offset); + + sadev->dev.parent = sachip->dev; + sadev->dev.bus = &sa1111_bus_type; + sadev->res.start = sachip->res.start + offset; + sadev->res.end = sadev->res.start + 511; + sadev->res.name = sadev->dev.name; + sadev->res.flags = IORESOURCE_MEM; + sadev->mapbase = sachip->base + offset; + + if (request_resource(&sachip->res, &sadev->res)) { + printk("SA1111: failed to allocate resource for %s\n", + sadev->res.name); + return; + } + + device_register(&sadev->dev); +} /** * sa1111_probe - probe for a single SA1111 chip. @@ -387,11 +484,11 @@ static struct device_driver sa1111_device_driver; * %0 successful. */ static int __init -sa1111_probe(unsigned long phys_addr, int irq) +__sa1111_probe(struct device *me, unsigned long phys_addr, int irq) { struct sa1111 *sachip; unsigned long id; - unsigned int has_devs; + unsigned int has_devs, val; int i, ret = -ENODEV; sachip = kmalloc(sizeof(struct sa1111), GFP_KERNEL); @@ -402,12 +499,10 @@ sa1111_probe(unsigned long phys_addr, int irq) spin_lock_init(&sachip->lock); - strncpy(sachip->dev.name, "Intel Corporation SA1111", sizeof(sachip->dev.name)); - snprintf(sachip->dev.bus_id, sizeof(sachip->dev.bus_id), "%8.8lx", phys_addr); - sachip->dev.driver = &sa1111_device_driver; - sachip->dev.driver_data = sachip; + sachip->dev = me; + dev_set_drvdata(sachip->dev, sachip); - sachip->res.name = sachip->dev.name; + sachip->res.name = me->name; sachip->res.start = phys_addr; sachip->res.end = phys_addr + 0x2000; sachip->irq = irq; @@ -417,6 +512,10 @@ sa1111_probe(unsigned long phys_addr, int irq) goto out; } + /* + * Map the whole region. This also maps the + * registers for our children. + */ sachip->base = ioremap(phys_addr, PAGE_SIZE * 2); if (!sachip->base) { ret = -ENOMEM; @@ -433,17 +532,47 @@ sa1111_probe(unsigned long phys_addr, int irq) goto unmap; } - /* - * We found the chip. - */ - ret = register_sys_device(&sachip->dev); - if (ret) - printk("sa1111 device_register failed: %d\n", ret); - printk(KERN_INFO "SA1111 Microprocessor Companion Chip: " "silicon revision %lx, metal revision %lx\n", (id & SKID_SIREV_MASK)>>4, (id & SKID_MTREV_MASK)); + /* + * We found it. Wake the chip up, and initialise. + */ + sa1111_wake(sachip); + + /* + * The SDRAM configuration of the SA1110 and the SA1111 must + * match. This is very important to ensure that SA1111 accesses + * don't corrupt the SDRAM. Note that this ungates the SA1111's + * MBGNT signal, so we must have called sa1110_mb_disable() + * beforehand. + */ + sa1111_configure_smc(sachip, 1, + FExtr(MDCNFG, MDCNFG_SA1110_DRAC0), + FExtr(MDCNFG, MDCNFG_SA1110_TDL0)); + + /* + * We only need to turn on DCLK whenever we want to use the + * DMA. It can otherwise be held firmly in the off position. + * (currently, we always enable it.) + */ + val = sa1111_readl(sachip->base + SA1111_SKPCR); + sa1111_writel(val | SKPCR_DCLKEN, sachip->base + SA1111_SKPCR); + + /* + * Enable the SA1110 memory bus request and grant signals. + */ + sa1110_mb_enable(); + + /* + * The interrupt controller must be initialised before any + * other device to ensure that the interrupts are available. + */ + int_dev.irq[0] = irq; + sa1111_init_one_child(sachip, &int_dev, SA1111_INTC); + sa1111_init_irq(&int_dev); + g_sa1111 = sachip; has_devs = ~0; @@ -453,29 +582,9 @@ sa1111_probe(unsigned long phys_addr, int irq) else has_devs &= ~(1 << 1); - for (i = 0; i < ARRAY_SIZE(devs); i++) { - if (!(has_devs & (1 << i))) - continue; - - snprintf(devs[i]->dev.bus_id, sizeof(devs[i]->dev.bus_id), - "%4.4x", dev_offset[i]); - - devs[i]->dev.parent = &sachip->dev; - devs[i]->dev.bus = &sa1111_bus_type; - devs[i]->res.start = sachip->res.start + dev_offset[i]; - devs[i]->res.end = devs[i]->res.start + 511; - devs[i]->res.name = devs[i]->dev.name; - devs[i]->res.flags = IORESOURCE_MEM; - devs[i]->mapbase = sachip->base + dev_offset[i]; - - if (request_resource(&sachip->res, &devs[i]->res)) { - printk("SA1111: failed to allocate resource for %s\n", - devs[i]->res.name); - continue; - } - - device_register(&devs[i]->dev); - } + for (i = 0; i < ARRAY_SIZE(devs); i++) + if (has_devs & (1 << i)) + sa1111_init_one_child(sachip, devs[i], dev_offset[i]); return 0; @@ -502,84 +611,6 @@ static void __sa1111_remove(struct sa1111 *sachip) kfree(sachip); } -/* - * Bring the SA1111 out of reset. This requires a set procedure: - * 1. nRESET asserted (by hardware) - * 2. CLK turned on from SA1110 - * 3. nRESET deasserted - * 4. VCO turned on, PLL_BYPASS turned off - * 5. Wait lock time, then assert RCLKEn - * 7. PCR set to allow clocking of individual functions - * - * Until we've done this, the only registers we can access are: - * SBI_SKCR - * SBI_SMCR - * SBI_SKID - */ -static void sa1111_wake(struct sa1111 *sachip) -{ - unsigned long flags, r; - - spin_lock_irqsave(&sachip->lock, flags); - - /* - * First, set up the 3.6864MHz clock on GPIO 27 for the SA-1111: - * (SA-1110 Developer's Manual, section 9.1.2.1) - */ - GAFR |= GPIO_32_768kHz; - GPDR |= GPIO_32_768kHz; - TUCR = TUCR_3_6864MHz; - - /* - * Turn VCO on, and disable PLL Bypass. - */ - r = sa1111_readl(sachip->base + SA1111_SKCR); - r &= ~SKCR_VCO_OFF; - sa1111_writel(r, sachip->base + SA1111_SKCR); - r |= SKCR_PLL_BYPASS | SKCR_OE_EN; - sa1111_writel(r, sachip->base + SA1111_SKCR); - - /* - * Wait lock time. SA1111 manual _doesn't_ - * specify a figure for this! We choose 100us. - */ - udelay(100); - - /* - * Enable RCLK. We also ensure that RDYEN is set. - */ - r |= SKCR_RCLKEN | SKCR_RDYEN; - sa1111_writel(r, sachip->base + SA1111_SKCR); - - /* - * Wait 14 RCLK cycles for the chip to finish coming out - * of reset. (RCLK=24MHz). This is 590ns. - */ - udelay(1); - - /* - * Ensure all clocks are initially off. - */ - sa1111_writel(0, sachip->base + SA1111_SKPCR); - - spin_unlock_irqrestore(&sachip->lock, flags); -} - -/* - * Configure the SA1111 shared memory controller. - */ -void -sa1111_configure_smc(struct sa1111 *sachip, int sdram, unsigned int drac, - unsigned int cas_latency) -{ - unsigned int smcr = SMCR_DTIM | SMCR_MBGE | FInsrt(drac, SMCR_DRAC); - - if (cas_latency == 3) - smcr |= SMCR_CLAT; - - sa1111_writel(smcr, sachip->base + SA1111_SMCR); -} - /* * According to the "Intel StrongARM SA-1111 Microprocessor Companion * Chip Specification Update" (June 2000), erratum #7, there is a @@ -648,52 +679,6 @@ int sa1111_check_dma_bug(dma_addr_t addr) return 0; } -int sa1111_init(unsigned long phys, unsigned int irq) -{ - unsigned int val; - int ret; - - ret = sa1111_probe(phys, irq); - if (ret < 0) - return ret; - - /* - * We found it. Wake the chip up. - */ - sa1111_wake(g_sa1111); - - /* - * The SDRAM configuration of the SA1110 and the SA1111 must - * match. This is very important to ensure that SA1111 accesses - * don't corrupt the SDRAM. Note that this ungates the SA1111's - * MBGNT signal, so we must have called sa1110_mb_disable() - * beforehand. - */ - sa1111_configure_smc(g_sa1111, 1, - FExtr(MDCNFG, MDCNFG_SA1110_DRAC0), - FExtr(MDCNFG, MDCNFG_SA1110_TDL0)); - - /* - * We only need to turn on DCLK whenever we want to use the - * DMA. It can otherwise be held firmly in the off position. - */ - val = sa1111_readl(g_sa1111->base + SA1111_SKPCR); - sa1111_writel(val | SKPCR_DCLKEN, g_sa1111->base + SA1111_SKPCR); - - /* - * Enable the SA1110 memory bus request and grant signals. - */ - sa1110_mb_enable(); - - /* - * Initialise SA1111 IRQs - */ - int_dev.irq[0] = irq; - sa1111_init_irq(&int_dev); - - return 0; -} - struct sa1111_save_data { unsigned int skcr; unsigned int skpcr; @@ -717,7 +702,7 @@ struct sa1111_save_data { static int sa1111_suspend(struct device *dev, u32 state, u32 level) { - struct sa1111 *sachip = dev->driver_data; + struct sa1111 *sachip = dev_get_drvdata(dev); unsigned long flags; char *base; @@ -789,7 +774,7 @@ static int sa1111_suspend(struct device *dev, u32 state, u32 level) */ static int sa1111_resume(struct device *dev, u32 level) { - struct sa1111 *sachip = dev->driver_data; + struct sa1111 *sachip = dev_get_drvdata(dev); struct sa1111_save_data *save; unsigned long flags, id; char *base; @@ -809,6 +794,7 @@ static int sa1111_resume(struct device *dev, u32 level) id = sa1111_readl(sachip->base + SA1111_SKID); if ((id & SKID_ID_MASK) != SKID_SA1111_ID) { __sa1111_remove(sachip); + dev_set_drvdata(dev, NULL); kfree(save); return 0; } @@ -840,18 +826,85 @@ static int sa1111_resume(struct device *dev, u32 level) return 0; } +static int sa1111_probe(struct device *dev) +{ + return -ENODEV; +} + +static int sa1111_remove(struct device *dev) +{ + struct sa1111 *sachip = dev_get_drvdata(dev); + + if (sachip) { + __sa1111_remove(sachip); + dev_set_drvdata(dev, NULL); + + kfree(dev->saved_state); + dev->saved_state = NULL; + } + + return 0; +} + +/* + * Not sure if this should be on the system bus or not yet. + * We really want some way to register a system device at + * the per-machine level, and then have this driver pick + * up the registered devices. + * + * We also need to handle the SDRAM configuration for + * PXA250/SA1110 machine classes. + */ static struct device_driver sa1111_device_driver = { + .name = "SA1111", + .bus = &system_bus_type, + .probe = sa1111_probe, + .remove = sa1111_remove, .suspend = sa1111_suspend, .resume = sa1111_resume, }; +/* + * Register the SA1111 driver with LDM. + */ +static int sa1111_driver_init(void) +{ + driver_register(&sa1111_device_driver); + return 0; +} + +arch_initcall(sa1111_driver_init); + +static struct sys_device sa1111_device = { + .name = "SA1111", + .id = 0, + .root = NULL, + .dev = { + .name = "Intel Corporation SA1111", + .driver = &sa1111_device_driver, + }, +}; + +int sa1111_init(unsigned long phys, unsigned int irq) +{ + int ret; + + snprintf(sa1111_device.dev.bus_id, sizeof(sa1111_device.dev.bus_id), "%8.8lx", phys); + + ret = sys_device_register(&sa1111_device); + if (ret) + printk("sa1111 device_register failed: %d\n", ret); + + return __sa1111_probe(&sa1111_device.dev, phys, irq); +} + /* * Get the parent device driver (us) structure * from a child function device */ static inline struct sa1111 *sa1111_chip_driver(struct sa1111_dev *sadev) { - return (struct sa1111 *)sadev->dev.parent->driver_data; + return (struct sa1111 *)dev_get_drvdata(sadev->dev.parent); } /* diff --git a/include/asm-arm/arch-sa1100/irqs.h b/include/asm-arm/arch-sa1100/irqs.h index 66e4f596c739..4d906d33a78e 100644 --- a/include/asm-arm/arch-sa1100/irqs.h +++ b/include/asm-arm/arch-sa1100/irqs.h @@ -109,7 +109,7 @@ #define AUDRDD (IRQ_BOARD_END + 41) #define AUDSTO (IRQ_BOARD_END + 42) #define IRQ_USBPWR (IRQ_BOARD_END + 43) -#define IRQ_NHCIM (IRQ_BOARD_END + 44) +#define IRQ_HCIM (IRQ_BOARD_END + 44) #define IRQ_HCIBUFFACC (IRQ_BOARD_END + 45) #define IRQ_HCIRMTWKP (IRQ_BOARD_END + 46) #define IRQ_NHCIMFCIR (IRQ_BOARD_END + 47) diff --git a/include/asm-arm/hardware/sa1111.h b/include/asm-arm/hardware/sa1111.h index 311e4832eb2f..30e1f8141633 100644 --- a/include/asm-arm/hardware/sa1111.h +++ b/include/asm-arm/hardware/sa1111.h @@ -64,10 +64,6 @@ #define SA1111_SMCR 0x0004 #define SA1111_SKID 0x0008 -#define SBI_SKCR __CCREG(SA1111_SKCR) -#define SBI_SMCR __CCREG(SA1111_SMCR) -#define SBI_SKID __CCREG(SA1111_SKID) - #define SKCR_PLL_BYPASS (1<<0) #define SKCR_RCLKEN (1<<1) #define SKCR_SLEEP (1<<2) @@ -131,16 +127,6 @@ #define SA1111_SKPEN1 0x021c #define SA1111_SKPWM1 0x0220 -#define SKPCR __CCREG(SA1111_SKPCR) -#define SKCDR __CCREG(SA1111_SKCDR) -#define SKAUD __CCREG(SA1111_SKAUD) -#define SKPMC __CCREG(SA1111_SKPMC) -#define SKPTC __CCREG(SA1111_SKPTC) -#define SKPEN0 __CCREG(SA1111_SKPEN0) -#define SKPWM0 __CCREG(SA1111_SKPWM0) -#define SKPEN1 __CCREG(SA1111_SKPEN1) -#define SKPWM1 __CCREG(SA1111_SKPWM1) - #define SKPCR_UCLKEN (1<<0) #define SKPCR_ACCLKEN (1<<1) #define SKPCR_I2SCLKEN (1<<2) -- cgit v1.2.3 From 17b77695c00c2979fe75b994b8149f1636458eeb Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Oct 2002 14:04:56 +0100 Subject: [ARM] Other updates for changes in 2.5.42 This adds ARM support for in_atomic() and asm/numnodes.h --- include/asm-arm/hardirq.h | 12 ++++++++++-- include/asm-arm/numnodes.h | 17 +++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 include/asm-arm/numnodes.h (limited to 'include') diff --git a/include/asm-arm/hardirq.h b/include/asm-arm/hardirq.h index 0e6a5492e1e4..3f164e01f7a1 100644 --- a/include/asm-arm/hardirq.h +++ b/include/asm-arm/hardirq.h @@ -69,10 +69,18 @@ typedef struct { #define irq_enter() (preempt_count() += HARDIRQ_OFFSET) +#ifdef CONFIG_PREEMPT +# define in_atomic() ((preempt_count() & ~PREEMPT_ACTIVE) != kernel_locked()) +# define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1) +#else +# define in_atomic() (preempt_count() != 0) +# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET +#endif + #ifndef CONFIG_SMP #define irq_exit() \ do { \ - preempt_count() -= HARDIRQ_OFFSET; \ + preempt_count() -= IRQ_EXIT_OFFSET; \ if (!in_interrupt() && softirq_pending(smp_processor_id())) \ __asm__("bl%? __do_softirq": : : "lr");/* out of line */\ preempt_enable_no_resched(); \ @@ -80,7 +88,7 @@ typedef struct { #define synchronize_irq(irq) barrier() #else -#error SMP not supported +extern void synchronize_irq(unsigned int irq); #endif #endif /* __ASM_HARDIRQ_H */ diff --git a/include/asm-arm/numnodes.h b/include/asm-arm/numnodes.h new file mode 100644 index 000000000000..4c12812f73dc --- /dev/null +++ b/include/asm-arm/numnodes.h @@ -0,0 +1,17 @@ +/* + * linux/include/asm-arm/numnodes.h + * + * Copyright (C) 2002 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ASM_ARM_NUMNODES_H +#define __ASM_ARM_NUMNODES_H + +#include + +#define MAX_NUMNODES NR_NODES + +#endif -- cgit v1.2.3 From 51e9ac4bafc590a16a2bd5c728b75db434236b52 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Oct 2002 14:20:47 +0100 Subject: [ARM] IDE updates - ide_register_hw takes two arguments, not one. - ide_fix_driveid is no longer used. --- include/asm-arm/arch-rpc/ide.h | 2 +- include/asm-arm/ide.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/include/asm-arm/arch-rpc/ide.h b/include/asm-arm/arch-rpc/ide.h index aaf49b1be75c..15fecab1f7b8 100644 --- a/include/asm-arm/arch-rpc/ide.h +++ b/include/asm-arm/arch-rpc/ide.h @@ -44,5 +44,5 @@ ide_init_default_hwifs(void) ide_init_hwif_ports(&hw, 0x1f0, 0x3f6, NULL); hw.irq = IRQ_HARDDISK; - ide_register_hw(&hw); + ide_register_hw(&hw, NULL); } diff --git a/include/asm-arm/ide.h b/include/asm-arm/ide.h index 3b9c4c18c6af..e872974aa009 100644 --- a/include/asm-arm/ide.h +++ b/include/asm-arm/ide.h @@ -36,7 +36,6 @@ * The following are not needed for the non-m68k ports */ #define ide_ack_intr(hwif) (1) -#define ide_fix_driveid(id) do {} while (0) #define ide_release_lock(lock) do {} while (0) #define ide_get_lock(lock, hdlr, data) do {} while (0) -- cgit v1.2.3 From 074a2e78cdf9b3fec532def714ec253e4480d359 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Oct 2002 14:24:33 +0100 Subject: [ARM] Remove second serial port address. The second serial port is never present in these machines, so its pointless listing it in the first place. --- include/asm-arm/arch-rpc/serial.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/asm-arm/arch-rpc/serial.h b/include/asm-arm/arch-rpc/serial.h index 1497fc6fec8c..2f12e6da9bf6 100644 --- a/include/asm-arm/arch-rpc/serial.h +++ b/include/asm-arm/arch-rpc/serial.h @@ -29,7 +29,7 @@ /* UART CLK PORT IRQ FLAGS */ #define STD_SERIAL_PORT_DEFNS \ { 0, BASE_BAUD, 0x3F8, 10, STD_COM_FLAGS }, /* ttyS0 */ \ - { 0, BASE_BAUD, 0x2F8, 10, STD_COM_FLAGS }, /* ttyS1 */ \ + { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS1 */ \ { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS2 */ \ { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS3 */ \ { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS4 */ \ -- cgit v1.2.3 From 8025567f7d66dc82a3293b50f7607e8eb8ad546d Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Oct 2002 15:35:29 +0100 Subject: Convert acorn expansion card probing code to the Linux device model. Provide LDM-based driver registration/removal interface for drivers to use. We make the old device discovery code ignore devices claimed via the LDM framework. However, the LDM framework ignores devices that may be in use by the old device discovery code. This is fine since the only devices that will still use the old discovery code will be SCSI drivers. Currently, we don't provide a useful dev.name entry. --- arch/arm/kernel/ecard.c | 116 +++++++++++++++++++++++++++++++++++++++++++----- include/asm-arm/ecard.h | 29 ++++++++++-- 2 files changed, 130 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/arch/arm/kernel/ecard.c b/arch/arm/kernel/ecard.c index 4a4d89003ecc..4cb68c31b86e 100644 --- a/arch/arm/kernel/ecard.c +++ b/arch/arm/kernel/ecard.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -48,6 +49,7 @@ #include #include #include +#include #ifndef CONFIG_ARCH_RPC #define HAVE_EXPMASK @@ -92,6 +94,8 @@ ecard_loader_reset(volatile unsigned char *pa, loader_t loader); asmlinkage extern int ecard_loader_read(int off, volatile unsigned char *pa, loader_t loader); +static const struct ecard_id * +ecard_match_device(const struct ecard_id *ids, struct expansion_card *ec); static inline unsigned short ecard_getu16(unsigned char *v) @@ -969,6 +973,14 @@ ecard_probe(int slot, card_type_t type) *ecp = ec; slot_to_expcard[slot] = ec; + + snprintf(ec->dev.bus_id, sizeof(ec->dev.bus_id), "ecard%d", slot); + strcpy(ec->dev.name, "fixme!"); + ec->dev.parent = NULL; + ec->dev.bus = &ecard_bus_type; + + device_register(&ec->dev); + return 0; nodev: @@ -995,22 +1007,17 @@ ecard_t *ecard_find(int cid, const card_ids *cids) if (finding_pos->claimed) continue; + if (finding_pos->dev.driver) + continue; + if (!cids) { if ((finding_pos->cid.id ^ cid) == 0) break; } else { - unsigned int manufacturer, product; - int i; - - manufacturer = finding_pos->cid.manufacturer; - product = finding_pos->cid.product; - - for (i = 0; cids[i].manufacturer != 65535; i++) - if (manufacturer == cids[i].manufacturer && - product == cids[i].product) - break; + const struct ecard_id *id; - if (cids[i].manufacturer != 65535) + id = ecard_match_device(cids, finding_pos); + if (id) break; } } @@ -1023,7 +1030,7 @@ ecard_t *ecard_find(int cid, const card_ids *cids) * Locate all hardware - interrupt management and * actual cards. */ -void __init ecard_init(void) +static int __init ecard_init(void) { int slot, irqhw; @@ -1053,11 +1060,96 @@ void __init ecard_init(void) irqhw ? ecard_irqexp_handler : ecard_irq_handler); ecard_proc_init(); + + return 0; } subsys_initcall(ecard_init); +/* + * ECARD "bus" + */ +static const struct ecard_id * +ecard_match_device(const struct ecard_id *ids, struct expansion_card *ec) +{ + int i; + + for (i = 0; ids[i].manufacturer != 65535; i++) + if (ec->cid.manufacturer == ids[i].manufacturer && + ec->cid.product == ids[i].product) + return ids + i; + + return NULL; +} + +static int ecard_drv_probe(struct device *dev) +{ + struct expansion_card *ec = ECARD_DEV(dev); + struct ecard_driver *drv = ECARD_DRV(dev->driver); + const struct ecard_id *id; + + id = ecard_match_device(drv->id_table, ec); + + return drv->probe(ec, id); +} + +static int ecard_drv_remove(struct device *dev) +{ + struct expansion_card *ec = ECARD_DEV(dev); + struct ecard_driver *drv = ECARD_DRV(dev->driver); + + drv->remove(ec); + + return 0; +} + +int ecard_register_driver(struct ecard_driver *drv) +{ + drv->drv.bus = &ecard_bus_type; + drv->drv.probe = ecard_drv_probe; + drv->drv.remove = ecard_drv_remove; + + return driver_register(&drv->drv); +} + +void ecard_remove_driver(struct ecard_driver *drv) +{ + remove_driver(&drv->drv); +} + +static int ecard_match(struct device *_dev, struct device_driver *_drv) +{ + struct expansion_card *ec = ECARD_DEV(_dev); + struct ecard_driver *drv = ECARD_DRV(_drv); + int ret; + + if (drv->id_table) { + ret = ecard_match_device(drv->id_table, ec) != NULL; + } else { + ret = ec->cid.id == drv->id; + } + + return ret; +} + +struct bus_type ecard_bus_type = { + .name = "ecard", + .match = ecard_match, +}; + +static int ecard_bus_init(void) +{ + return bus_register(&ecard_bus_type); +} + +postcore_initcall(ecard_bus_init); + EXPORT_SYMBOL(ecard_startfind); EXPORT_SYMBOL(ecard_find); EXPORT_SYMBOL(ecard_readchunk); EXPORT_SYMBOL(ecard_address); + +EXPORT_SYMBOL(ecard_register_driver); +EXPORT_SYMBOL(ecard_remove_driver); + +EXPORT_SYMBOL(ecard_bus_type); diff --git a/include/asm-arm/ecard.h b/include/asm-arm/ecard.h index fa0adbaeb12d..7c719a206399 100644 --- a/include/asm-arm/ecard.h +++ b/include/asm-arm/ecard.h @@ -95,9 +95,10 @@ typedef enum { /* Speed for ECARD_IOC space */ ECARD_SYNC = 3 } card_speed_t; -typedef struct { /* Card ID structure */ - unsigned short manufacturer; - unsigned short product; +typedef struct ecard_id { /* Card ID structure */ + unsigned short manufacturer; + unsigned short product; + void *data; } card_ids; struct in_ecid { /* Packed card ID information */ @@ -132,6 +133,8 @@ typedef struct { /* Card handler routines */ struct expansion_card { struct expansion_card *next; + struct device dev; + /* Public data */ volatile unsigned char *irqaddr; /* address of IRQ register */ volatile unsigned char *fiqaddr; /* address of FIQ register */ @@ -248,4 +251,24 @@ struct ex_chunk_dir { #endif +extern struct bus_type ecard_bus_type; + +#define ECARD_DEV(_d) container_of((_d), struct expansion_card, dev) + +struct ecard_driver { + int (*probe)(struct expansion_card *, const struct ecard_id *id); + void (*remove)(struct expansion_card *); + const struct ecard_id *id_table; + unsigned int id; + struct device_driver drv; +}; + +#define ECARD_DRV(_d) container_of((_d), struct ecard_driver, drv) + +#define ecard_set_drvdata(ec,data) dev_set_drvdata(&(ec)->dev, (data)) +#define ecard_get_drvdata(ec) dev_get_drvdata(&(ec)->dev) + +int ecard_register_driver(struct ecard_driver *); +void ecard_remove_driver(struct ecard_driver *); + #endif -- cgit v1.2.3 From f6737e3ed1b0d9f0328d921eab43d4bf4c132373 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Oct 2002 17:32:58 +0100 Subject: [ARM] Rudimentary support for Thumb ptracing. Add rudimentary support for Thumb ptracing; we aren't able to single step through thumb branches yet, but this change provides enough infrastructure to make this possible. --- arch/arm/kernel/process.c | 4 +- arch/arm/kernel/ptrace.c | 139 ++++++++++++++++++++++++++++++++------------ include/asm-arm/processor.h | 26 ++++++--- 3 files changed, 121 insertions(+), 48 deletions(-) (limited to 'include') diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index eff2a147014b..cff2f0508439 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -367,8 +367,8 @@ void dump_thread(struct pt_regs * regs, struct user * dump) dump->u_debugreg[0] = tsk->thread.debug.bp[0].address; dump->u_debugreg[1] = tsk->thread.debug.bp[1].address; - dump->u_debugreg[2] = tsk->thread.debug.bp[0].insn; - dump->u_debugreg[3] = tsk->thread.debug.bp[1].insn; + dump->u_debugreg[2] = tsk->thread.debug.bp[0].insn.arm; + dump->u_debugreg[3] = tsk->thread.debug.bp[1].insn.arm; dump->u_debugreg[4] = tsk->thread.debug.nsaved; if (dump->start_stack < 0x04000000) diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index 65a96d722b29..8e4b131eff89 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c @@ -32,10 +32,24 @@ * in exit.c or in signal.c. */ +#if 1 /* * Breakpoint SWI instruction: SWI &9F0001 */ #define BREAKINST_ARM 0xef9f0001 +#define BREAKINST_THUMB 0xdf00 /* fill this in later */ +#else +/* + * New breakpoints - use an undefined instruction. The ARM architecture + * reference manual guarantees that the following instruction space + * will produce an undefined instruction exception on all CPUs: + * + * ARM: xxxx 0111 1111 xxxx xxxx xxxx 1111 xxxx + * Thumb: 1101 1110 xxxx xxxx + */ +#define BREAKINST_ARM 0xe7f001f0 +#define BREAKINST_THUMB 0xde01 +#endif /* * Get the address of the live pt_regs for the specified task. @@ -89,23 +103,32 @@ put_user_reg(struct task_struct *task, int offset, long data) } static inline int -read_tsk_long(struct task_struct *child, unsigned long addr, unsigned long *res) +read_u32(struct task_struct *task, unsigned long addr, u32 *res) { - int copied; + int ret; - copied = access_process_vm(child, addr, res, sizeof(*res), 0); + ret = access_process_vm(task, addr, res, sizeof(*res), 0); - return copied != sizeof(*res) ? -EIO : 0; + return ret == sizeof(*res) ? 0 : -EIO; } static inline int -write_tsk_long(struct task_struct *child, unsigned long addr, unsigned long val) +read_instr(struct task_struct *task, unsigned long addr, u32 *res) { - int copied; - - copied = access_process_vm(child, addr, &val, sizeof(val), 1); + int ret; - return copied != sizeof(val) ? -EIO : 0; + if (addr & 1) { + u16 val; + ret = access_process_vm(task, addr & ~1, &val, sizeof(val), 0); + ret = ret == sizeof(val) ? 0 : -EIO; + *res = val; + } else { + u32 val; + ret = access_process_vm(task, addr & ~3, &val, sizeof(val), 0); + ret = ret == sizeof(val) ? 0 : -EIO; + *res = val; + } + return ret; } /* @@ -206,7 +229,7 @@ ptrace_getldrop2(struct task_struct *child, unsigned long insn) static unsigned long get_branch_address(struct task_struct *child, unsigned long pc, unsigned long insn) { - unsigned long alt = 0; + u32 alt = 0; switch (insn & 0x0e000000) { case 0x00000000: @@ -262,7 +285,7 @@ get_branch_address(struct task_struct *child, unsigned long pc, unsigned long in else base -= aluop2; } - if (read_tsk_long(child, base, &alt) == 0) + if (read_u32(child, base, &alt) == 0) alt = pc_pointer(alt); } break; @@ -289,7 +312,7 @@ get_branch_address(struct task_struct *child, unsigned long pc, unsigned long in base = ptrace_getrn(child, insn); - if (read_tsk_long(child, base + nr_regs, &alt) == 0) + if (read_u32(child, base + nr_regs, &alt) == 0) alt = pc_pointer(alt); break; } @@ -319,30 +342,71 @@ get_branch_address(struct task_struct *child, unsigned long pc, unsigned long in } static int -add_breakpoint(struct task_struct *child, struct debug_info *dbg, unsigned long addr) +swap_insn(struct task_struct *task, unsigned long addr, + void *old_insn, void *new_insn, int size) +{ + int ret; + + ret = access_process_vm(task, addr, old_insn, size, 0); + if (ret == size) + ret = access_process_vm(task, addr, new_insn, size, 1); + return ret; +} + +static void +add_breakpoint(struct task_struct *task, struct debug_info *dbg, unsigned long addr) { int nr = dbg->nsaved; - int res = -EINVAL; if (nr < 2) { - res = read_tsk_long(child, addr, &dbg->bp[nr].insn); - if (res == 0) - res = write_tsk_long(child, addr, BREAKINST_ARM); + u32 new_insn = BREAKINST_ARM; + int res; - if (res == 0) { + res = swap_insn(task, addr, &dbg->bp[nr].insn, &new_insn, 4); + + if (res == 4) { dbg->bp[nr].address = addr; dbg->nsaved += 1; } } else printk(KERN_ERR "ptrace: too many breakpoints\n"); +} + +/* + * Clear one breakpoint in the user program. We copy what the hardware + * does and use bit 0 of the address to indicate whether this is a Thumb + * breakpoint or an ARM breakpoint. + */ +static void clear_breakpoint(struct task_struct *task, struct debug_entry *bp) +{ + unsigned long addr = bp->address; + union debug_insn old_insn; + int ret; + + if (addr & 1) { + ret = swap_insn(task, addr & ~1, &old_insn.thumb, + &bp->insn.thumb, 2); - return res; + if (ret != 2 || old_insn.thumb != BREAKINST_THUMB) + printk(KERN_ERR "%s:%d: corrupted Thumb breakpoint at " + "0x%08lx (0x%04x)\n", task->comm, task->pid, + addr, old_insn.thumb); + } else { + ret = swap_insn(task, addr & ~3, &old_insn.arm, + &bp->insn.arm, 4); + + if (ret != 4 || old_insn.arm != BREAKINST_ARM) + printk(KERN_ERR "%s:%d: corrupted ARM breakpoint at " + "0x%08lx (0x%08x)\n", task->comm, task->pid, + addr, old_insn.arm); + } } void ptrace_set_bpt(struct task_struct *child) { struct pt_regs *regs; - unsigned long pc, insn; + unsigned long pc; + u32 insn; int res; regs = get_user_regs(child); @@ -353,7 +417,7 @@ void ptrace_set_bpt(struct task_struct *child) return; } - res = read_tsk_long(child, pc, &insn); + res = read_instr(child, pc, &insn); if (!res) { struct debug_info *dbg = &child->thread.debug; unsigned long alt; @@ -362,7 +426,7 @@ void ptrace_set_bpt(struct task_struct *child) alt = get_branch_address(child, pc, insn); if (alt) - res = add_breakpoint(child, dbg, alt); + add_breakpoint(child, dbg, alt); /* * Note that we ignore the result of setting the above @@ -374,7 +438,7 @@ void ptrace_set_bpt(struct task_struct *child) * loose control of the thread during single stepping. */ if (!alt || predicate(insn) != PREDICATE_ALWAYS) - res = add_breakpoint(child, dbg, pc + 4); + add_breakpoint(child, dbg, pc + 4); } } @@ -384,24 +448,17 @@ void ptrace_set_bpt(struct task_struct *child) */ void __ptrace_cancel_bpt(struct task_struct *child) { - struct debug_info *dbg = &child->thread.debug; - int i, nsaved = dbg->nsaved; + int i, nsaved = child->thread.debug.nsaved; - dbg->nsaved = 0; + child->thread.debug.nsaved = 0; if (nsaved > 2) { printk("ptrace_cancel_bpt: bogus nsaved: %d!\n", nsaved); nsaved = 2; } - for (i = 0; i < nsaved; i++) { - unsigned long tmp; - - read_tsk_long(child, dbg->bp[i].address, &tmp); - write_tsk_long(child, dbg->bp[i].address, dbg->bp[i].insn); - if (tmp != BREAKINST_ARM) - printk(KERN_ERR "ptrace_cancel_bpt: weirdness\n"); - } + for (i = 0; i < nsaved; i++) + clear_breakpoint(child, &child->thread.debug.bp[i]); } /* @@ -537,9 +594,12 @@ static int do_ptrace(int request, struct task_struct *child, long addr, long dat */ case PTRACE_PEEKTEXT: case PTRACE_PEEKDATA: - ret = read_tsk_long(child, addr, &tmp); - if (!ret) + ret = access_process_vm(child, addr, &tmp, + sizeof(unsigned long), 0); + if (ret == sizeof(unsigned long)) ret = put_user(tmp, (unsigned long *) data); + else + ret = -EIO; break; case PTRACE_PEEKUSR: @@ -551,7 +611,12 @@ static int do_ptrace(int request, struct task_struct *child, long addr, long dat */ case PTRACE_POKETEXT: case PTRACE_POKEDATA: - ret = write_tsk_long(child, addr, data); + ret = access_process_vm(child, addr, &data, + sizeof(unsigned long), 1); + if (ret == sizeof(unsigned long)) + ret = 0; + else + ret = -EIO; break; case PTRACE_POKEUSR: diff --git a/include/asm-arm/processor.h b/include/asm-arm/processor.h index 7f3bed11ebd1..b13e4e54282c 100644 --- a/include/asm-arm/processor.h +++ b/include/asm-arm/processor.h @@ -28,22 +28,30 @@ #include #include #include +#include + +union debug_insn { + u32 arm; + u16 thumb; +}; + +struct debug_entry { + u32 address; + union debug_insn insn; +}; struct debug_info { - int nsaved; - struct { - unsigned long address; - unsigned long insn; - } bp[2]; + int nsaved; + struct debug_entry bp[2]; }; struct thread_struct { /* fault info */ - unsigned long address; - unsigned long trap_no; - unsigned long error_code; + unsigned long address; + unsigned long trap_no; + unsigned long error_code; /* debugging */ - struct debug_info debug; + struct debug_info debug; }; #define INIT_THREAD { } -- cgit v1.2.3 From 321d6a826727fac90daa8cf21c4c9d973400750d Mon Sep 17 00:00:00 2001 From: David Brownell Date: Sun, 13 Oct 2002 01:40:00 -0700 Subject: [PATCH] usbcore doc + minor fixes Cleaning out my queue of most minor patches: - Provides some kerneldoc for 'struct usb_interface' now that the API is highlighting it. - Fixes usb_set_interface() so it doesn't affect other interfaces. This provides the right place for an eventual HCD call to clean out now-invalid records of endpoint state, and also gets rid of a potential SMP issue where drivers on different interfaces calling concurrently could clobber each other. (Per-interface data doesn't need locking except against config changes.) - It's OK to pass URB_NO_INTERRUPT hints if you're queueing a bunch of interrupt transfers. The set_interface call should eventually take the interface as a parameter, it's one of the few left using the "device plus magic number" identifier. I have a partial patch for that, but it doesn't handle the (newish) ALSA usb audio driver or a few other callers. --- drivers/usb/core/message.c | 30 +++++++++++++++++++++++------- drivers/usb/core/urb.c | 2 +- include/linux/usb.h | 36 +++++++++++++++++++++++++++++++++--- 3 files changed, 57 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 17ab912a3f4b..0d1dad8253fc 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -756,6 +756,7 @@ int usb_clear_halt(struct usb_device *dev, int pipe) * * This is used to enable data transfers on interfaces that may not * be enabled by default. Not all devices support such configurability. + * Only the driver bound to an interface may change its setting. * * Within any given configuration, each interface may have several * alternative settings. These are often used to control levels of @@ -808,6 +809,22 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) interface, NULL, 0, HZ * 5)) < 0) return ret; + /* FIXME drivers shouldn't need to replicate/bugfix the logic here + * when they implement async or easily-killable versions of this or + * other "should-be-internal" functions (like clear_halt). + * should hcd+usbcore postprocess control requests? + */ + + /* prevent submissions using previous endpoint settings */ + iface_as = iface->altsetting + iface->act_altsetting; + for (i = 0; i < iface_as->bNumEndpoints; i++) { + u8 ep = iface_as->endpoint [i].bEndpointAddress; + int out = !(ep & USB_DIR_IN); + + ep &= USB_ENDPOINT_NUMBER_MASK; + (out ? dev->epmaxpacketout : dev->epmaxpacketin ) [ep] = 0; + // FIXME want hcd hook here, "no such endpoint" + } iface->act_altsetting = alternate; /* 9.1.1.5: reset toggles for all endpoints affected by this iface-as @@ -819,21 +836,20 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) * any SetInterface request and hence assume toggles need to be reset. * However, EP0 toggles are re-synced for every individual transfer * during the SETUP stage - hence EP0 toggles are "don't care" here. + * (Likewise, EP0 never "halts" on well designed devices.) */ iface_as = &iface->altsetting[alternate]; for (i = 0; i < iface_as->bNumEndpoints; i++) { u8 ep = iface_as->endpoint[i].bEndpointAddress; + int out = !(ep & USB_DIR_IN); - usb_settoggle(dev, ep&USB_ENDPOINT_NUMBER_MASK, usb_endpoint_out(ep), 0); + ep &= USB_ENDPOINT_NUMBER_MASK; + usb_settoggle (dev, ep, out, 0); + (out ? dev->epmaxpacketout : dev->epmaxpacketin) [ep] + = iface_as->endpoint [ep].wMaxPacketSize; } - /* usb_set_maxpacket() sets the maxpacket size for all EP in all - * interfaces but it shouldn't do any harm here: we have changed - * the AS for the requested interface only, hence for unaffected - * interfaces it's just re-application of still-valid values. - */ - usb_set_maxpacket(dev); return 0; } diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 30a2ba8585b1..aaf31af0a45b 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -272,9 +272,9 @@ int usb_submit_urb(struct urb *urb, int mem_flags) /* enforce simple/standard policy */ allowed = USB_ASYNC_UNLINK; // affects later unlinks allowed |= URB_NO_DMA_MAP; + allowed |= URB_NO_INTERRUPT; switch (temp) { case PIPE_BULK: - allowed |= URB_NO_INTERRUPT; if (is_out) allowed |= USB_ZERO_PACKET; /* FALLTHROUGH */ diff --git a/include/linux/usb.h b/include/linux/usb.h index 89ecca734172..fe144bda56f0 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -222,12 +222,42 @@ struct usb_interface_descriptor { int extralen; }; +/** + * struct usb_interface - what usb device drivers talk to + * @altsetting: array of interface descriptors, one for each alternate + * setting that may be selected. each one includes a set of + * endpoint configurations. + * @num_altsetting: number of altsettings defined. + * @act_altsetting: index of current altsetting. this number is always + * less than num_altsetting. after the device is configured, each + * interface uses its default setting of zero. + * @dev: driver model's view of this device + * + * USB device drivers attach to interfaces on a physical device. Each + * interface encapsulates a single high level function, such as feeding + * an audio stream to a speaker or reporting a change in a volume control. + * Many USB devices only have one interface. The protocol used to talk to + * an interface's endpoints can be defined in a usb "class" specification, + * or by a product's vendor. The (default) control endpoint is part of + * every interface, but is never listed among the interface's descriptors. + * + * The driver that is bound to the interface can use standard driver model + * calls such as dev_get_drvdata() on the dev member of this structure. + * + * Each interface may have alternate settings. The initial configuration + * of a device sets the first of these, but the device driver can change + * that setting using usb_set_interface(). Alternate settings are often + * used to control the the use of periodic endpoints, such as by having + * different endpoints use different amounts of reserved USB bandwidth. + * All standards-conformant USB devices that use isochronous endpoints + * will use them in non-default settings. + */ struct usb_interface { struct usb_interface_descriptor *altsetting; - int act_altsetting; /* active alternate setting */ - int num_altsetting; /* number of alternate settings */ - int max_altsetting; /* total memory allocated */ + unsigned act_altsetting; /* active alternate setting */ + unsigned num_altsetting; /* number of alternate settings */ + unsigned max_altsetting; /* total memory allocated */ struct usb_driver *driver; /* driver */ struct device dev; /* interface specific device info */ -- cgit v1.2.3 From 71419dc7e039a8953861df2a28fad639d12ae6b9 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sun, 13 Oct 2002 02:58:45 -0700 Subject: [PATCH] batched slab shrink and registration API From Ed Tomlinson, then mauled by yours truly. The current shrinking of the dentry, inode and dquot caches seems to work OK, but it is slightly CPU-inefficient: we call the shrinking functions many times, for tiny numbers of objects. So here, we just batch that up - shrinking happens at the same rate but we perform it in larger units of work. To do this, we need a way of knowing how many objects are currently in use by individual caches. slab does not actually track this information, but the existing shrinkable caches do have this on hand. So rather than adding the counters to slab, we require that the shrinker callback functions keep their own count - we query that via the callback. We add a simple registration API which is exported to modules. A subsystem may register its own callback function via set_shrinker(). set_shrinker() simply takes a function pointer. The function is called with int (*shrinker)(int nr_to_shrink, unsigned int gfp_mask); The shrinker callback must scan `nr_to_scan' objects and free all freeable scanned objects. Note: it doesn't have to *free* `nr_to_scan' objects. It need only scan that many. Which is a fairly pedantic detail, really. The shrinker callback must return the number of objects which are in its cache at the end of the scanning attempt. It will be called with nr_to_scan == 0 when we're just querying the cache size. The set_shrinker() registration API is passed a hint as to how many disk seeks a single cache object is worth. Everything uses "2" at present. I saw no need to add the traditional `here is my void *data' to the registration/callback. Because there is a one-to-one relationship between caches and their shrinkers. Various cleanups became possible: - shrink_icache_memory() is no longer exported to modules. - shrink_icache_memory() is now static to fs/inode.c - prune_icache() is now static to fs/inode.c, and made inline (single caller) - shrink_dcache_memory() is made static to fs/dcache.c - prune_dcache() is no longer exported to modules - prune_dcache() is made static to fs/dcache.c - shrink_dqcache_memory() is made static to fs/dquot.c - All the quota init code has been moved from fs/dcache.c into fs/dquot.c - All modifications to inodes_stat.nr_inodes are now inside inode_lock - the dispose_list one was racy. --- fs/dcache.c | 50 +++++++++------------ fs/dquot.c | 28 ++++++++---- fs/inode.c | 49 ++++++++++---------- include/linux/dcache.h | 11 ----- include/linux/mm.h | 23 ++++++++++ kernel/ksyms.c | 3 +- mm/vmscan.c | 119 ++++++++++++++++++++++++++++++++++++------------- 7 files changed, 179 insertions(+), 104 deletions(-) (limited to 'include') diff --git a/fs/dcache.c b/fs/dcache.c index 4528be4d90d1..ef0871dbcdb2 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -328,7 +328,7 @@ static inline void prune_one_dentry(struct dentry * dentry) * all the dentries are in use. */ -void prune_dcache(int count) +static void prune_dcache(int count) { spin_lock(&dcache_lock); for (; count ; count--) { @@ -572,25 +572,24 @@ void shrink_dcache_anon(struct list_head *head) * This is called from kswapd when we think we need some * more memory. */ -int shrink_dcache_memory(int ratio, unsigned int gfp_mask) +static int shrink_dcache_memory(int nr, unsigned int gfp_mask) { - int entries = dentry_stat.nr_dentry / ratio + 1; - /* - * Nasty deadlock avoidance. - * - * ext2_new_block->getblk->GFP->shrink_dcache_memory->prune_dcache-> - * prune_one_dentry->dput->dentry_iput->iput->inode->i_sb->s_op-> - * put_inode->ext2_discard_prealloc->ext2_free_blocks->lock_super-> - * DEADLOCK. - * - * We should make sure we don't hold the superblock lock over - * block allocations, but for now: - */ - if (!(gfp_mask & __GFP_FS)) - return 0; - - prune_dcache(entries); - return entries; + if (nr) { + /* + * Nasty deadlock avoidance. + * + * ext2_new_block->getblk->GFP->shrink_dcache_memory-> + * prune_dcache->prune_one_dentry->dput->dentry_iput->iput-> + * inode->i_sb->s_op->put_inode->ext2_discard_prealloc-> + * ext2_free_blocks->lock_super->DEADLOCK. + * + * We should make sure we don't hold the superblock lock over + * block allocations, but for now: + */ + if (gfp_mask & __GFP_FS) + prune_dcache(nr); + } + return dentry_stat.nr_dentry; } #define NAME_ALLOC_LEN(len) ((len+16) & ~15) @@ -1330,6 +1329,8 @@ static void __init dcache_init(unsigned long mempages) NULL, NULL); if (!dentry_cache) panic("Cannot create dentry cache"); + + set_shrinker(DEFAULT_SEEKS, shrink_dcache_memory); #if PAGE_SHIFT < 13 mempages >>= (13 - PAGE_SHIFT); @@ -1375,9 +1376,6 @@ kmem_cache_t *names_cachep; /* SLAB cache for file structures */ kmem_cache_t *filp_cachep; -/* SLAB cache for dquot structures */ -kmem_cache_t *dquot_cachep; - EXPORT_SYMBOL(d_genocide); extern void bdev_cache_init(void); @@ -1397,14 +1395,6 @@ void __init vfs_caches_init(unsigned long mempages) if(!filp_cachep) panic("Cannot create filp SLAB cache"); -#if defined (CONFIG_QUOTA) - dquot_cachep = kmem_cache_create("dquot", - sizeof(struct dquot), sizeof(unsigned long) * 4, - SLAB_HWCACHE_ALIGN, NULL, NULL); - if (!dquot_cachep) - panic("Cannot create dquot SLAB cache"); -#endif - dcache_init(mempages); inode_init(mempages); files_init(mempages); diff --git a/fs/dquot.c b/fs/dquot.c index f97b3609c894..24d50ae34824 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -55,6 +55,7 @@ #include #include #include +#include #include #include #include @@ -481,14 +482,14 @@ static void prune_dqcache(int count) * more memory */ -int shrink_dqcache_memory(int ratio, unsigned int gfp_mask) +static int shrink_dqcache_memory(int nr, unsigned int gfp_mask) { - int entries = dqstats.allocated_dquots / ratio + 1; - - lock_kernel(); - prune_dqcache(entries); - unlock_kernel(); - return entries; + if (nr) { + lock_kernel(); + prune_dqcache(nr); + unlock_kernel(); + } + return dqstats.allocated_dquots; } /* @@ -1490,6 +1491,9 @@ static ctl_table sys_table[] = { {}, }; +/* SLAB cache for dquot structures */ +kmem_cache_t *dquot_cachep; + static int __init dquot_init(void) { int i; @@ -1499,9 +1503,17 @@ static int __init dquot_init(void) INIT_LIST_HEAD(dquot_hash + i); printk(KERN_NOTICE "VFS: Disk quotas v%s\n", __DQUOT_VERSION__); + dquot_cachep = kmem_cache_create("dquot", + sizeof(struct dquot), sizeof(unsigned long) * 4, + SLAB_HWCACHE_ALIGN, NULL, NULL); + if (!dquot_cachep) + panic("Cannot create dquot SLAB cache"); + + set_shrinker(DEFAULT_SEEKS, shrink_dqcache_memory); + return 0; } -__initcall(dquot_init); +module_init(dquot_init); EXPORT_SYMBOL(register_quota_format); EXPORT_SYMBOL(unregister_quota_format); diff --git a/fs/inode.c b/fs/inode.c index d56785889730..4f56d96031ea 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -243,22 +243,25 @@ void clear_inode(struct inode *inode) * Dispose-list gets a local list with local inodes in it, so it doesn't * need to worry about list corruption and SMP locks. */ -static void dispose_list(struct list_head * head) +static void dispose_list(struct list_head *head) { - struct list_head * inode_entry; - struct inode * inode; + int nr_disposed = 0; + + while (!list_empty(head)) { + struct inode *inode; - while ((inode_entry = head->next) != head) - { - list_del(inode_entry); + inode = list_entry(head->next, struct inode, i_list); + list_del(&inode->i_list); - inode = list_entry(inode_entry, struct inode, i_list); if (inode->i_data.nrpages) truncate_inode_pages(&inode->i_data, 0); clear_inode(inode); destroy_inode(inode); - inodes_stat.nr_inodes--; + nr_disposed++; } + spin_lock(&inode_lock); + inodes_stat.nr_inodes -= nr_disposed; + spin_unlock(&inode_lock); } /* @@ -377,7 +380,7 @@ int invalidate_device(kdev_t dev, int do_sync) !inode_has_buffers(inode)) #define INODE(entry) (list_entry(entry, struct inode, i_list)) -void prune_icache(int goal) +static inline void prune_icache(int goal) { LIST_HEAD(list); struct list_head *entry, *freeable = &list; @@ -417,23 +420,19 @@ void prune_icache(int goal) * This is called from kswapd when we think we need some * more memory. */ -int shrink_icache_memory(int ratio, unsigned int gfp_mask) +static int shrink_icache_memory(int nr, unsigned int gfp_mask) { - int entries = inodes_stat.nr_inodes / ratio + 1; - /* - * Nasty deadlock avoidance.. - * - * We may hold various FS locks, and we don't - * want to recurse into the FS that called us - * in clear_inode() and friends.. - */ - if (!(gfp_mask & __GFP_FS)) - return 0; - - prune_icache(entries); - return entries; + if (nr) { + /* + * Nasty deadlock avoidance. We may hold various FS locks, + * and we don't want to recurse into the FS that called us + * in clear_inode() and friends.. + */ + if (gfp_mask & __GFP_FS) + prune_icache(nr); + } + return inodes_stat.nr_inodes; } -EXPORT_SYMBOL(shrink_icache_memory); /* * Called with the inode lock held. @@ -1226,4 +1225,6 @@ void __init inode_init(unsigned long mempages) NULL); if (!inode_cachep) panic("cannot create inode slab cache"); + + set_shrinker(DEFAULT_SEEKS, shrink_icache_memory); } diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 0abaaaa2c96d..71708edafce9 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -180,17 +180,6 @@ extern void shrink_dcache_parent(struct dentry *); extern void shrink_dcache_anon(struct list_head *); extern int d_invalidate(struct dentry *); -/* dcache memory management */ -extern int shrink_dcache_memory(int, unsigned int); -extern void prune_dcache(int); - -/* icache memory management (defined in linux/fs/inode.c) */ -extern int shrink_icache_memory(int, unsigned int); -extern void prune_icache(int); - -/* quota cache memory management (defined in linux/fs/dquot.c) */ -extern int shrink_dqcache_memory(int, unsigned int); - /* only used at mount-time */ extern struct dentry * d_alloc_root(struct inode *); diff --git a/include/linux/mm.h b/include/linux/mm.h index a5107b5043f7..a6c66cc418ee 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -391,6 +391,29 @@ extern int free_hugepages(struct vm_area_struct *); #endif +/* + * Prototype to add a shrinker callback for ageable caches. + * + * These functions are passed a count `nr_to_scan' and a gfpmask. They should + * scan `nr_to_scan' objects, attempting to free them. + * + * The callback must the number of objects which remain in the cache. + * + * The callback will be passes nr_to_scan == 0 when the VM is querying the + * cache size, so a fastpath for that case is appropriate. + */ +typedef int (*shrinker_t)(int nr_to_scan, unsigned int gfp_mask); + +/* + * Add an aging callback. The int is the number of 'seeks' it takes + * to recreate one of the objects that these functions age. + */ + +#define DEFAULT_SEEKS 2 +struct shrinker; +extern struct shrinker *set_shrinker(int, shrinker_t); +extern void remove_shrinker(struct shrinker *shrinker); + /* * If the mapping doesn't provide a set_page_dirty a_op, then * just fall through and assume that it wants buffer_heads. diff --git a/kernel/ksyms.c b/kernel/ksyms.c index 9beb67e2a999..bd0a43fcf7f4 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -103,6 +103,8 @@ EXPORT_SYMBOL(kmem_cache_shrink); EXPORT_SYMBOL(kmem_cache_alloc); EXPORT_SYMBOL(kmem_cache_free); EXPORT_SYMBOL(kmem_cache_size); +EXPORT_SYMBOL(set_shrinker); +EXPORT_SYMBOL(remove_shrinker); EXPORT_SYMBOL(kmalloc); EXPORT_SYMBOL(kfree); EXPORT_SYMBOL(vfree); @@ -246,7 +248,6 @@ EXPORT_SYMBOL(dput); EXPORT_SYMBOL(have_submounts); EXPORT_SYMBOL(d_find_alias); EXPORT_SYMBOL(d_prune_aliases); -EXPORT_SYMBOL(prune_dcache); EXPORT_SYMBOL(shrink_dcache_sb); EXPORT_SYMBOL(shrink_dcache_parent); EXPORT_SYMBOL(shrink_dcache_anon); diff --git a/mm/vmscan.c b/mm/vmscan.c index 0086407047f6..31856732ed7b 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -77,9 +77,94 @@ static long total_memory; #define prefetchw_prev_lru_page(_page, _base, _field) do { } while (0) #endif -#ifndef CONFIG_QUOTA -#define shrink_dqcache_memory(ratio, gfp_mask) do { } while (0) -#endif +/* + * The list of shrinker callbacks used by to apply pressure to + * ageable caches. + */ +struct shrinker { + shrinker_t shrinker; + struct list_head list; + int seeks; /* seeks to recreate an obj */ + int nr; /* objs pending delete */ +}; + +static LIST_HEAD(shrinker_list); +static DECLARE_MUTEX(shrinker_sem); + +/* + * Add a shrinker callback to be called from the vm + */ +struct shrinker *set_shrinker(int seeks, shrinker_t theshrinker) +{ + struct shrinker *shrinker; + + shrinker = kmalloc(sizeof(*shrinker), GFP_KERNEL); + if (shrinker) { + shrinker->shrinker = theshrinker; + shrinker->seeks = seeks; + shrinker->nr = 0; + down(&shrinker_sem); + list_add(&shrinker->list, &shrinker_list); + up(&shrinker_sem); + } + return shrinker; +} + +/* + * Remove one + */ +void remove_shrinker(struct shrinker *shrinker) +{ + down(&shrinker_sem); + list_del(&shrinker->list); + up(&shrinker_sem); + kfree(shrinker); +} + +#define SHRINK_BATCH 32 +/* + * Call the shrink functions to age shrinkable caches + * + * Here we assume it costs one seek to replace a lru page and that it also + * takes a seek to recreate a cache object. With this in mind we age equal + * percentages of the lru and ageable caches. This should balance the seeks + * generated by these structures. + * + * If the vm encounted mapped pages on the LRU it increase the pressure on + * slab to avoid swapping. + * + * FIXME: do not do for zone highmem + */ +static int shrink_slab(int scanned, unsigned int gfp_mask) +{ + struct list_head *lh; + int pages; + + if (down_trylock(&shrinker_sem)) + return 0; + + pages = nr_used_zone_pages(); + list_for_each(lh, &shrinker_list) { + struct shrinker *shrinker; + int entries; + unsigned long delta; + + shrinker = list_entry(lh, struct shrinker, list); + entries = (*shrinker->shrinker)(0, gfp_mask); + if (!entries) + continue; + delta = scanned * shrinker->seeks * entries; + shrinker->nr += delta / (pages + 1); + if (shrinker->nr > SHRINK_BATCH) { + int nr = shrinker->nr; + + shrinker->nr = 0; + (*shrinker->shrinker)(nr, gfp_mask); + } + } + up(&shrinker_sem); + return 0; +} /* Must be called with page's pte_chain_lock held. */ static inline int page_mapping_inuse(struct page * page) @@ -626,32 +711,6 @@ shrink_zone(struct zone *zone, int max_scan, unsigned int gfp_mask, max_scan, nr_mapped); } -/* - * FIXME: don't do this for ZONE_HIGHMEM - */ -/* - * Here we assume it costs one seek to replace a lru page and that it also - * takes a seek to recreate a cache object. With this in mind we age equal - * percentages of the lru and ageable caches. This should balance the seeks - * generated by these structures. - * - * NOTE: for now I do this for all zones. If we find this is too aggressive - * on large boxes we may want to exclude ZONE_HIGHMEM. - * - * If we're encountering mapped pages on the LRU then increase the pressure on - * slab to avoid swapping. - */ -static void shrink_slab(int total_scanned, int gfp_mask) -{ - int shrink_ratio; - int pages = nr_used_zone_pages(); - - shrink_ratio = (pages / (total_scanned + 1)) + 1; - shrink_dcache_memory(shrink_ratio, gfp_mask); - shrink_icache_memory(shrink_ratio, gfp_mask); - shrink_dqcache_memory(shrink_ratio, gfp_mask); -} - /* * This is the direct reclaim path, for page-allocating processes. We only * try to reclaim pages from zones which will satisfy the caller's allocation @@ -695,7 +754,7 @@ shrink_caches(struct zone *classzone, int priority, int *total_scanned, } return ret; } - + /* * This is the main entry point to direct page reclaim. * -- cgit v1.2.3 From 2dcb8ff9ea7bfdc161eec1eeb8f94c2ba5c3c8a8 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sun, 13 Oct 2002 02:59:10 -0700 Subject: [PATCH] remove kiobufs This patch from Christoph Hellwig removes the kiobuf/kiovec infrastructure. This affects three subsystems: video-buf.c: This patch includes an earlier diff from Gerd which converts video-buf.c to use get_user_pages() directly. Gerd has acked this patch. LVM1: Is now even more broken. drivers/mtd/devices/blkmtd.c: blkmtd is broken by this change. I contacted Simon Evans, who said "I had done a rewrite of blkmtd anyway and just need to convert it to BIO. Feel free to break it in the 2.5 tree, it will force me to finish my code." Neither EVMS nor LVM2 use kiobufs. The only remaining breakage of which I am aware is a proprietary MPEG2 streaming module. It could use get_user_pages(). --- arch/cris/drivers/examples/kiobuftest.c | 111 ---------------- drivers/media/video/bttv-risc.c | 1 - drivers/media/video/bttvp.h | 1 - drivers/media/video/video-buf.c | 109 ++++++++++------ drivers/media/video/video-buf.h | 8 +- fs/Makefile | 2 +- fs/aio.c | 7 +- fs/bio.c | 125 +----------------- fs/block_dev.c | 1 - fs/buffer.c | 60 --------- fs/fcntl.c | 1 - fs/file_table.c | 1 - fs/iobuf.c | 125 ------------------ fs/open.c | 1 - fs/xfs/linux/xfs_aops.c | 1 - fs/xfs/linux/xfs_ioctl.c | 2 +- include/linux/buffer_head.h | 1 - include/linux/iobuf.h | 88 ------------- init/main.c | 1 - kernel/ksyms.c | 13 -- mm/filemap.c | 1 - mm/memory.c | 221 +------------------------------- 22 files changed, 82 insertions(+), 799 deletions(-) delete mode 100644 arch/cris/drivers/examples/kiobuftest.c delete mode 100644 fs/iobuf.c delete mode 100644 include/linux/iobuf.h (limited to 'include') diff --git a/arch/cris/drivers/examples/kiobuftest.c b/arch/cris/drivers/examples/kiobuftest.c deleted file mode 100644 index 784418f9c4d6..000000000000 --- a/arch/cris/drivers/examples/kiobuftest.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Example showing how to pin down a range of virtual pages from user-space - * to be able to do for example DMA directly into them. - * - * It is necessary because the pages the virtual pointers reference, might - * not exist in memory (could be mapped to the zero-page, filemapped etc) - * and DMA cannot trigger the MMU to force them in (and would have time - * contraints making it impossible to wait for it anyway). - * - * Author: Bjorn Wesen - * - * $Log: kiobuftest.c,v $ - * Revision 1.1.1.1 2001/12/17 13:59:27 bjornw - * Import of Linux 2.5.1 - * - * Revision 1.2 2001/02/27 13:52:50 bjornw - * malloc.h -> slab.h - * - * Revision 1.1 2001/01/19 15:57:49 bjornw - * Example of how to do direct HW -> user-mode DMA - * - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define KIOBUFTEST_MAJOR 124 /* in the local range, experimental */ - - -static ssize_t -kiobuf_read(struct file *filp, char *buf, size_t len, loff_t *ppos) -{ - - struct kiobuf *iobuf; - int res, i; - - /* Make a kiobuf that maps the entire length the reader has given - * us - */ - - res = alloc_kiovec(1, &iobuf); - if (res) - return res; - - if((res = map_user_kiobuf(READ, iobuf, (unsigned long)buf, len))) { - printk("map_user_kiobuf failed, return %d\n", res); - return res; - } - - /* At this point, the virtual area buf[0] -> buf[len-1] will - * have corresponding pages mapped in physical memory and locked - * until we unmap the kiobuf. They cannot be swapped out or moved - * around. - */ - - printk("nr_pages == %d\noffset == %d\nlength == %d\n", - iobuf->nr_pages, iobuf->offset, iobuf->length); - - for(i = 0; i < iobuf->nr_pages; i++) { - printk("page_add(maplist[%d]) == 0x%x\n", i, - page_address(iobuf->maplist[i])); - } - - /* This is the place to create the necessary scatter-gather vector - * for the DMA using the iobuf->maplist array and page_address - * (don't forget __pa if the DMA needs the actual physical DRAM address) - * and run it. - */ - - - - - /* Release the mapping and exit */ - - unmap_kiobuf(iobuf); /* The unlock_kiobuf is implicit here */ - - return len; -} - - -static struct file_operations kiobuf_fops = { - owner: THIS_MODULE, - read: kiobuf_read -}; - -static int __init -kiobuftest_init(void) -{ - int res; - - /* register char device */ - - res = register_chrdev(KIOBUFTEST_MAJOR, "kiobuftest", &kiobuf_fops); - if(res < 0) { - printk(KERN_ERR "kiobuftest: couldn't get a major number.\n"); - return res; - } - - printk("Initializing kiobuf-test device\n"); -} - -module_init(kiobuftest_init); diff --git a/drivers/media/video/bttv-risc.c b/drivers/media/video/bttv-risc.c index d63b5b48481c..ddb6f4328189 100644 --- a/drivers/media/video/bttv-risc.c +++ b/drivers/media/video/bttv-risc.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/media/video/bttvp.h b/drivers/media/video/bttvp.h index c58be937f95f..01443316cf39 100644 --- a/drivers/media/video/bttvp.h +++ b/drivers/media/video/bttvp.h @@ -31,7 +31,6 @@ #include #include #include -#include #include #include diff --git a/drivers/media/video/video-buf.c b/drivers/media/video/video-buf.c index d1c783401b29..03d3bbd1356a 100644 --- a/drivers/media/video/video-buf.c +++ b/drivers/media/video/video-buf.c @@ -18,8 +18,8 @@ #include #include -#include #include +#include #include #include #include @@ -65,32 +65,31 @@ videobuf_vmalloc_to_sg(unsigned char *virt, int nr_pages) return NULL; } -struct scatterlist* -videobuf_iobuf_to_sg(struct kiobuf *iobuf) +struct scatterlist * +videobuf_pages_to_sg(struct page **pages, int nr_pages, int offset) { struct scatterlist *sglist; int i = 0; - - sglist = kmalloc(sizeof(struct scatterlist) * iobuf->nr_pages, - GFP_KERNEL); + + if (NULL == pages[0]) + return NULL; + sglist = kmalloc(sizeof(*sglist) * nr_pages, GFP_KERNEL); if (NULL == sglist) return NULL; - memset(sglist,0,sizeof(struct scatterlist) * iobuf->nr_pages); + memset(sglist, 0, sizeof(*sglist) * nr_pages); - if (NULL == iobuf->maplist[0]) - goto err; - if (PageHighMem(iobuf->maplist[0])) + if (PageHighMem(pages[0])) /* DMA to highmem pages might not work */ goto err; - sglist[0].page = iobuf->maplist[0]; - sglist[0].offset = iobuf->offset; - sglist[0].length = PAGE_SIZE - iobuf->offset; - for (i = 1; i < iobuf->nr_pages; i++) { - if (NULL == iobuf->maplist[i]) + sglist[0].page = pages[0]; + sglist[0].offset = offset; + sglist[0].length = PAGE_SIZE - offset; + for (i = 1; i < nr_pages; i++) { + if (NULL == pages[i]) goto err; - if (PageHighMem(iobuf->maplist[i])) + if (PageHighMem(pages[i])) goto err; - sglist[i].page = iobuf->maplist[i]; + sglist[i].page = pages[i]; sglist[i].length = PAGE_SIZE; } return sglist; @@ -100,6 +99,30 @@ videobuf_iobuf_to_sg(struct kiobuf *iobuf) return NULL; } +int videobuf_lock(struct page **pages, int nr_pages) +{ + int i; + + for (i = 0; i < nr_pages; i++) + if (TestSetPageLocked(pages[i])) + goto err; + return 0; + + err: + while (i > 0) + unlock_page(pages[--i]); + return -EINVAL; +} + +int videobuf_unlock(struct page **pages, int nr_pages) +{ + int i; + + for (i = 0; i < nr_pages; i++) + unlock_page(pages[i]); + return 0; +} + /* --------------------------------------------------------------------- */ int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction, @@ -113,14 +136,21 @@ int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction, case PCI_DMA_TODEVICE: rw = WRITE; break; default: BUG(); } - if (0 != (err = alloc_kiovec(1,&dma->iobuf))) - return err; - if (0 != (err = map_user_kiobuf(rw, dma->iobuf, data, size))) { - dprintk(1,"map_user_kiobuf: %d\n",err); - return err; - } - dma->nr_pages = dma->iobuf->nr_pages; - return 0; + + dma->offset = data & PAGE_MASK; + dma->nr_pages = ((((data+size) & ~PAGE_MASK) - + (data & ~PAGE_MASK)) >> PAGE_SHIFT) +1; + dma->pages = kmalloc(dma->nr_pages * sizeof(struct page*), + GFP_KERNEL); + if (NULL == dma->pages) + return -ENOMEM; + down_read(¤t->mm->mmap_sem); + err = get_user_pages(current,current->mm, + data, dma->nr_pages, + rw == READ, 0, /* don't force */ + dma->pages, NULL); + up_read(¤t->mm->mmap_sem); + return err; } int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, @@ -144,13 +174,15 @@ int videobuf_dma_pci_map(struct pci_dev *dev, struct videobuf_dmabuf *dma) if (0 == dma->nr_pages) BUG(); - if (dma->iobuf) { - if (0 != (err = lock_kiovec(1,&dma->iobuf,1))) { - dprintk(1,"lock_kiovec: %d\n",err); + if (dma->pages) { + if (0 != (err = videobuf_lock(dma->pages, dma->nr_pages))) { + dprintk(1,"videobuf_lock_pages: %d\n",err); return err; } - dma->sglist = videobuf_iobuf_to_sg(dma->iobuf); + dma->sglist = videobuf_pages_to_sg(dma->pages, dma->nr_pages, + dma->offset); } + if (dma->vmalloc) { dma->sglist = videobuf_vmalloc_to_sg (dma->vmalloc,dma->nr_pages); @@ -160,7 +192,7 @@ int videobuf_dma_pci_map(struct pci_dev *dev, struct videobuf_dmabuf *dma) return -ENOMEM; } dma->sglen = pci_map_sg(dev,dma->sglist,dma->nr_pages, - dma->direction); + dma->direction); return 0; } @@ -182,8 +214,8 @@ int videobuf_dma_pci_unmap(struct pci_dev *dev, struct videobuf_dmabuf *dma) kfree(dma->sglist); dma->sglist = NULL; dma->sglen = 0; - if (dma->iobuf) - unlock_kiovec(1,&dma->iobuf); + if (dma->pages) + videobuf_lock(dma->pages, dma->nr_pages); return 0; } @@ -192,11 +224,14 @@ int videobuf_dma_free(struct videobuf_dmabuf *dma) if (dma->sglen) BUG(); - if (dma->iobuf) { - unmap_kiobuf(dma->iobuf); - free_kiovec(1,&dma->iobuf); - dma->iobuf = NULL; + if (dma->pages) { + int i; + for (i=0; i < dma->nr_pages; i++) + page_cache_release(dma->pages[i]); + kfree(dma->pages); + dma->pages = NULL; } + if (dma->vmalloc) { vfree(dma->vmalloc); dma->vmalloc = NULL; @@ -959,6 +994,7 @@ int videobuf_mmap_mapper(struct vm_area_struct *vma, map->q = q; vma->vm_ops = &videobuf_vm_ops; vma->vm_flags |= VM_DONTEXPAND; + vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */ vma->vm_private_data = map; dprintk(1,"mmap %p: %08lx-%08lx pgoff %08lx bufs %d-%d\n", map,vma->vm_start,vma->vm_end,vma->vm_pgoff,first,last); @@ -972,7 +1008,6 @@ int videobuf_mmap_mapper(struct vm_area_struct *vma, /* --------------------------------------------------------------------- */ EXPORT_SYMBOL_GPL(videobuf_vmalloc_to_sg); -EXPORT_SYMBOL_GPL(videobuf_iobuf_to_sg); EXPORT_SYMBOL_GPL(videobuf_dma_init_user); EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel); diff --git a/drivers/media/video/video-buf.h b/drivers/media/video/video-buf.h index 0e2c5860b953..3d8710848ca1 100644 --- a/drivers/media/video/video-buf.h +++ b/drivers/media/video/video-buf.h @@ -28,11 +28,12 @@ struct scatterlist* videobuf_vmalloc_to_sg(unsigned char *virt, int nr_pages); /* - * Return a scatterlist for a locked iobuf (NULL on errors). Memory + * Return a scatterlist for a an array of userpages (NULL on errors). Memory * for the scatterlist is allocated using kmalloc. The caller must * free the memory. */ -struct scatterlist* videobuf_iobuf_to_sg(struct kiobuf *iobuf); +struct scatterlist *videobuf_pages_to_sg(struct page **pages, int nr_pages, + int offset); /* --------------------------------------------------------------------- */ @@ -57,7 +58,8 @@ struct scatterlist* videobuf_iobuf_to_sg(struct kiobuf *iobuf); struct videobuf_dmabuf { /* for userland buffer */ - struct kiobuf *iobuf; + struct page **pages; + int offset; /* for kernel buffers */ void *vmalloc; diff --git a/fs/Makefile b/fs/Makefile index 7f349ff168ad..d902bdd8bda3 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -11,7 +11,7 @@ export-objs := open.o dcache.o buffer.o bio.o inode.o dquot.o mpage.o aio.o \ obj-y := open.o read_write.o devices.o file_table.o buffer.o \ bio.o super.o block_dev.o char_dev.o stat.o exec.o pipe.o \ namei.o fcntl.o ioctl.o readdir.o select.o fifo.o locks.o \ - dcache.o inode.o attr.o bad_inode.o file.o iobuf.o dnotify.o \ + dcache.o inode.o attr.o bad_inode.o file.o dnotify.o \ filesystems.o namespace.o seq_file.o xattr.o libfs.o \ fs-writeback.o mpage.o direct-io.o aio.o diff --git a/fs/aio.c b/fs/aio.c index e561c8b83a00..6b51c1316ab2 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -9,6 +9,7 @@ * See ../COPYING for licensing terms. */ #include +#include #include #include #include @@ -21,15 +22,9 @@ #include #include #include -#include -#include #include #include -#include #include -#include -#include -#include #include #include #include diff --git a/fs/bio.c b/fs/bio.c index 407f18c90a48..6c196406c90a 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -438,128 +438,6 @@ retry_segments: return 0; } -static int bio_end_io_kio(struct bio *bio, unsigned int bytes_done, int error) -{ - struct kiobuf *kio = (struct kiobuf *) bio->bi_private; - - if (bio->bi_size) - return 1; - - end_kio_request(kio, error); - bio_put(bio); - return 0; -} - -/** - * ll_rw_kio - submit a &struct kiobuf for I/O - * @rw: %READ or %WRITE - * @kio: the kiobuf to do I/O on - * @bdev: target device - * @sector: start location on disk - * - * Description: - * ll_rw_kio will map the page list inside the &struct kiobuf to - * &struct bio and queue them for I/O. The kiobuf given must describe - * a continous range of data, and must be fully prepared for I/O. - **/ -void ll_rw_kio(int rw, struct kiobuf *kio, struct block_device *bdev, sector_t sector) -{ - int i, offset, size, err, map_i, total_nr_pages, nr_pages; - struct bio *bio; - - err = 0; - if ((rw & WRITE) && bdev_read_only(bdev)) { - printk("ll_rw_bio: WRITE to ro device %s\n", bdevname(bdev)); - err = -EPERM; - goto out; - } - - if (!kio->nr_pages) { - err = -EINVAL; - goto out; - } - - /* - * maybe kio is bigger than the max we can easily map into a bio. - * if so, split it up in appropriately sized chunks. - */ - total_nr_pages = kio->nr_pages; - offset = kio->offset & ~PAGE_MASK; - size = kio->length; - - atomic_set(&kio->io_count, 1); - - map_i = 0; - -next_chunk: - nr_pages = BIO_MAX_PAGES; - if (nr_pages > total_nr_pages) - nr_pages = total_nr_pages; - - atomic_inc(&kio->io_count); - - /* - * allocate bio and do initial setup - */ - if ((bio = bio_alloc(GFP_NOIO, nr_pages)) == NULL) { - err = -ENOMEM; - goto out; - } - - bio->bi_sector = sector; - bio->bi_bdev = bdev; - bio->bi_idx = 0; - bio->bi_end_io = bio_end_io_kio; - bio->bi_private = kio; - - for (i = 0; i < nr_pages; i++, map_i++) { - int nbytes = PAGE_SIZE - offset; - - if (nbytes > size) - nbytes = size; - - BUG_ON(kio->maplist[map_i] == NULL); - - /* - * if we can't add this page to the bio, submit for i/o - * and alloc a new one if needed - */ - if (bio_add_page(bio, kio->maplist[map_i], nbytes, offset)) - break; - - /* - * kiobuf only has an offset into the first page - */ - offset = 0; - - sector += nbytes >> 9; - size -= nbytes; - total_nr_pages--; - kio->offset += nbytes; - } - - submit_bio(rw, bio); - - if (total_nr_pages) - goto next_chunk; - - if (size) { - printk("ll_rw_kio: size %d left (kio %d)\n", size, kio->length); - BUG(); - } - -out: - if (err) - kio->errno = err; - - /* - * final atomic_dec of io_count to match our initial setting of 1. - * I/O may or may not have completed at this point, final completion - * handler is only run on last decrement. - */ - end_kio_request(kio, !err); -} - /** * bio_endio - end I/O on a bio * @bio: bio @@ -662,7 +540,6 @@ module_init(init_bio); EXPORT_SYMBOL(bio_alloc); EXPORT_SYMBOL(bio_put); -EXPORT_SYMBOL(ll_rw_kio); EXPORT_SYMBOL(bio_endio); EXPORT_SYMBOL(bio_init); EXPORT_SYMBOL(bio_copy); diff --git a/fs/block_dev.c b/fs/block_dev.c index 3b95ff2d40a4..33fc669b7842 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/fs/buffer.c b/fs/buffer.c index 30c0adeec762..d024b78c3e60 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -2300,65 +2299,6 @@ sector_t generic_block_bmap(struct address_space *mapping, sector_t block, return tmp.b_blocknr; } -/* - * Start I/O on a physical range of kernel memory, defined by a vector - * of kiobuf structs (much like a user-space iovec list). - * - * The kiobuf must already be locked for IO. IO is submitted - * asynchronously: you need to check page->locked and page->uptodate. - * - * It is up to the caller to make sure that there are enough blocks - * passed in to completely map the iobufs to disk. - */ -int brw_kiovec(int rw, int nr, struct kiobuf *iovec[], - struct block_device *bdev, sector_t b[], int size) -{ - int transferred; - int i; - int err; - struct kiobuf * iobuf; - - if (!nr) - return 0; - - /* - * First, do some alignment and validity checks - */ - for (i = 0; i < nr; i++) { - iobuf = iovec[i]; - if ((iobuf->offset & (size-1)) || (iobuf->length & (size-1))) - return -EINVAL; - if (!iobuf->nr_pages) - panic("brw_kiovec: iobuf not initialised"); - } - - /* - * OK to walk down the iovec doing page IO on each page we find. - */ - for (i = 0; i < nr; i++) { - iobuf = iovec[i]; - iobuf->errno = 0; - - ll_rw_kio(rw, iobuf, bdev, b[i] * (size >> 9)); - } - - /* - * now they are all submitted, wait for completion - */ - transferred = 0; - err = 0; - for (i = 0; i < nr; i++) { - iobuf = iovec[i]; - kiobuf_wait_for_io(iobuf); - if (iobuf->errno && !err) - err = iobuf->errno; - if (!err) - transferred += iobuf->length; - } - - return err ? err : transferred; -} - static int end_bio_bh_io_sync(struct bio *bio, unsigned int bytes_done, int err) { struct buffer_head *bh = bio->bi_private; diff --git a/fs/fcntl.c b/fs/fcntl.c index 539711ef1061..c2fc83cdfed6 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include diff --git a/fs/file_table.c b/fs/file_table.c index d6093fc0b1b5..fe6c048c2bab 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include diff --git a/fs/iobuf.c b/fs/iobuf.c deleted file mode 100644 index 62c44534c68a..000000000000 --- a/fs/iobuf.c +++ /dev/null @@ -1,125 +0,0 @@ -/* - * iobuf.c - * - * Keep track of the general-purpose IO-buffer structures used to track - * abstract kernel-space io buffers. - * - */ - -#include -#include - -int end_kio_request(struct kiobuf *kiobuf, int uptodate) -{ - int ret = 1; - - if ((!uptodate) && !kiobuf->errno) - kiobuf->errno = -EIO; - - if (atomic_dec_and_test(&kiobuf->io_count)) { - ret = 0; - if (kiobuf->end_io) - kiobuf->end_io(kiobuf); - wake_up(&kiobuf->wait_queue); - } - - return ret; -} - -static void kiobuf_init(struct kiobuf *iobuf) -{ - init_waitqueue_head(&iobuf->wait_queue); - atomic_set(&iobuf->io_count, 0); - iobuf->array_len = KIO_STATIC_PAGES; - iobuf->maplist = iobuf->map_array; - iobuf->nr_pages = 0; - iobuf->locked = 0; - iobuf->io_count.counter = 0; - iobuf->end_io = NULL; -} - -int alloc_kiovec(int nr, struct kiobuf **bufp) -{ - int i; - struct kiobuf *iobuf; - - for (i = 0; i < nr; i++) { - iobuf = kmalloc(sizeof(struct kiobuf), GFP_KERNEL); - if (!iobuf) { - free_kiovec(i, bufp); - return -ENOMEM; - } - kiobuf_init(iobuf); - bufp[i] = iobuf; - } - - return 0; -} - -void free_kiovec(int nr, struct kiobuf **bufp) -{ - int i; - struct kiobuf *iobuf; - - for (i = 0; i < nr; i++) { - iobuf = bufp[i]; - if (iobuf->locked) - unlock_kiovec(1, &iobuf); - if (iobuf->array_len > KIO_STATIC_PAGES) - kfree (iobuf->maplist); - kfree(bufp[i]); - } -} - -int expand_kiobuf(struct kiobuf *iobuf, int wanted) -{ - struct page ** maplist; - - if (iobuf->array_len >= wanted) - return 0; - - maplist = (struct page **) - kmalloc(wanted * sizeof(struct page **), GFP_KERNEL); - if (!maplist) - return -ENOMEM; - - /* Did it grow while we waited? */ - if (iobuf->array_len >= wanted) { - kfree(maplist); - return 0; - } - - memcpy (maplist, iobuf->maplist, iobuf->array_len * sizeof(struct page **)); - - if (iobuf->array_len > KIO_STATIC_PAGES) - kfree (iobuf->maplist); - - iobuf->maplist = maplist; - iobuf->array_len = wanted; - return 0; -} - - -void kiobuf_wait_for_io(struct kiobuf *kiobuf) -{ - struct task_struct *tsk = current; - DECLARE_WAITQUEUE(wait, tsk); - - if (atomic_read(&kiobuf->io_count) == 0) - return; - - add_wait_queue(&kiobuf->wait_queue, &wait); -repeat: - set_task_state(tsk, TASK_UNINTERRUPTIBLE); - if (atomic_read(&kiobuf->io_count) != 0) { - blk_run_queues(); - schedule(); - if (atomic_read(&kiobuf->io_count) != 0) - goto repeat; - } - tsk->state = TASK_RUNNING; - remove_wait_queue(&kiobuf->wait_queue, &wait); -} - - - diff --git a/fs/open.c b/fs/open.c index 673d20cd1ee8..3e690b0cd50d 100644 --- a/fs/open.c +++ b/fs/open.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/fs/xfs/linux/xfs_aops.c b/fs/xfs/linux/xfs_aops.c index 184d173ee192..e749c3c3bbed 100644 --- a/fs/xfs/linux/xfs_aops.c +++ b/fs/xfs/linux/xfs_aops.c @@ -34,7 +34,6 @@ #include #include #include -#include STATIC int delalloc_convert(struct inode *, struct page *, int, int); diff --git a/fs/xfs/linux/xfs_ioctl.c b/fs/xfs/linux/xfs_ioctl.c index a2b5f0162ccd..5dbf4fd9debf 100644 --- a/fs/xfs/linux/xfs_ioctl.c +++ b/fs/xfs/linux/xfs_ioctl.c @@ -35,7 +35,6 @@ #include #include #include -#include extern int xfs_change_file_space(bhv_desc_t *, int, @@ -605,6 +604,7 @@ xfs_ioctl( * it is set to the file system block size to * avoid having to do block zeroing on short writes. */ +#define KIO_MAX_ATOMIC_IO 512 /* FIXME: what do we really want here? */ da.d_maxiosz = XFS_FSB_TO_B(mp, XFS_B_TO_FSBT(mp, KIO_MAX_ATOMIC_IO << 10)); diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index 71732e1216fc..0760d97cd6f9 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -32,7 +32,6 @@ enum bh_state_bits { #define MAX_BUF_PER_PAGE (PAGE_CACHE_SIZE / 512) struct page; -struct kiobuf; struct buffer_head; struct address_space; typedef void (bh_end_io_t)(struct buffer_head *bh, int uptodate); diff --git a/include/linux/iobuf.h b/include/linux/iobuf.h deleted file mode 100644 index fb147b5c48a7..000000000000 --- a/include/linux/iobuf.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * iobuf.h - * - * Defines the structures used to track abstract kernel-space io buffers. - * - */ - -#ifndef __LINUX_IOBUF_H -#define __LINUX_IOBUF_H - -#include -#include -#include -#include - -/* - * The kiobuf structure describes a physical set of pages reserved - * locked for IO. The reference counts on each page will have been - * incremented, and the flags field will indicate whether or not we have - * pre-locked all of the pages for IO. - * - * kiobufs may be passed in arrays to form a kiovec, but we must - * preserve the property that no page is present more than once over the - * entire iovec. - */ - -#define KIO_MAX_ATOMIC_IO 512 /* in kb */ -#define KIO_STATIC_PAGES (KIO_MAX_ATOMIC_IO / (PAGE_SIZE >> 10) + 1) -#define KIO_MAX_SECTORS (KIO_MAX_ATOMIC_IO * 2) - -/* The main kiobuf struct */ - -struct kiobuf -{ - int nr_pages; /* Pages actually referenced */ - int array_len; /* Space in the allocated lists */ - int offset; /* Offset to start of valid data */ - int length; /* Number of valid bytes of data */ - - /* Keep separate track of the physical addresses and page - * structs involved. If we do IO to a memory-mapped device - * region, there won't necessarily be page structs defined for - * every address. */ - - struct page ** maplist; - - unsigned int locked : 1; /* If set, pages has been locked */ - - /* Always embed enough struct pages for atomic IO */ - struct page * map_array[KIO_STATIC_PAGES]; - sector_t blocks[KIO_MAX_SECTORS]; - - /* Dynamic state for IO completion: */ - atomic_t io_count; /* IOs still in progress */ - int errno; /* Status of completed IO */ - void (*end_io) (struct kiobuf *); /* Completion callback */ - wait_queue_head_t wait_queue; -}; - - -/* mm/memory.c */ - -int map_user_kiobuf(int rw, struct kiobuf *, unsigned long va, size_t len); -void unmap_kiobuf(struct kiobuf *iobuf); -int lock_kiovec(int nr, struct kiobuf *iovec[], int wait); -int unlock_kiovec(int nr, struct kiobuf *iovec[]); -void mark_dirty_kiobuf(struct kiobuf *iobuf, int bytes); - -/* fs/iobuf.c */ - -int end_kio_request(struct kiobuf *, int); -void simple_wakeup_kiobuf(struct kiobuf *); -int alloc_kiovec(int nr, struct kiobuf **); -void free_kiovec(int nr, struct kiobuf **); -int expand_kiobuf(struct kiobuf *, int); -void kiobuf_wait_for_io(struct kiobuf *); -extern int alloc_kiobuf_bhs(struct kiobuf *); -extern void free_kiobuf_bhs(struct kiobuf *); - -/* fs/buffer.c */ - -int brw_kiovec(int rw, int nr, struct kiobuf *iovec[], - struct block_device *bdev, sector_t [], int size); - -/* fs/bio.c */ -void ll_rw_kio(int rw, struct kiobuf *kio, struct block_device *bdev, sector_t block); - -#endif /* __LINUX_IOBUF_H */ diff --git a/init/main.c b/init/main.c index f69c298b9a6f..c6023edc03f3 100644 --- a/init/main.c +++ b/init/main.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include diff --git a/kernel/ksyms.c b/kernel/ksyms.c index bd0a43fcf7f4..4b3e40b10a76 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include @@ -439,18 +438,6 @@ EXPORT_SYMBOL(__br_write_lock); EXPORT_SYMBOL(__br_write_unlock); #endif -/* Kiobufs */ -EXPORT_SYMBOL(alloc_kiovec); -EXPORT_SYMBOL(free_kiovec); -EXPORT_SYMBOL(expand_kiobuf); - -EXPORT_SYMBOL(map_user_kiobuf); -EXPORT_SYMBOL(unmap_kiobuf); -EXPORT_SYMBOL(lock_kiovec); -EXPORT_SYMBOL(unlock_kiovec); -EXPORT_SYMBOL(brw_kiovec); -EXPORT_SYMBOL(kiobuf_wait_for_io); - #ifdef HAVE_DISABLE_HLT EXPORT_SYMBOL(disable_hlt); EXPORT_SYMBOL(enable_hlt); diff --git a/mm/filemap.c b/mm/filemap.c index 4c25b92352c0..b2fbb1cbf90b 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include diff --git a/mm/memory.c b/mm/memory.c index 1c8d8af264f9..70403c0cb902 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -40,7 +40,6 @@ #include #include #include -#include #include #include #include @@ -504,7 +503,7 @@ out: /* * Given a physical address, is there a useful struct page pointing to * it? This may become more complex in the future if we start dealing - * with IO-aperture pages in kiobufs. + * with IO-aperture pages for direct-IO. */ static inline struct page *get_page_map(struct page *page) @@ -589,224 +588,6 @@ out: return i; } -/* - * Force in an entire range of pages from the current process's user VA, - * and pin them in physical memory. - */ -#define dprintk(x...) - -int map_user_kiobuf(int rw, struct kiobuf *iobuf, unsigned long va, size_t len) -{ - int pgcount, err; - struct mm_struct * mm; - - /* Make sure the iobuf is not already mapped somewhere. */ - if (iobuf->nr_pages) - return -EINVAL; - - mm = current->mm; - dprintk ("map_user_kiobuf: begin\n"); - - pgcount = (va + len + PAGE_SIZE - 1)/PAGE_SIZE - va/PAGE_SIZE; - /* mapping 0 bytes is not permitted */ - if (!pgcount) BUG(); - err = expand_kiobuf(iobuf, pgcount); - if (err) - return err; - - iobuf->locked = 0; - iobuf->offset = va & (PAGE_SIZE-1); - iobuf->length = len; - - /* Try to fault in all of the necessary pages */ - down_read(&mm->mmap_sem); - /* rw==READ means read from disk, write into memory area */ - err = get_user_pages(current, mm, va, pgcount, - (rw==READ), 0, iobuf->maplist, NULL); - up_read(&mm->mmap_sem); - if (err < 0) { - unmap_kiobuf(iobuf); - dprintk ("map_user_kiobuf: end %d\n", err); - return err; - } - iobuf->nr_pages = err; - while (pgcount--) { - /* FIXME: flush superflous for rw==READ, - * probably wrong function for rw==WRITE - */ - flush_dcache_page(iobuf->maplist[pgcount]); - } - dprintk ("map_user_kiobuf: end OK\n"); - return 0; -} - -/* - * Mark all of the pages in a kiobuf as dirty - * - * We need to be able to deal with short reads from disk: if an IO error - * occurs, the number of bytes read into memory may be less than the - * size of the kiobuf, so we have to stop marking pages dirty once the - * requested byte count has been reached. - */ - -void mark_dirty_kiobuf(struct kiobuf *iobuf, int bytes) -{ - int index, offset, remaining; - struct page *page; - - index = iobuf->offset >> PAGE_SHIFT; - offset = iobuf->offset & ~PAGE_MASK; - remaining = bytes; - if (remaining > iobuf->length) - remaining = iobuf->length; - - while (remaining > 0 && index < iobuf->nr_pages) { - page = iobuf->maplist[index]; - - if (!PageReserved(page)) - set_page_dirty(page); - - remaining -= (PAGE_SIZE - offset); - offset = 0; - index++; - } -} - -/* - * Unmap all of the pages referenced by a kiobuf. We release the pages, - * and unlock them if they were locked. - */ - -void unmap_kiobuf (struct kiobuf *iobuf) -{ - int i; - struct page *map; - - for (i = 0; i < iobuf->nr_pages; i++) { - map = iobuf->maplist[i]; - if (map) { - if (iobuf->locked) - unlock_page(map); - /* FIXME: cache flush missing for rw==READ - * FIXME: call the correct reference counting function - */ - page_cache_release(map); - } - } - - iobuf->nr_pages = 0; - iobuf->locked = 0; -} - - -/* - * Lock down all of the pages of a kiovec for IO. - * - * If any page is mapped twice in the kiovec, we return the error -EINVAL. - * - * The optional wait parameter causes the lock call to block until all - * pages can be locked if set. If wait==0, the lock operation is - * aborted if any locked pages are found and -EAGAIN is returned. - */ - -int lock_kiovec(int nr, struct kiobuf *iovec[], int wait) -{ - struct kiobuf *iobuf; - int i, j; - struct page *page, **ppage; - int doublepage = 0; - int repeat = 0; - - repeat: - - for (i = 0; i < nr; i++) { - iobuf = iovec[i]; - - if (iobuf->locked) - continue; - - ppage = iobuf->maplist; - for (j = 0; j < iobuf->nr_pages; ppage++, j++) { - page = *ppage; - if (!page) - continue; - - if (TestSetPageLocked(page)) { - while (j--) { - struct page *tmp = *--ppage; - if (tmp) - unlock_page(tmp); - } - goto retry; - } - } - iobuf->locked = 1; - } - - return 0; - - retry: - - /* - * We couldn't lock one of the pages. Undo the locking so far, - * wait on the page we got to, and try again. - */ - - unlock_kiovec(nr, iovec); - if (!wait) - return -EAGAIN; - - /* - * Did the release also unlock the page we got stuck on? - */ - if (!PageLocked(page)) { - /* - * If so, we may well have the page mapped twice - * in the IO address range. Bad news. Of - * course, it _might_ just be a coincidence, - * but if it happens more than once, chances - * are we have a double-mapped page. - */ - if (++doublepage >= 3) - return -EINVAL; - - /* Try again... */ - wait_on_page_locked(page); - } - - if (++repeat < 16) - goto repeat; - return -EAGAIN; -} - -/* - * Unlock all of the pages of a kiovec after IO. - */ - -int unlock_kiovec(int nr, struct kiobuf *iovec[]) -{ - struct kiobuf *iobuf; - int i, j; - struct page *page, **ppage; - - for (i = 0; i < nr; i++) { - iobuf = iovec[i]; - - if (!iobuf->locked) - continue; - iobuf->locked = 0; - - ppage = iobuf->maplist; - for (j = 0; j < iobuf->nr_pages; ppage++, j++) { - page = *ppage; - if (!page) - continue; - unlock_page(page); - } - } - return 0; -} - static inline void zeromap_pte_range(pte_t * pte, unsigned long address, unsigned long size, pgprot_t prot) { -- cgit v1.2.3