diff options
| author | Andy Grover <agrover@groveronline.com> | 2003-03-19 00:09:51 -0800 |
|---|---|---|
| committer | Andy Grover <agrover@groveronline.com> | 2003-03-19 00:09:51 -0800 |
| commit | b39e199b453be169dfd1adec9686978057c1dc0f (patch) | |
| tree | dd7162a8e62d003159d6250069194b48a72b76d0 /drivers | |
| parent | 99d5dfb972d15c955e274d3f67e3862ce8004311 (diff) | |
| parent | d99c48c60bc52561b3125dd1f85db7a77b7031ca (diff) | |
Merge groveronline.com:/root/bk/linux-2.5
into groveronline.com:/root/bk/linux-acpi
Diffstat (limited to 'drivers')
122 files changed, 6652 insertions, 3651 deletions
diff --git a/drivers/base/platform.c b/drivers/base/platform.c index de932ddea39b..4f76511d902e 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -9,7 +9,7 @@ #include <linux/module.h> #include <linux/init.h> -static struct device legacy_bus = { +struct device legacy_bus = { .name = "legacy bus", .bus_id = "legacy", }; @@ -75,5 +75,7 @@ int __init platform_bus_init(void) return bus_register(&platform_bus_type); } +EXPORT_SYMBOL(legacy_bus); +EXPORT_SYMBOL(platform_bus_type); EXPORT_SYMBOL(platform_device_register); EXPORT_SYMBOL(platform_device_unregister); diff --git a/drivers/block/deadline-iosched.c b/drivers/block/deadline-iosched.c index a107ec9682ce..6735d56e2264 100644 --- a/drivers/block/deadline-iosched.c +++ b/drivers/block/deadline-iosched.c @@ -608,6 +608,12 @@ deadline_insert_request(request_queue_t *q, struct request *rq, if (unlikely(rq->flags & REQ_HARDBARRIER)) { DL_INVALIDATE_HASH(dd); q->last_merge = NULL; + + while (deadline_dispatch_requests(dd)) + ; + + list_add_tail(&rq->queuelist, dd->dispatch); + return; } if (unlikely(!blk_fs_request(rq))) { diff --git a/drivers/block/genhd.c b/drivers/block/genhd.c index 04d9cc8a48b5..9812dcf39fe8 100644 --- a/drivers/block/genhd.c +++ b/drivers/block/genhd.c @@ -538,12 +538,20 @@ struct gendisk *alloc_disk(int minors) struct gendisk *get_disk(struct gendisk *disk) { struct module *owner; + struct kobject *kobj; + if (!disk->fops) return NULL; owner = disk->fops->owner; if (owner && !try_module_get(owner)) return NULL; - return to_disk(kobject_get(&disk->kobj)); + kobj = kobject_get(&disk->kobj); + if (kobj == NULL) { + module_put(owner); + return NULL; + } + return to_disk(kobj); + } void put_disk(struct gendisk *disk) diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 691ccc1f0d60..c86baf21c2b2 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -56,11 +56,7 @@ static int batch_requests; unsigned long blk_max_low_pfn, blk_max_pfn; int blk_nohighio = 0; -static struct congestion_state { - wait_queue_head_t wqh; - atomic_t nr_congested_queues; - atomic_t nr_active_queues; -} congestion_states[2]; +static wait_queue_head_t congestion_wqh[2]; /* * Return the threshold (number of free requests) at which the queue is @@ -98,14 +94,12 @@ static inline int queue_congestion_off_threshold(void) static void clear_queue_congested(request_queue_t *q, int rw) { enum bdi_state bit; - struct congestion_state *cs = &congestion_states[rw]; + wait_queue_head_t *wqh = &congestion_wqh[rw]; bit = (rw == WRITE) ? BDI_write_congested : BDI_read_congested; - - if (test_and_clear_bit(bit, &q->backing_dev_info.state)) - atomic_dec(&cs->nr_congested_queues); - if (waitqueue_active(&cs->wqh)) - wake_up(&cs->wqh); + clear_bit(bit, &q->backing_dev_info.state); + if (waitqueue_active(wqh)) + wake_up(wqh); } /* @@ -117,37 +111,7 @@ static void set_queue_congested(request_queue_t *q, int rw) enum bdi_state bit; bit = (rw == WRITE) ? BDI_write_congested : BDI_read_congested; - - if (!test_and_set_bit(bit, &q->backing_dev_info.state)) - atomic_inc(&congestion_states[rw].nr_congested_queues); -} - -/* - * A queue has just put back its last read or write request and has fallen - * idle. - */ -static void clear_queue_active(request_queue_t *q, int rw) -{ - enum bdi_state bit; - - bit = (rw == WRITE) ? BDI_write_active : BDI_read_active; - - if (test_and_clear_bit(bit, &q->backing_dev_info.state)) - atomic_dec(&congestion_states[rw].nr_active_queues); -} - -/* - * A queue has just taken its first read or write request and has become - * active. - */ -static void set_queue_active(request_queue_t *q, int rw) -{ - enum bdi_state bit; - - bit = (rw == WRITE) ? BDI_write_active : BDI_read_active; - - if (!test_and_set_bit(bit, &q->backing_dev_info.state)) - atomic_inc(&congestion_states[rw].nr_active_queues); + set_bit(bit, &q->backing_dev_info.state); } /** @@ -1040,7 +1004,8 @@ void generic_unplug_device(void *data) static void blk_unplug_work(void *data) { - generic_unplug_device(data); + request_queue_t *q = data; + q->unplug_fn(q); } static void blk_unplug_timeout(unsigned long data) @@ -1324,8 +1289,6 @@ static struct request *get_request(request_queue_t *q, int rw) rq = blkdev_free_rq(&rl->free); list_del_init(&rq->queuelist); rq->ref_count = 1; - if (rl->count == queue_nr_requests) - set_queue_active(q, rw); rl->count--; if (rl->count < queue_congestion_on_threshold()) set_queue_congested(q, rw); @@ -1568,8 +1531,6 @@ void __blk_put_request(request_queue_t *q, struct request *req) rl->count++; if (rl->count >= queue_congestion_off_threshold()) clear_queue_congested(q, rw); - if (rl->count == queue_nr_requests) - clear_queue_active(q, rw); if (rl->count >= batch_requests && waitqueue_active(&rl->wait)) wake_up(&rl->wait); } @@ -1604,12 +1565,12 @@ void blk_put_request(struct request *req) void blk_congestion_wait(int rw, long timeout) { DEFINE_WAIT(wait); - struct congestion_state *cs = &congestion_states[rw]; + wait_queue_head_t *wqh = &congestion_wqh[rw]; blk_run_queues(); - prepare_to_wait(&cs->wqh, &wait, TASK_UNINTERRUPTIBLE); + prepare_to_wait(wqh, &wait, TASK_UNINTERRUPTIBLE); io_schedule_timeout(timeout); - finish_wait(&cs->wqh, &wait); + finish_wait(wqh, &wait); } /* @@ -2248,11 +2209,8 @@ int __init blk_dev_init(void) blk_max_low_pfn = max_low_pfn; blk_max_pfn = max_pfn; - for (i = 0; i < ARRAY_SIZE(congestion_states); i++) { - init_waitqueue_head(&congestion_states[i].wqh); - atomic_set(&congestion_states[i].nr_congested_queues, 0); - atomic_set(&congestion_states[i].nr_active_queues, 0); - } + for (i = 0; i < ARRAY_SIZE(congestion_wqh); i++) + init_waitqueue_head(&congestion_wqh[i]); return 0; }; diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index d98a11175015..ebc50ca263d2 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -709,39 +709,20 @@ config NWFLASH If you're not sure, say N. -config INTEL_RNG - tristate "Intel i8x0 Random Number Generator support" +config HW_RANDOM + tristate "Intel/AMD/VIA HW Random Number Generator support" depends on (X86 || IA64) && PCI ---help--- This driver provides kernel-side support for the Random Number - Generator hardware found on Intel i8xx-based motherboards. + Generator hardware found on Intel i8xx-based motherboards, + AMD 76x-based motherboards, and Via Nehemiah CPUs. - Both a character driver, used to read() entropy data, and a timer - function which automatically adds entropy directly into the - kernel pool, are exported by this driver. + Provides a character driver, used to read() entropy data. To compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read <file:Documentation/modules.txt>. The module will be called - i810_rng. - - If unsure, say N. - -config AMD_RNG - tristate "AMD 768 Random Number Generator support" - depends on X86 && PCI - ---help--- - This driver provides kernel-side support for the Random Number - Generator hardware found on AMD 76x based motherboards. - - Both a character driver, used to read() entropy data, and a timer - function which automatically adds entropy directly into the - kernel pool, are exported by this driver. - - To compile this driver as a module ( = code which can be inserted in - and removed from the running kernel whenever you want), say M here - and read <file:Documentation/modules.txt>. The module will be called - amd768_rng. + hw_random. If unsure, say N. diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 147b19429bf5..b03db0a153c0 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -59,8 +59,7 @@ endif obj-$(CONFIG_TOSHIBA) += toshiba.o obj-$(CONFIG_I8K) += i8k.o obj-$(CONFIG_DS1620) += ds1620.o -obj-$(CONFIG_INTEL_RNG) += i810_rng.o -obj-$(CONFIG_AMD_RNG) += amd768_rng.o +obj-$(CONFIG_HW_RANDOM) += hw_random.o obj-$(CONFIG_QIC02_TAPE) += tpqic02.o obj-$(CONFIG_FTAPE) += ftape/ obj-$(CONFIG_H8) += h8.o diff --git a/drivers/char/amd768_rng.c b/drivers/char/amd768_rng.c deleted file mode 100644 index 6b059eef4556..000000000000 --- a/drivers/char/amd768_rng.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - Hardware driver for the AMD 768 Random Number Generator (RNG) - (c) Copyright 2001 Red Hat Inc <alan@redhat.com> - - derived from - - Hardware driver for Intel i810 Random Number Generator (RNG) - Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com> - Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com> - - Please read Documentation/i810_rng.txt for details on use. - - ---------------------------------------------------------- - This software may be used and distributed according to the terms - of the GNU General Public License, incorporated herein by reference. - - */ - - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/fs.h> -#include <linux/init.h> -#include <linux/pci.h> -#include <linux/interrupt.h> -#include <linux/spinlock.h> -#include <linux/random.h> -#include <linux/miscdevice.h> -#include <linux/smp_lock.h> -#include <linux/mm.h> -#include <linux/delay.h> - -#include <asm/io.h> -#include <asm/uaccess.h> - - -/* - * core module and version information - */ -#define RNG_VERSION "0.1.0" -#define RNG_MODULE_NAME "amd768_rng" -#define RNG_DRIVER_NAME RNG_MODULE_NAME " hardware driver " RNG_VERSION -#define PFX RNG_MODULE_NAME ": " - - -/* - * debugging macros - */ -#undef RNG_DEBUG /* define to enable copious debugging info */ - -#ifdef RNG_DEBUG -/* note: prints function name for you */ -#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) -#else -#define DPRINTK(fmt, args...) -#endif - -#undef RNG_NDEBUG /* define to disable lightweight runtime checks */ -#ifdef RNG_NDEBUG -#define assert(expr) -#else -#define assert(expr) \ - if(!(expr)) { \ - printk( "Assertion failed! %s,%s,%s,line=%d\n", \ - #expr,__FILE__,__FUNCTION__,__LINE__); \ - } -#endif - -#define RNG_MISCDEV_MINOR 183 /* official */ - -/* - * various RNG status variables. they are globals - * as we only support a single RNG device - */ - -static u32 pmbase; /* PMxx I/O base */ -static struct semaphore rng_open_sem; /* Semaphore for serializing rng_open/release */ - - -/* - * inlined helper functions for accessing RNG registers - */ - -static inline int rng_data_present (void) -{ - return inl(pmbase+0xF4) & 1; -} - - -static inline int rng_data_read (void) -{ - return inl(pmbase+0xF0); -} - -static int rng_dev_open (struct inode *inode, struct file *filp) -{ - if ((filp->f_mode & FMODE_READ) == 0) - return -EINVAL; - if (filp->f_mode & FMODE_WRITE) - return -EINVAL; - - /* wait for device to become free */ - if (filp->f_flags & O_NONBLOCK) { - if (down_trylock (&rng_open_sem)) - return -EAGAIN; - } else { - if (down_interruptible (&rng_open_sem)) - return -ERESTARTSYS; - } - return 0; -} - - -static int rng_dev_release (struct inode *inode, struct file *filp) -{ - up(&rng_open_sem); - return 0; -} - - -static ssize_t rng_dev_read (struct file *filp, char *buf, size_t size, - loff_t * offp) -{ - static spinlock_t rng_lock = SPIN_LOCK_UNLOCKED; - int have_data; - u32 data = 0; - ssize_t ret = 0; - - while (size) { - spin_lock(&rng_lock); - - have_data = 0; - if (rng_data_present()) { - data = rng_data_read(); - have_data = 4; - } - - spin_unlock (&rng_lock); - - while (have_data > 0) { - if (put_user((u8)data, buf++)) { - ret = ret ? : -EFAULT; - break; - } - size--; - ret++; - have_data--; - data>>=8; - } - - if (filp->f_flags & O_NONBLOCK) - return ret ? : -EAGAIN; - - if(need_resched()) - { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); - } - else - udelay(200); /* FIXME: We could poll for 250uS ?? */ - - if (signal_pending (current)) - return ret ? : -ERESTARTSYS; - } - return ret; -} - - -static struct file_operations rng_chrdev_ops = { - .owner = THIS_MODULE, - .open = rng_dev_open, - .release = rng_dev_release, - .read = rng_dev_read, -}; - - -static struct miscdevice rng_miscdev = { - RNG_MISCDEV_MINOR, - RNG_MODULE_NAME, - &rng_chrdev_ops, -}; - - -/* - * rng_init_one - look for and attempt to init a single RNG - */ -static int __init rng_init_one (struct pci_dev *dev) -{ - int rc; - u8 rnen; - - DPRINTK ("ENTER\n"); - - rc = misc_register (&rng_miscdev); - if (rc) { - printk (KERN_ERR PFX "cannot register misc device\n"); - DPRINTK ("EXIT, returning %d\n", rc); - goto err_out; - } - - pci_read_config_dword(dev, 0x58, &pmbase); - - pmbase&=0x0000FF00; - - if(pmbase == 0) - { - printk (KERN_ERR PFX "power management base not set\n"); - DPRINTK ("EXIT, returning %d\n", rc); - goto err_out_free_miscdev; - } - - pci_read_config_byte(dev, 0x40, &rnen); - rnen|=(1<<7); /* RNG on */ - pci_write_config_byte(dev, 0x40, rnen); - - pci_read_config_byte(dev, 0x41, &rnen); - rnen|=(1<<7); /* PMIO enable */ - pci_write_config_byte(dev, 0x41, rnen); - - printk(KERN_INFO PFX "AMD768 system management I/O registers at 0x%X.\n", pmbase); - DPRINTK ("EXIT, returning 0\n"); - return 0; - -err_out_free_miscdev: - misc_deregister (&rng_miscdev); -err_out: - return rc; -} - - -/* - * Data for PCI driver interface - * - * This data only exists for exporting the supported - * PCI ids via MODULE_DEVICE_TABLE. We do not actually - * register a pci_driver, because someone else might one day - * want to register another driver on the same PCI id. - */ -static struct pci_device_id rng_pci_tbl[] __initdata = { - { 0x1022, 0x7443, PCI_ANY_ID, PCI_ANY_ID, }, - { 0, }, -}; -MODULE_DEVICE_TABLE (pci, rng_pci_tbl); - - -MODULE_AUTHOR("Alan Cox, Jeff Garzik, Philipp Rumpf, Matt Sottek"); -MODULE_DESCRIPTION("AMD 768 Random Number Generator (RNG) driver"); -MODULE_LICENSE("GPL"); - - -/* - * rng_init - initialize RNG module - */ -static int __init rng_init (void) -{ - int rc; - struct pci_dev *pdev; - - DPRINTK ("ENTER\n"); - - init_MUTEX (&rng_open_sem); - - pci_for_each_dev(pdev) { - if (pci_match_device (rng_pci_tbl, pdev) != NULL) - goto match; - } - - DPRINTK ("EXIT, returning -ENODEV\n"); - return -ENODEV; - -match: - rc = rng_init_one (pdev); - if (rc) - return rc; - - printk (KERN_INFO RNG_DRIVER_NAME " loaded\n"); - - DPRINTK ("EXIT, returning 0\n"); - return 0; -} - - -/* - * rng_init - shutdown RNG module - */ -static void __exit rng_cleanup (void) -{ - DPRINTK ("ENTER\n"); - misc_deregister (&rng_miscdev); - DPRINTK ("EXIT\n"); -} - - -module_init (rng_init); -module_exit (rng_cleanup); diff --git a/drivers/char/hw_random.c b/drivers/char/hw_random.c new file mode 100644 index 000000000000..a3a483e16aeb --- /dev/null +++ b/drivers/char/hw_random.c @@ -0,0 +1,630 @@ +/* + Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG) + (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com> + + derived from + + Hardware driver for the AMD 768 Random Number Generator (RNG) + (c) Copyright 2001 Red Hat Inc <alan@redhat.com> + + derived from + + Hardware driver for Intel i810 Random Number Generator (RNG) + Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com> + Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com> + + Please read Documentation/hw_random.txt for details on use. + + ---------------------------------------------------------- + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + */ + + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/random.h> +#include <linux/miscdevice.h> +#include <linux/smp_lock.h> +#include <linux/mm.h> +#include <linux/delay.h> + +#ifdef __i386__ +#include <asm/msr.h> +#include <asm/cpufeature.h> +#endif + +#include <asm/io.h> +#include <asm/uaccess.h> + + +/* + * core module and version information + */ +#define RNG_VERSION "0.9.0" +#define RNG_MODULE_NAME "hw_random" +#define RNG_DRIVER_NAME RNG_MODULE_NAME " hardware driver " RNG_VERSION +#define PFX RNG_MODULE_NAME ": " + + +/* + * debugging macros + */ +#undef RNG_DEBUG /* define to enable copious debugging info */ + +#ifdef RNG_DEBUG +/* note: prints function name for you */ +#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) +#else +#define DPRINTK(fmt, args...) +#endif + +#define RNG_NDEBUG /* define to disable lightweight runtime checks */ +#ifdef RNG_NDEBUG +#define assert(expr) +#else +#define assert(expr) \ + if(!(expr)) { \ + printk( "Assertion failed! %s,%s,%s,line=%d\n", \ + #expr,__FILE__,__FUNCTION__,__LINE__); \ + } +#endif + +#define RNG_MISCDEV_MINOR 183 /* official */ + +static int rng_dev_open (struct inode *inode, struct file *filp); +static ssize_t rng_dev_read (struct file *filp, char *buf, size_t size, + loff_t * offp); + +static int __init intel_init (struct pci_dev *dev); +static void intel_cleanup(void); +static unsigned int intel_data_present (void); +static u32 intel_data_read (void); + +static int __init amd_init (struct pci_dev *dev); +static void amd_cleanup(void); +static unsigned int amd_data_present (void); +static u32 amd_data_read (void); + +static int __init via_init(struct pci_dev *dev); +static void via_cleanup(void); +static unsigned int via_data_present (void); +static u32 via_data_read (void); + +struct rng_operations { + int (*init) (struct pci_dev *dev); + void (*cleanup) (void); + unsigned int (*data_present) (void); + u32 (*data_read) (void); + unsigned int n_bytes; /* number of bytes per ->data_read */ +}; +static struct rng_operations *rng_ops; + +static struct file_operations rng_chrdev_ops = { + .owner = THIS_MODULE, + .open = rng_dev_open, + .read = rng_dev_read, +}; + + +static struct miscdevice rng_miscdev = { + RNG_MISCDEV_MINOR, + RNG_MODULE_NAME, + &rng_chrdev_ops, +}; + +enum { + rng_hw_none, + rng_hw_intel, + rng_hw_amd, + rng_hw_via, +}; + +static struct rng_operations rng_vendor_ops[] __initdata = { + /* rng_hw_none */ + { }, + + /* rng_hw_intel */ + { intel_init, intel_cleanup, intel_data_present, + intel_data_read, 1 }, + + /* rng_hw_amd */ + { amd_init, amd_cleanup, amd_data_present, amd_data_read, 4 }, + + /* rng_hw_via */ + { via_init, via_cleanup, via_data_present, via_data_read, 1 }, +}; + +/* + * Data for PCI driver interface + * + * This data only exists for exporting the supported + * PCI ids via MODULE_DEVICE_TABLE. We do not actually + * register a pci_driver, because someone else might one day + * want to register another driver on the same PCI id. + */ +static struct pci_device_id rng_pci_tbl[] __initdata = { + { 0x1022, 0x7443, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_amd }, + + { 0x8086, 0x2418, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel }, + { 0x8086, 0x2428, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel }, + { 0x8086, 0x2448, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel }, + { 0x8086, 0x244e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel }, + { 0x8086, 0x245e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel }, + + { 0, }, /* terminate list */ +}; +MODULE_DEVICE_TABLE (pci, rng_pci_tbl); + + +/*********************************************************************** + * + * Intel RNG operations + * + */ + +/* + * RNG registers (offsets from rng_mem) + */ +#define INTEL_RNG_HW_STATUS 0 +#define INTEL_RNG_PRESENT 0x40 +#define INTEL_RNG_ENABLED 0x01 +#define INTEL_RNG_STATUS 1 +#define INTEL_RNG_DATA_PRESENT 0x01 +#define INTEL_RNG_DATA 2 + +/* + * Magic address at which Intel PCI bridges locate the RNG + */ +#define INTEL_RNG_ADDR 0xFFBC015F +#define INTEL_RNG_ADDR_LEN 3 + +/* token to our ioremap'd RNG register area */ +static void *rng_mem; + +static inline u8 intel_hwstatus (void) +{ + assert (rng_mem != NULL); + return readb (rng_mem + INTEL_RNG_HW_STATUS); +} + +static inline u8 intel_hwstatus_set (u8 hw_status) +{ + assert (rng_mem != NULL); + writeb (hw_status, rng_mem + INTEL_RNG_HW_STATUS); + return intel_hwstatus (); +} + +static unsigned int intel_data_present(void) +{ + assert (rng_mem != NULL); + + return (readb (rng_mem + INTEL_RNG_STATUS) & INTEL_RNG_DATA_PRESENT) ? + 1 : 0; +} + +static u32 intel_data_read(void) +{ + assert (rng_mem != NULL); + + return readb (rng_mem + INTEL_RNG_DATA); +} + +static int __init intel_init (struct pci_dev *dev) +{ + int rc; + u8 hw_status; + + DPRINTK ("ENTER\n"); + + rng_mem = ioremap (INTEL_RNG_ADDR, INTEL_RNG_ADDR_LEN); + if (rng_mem == NULL) { + printk (KERN_ERR PFX "cannot ioremap RNG Memory\n"); + rc = -EBUSY; + goto err_out; + } + + /* Check for Intel 82802 */ + hw_status = intel_hwstatus (); + if ((hw_status & INTEL_RNG_PRESENT) == 0) { + printk (KERN_ERR PFX "RNG not detected\n"); + rc = -ENODEV; + goto err_out_free_map; + } + + /* turn RNG h/w on, if it's off */ + if ((hw_status & INTEL_RNG_ENABLED) == 0) + hw_status = intel_hwstatus_set (hw_status | INTEL_RNG_ENABLED); + if ((hw_status & INTEL_RNG_ENABLED) == 0) { + printk (KERN_ERR PFX "cannot enable RNG, aborting\n"); + rc = -EIO; + goto err_out_free_map; + } + + DPRINTK ("EXIT, returning 0\n"); + return 0; + +err_out_free_map: + iounmap (rng_mem); + rng_mem = NULL; +err_out: + DPRINTK ("EXIT, returning %d\n", rc); + return rc; +} + +static void intel_cleanup(void) +{ + u8 hw_status; + + hw_status = intel_hwstatus (); + if (hw_status & INTEL_RNG_ENABLED) + intel_hwstatus_set (hw_status & ~INTEL_RNG_ENABLED); + else + printk(KERN_WARNING PFX "unusual: RNG already disabled\n"); + iounmap(rng_mem); + rng_mem = NULL; +} + +/*********************************************************************** + * + * AMD RNG operations + * + */ + +static u32 pmbase; /* PMxx I/O base */ +static struct pci_dev *amd_dev; + +static unsigned int amd_data_present (void) +{ + return inl(pmbase + 0xF4) & 1; +} + + +static u32 amd_data_read (void) +{ + return inl(pmbase + 0xF0); +} + +static int __init amd_init (struct pci_dev *dev) +{ + int rc; + u8 rnen; + + DPRINTK ("ENTER\n"); + + pci_read_config_dword(dev, 0x58, &pmbase); + + pmbase &= 0x0000FF00; + + if (pmbase == 0) + { + printk (KERN_ERR PFX "power management base not set\n"); + rc = -EIO; + goto err_out; + } + + pci_read_config_byte(dev, 0x40, &rnen); + rnen |= (1 << 7); /* RNG on */ + pci_write_config_byte(dev, 0x40, rnen); + + pci_read_config_byte(dev, 0x41, &rnen); + rnen |= (1 << 7); /* PMIO enable */ + pci_write_config_byte(dev, 0x41, rnen); + + printk(KERN_INFO PFX "AMD768 system management I/O registers at 0x%X.\n", pmbase); + + amd_dev = dev; + + DPRINTK ("EXIT, returning 0\n"); + return 0; + +err_out: + DPRINTK ("EXIT, returning %d\n", rc); + return rc; +} + +static void amd_cleanup(void) +{ + u8 rnen; + + pci_read_config_byte(amd_dev, 0x40, &rnen); + rnen &= ~(1 << 7); /* RNG off */ + pci_write_config_byte(amd_dev, 0x40, rnen); + + /* FIXME: twiddle pmio, also? */ +} + +/*********************************************************************** + * + * VIA RNG operations + * + */ + +enum { + VIA_STRFILT_CNT_SHIFT = 16, + VIA_STRFILT_FAIL = (1 << 15), + VIA_STRFILT_ENABLE = (1 << 14), + VIA_RAWBITS_ENABLE = (1 << 13), + VIA_RNG_ENABLE = (1 << 6), + VIA_XSTORE_CNT_MASK = 0x0F, + + VIA_RNG_CHUNK_8 = 0x00, /* 64 rand bits, 64 stored bits */ + VIA_RNG_CHUNK_4 = 0x01, /* 32 rand bits, 32 stored bits */ + VIA_RNG_CHUNK_4_MASK = 0xFFFFFFFF, + VIA_RNG_CHUNK_2 = 0x02, /* 16 rand bits, 32 stored bits */ + VIA_RNG_CHUNK_2_MASK = 0xFFFF, + VIA_RNG_CHUNK_1 = 0x03, /* 8 rand bits, 32 stored bits */ + VIA_RNG_CHUNK_1_MASK = 0xFF, +}; + +u32 via_rng_datum; + +/* + * Investigate using the 'rep' prefix to obtain 32 bits of random data + * in one insn. The upside is potentially better performance. The + * downside is that the instruction becomes no longer atomic. Due to + * this, just like familiar issues with /dev/random itself, the worst + * case of a 'rep xstore' could potentially pause a cpu for an + * unreasonably long time. In practice, this condition would likely + * only occur when the hardware is failing. (or so we hope :)) + * + * Another possible performance boost may come from simply buffering + * until we have 4 bytes, thus returning a u32 at a time, + * instead of the current u8-at-a-time. + */ + +static inline u32 xstore(u32 *addr, u32 edx_in) +{ + u32 eax_out; + + asm(".byte 0x0F,0xA7,0xC0 /* xstore %%edi (addr=%0) */" + :"=m"(*addr), "=a"(eax_out) + :"D"(addr), "d"(edx_in)); + + return eax_out; +} + +static unsigned int via_data_present(void) +{ + u32 bytes_out; + + /* We choose the recommended 1-byte-per-instruction RNG rate, + * for greater randomness at the expense of speed. Larger + * values 2, 4, or 8 bytes-per-instruction yield greater + * speed at lesser randomness. + * + * If you change this to another VIA_CHUNK_n, you must also + * change the ->n_bytes values in rng_vendor_ops[] tables. + * VIA_CHUNK_8 requires further code changes. + * + * A copy of MSR_VIA_RNG is placed in eax_out when xstore + * completes. + */ + via_rng_datum = 0; /* paranoia, not really necessary */ + bytes_out = xstore(&via_rng_datum, VIA_RNG_CHUNK_1) & VIA_XSTORE_CNT_MASK; + if (bytes_out == 0) + return 0; + + return 1; +} + +static u32 via_data_read(void) +{ + return via_rng_datum; +} + +static int __init via_init(struct pci_dev *dev) +{ + u32 lo, hi, old_lo; + + /* Control the RNG via MSR. Tread lightly and pay very close + * close attention to values written, as the reserved fields + * are documented to be "undefined and unpredictable"; but it + * does not say to write them as zero, so I make a guess that + * we restore the values we find in the register. + */ + rdmsr(MSR_VIA_RNG, lo, hi); + + old_lo = lo; + lo &= ~(0x7f << VIA_STRFILT_CNT_SHIFT); + lo &= ~VIA_XSTORE_CNT_MASK; + lo &= ~(VIA_STRFILT_ENABLE | VIA_STRFILT_FAIL | VIA_RAWBITS_ENABLE); + lo |= VIA_RNG_ENABLE; + + if (lo != old_lo) + wrmsr(MSR_VIA_RNG, lo, hi); + + /* perhaps-unnecessary sanity check; remove after testing if + unneeded */ + rdmsr(MSR_VIA_RNG, lo, hi); + if ((lo & VIA_RNG_ENABLE) == 0) { + printk(KERN_ERR PFX "cannot enable VIA C3 RNG, aborting\n"); + return -ENODEV; + } + + return 0; +} + +static void via_cleanup(void) +{ + u32 lo, hi; + + rdmsr(MSR_VIA_RNG, lo, hi); + lo &= ~VIA_RNG_ENABLE; + wrmsr(MSR_VIA_RNG, lo, hi); +} + + +/*********************************************************************** + * + * /dev/hwrandom character device handling (major 10, minor 183) + * + */ + +static int rng_dev_open (struct inode *inode, struct file *filp) +{ + /* enforce read-only access to this chrdev */ + if ((filp->f_mode & FMODE_READ) == 0) + return -EINVAL; + if (filp->f_mode & FMODE_WRITE) + return -EINVAL; + + return 0; +} + + +static ssize_t rng_dev_read (struct file *filp, char *buf, size_t size, + loff_t * offp) +{ + static spinlock_t rng_lock = SPIN_LOCK_UNLOCKED; + unsigned int have_data; + u32 data = 0; + ssize_t ret = 0; + + while (size) { + spin_lock(&rng_lock); + + have_data = 0; + if (rng_ops->data_present()) { + data = rng_ops->data_read(); + have_data = rng_ops->n_bytes; + } + + spin_unlock (&rng_lock); + + while (have_data > 0) { + if (put_user((u8)data, buf++)) { + ret = ret ? : -EFAULT; + break; + } + size--; + ret++; + have_data--; + data>>=8; + } + + if (filp->f_flags & O_NONBLOCK) + return ret ? : -EAGAIN; + + if(need_resched()) + { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + else + udelay(200); /* FIXME: We could poll for 250uS ?? */ + + if (signal_pending (current)) + return ret ? : -ERESTARTSYS; + } + return ret; +} + + + +/* + * rng_init_one - look for and attempt to init a single RNG + */ +static int __init rng_init_one (struct pci_dev *dev) +{ + int rc; + + DPRINTK ("ENTER\n"); + + assert(rng_ops != NULL); + + rc = rng_ops->init(dev); + if (rc) + goto err_out; + + rc = misc_register (&rng_miscdev); + if (rc) { + printk (KERN_ERR PFX "misc device register failed\n"); + goto err_out_cleanup_hw; + } + + DPRINTK ("EXIT, returning 0\n"); + return 0; + +err_out_cleanup_hw: + rng_ops->cleanup(); +err_out: + DPRINTK ("EXIT, returning %d\n", rc); + return rc; +} + + + +MODULE_AUTHOR("The Linux Kernel team"); +MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver"); +MODULE_LICENSE("GPL"); + + +/* + * rng_init - initialize RNG module + */ +static int __init rng_init (void) +{ + int rc; + struct pci_dev *pdev; + const struct pci_device_id *ent; + + DPRINTK ("ENTER\n"); + + /* Probe for Intel, AMD RNGs */ + pci_for_each_dev(pdev) { + ent = pci_match_device (rng_pci_tbl, pdev); + if (ent) { + rng_ops = &rng_vendor_ops[ent->driver_data]; + goto match; + } + } + +#ifdef __i386__ + /* Probe for VIA RNG */ + if (cpu_has_xstore) { + rng_ops = &rng_vendor_ops[rng_hw_via]; + pdev = NULL; + goto match; + } +#endif + + DPRINTK ("EXIT, returning -ENODEV\n"); + return -ENODEV; + +match: + rc = rng_init_one (pdev); + if (rc) + return rc; + + printk (KERN_INFO RNG_DRIVER_NAME " loaded\n"); + + DPRINTK ("EXIT, returning 0\n"); + return 0; +} + + +/* + * rng_init - shutdown RNG module + */ +static void __exit rng_cleanup (void) +{ + DPRINTK ("ENTER\n"); + + misc_deregister (&rng_miscdev); + + if (rng_ops->cleanup) + rng_ops->cleanup(); + + DPRINTK ("EXIT\n"); +} + + +module_init (rng_init); +module_exit (rng_cleanup); diff --git a/drivers/char/i810_rng.c b/drivers/char/i810_rng.c deleted file mode 100644 index 52671e065ede..000000000000 --- a/drivers/char/i810_rng.c +++ /dev/null @@ -1,404 +0,0 @@ -/* - - Hardware driver for Intel i810 Random Number Generator (RNG) - Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com> - Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com> - - Driver Web site: http://sourceforge.net/projects/gkernel/ - - Please read Documentation/i810_rng.txt for details on use. - - ---------------------------------------------------------- - - This software may be used and distributed according to the terms - of the GNU General Public License, incorporated herein by reference. - - */ - - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/fs.h> -#include <linux/init.h> -#include <linux/pci.h> -#include <linux/interrupt.h> -#include <linux/spinlock.h> -#include <linux/random.h> -#include <linux/miscdevice.h> -#include <linux/smp_lock.h> -#include <linux/mm.h> -#include <linux/delay.h> - -#include <asm/io.h> -#include <asm/uaccess.h> - - -/* - * core module and version information - */ -#define RNG_VERSION "0.9.8" -#define RNG_MODULE_NAME "i810_rng" -#define RNG_DRIVER_NAME RNG_MODULE_NAME " hardware driver " RNG_VERSION -#define PFX RNG_MODULE_NAME ": " - - -/* - * debugging macros - */ -#undef RNG_DEBUG /* define to enable copious debugging info */ - -#ifdef RNG_DEBUG -/* note: prints function name for you */ -#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) -#else -#define DPRINTK(fmt, args...) -#endif - -#undef RNG_NDEBUG /* define to disable lightweight runtime checks */ -#ifdef RNG_NDEBUG -#define assert(expr) -#else -#define assert(expr) \ - if(!(expr)) { \ - printk( "Assertion failed! %s,%s,%s,line=%d\n", \ - #expr,__FILE__,__FUNCTION__,__LINE__); \ - } -#endif - - -/* - * RNG registers (offsets from rng_mem) - */ -#define RNG_HW_STATUS 0 -#define RNG_PRESENT 0x40 -#define RNG_ENABLED 0x01 -#define RNG_STATUS 1 -#define RNG_DATA_PRESENT 0x01 -#define RNG_DATA 2 - -/* - * Magic address at which Intel PCI bridges locate the RNG - */ -#define RNG_ADDR 0xFFBC015F -#define RNG_ADDR_LEN 3 - -#define RNG_MISCDEV_MINOR 183 /* official */ - -/* - * various RNG status variables. they are globals - * as we only support a single RNG device - */ -static void *rng_mem; /* token to our ioremap'd RNG register area */ -static struct semaphore rng_open_sem; /* Semaphore for serializing rng_open/release */ - - -/* - * inlined helper functions for accessing RNG registers - */ -static inline u8 rng_hwstatus (void) -{ - assert (rng_mem != NULL); - return readb (rng_mem + RNG_HW_STATUS); -} - -static inline u8 rng_hwstatus_set (u8 hw_status) -{ - assert (rng_mem != NULL); - writeb (hw_status, rng_mem + RNG_HW_STATUS); - return rng_hwstatus (); -} - - -static inline int rng_data_present (void) -{ - assert (rng_mem != NULL); - - return (readb (rng_mem + RNG_STATUS) & RNG_DATA_PRESENT) ? 1 : 0; -} - - -static inline int rng_data_read (void) -{ - assert (rng_mem != NULL); - - return readb (rng_mem + RNG_DATA); -} - -/* - * rng_enable - enable the RNG hardware - */ - -static int rng_enable (void) -{ - int rc = 0; - u8 hw_status, new_status; - - DPRINTK ("ENTER\n"); - - hw_status = rng_hwstatus (); - - if ((hw_status & RNG_ENABLED) == 0) { - new_status = rng_hwstatus_set (hw_status | RNG_ENABLED); - - if (new_status & RNG_ENABLED) - printk (KERN_INFO PFX "RNG h/w enabled\n"); - else { - printk (KERN_ERR PFX "Unable to enable the RNG\n"); - rc = -EIO; - } - } - - DPRINTK ("EXIT, returning %d\n", rc); - return rc; -} - -/* - * rng_disable - disable the RNG hardware - */ - -static void rng_disable(void) -{ - u8 hw_status, new_status; - - DPRINTK ("ENTER\n"); - - hw_status = rng_hwstatus (); - - if (hw_status & RNG_ENABLED) { - new_status = rng_hwstatus_set (hw_status & ~RNG_ENABLED); - - if ((new_status & RNG_ENABLED) == 0) - printk (KERN_INFO PFX "RNG h/w disabled\n"); - else { - printk (KERN_ERR PFX "Unable to disable the RNG\n"); - } - } - - DPRINTK ("EXIT\n"); -} - -static int rng_dev_open (struct inode *inode, struct file *filp) -{ - int rc; - - if ((filp->f_mode & FMODE_READ) == 0) - return -EINVAL; - if (filp->f_mode & FMODE_WRITE) - return -EINVAL; - - /* wait for device to become free */ - if (filp->f_flags & O_NONBLOCK) { - if (down_trylock (&rng_open_sem)) - return -EAGAIN; - } else { - if (down_interruptible (&rng_open_sem)) - return -ERESTARTSYS; - } - - rc = rng_enable (); - if (rc) { - up (&rng_open_sem); - return rc; - } - - return 0; -} - - -static int rng_dev_release (struct inode *inode, struct file *filp) -{ - rng_disable (); - up (&rng_open_sem); - return 0; -} - - -static ssize_t rng_dev_read (struct file *filp, char *buf, size_t size, - loff_t * offp) -{ - static spinlock_t rng_lock = SPIN_LOCK_UNLOCKED; - int have_data; - u8 data = 0; - ssize_t ret = 0; - - while (size) { - spin_lock (&rng_lock); - - have_data = 0; - if (rng_data_present ()) { - data = rng_data_read (); - have_data = 1; - } - - spin_unlock (&rng_lock); - - if (have_data) { - if (put_user (data, buf++)) { - ret = ret ? : -EFAULT; - break; - } - size--; - ret++; - } - - if (filp->f_flags & O_NONBLOCK) - return ret ? : -EAGAIN; - - if (need_resched()) - { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); - } - else - udelay(200); - - if (signal_pending (current)) - return ret ? : -ERESTARTSYS; - } - - return ret; -} - - -static struct file_operations rng_chrdev_ops = { - .owner = THIS_MODULE, - .open = rng_dev_open, - .release = rng_dev_release, - .read = rng_dev_read, -}; - - -static struct miscdevice rng_miscdev = { - RNG_MISCDEV_MINOR, - RNG_MODULE_NAME, - &rng_chrdev_ops, -}; - - -/* - * rng_init_one - look for and attempt to init a single RNG - */ -static int __init rng_init_one (struct pci_dev *dev) -{ - int rc; - u8 hw_status; - - DPRINTK ("ENTER\n"); - - rc = misc_register (&rng_miscdev); - if (rc) { - printk (KERN_ERR PFX "cannot register misc device\n"); - DPRINTK ("EXIT, returning %d\n", rc); - goto err_out; - } - - rng_mem = ioremap (RNG_ADDR, RNG_ADDR_LEN); - if (rng_mem == NULL) { - printk (KERN_ERR PFX "cannot ioremap RNG Memory\n"); - DPRINTK ("EXIT, returning -EBUSY\n"); - rc = -EBUSY; - goto err_out_free_miscdev; - } - - /* Check for Intel 82802 */ - hw_status = rng_hwstatus (); - if ((hw_status & RNG_PRESENT) == 0) { - printk (KERN_ERR PFX "RNG not detected\n"); - DPRINTK ("EXIT, returning -ENODEV\n"); - rc = -ENODEV; - goto err_out_free_map; - } - - /* turn RNG h/w off, if it's on */ - if (hw_status & RNG_ENABLED) - hw_status = rng_hwstatus_set (hw_status & ~RNG_ENABLED); - if (hw_status & RNG_ENABLED) { - printk (KERN_ERR PFX "cannot disable RNG, aborting\n"); - goto err_out_free_map; - } - - DPRINTK ("EXIT, returning 0\n"); - return 0; - -err_out_free_map: - iounmap (rng_mem); -err_out_free_miscdev: - misc_deregister (&rng_miscdev); -err_out: - return rc; -} - - -/* - * Data for PCI driver interface - * - * This data only exists for exporting the supported - * PCI ids via MODULE_DEVICE_TABLE. We do not actually - * register a pci_driver, because someone else might one day - * want to register another driver on the same PCI id. - */ -static struct pci_device_id rng_pci_tbl[] __initdata = { - { 0x8086, 0x2418, PCI_ANY_ID, PCI_ANY_ID, }, - { 0x8086, 0x2428, PCI_ANY_ID, PCI_ANY_ID, }, - { 0x8086, 0x2448, PCI_ANY_ID, PCI_ANY_ID, }, - { 0x8086, 0x244e, PCI_ANY_ID, PCI_ANY_ID, }, - { 0x8086, 0x245e, PCI_ANY_ID, PCI_ANY_ID, }, - { 0, }, -}; -MODULE_DEVICE_TABLE (pci, rng_pci_tbl); - - -MODULE_AUTHOR("Jeff Garzik, Philipp Rumpf, Matt Sottek"); -MODULE_DESCRIPTION("Intel i8xx chipset Random Number Generator (RNG) driver"); -MODULE_LICENSE("GPL"); - - -/* - * rng_init - initialize RNG module - */ -static int __init rng_init (void) -{ - int rc; - struct pci_dev *pdev; - - DPRINTK ("ENTER\n"); - - init_MUTEX (&rng_open_sem); - - pci_for_each_dev(pdev) { - if (pci_match_device (rng_pci_tbl, pdev) != NULL) - goto match; - } - - DPRINTK ("EXIT, returning -ENODEV\n"); - return -ENODEV; - -match: - rc = rng_init_one (pdev); - if (rc) - return rc; - - printk (KERN_INFO RNG_DRIVER_NAME " loaded\n"); - - DPRINTK ("EXIT, returning 0\n"); - return 0; -} - - -/* - * rng_init - shutdown RNG module - */ -static void __exit rng_cleanup (void) -{ - DPRINTK ("ENTER\n"); - - misc_deregister (&rng_miscdev); - - iounmap (rng_mem); - - DPRINTK ("EXIT\n"); -} - - -module_init (rng_init); -module_exit (rng_cleanup); diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 32488d71037f..c712200fd2ea 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1034,7 +1034,9 @@ static void release_mem(struct tty_struct *tty, int idx) } o_tty->magic = 0; (*o_tty->driver.refcount)--; + file_list_lock(); list_del(&o_tty->tty_files); + file_list_unlock(); free_tty_struct(o_tty); } @@ -1046,7 +1048,9 @@ static void release_mem(struct tty_struct *tty, int idx) } tty->magic = 0; (*tty->driver.refcount)--; + file_list_lock(); list_del(&tty->tty_files); + file_list_unlock(); module_put(tty->driver.owner); free_tty_struct(tty); } @@ -2235,14 +2239,19 @@ struct device_class tty_devclass = { }; EXPORT_SYMBOL(tty_devclass); +static int __init tty_devclass_init(void) +{ + return devclass_register(&tty_devclass); +} + +postcore_initcall(tty_devclass_init); + /* * Ok, now we can initialize the rest of the tty devices and can count * on memory allocations, interrupts etc.. */ void __init tty_init(void) { - devclass_register(&tty_devclass); - /* * dev_tty_driver and dev_console_driver are actually magic * devices which get redirected at open time. Nevertheless, diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c index cda2f20d8308..b29ecd8bca5f 100644 --- a/drivers/char/vt_ioctl.c +++ b/drivers/char/vt_ioctl.c @@ -191,38 +191,56 @@ do_kbkeycode_ioctl(int cmd, struct kbkeycode *user_kbkc, int perm) static inline int do_kdgkb_ioctl(int cmd, struct kbsentry *user_kdgkb, int perm) { - struct kbsentry tmp; + struct kbsentry *kbs; char *p; u_char *q; int sz; int delta; char *first_free, *fj, *fnw; int i, j, k; + int ret; + + kbs = kmalloc(sizeof(*kbs), GFP_KERNEL); + if (!kbs) { + ret = -ENOMEM; + goto reterr; + } /* we mostly copy too much here (512bytes), but who cares ;) */ - if (copy_from_user(&tmp, user_kdgkb, sizeof(struct kbsentry))) - return -EFAULT; - tmp.kb_string[sizeof(tmp.kb_string)-1] = '\0'; - if (tmp.kb_func >= MAX_NR_FUNC) - return -EINVAL; - i = tmp.kb_func; + if (copy_from_user(kbs, user_kdgkb, sizeof(struct kbsentry))) { + ret = -EFAULT; + goto reterr; + } + kbs->kb_string[sizeof(kbs->kb_string)-1] = '\0'; + if (kbs->kb_func >= MAX_NR_FUNC) { + ret = -EINVAL; + goto reterr; + } + i = kbs->kb_func; switch (cmd) { case KDGKBSENT: - sz = sizeof(tmp.kb_string) - 1; /* sz should have been + sz = sizeof(kbs->kb_string) - 1; /* sz should have been a struct member */ q = user_kdgkb->kb_string; p = func_table[i]; if(p) for ( ; *p && sz; p++, sz--) - if (put_user(*p, q++)) - return -EFAULT; - if (put_user('\0', q)) - return -EFAULT; + if (put_user(*p, q++)) { + ret = -EFAULT; + goto reterr; + } + if (put_user('\0', q)) { + ret = -EFAULT; + goto reterr; + } + kfree(kbs); return ((p && *p) ? -EOVERFLOW : 0); case KDSKBSENT: - if (!perm) - return -EPERM; + if (!perm) { + ret = -EPERM; + goto reterr; + } q = func_table[i]; first_free = funcbufptr + (funcbufsize - funcbufleft); @@ -233,7 +251,7 @@ do_kdgkb_ioctl(int cmd, struct kbsentry *user_kdgkb, int perm) else fj = first_free; - delta = (q ? -strlen(q) : 1) + strlen(tmp.kb_string); + delta = (q ? -strlen(q) : 1) + strlen(kbs->kb_string); if (delta <= funcbufleft) { /* it fits in current buf */ if (j < MAX_NR_FUNC) { memmove(fj + delta, fj, first_free - fj); @@ -249,8 +267,10 @@ do_kdgkb_ioctl(int cmd, struct kbsentry *user_kdgkb, int perm) while (sz < funcbufsize - funcbufleft + delta) sz <<= 1; fnw = (char *) kmalloc(sz, GFP_KERNEL); - if(!fnw) - return -ENOMEM; + if(!fnw) { + ret = -ENOMEM; + goto reterr; + } if (!q) func_table[i] = fj; @@ -272,17 +292,19 @@ do_kdgkb_ioctl(int cmd, struct kbsentry *user_kdgkb, int perm) funcbufleft = funcbufleft - delta + sz - funcbufsize; funcbufsize = sz; } - strcpy(func_table[i], tmp.kb_string); + strcpy(func_table[i], kbs->kb_string); break; } - return 0; + ret = 0; +reterr: + kfree(kbs); + return ret; } static inline int -do_fontx_ioctl(int cmd, struct consolefontdesc *user_cfd, int perm) +do_fontx_ioctl(int cmd, struct consolefontdesc *user_cfd, int perm, struct console_font_op *op) { struct consolefontdesc cfdarg; - struct console_font_op op; int i; if (copy_from_user(&cfdarg, user_cfd, sizeof(struct consolefontdesc))) @@ -292,25 +314,25 @@ do_fontx_ioctl(int cmd, struct consolefontdesc *user_cfd, int perm) case PIO_FONTX: if (!perm) return -EPERM; - op.op = KD_FONT_OP_SET; - op.flags = KD_FONT_FLAG_OLD; - op.width = 8; - op.height = cfdarg.charheight; - op.charcount = cfdarg.charcount; - op.data = cfdarg.chardata; - return con_font_op(fg_console, &op); + op->op = KD_FONT_OP_SET; + op->flags = KD_FONT_FLAG_OLD; + op->width = 8; + op->height = cfdarg.charheight; + op->charcount = cfdarg.charcount; + op->data = cfdarg.chardata; + return con_font_op(fg_console, op); case GIO_FONTX: { - op.op = KD_FONT_OP_GET; - op.flags = KD_FONT_FLAG_OLD; - op.width = 8; - op.height = cfdarg.charheight; - op.charcount = cfdarg.charcount; - op.data = cfdarg.chardata; - i = con_font_op(fg_console, &op); + op->op = KD_FONT_OP_GET; + op->flags = KD_FONT_FLAG_OLD; + op->width = 8; + op->height = cfdarg.charheight; + op->charcount = cfdarg.charcount; + op->data = cfdarg.chardata; + i = con_font_op(fg_console, op); if (i) return i; - cfdarg.charheight = op.height; - cfdarg.charcount = op.charcount; + cfdarg.charheight = op->height; + cfdarg.charcount = op->charcount; if (copy_to_user(user_cfd, &cfdarg, sizeof(struct consolefontdesc))) return -EFAULT; return 0; @@ -355,6 +377,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, unsigned char ucval; struct kbd_struct * kbd; struct vt_struct *vt = (struct vt_struct *)tty->driver_data; + struct console_font_op op; /* used in multiple places here */ console = vt->vc_num; @@ -860,7 +883,6 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, } case PIO_FONT: { - struct console_font_op op; if (!perm) return -EPERM; op.op = KD_FONT_OP_SET; @@ -873,7 +895,6 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, } case GIO_FONT: { - struct console_font_op op; op.op = KD_FONT_OP_GET; op.flags = KD_FONT_FLAG_OLD; op.width = 8; @@ -893,7 +914,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, case PIO_FONTX: case GIO_FONTX: - return do_fontx_ioctl(cmd, (struct consolefontdesc *)arg, perm); + return do_fontx_ioctl(cmd, (struct consolefontdesc *)arg, perm, &op); case PIO_FONTRESET: { @@ -906,7 +927,6 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, return -ENOSYS; #else { - struct console_font_op op; op.op = KD_FONT_OP_SET_DEFAULT; op.data = NULL; i = con_font_op(fg_console, &op); @@ -918,7 +938,6 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, } case KDFONTOP: { - struct console_font_op op; if (copy_from_user(&op, (void *) arg, sizeof(op))) return -EFAULT; if (!perm && op.op != KD_FONT_OP_GET) diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index dcd25b1c59c3..67abe170a8de 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -5,6 +5,20 @@ menu "I2C Hardware Sensors Mainboard support" +config I2C_ALI15X3 + tristate " ALI 15x3" + depends on I2C && I2C_PROC && PCI && EXPERIMENTAL + help + If you say yes to this option, support will be included for the + Acer Labs Inc. (ALI) M1514 and M1543 motherboard I2C interfaces. + + This can also be built as a module. If so, the module will be + called i2c-ali15x3. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + config I2C_AMD756 tristate " AMD 756/766" depends on I2C && I2C_PROC @@ -39,5 +53,51 @@ config I2C_AMD8111 in the lm_sensors package, which you can download at http://www.lm-sensors.nu +config I2C_I801 + tristate " Intel 801" + depends on I2C && I2C_PROC && PCI && EXPERIMENTAL + help + If you say yes to this option, support will be included for the Intel + 801 family of mainboard I2C interfaces. Specifically, the following + versions of the chipset is supported: + 82801AA + 82801AB + 82801BA + 82801CA/CAM + 82801DB + + This can also be built as a module which can be inserted and removed + while the kernel is running. If you want to compile it as a module, + say M here and read <file:Documentation/modules.txt>. + + The module will be called i2c-i801. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +config I2C_PIIX4 + tristate " Intel PIIX4" + depends on I2C && I2C_PROC && PCI && EXPERIMENTAL + help + If you say yes to this option, support will be included for the Intel + PIIX4 family of mainboard I2C interfaces. Specifically, the following + versions of the chipset is supported: + Intel PIIX4 + Intel 440MX + Serverworks OSB4 + Serverworks CSB5 + SMSC Victory66 + + This can also be built as a module which can be inserted and removed + while the kernel is running. If you want to compile it as a module, + say M here and read <file:Documentation/modules.txt>. + + The module will be called i2c-piix4. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + endmenu diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 181b1d97e0a9..c8e61d4a6f88 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -2,5 +2,8 @@ # Makefile for the kernel hardware sensors bus drivers. # +obj-$(CONFIG_I2C_ALI15X3) += i2c-ali15x3.o obj-$(CONFIG_I2C_AMD756) += i2c-amd756.o obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o +obj-$(CONFIG_I2C_I801) += i2c-i801.o +obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o diff --git a/drivers/i2c/busses/i2c-ali15x3.c b/drivers/i2c/busses/i2c-ali15x3.c new file mode 100644 index 000000000000..8d7f372e109f --- /dev/null +++ b/drivers/i2c/busses/i2c-ali15x3.c @@ -0,0 +1,575 @@ +/* + ali15x3.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1999 Frodo Looijaard <frodol@dds.nl> and + Philip Edelbrock <phil@netroedge.com> and + Mark D. Studebaker <mdsxyz123@yahoo.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + This is the driver for the SMB Host controller on + Acer Labs Inc. (ALI) M1541 and M1543C South Bridges. + + The M1543C is a South bridge for desktop systems. + The M1533 is a South bridge for portable systems. + They are part of the following ALI chipsets: + "Aladdin Pro 2": Includes the M1621 Slot 1 North bridge + with AGP and 100MHz CPU Front Side bus + "Aladdin V": Includes the M1541 Socket 7 North bridge + with AGP and 100MHz CPU Front Side bus + "Aladdin IV": Includes the M1541 Socket 7 North bridge + with host bus up to 83.3 MHz. + For an overview of these chips see http://www.acerlabs.com + + The M1533/M1543C devices appear as FOUR separate devices + on the PCI bus. An output of lspci will show something similar + to the following: + + 00:02.0 USB Controller: Acer Laboratories Inc. M5237 + 00:03.0 Bridge: Acer Laboratories Inc. M7101 + 00:07.0 ISA bridge: Acer Laboratories Inc. M1533 + 00:0f.0 IDE interface: Acer Laboratories Inc. M5229 + + The SMB controller is part of the 7101 device, which is an + ACPI-compliant Power Management Unit (PMU). + + The whole 7101 device has to be enabled for the SMB to work. + You can't just enable the SMB alone. + The SMB and the ACPI have separate I/O spaces. + We make sure that the SMB is enabled. We leave the ACPI alone. + + This driver controls the SMB Host only. + The SMB Slave controller on the M15X3 is not enabled. + + This driver does not use interrupts. +*/ + +/* Note: we assume there can only be one ALI15X3, with one SMBus interface */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/stddef.h> +#include <linux/sched.h> +#include <linux/ioport.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <asm/io.h> + +/* ALI15X3 SMBus address offsets */ +#define SMBHSTSTS (0 + ali15x3_smba) +#define SMBHSTCNT (1 + ali15x3_smba) +#define SMBHSTSTART (2 + ali15x3_smba) +#define SMBHSTCMD (7 + ali15x3_smba) +#define SMBHSTADD (3 + ali15x3_smba) +#define SMBHSTDAT0 (4 + ali15x3_smba) +#define SMBHSTDAT1 (5 + ali15x3_smba) +#define SMBBLKDAT (6 + ali15x3_smba) + +/* PCI Address Constants */ +#define SMBCOM 0x004 +#define SMBBA 0x014 +#define SMBATPC 0x05B /* used to unlock xxxBA registers */ +#define SMBHSTCFG 0x0E0 +#define SMBSLVC 0x0E1 +#define SMBCLK 0x0E2 +#define SMBREV 0x008 + +/* Other settings */ +#define MAX_TIMEOUT 200 /* times 1/100 sec */ +#define ALI15X3_SMB_IOSIZE 32 + +/* this is what the Award 1004 BIOS sets them to on a ASUS P5A MB. + We don't use these here. If the bases aren't set to some value we + tell user to upgrade BIOS and we fail. +*/ +#define ALI15X3_SMB_DEFAULTBASE 0xE800 + +/* ALI15X3 address lock bits */ +#define ALI15X3_LOCK 0x06 + +/* ALI15X3 command constants */ +#define ALI15X3_ABORT 0x02 +#define ALI15X3_T_OUT 0x04 +#define ALI15X3_QUICK 0x00 +#define ALI15X3_BYTE 0x10 +#define ALI15X3_BYTE_DATA 0x20 +#define ALI15X3_WORD_DATA 0x30 +#define ALI15X3_BLOCK_DATA 0x40 +#define ALI15X3_BLOCK_CLR 0x80 + +/* ALI15X3 status register bits */ +#define ALI15X3_STS_IDLE 0x04 +#define ALI15X3_STS_BUSY 0x08 +#define ALI15X3_STS_DONE 0x10 +#define ALI15X3_STS_DEV 0x20 /* device error */ +#define ALI15X3_STS_COLL 0x40 /* collision or no response */ +#define ALI15X3_STS_TERM 0x80 /* terminated by abort */ +#define ALI15X3_STS_ERR 0xE0 /* all the bad error bits */ + + +/* If force_addr is set to anything different from 0, we forcibly enable + the device at the given address. */ +static int force_addr = 0; +MODULE_PARM(force_addr, "i"); +MODULE_PARM_DESC(force_addr, + "Initialize the base address of the i2c controller"); + + +static void ali15x3_do_pause(unsigned int amount); +static int ali15x3_transaction(void); + +static unsigned short ali15x3_smba = 0; + +int ali15x3_setup(struct pci_dev *ALI15X3_dev) +{ + u16 a; + unsigned char temp; + +/* Check the following things: + - SMB I/O address is initialized + - Device is enabled + - We can use the addresses +*/ + +/* Unlock the register. + The data sheet says that the address registers are read-only + if the lock bits are 1, but in fact the address registers + are zero unless you clear the lock bits. +*/ + pci_read_config_byte(ALI15X3_dev, SMBATPC, &temp); + if (temp & ALI15X3_LOCK) { + temp &= ~ALI15X3_LOCK; + pci_write_config_byte(ALI15X3_dev, SMBATPC, temp); + } + +/* Determine the address of the SMBus area */ + pci_read_config_word(ALI15X3_dev, SMBBA, &ali15x3_smba); + ali15x3_smba &= (0xffff & ~(ALI15X3_SMB_IOSIZE - 1)); + if (ali15x3_smba == 0 && force_addr == 0) { + printk + ("i2c-ali15x3.o: ALI15X3_smb region uninitialized - upgrade BIOS or use force_addr=0xaddr\n"); + return -ENODEV; + } + + if(force_addr) + ali15x3_smba = force_addr & ~(ALI15X3_SMB_IOSIZE - 1); + + if (check_region(ali15x3_smba, ALI15X3_SMB_IOSIZE)) { + printk + ("i2c-ali15x3.o: ALI15X3_smb region 0x%x already in use!\n", + ali15x3_smba); + return -ENODEV; + } + + if(force_addr) { + printk("i2c-ali15x3.o: forcing ISA address 0x%04X\n", ali15x3_smba); + if (PCIBIOS_SUCCESSFUL != + pci_write_config_word(ALI15X3_dev, SMBBA, ali15x3_smba)) + return -ENODEV; + if (PCIBIOS_SUCCESSFUL != + pci_read_config_word(ALI15X3_dev, SMBBA, &a)) + return -ENODEV; + if ((a & ~(ALI15X3_SMB_IOSIZE - 1)) != ali15x3_smba) { + /* make sure it works */ + printk("i2c-ali15x3.o: force address failed - not supported?\n"); + return -ENODEV; + } + } +/* check if whole device is enabled */ + pci_read_config_byte(ALI15X3_dev, SMBCOM, &temp); + if ((temp & 1) == 0) { + printk("i2c-ali15x3: enabling SMBus device\n"); + pci_write_config_byte(ALI15X3_dev, SMBCOM, temp | 0x01); + } + +/* Is SMB Host controller enabled? */ + pci_read_config_byte(ALI15X3_dev, SMBHSTCFG, &temp); + if ((temp & 1) == 0) { + printk("i2c-ali15x3: enabling SMBus controller\n"); + pci_write_config_byte(ALI15X3_dev, SMBHSTCFG, temp | 0x01); + } + +/* set SMB clock to 74KHz as recommended in data sheet */ + pci_write_config_byte(ALI15X3_dev, SMBCLK, 0x20); + + /* Everything is happy, let's grab the memory and set things up. */ + request_region(ali15x3_smba, ALI15X3_SMB_IOSIZE, "ali15x3-smb"); + +#ifdef DEBUG +/* + The interrupt routing for SMB is set up in register 0x77 in the + 1533 ISA Bridge device, NOT in the 7101 device. + Don't bother with finding the 1533 device and reading the register. + if ((....... & 0x0F) == 1) + printk("i2c-ali15x3.o: ALI15X3 using Interrupt 9 for SMBus.\n"); +*/ + pci_read_config_byte(ALI15X3_dev, SMBREV, &temp); + printk("i2c-ali15x3.o: SMBREV = 0x%X\n", temp); + printk("i2c-ali15x3.o: ALI15X3_smba = 0x%X\n", ali15x3_smba); +#endif /* DEBUG */ + + return 0; +} + + +/* Internally used pause function */ +void ali15x3_do_pause(unsigned int amount) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(amount); +} + +/* Another internally used function */ +int ali15x3_transaction(void) +{ + int temp; + int result = 0; + int timeout = 0; + +#ifdef DEBUG + printk + ("i2c-ali15x3.o: Transaction (pre): STS=%02x, CNT=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, " + "DAT1=%02x\n", inb_p(SMBHSTSTS), inb_p(SMBHSTCNT), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBHSTDAT1)); +#endif + + /* get status */ + temp = inb_p(SMBHSTSTS); + + /* Make sure the SMBus host is ready to start transmitting */ + /* Check the busy bit first */ + if (temp & ALI15X3_STS_BUSY) { +/* + If the host controller is still busy, it may have timed out in the previous transaction, + resulting in a "SMBus Timeout" printk. + I've tried the following to reset a stuck busy bit. + 1. Reset the controller with an ABORT command. + (this doesn't seem to clear the controller if an external device is hung) + 2. Reset the controller and the other SMBus devices with a T_OUT command. + (this clears the host busy bit if an external device is hung, + but it comes back upon a new access to a device) + 3. Disable and reenable the controller in SMBHSTCFG + Worst case, nothing seems to work except power reset. +*/ +/* Abort - reset the host controller */ +/* +#ifdef DEBUG + printk("i2c-ali15x3.o: Resetting host controller to clear busy condition\n",temp); +#endif + outb_p(ALI15X3_ABORT, SMBHSTCNT); + temp = inb_p(SMBHSTSTS); + if (temp & ALI15X3_STS_BUSY) { +*/ + +/* + Try resetting entire SMB bus, including other devices - + This may not work either - it clears the BUSY bit but + then the BUSY bit may come back on when you try and use the chip again. + If that's the case you are stuck. +*/ + printk + ("i2c-ali15x3.o: Resetting entire SMB Bus to clear busy condition (%02x)\n", + temp); + outb_p(ALI15X3_T_OUT, SMBHSTCNT); + temp = inb_p(SMBHSTSTS); + } +/* + } +*/ + + /* now check the error bits and the busy bit */ + if (temp & (ALI15X3_STS_ERR | ALI15X3_STS_BUSY)) { + /* do a clear-on-write */ + outb_p(0xFF, SMBHSTSTS); + if ((temp = inb_p(SMBHSTSTS)) & + (ALI15X3_STS_ERR | ALI15X3_STS_BUSY)) { + /* this is probably going to be correctable only by a power reset + as one of the bits now appears to be stuck */ + /* This may be a bus or device with electrical problems. */ + printk + ("i2c-ali15x3.o: SMBus reset failed! (0x%02x) - controller or device on bus is probably hung\n", + temp); + return -1; + } + } else { + /* check and clear done bit */ + if (temp & ALI15X3_STS_DONE) { + outb_p(temp, SMBHSTSTS); + } + } + + /* start the transaction by writing anything to the start register */ + outb_p(0xFF, SMBHSTSTART); + + /* We will always wait for a fraction of a second! */ + timeout = 0; + do { + ali15x3_do_pause(1); + temp = inb_p(SMBHSTSTS); + } while ((!(temp & (ALI15X3_STS_ERR | ALI15X3_STS_DONE))) + && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { + result = -1; + printk("i2c-ali15x3.o: SMBus Timeout!\n"); + } + + if (temp & ALI15X3_STS_TERM) { + result = -1; +#ifdef DEBUG + printk("i2c-ali15x3.o: Error: Failed bus transaction\n"); +#endif + } + +/* + Unfortunately the ALI SMB controller maps "no response" and "bus collision" + into a single bit. No reponse is the usual case so don't + do a printk. + This means that bus collisions go unreported. +*/ + if (temp & ALI15X3_STS_COLL) { + result = -1; +#ifdef DEBUG + printk + ("i2c-ali15x3.o: Error: no response or bus collision ADD=%02x\n", + inb_p(SMBHSTADD)); +#endif + } + +/* haven't ever seen this */ + if (temp & ALI15X3_STS_DEV) { + result = -1; + printk("i2c-ali15x3.o: Error: device error\n"); + } +#ifdef DEBUG + printk + ("i2c-ali15x3.o: Transaction (post): STS=%02x, CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTSTS), inb_p(SMBHSTCNT), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBHSTDAT1)); +#endif + return result; +} + +/* Return -1 on error. */ +s32 ali15x3_access(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data * data) +{ + int i, len; + int temp; + int timeout; + +/* clear all the bits (clear-on-write) */ + outb_p(0xFF, SMBHSTSTS); +/* make sure SMBus is idle */ + temp = inb_p(SMBHSTSTS); + for (timeout = 0; + (timeout < MAX_TIMEOUT) && !(temp & ALI15X3_STS_IDLE); + timeout++) { + ali15x3_do_pause(1); + temp = inb_p(SMBHSTSTS); + } + if (timeout >= MAX_TIMEOUT) { + printk("i2c-ali15x3.o: Idle wait Timeout! STS=0x%02x\n", + temp); + } + + switch (size) { + case I2C_SMBUS_PROC_CALL: + printk + ("i2c-ali15x3.o: I2C_SMBUS_PROC_CALL not supported!\n"); + return -1; + case I2C_SMBUS_QUICK: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = ALI15X3_QUICK; + break; + case I2C_SMBUS_BYTE: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD); + size = ALI15X3_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0); + size = ALI15X3_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + size = ALI15X3_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 0) { + len = 0; + data->block[0] = len; + } + if (len > 32) { + len = 32; + data->block[0] = len; + } + outb_p(len, SMBHSTDAT0); + outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= len; i++) + outb_p(data->block[i], SMBBLKDAT); + } + size = ALI15X3_BLOCK_DATA; + break; + } + + outb_p(size, SMBHSTCNT); /* output command */ + + if (ali15x3_transaction()) /* Error in transaction */ + return -1; + + if ((read_write == I2C_SMBUS_WRITE) || (size == ALI15X3_QUICK)) + return 0; + + + switch (size) { + case ALI15X3_BYTE: /* Result put in SMBHSTDAT0 */ + data->byte = inb_p(SMBHSTDAT0); + break; + case ALI15X3_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case ALI15X3_WORD_DATA: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + case ALI15X3_BLOCK_DATA: + len = inb_p(SMBHSTDAT0); + if (len > 32) + len = 32; + data->block[0] = len; + outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= data->block[0]; i++) { + data->block[i] = inb_p(SMBBLKDAT); +#ifdef DEBUG + printk + ("i2c-ali15x3.o: Blk: len=%d, i=%d, data=%02x\n", + len, i, data->block[i]); +#endif /* DEBUG */ + } + break; + } + return 0; +} + + +u32 ali15x3_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; +} + +static struct i2c_algorithm smbus_algorithm = { + .name = "Non-I2C SMBus adapter", + .id = I2C_ALGO_SMBUS, + .smbus_xfer = ali15x3_access, + .functionality = ali15x3_func, +}; + +static struct i2c_adapter ali15x3_adapter = { + .owner = THIS_MODULE, + .name = "unset", + .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_ALI15X3, + .algo = &smbus_algorithm, +}; + + + +static struct pci_device_id ali15x3_ids[] __devinitdata = { + { + .vendor = PCI_VENDOR_ID_AL, + .device = PCI_DEVICE_ID_AL_M7101, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { 0, } +}; + +static int __devinit ali15x3_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + if (ali15x3_setup(dev)) { + printk + ("i2c-ali15x3.o: ALI15X3 not detected, module not inserted.\n"); + + return -ENODEV; + } + + /* set up the driverfs linkage to our parent device */ + ali15x3_adapter.dev.parent = &dev->dev; + + sprintf(ali15x3_adapter.name, "SMBus ALI15X3 adapter at %04x", + ali15x3_smba); + return i2c_add_adapter(&ali15x3_adapter); +} + +static void __devexit ali15x3_remove(struct pci_dev *dev) +{ + i2c_del_adapter(&ali15x3_adapter); +} + +static struct pci_driver ali15x3_driver = { + .name = "ali15x3 smbus", + .id_table = ali15x3_ids, + .probe = ali15x3_probe, + .remove = __devexit_p(ali15x3_remove), +}; + +static int __init i2c_ali15x3_init(void) +{ + printk("i2c-ali15x3.o version %s (%s)\n", I2C_VERSION, I2C_DATE); + return pci_module_init(&ali15x3_driver); +} + + +static void __exit i2c_ali15x3_exit(void) +{ + pci_unregister_driver(&ali15x3_driver); + release_region(ali15x3_smba, ALI15X3_SMB_IOSIZE); +} + + + +MODULE_AUTHOR + ("Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com>, and Mark D. Studebaker <mdsxyz123@yahoo.com>"); +MODULE_DESCRIPTION("ALI15X3 SMBus driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_ali15x3_init); +module_exit(i2c_ali15x3_exit); diff --git a/drivers/i2c/busses/i2c-amd756.c b/drivers/i2c/busses/i2c-amd756.c index b2627572924e..d4bc60ebac3d 100644 --- a/drivers/i2c/busses/i2c-amd756.c +++ b/drivers/i2c/busses/i2c-amd756.c @@ -375,6 +375,9 @@ static int __devinit amd756_probe(struct pci_dev *pdev, printk(KERN_DEBUG DRV_NAME ": AMD756_smba = 0x%X\n", amd756_ioport); #endif + /* set up the driverfs linkage to our parent device */ + amd756_adapter.dev.parent = &pdev->dev; + sprintf(amd756_adapter.name, "SMBus AMD75x adapter at %04x", amd756_ioport); diff --git a/drivers/i2c/busses/i2c-amd8111.c b/drivers/i2c/busses/i2c-amd8111.c index a3adbe770f8c..07f22e6e8495 100644 --- a/drivers/i2c/busses/i2c-amd8111.c +++ b/drivers/i2c/busses/i2c-amd8111.c @@ -363,6 +363,9 @@ static int __devinit amd8111_probe(struct pci_dev *dev, const struct pci_device_ smbus->adapter.algo = &smbus_algorithm; smbus->adapter.algo_data = smbus; + /* set up the driverfs linkage to our parent device */ + smbus->adapter.dev.parent = &dev->dev; + error = i2c_add_adapter(&smbus->adapter); if (error) goto out_release_region; @@ -389,7 +392,7 @@ static void __devexit amd8111_remove(struct pci_dev *dev) } static struct pci_driver amd8111_driver = { - .name = "amd8111 smbus 2.0", + .name = "amd8111 smbus", .id_table = amd8111_ids, .probe = amd8111_probe, .remove = __devexit_p(amd8111_remove), diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c new file mode 100644 index 000000000000..b1556e08ce54 --- /dev/null +++ b/drivers/i2c/busses/i2c-i801.c @@ -0,0 +1,715 @@ +/* + i801.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>, + Philip Edelbrock <phil@netroedge.com>, and Mark D. Studebaker + <mdsxyz123@yahoo.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + SUPPORTED DEVICES PCI ID + 82801AA 2413 + 82801AB 2423 + 82801BA 2443 + 82801CA/CAM 2483 + 82801DB 24C3 (HW PEC supported, 32 byte buffer not supported) + + This driver supports several versions of Intel's I/O Controller Hubs (ICH). + For SMBus support, they are similar to the PIIX4 and are part + of Intel's '810' and other chipsets. + See the doc/busses/i2c-i801 file for details. + I2C Block Read and Process Call are not supported. +*/ + +/* Note: we assume there can only be one I801, with one SMBus interface */ + +/* #define DEBUG 1 */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/stddef.h> +#include <linux/sched.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <asm/io.h> + +MODULE_LICENSE("GPL"); + +#ifdef I2C_FUNC_SMBUS_BLOCK_DATA_PEC +#define HAVE_PEC +#endif + +#ifndef PCI_DEVICE_ID_INTEL_82801CA_SMBUS +#define PCI_DEVICE_ID_INTEL_82801CA_SMBUS 0x2483 +#endif + +#ifndef PCI_DEVICE_ID_INTEL_82801DB_SMBUS +#define PCI_DEVICE_ID_INTEL_82801DB_SMBUS 0x24C3 +#endif + +static int supported[] = {PCI_DEVICE_ID_INTEL_82801AA_3, + PCI_DEVICE_ID_INTEL_82801AB_3, + PCI_DEVICE_ID_INTEL_82801BA_2, + PCI_DEVICE_ID_INTEL_82801CA_SMBUS, + PCI_DEVICE_ID_INTEL_82801DB_SMBUS, + 0 }; + +/* I801 SMBus address offsets */ +#define SMBHSTSTS (0 + i801_smba) +#define SMBHSTCNT (2 + i801_smba) +#define SMBHSTCMD (3 + i801_smba) +#define SMBHSTADD (4 + i801_smba) +#define SMBHSTDAT0 (5 + i801_smba) +#define SMBHSTDAT1 (6 + i801_smba) +#define SMBBLKDAT (7 + i801_smba) +#define SMBPEC (8 + i801_smba) /* ICH4 only */ +#define SMBAUXSTS (12 + i801_smba) /* ICH4 only */ +#define SMBAUXCTL (13 + i801_smba) /* ICH4 only */ + +/* PCI Address Constants */ +#define SMBBA 0x020 +#define SMBHSTCFG 0x040 +#define SMBREV 0x008 + +/* Host configuration bits for SMBHSTCFG */ +#define SMBHSTCFG_HST_EN 1 +#define SMBHSTCFG_SMB_SMI_EN 2 +#define SMBHSTCFG_I2C_EN 4 + +/* Other settings */ +#define MAX_TIMEOUT 100 +#define ENABLE_INT9 0 /* set to 0x01 to enable - untested */ + +/* I801 command constants */ +#define I801_QUICK 0x00 +#define I801_BYTE 0x04 +#define I801_BYTE_DATA 0x08 +#define I801_WORD_DATA 0x0C +#define I801_PROC_CALL 0x10 /* later chips only, unimplemented */ +#define I801_BLOCK_DATA 0x14 +#define I801_I2C_BLOCK_DATA 0x18 /* unimplemented */ +#define I801_BLOCK_LAST 0x34 +#define I801_I2C_BLOCK_LAST 0x38 /* unimplemented */ +#define I801_START 0x40 +#define I801_PEC_EN 0x80 /* ICH4 only */ + +/* insmod parameters */ + +/* If force_addr is set to anything different from 0, we forcibly enable + the I801 at the given address. VERY DANGEROUS! */ +static int force_addr = 0; +MODULE_PARM(force_addr, "i"); +MODULE_PARM_DESC(force_addr, + "Forcibly enable the I801 at the given address. " + "EXTREMELY DANGEROUS!"); + + + + + +static void i801_do_pause(unsigned int amount); +static int i801_transaction(void); +static int i801_block_transaction(union i2c_smbus_data *data, + char read_write, int command); + +static unsigned short i801_smba; +static struct pci_dev *I801_dev; +static int isich4; + +static int i801_setup(struct pci_dev *dev) +{ + int error_return = 0; + int *num = supported; + unsigned char temp; + + /* Note: we keep on searching until we have found 'function 3' */ + if(PCI_FUNC(dev->devfn) != 3) + return -ENODEV; + + I801_dev = dev; + isich4 = *num == PCI_DEVICE_ID_INTEL_82801DB_SMBUS; + +/* Determine the address of the SMBus areas */ + if (force_addr) { + i801_smba = force_addr & 0xfff0; + } else { + pci_read_config_word(I801_dev, SMBBA, &i801_smba); + i801_smba &= 0xfff0; + if(i801_smba == 0) { + printk(KERN_ERR "i2c-i801.o: SMB base address uninitialized - upgrade BIOS or use force_addr=0xaddr\n"); + return -ENODEV; + } + } + + if (check_region(i801_smba, (isich4 ? 16 : 8))) { + printk + (KERN_ERR "i2c-i801.o: I801_smb region 0x%x already in use!\n", + i801_smba); + error_return = -ENODEV; + goto END; + } + + pci_read_config_byte(I801_dev, SMBHSTCFG, &temp); + temp &= ~SMBHSTCFG_I2C_EN; /* SMBus timing */ + pci_write_config_byte(I801_dev, SMBHSTCFG, temp); +/* If force_addr is set, we program the new address here. Just to make + sure, we disable the device first. */ + if (force_addr) { + pci_write_config_byte(I801_dev, SMBHSTCFG, temp & 0xfe); + pci_write_config_word(I801_dev, SMBBA, i801_smba); + pci_write_config_byte(I801_dev, SMBHSTCFG, temp | 0x01); + printk + (KERN_WARNING "i2c-i801.o: WARNING: I801 SMBus interface set to new " + "address %04x!\n", i801_smba); + } else if ((temp & 1) == 0) { + pci_write_config_byte(I801_dev, SMBHSTCFG, temp | 1); + printk(KERN_WARNING "i2c-i801.o: enabling SMBus device\n"); + } + + request_region(i801_smba, (isich4 ? 16 : 8), "i801-smbus"); + +#ifdef DEBUG + if (temp & 0x02) + printk + (KERN_DEBUG "i2c-i801.o: I801 using Interrupt SMI# for SMBus.\n"); + else + printk + (KERN_DEBUG "i2c-i801.o: I801 using PCI Interrupt for SMBus.\n"); + + pci_read_config_byte(I801_dev, SMBREV, &temp); + printk(KERN_DEBUG "i2c-i801.o: SMBREV = 0x%X\n", temp); + printk(KERN_DEBUG "i2c-i801.o: I801_smba = 0x%X\n", i801_smba); +#endif /* DEBUG */ + + END: + return error_return; +} + + +void i801_do_pause(unsigned int amount) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(amount); +} + +int i801_transaction(void) +{ + int temp; + int result = 0; + int timeout = 0; + +#ifdef DEBUG + printk + (KERN_DEBUG "i2c-i801.o: Transaction (pre): CNT=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, " + "DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); +#endif + + /* Make sure the SMBus host is ready to start transmitting */ + /* 0x1f = Failed, Bus_Err, Dev_Err, Intr, Host_Busy */ + if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) { +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: SMBus busy (%02x). Resetting... \n", + temp); +#endif + outb_p(temp, SMBHSTSTS); + if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) { +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: Failed! (%02x)\n", temp); +#endif + return -1; + } else { +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: Successfull!\n"); +#endif + } + } + + outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT); + + /* We will always wait for a fraction of a second! */ + do { + i801_do_pause(1); + temp = inb_p(SMBHSTSTS); + } while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: SMBus Timeout!\n"); + result = -1; +#endif + } + + if (temp & 0x10) { + result = -1; +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: Error: Failed bus transaction\n"); +#endif + } + + if (temp & 0x08) { + result = -1; + printk + (KERN_ERR "i2c-i801.o: Bus collision! SMBus may be locked until next hard\n" + "reset. (sorry!)\n"); + /* Clock stops and slave is stuck in mid-transmission */ + } + + if (temp & 0x04) { + result = -1; +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: Error: no response!\n"); +#endif + } + + if ((inb_p(SMBHSTSTS) & 0x1f) != 0x00) + outb_p(inb(SMBHSTSTS), SMBHSTSTS); + + if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) { +#ifdef DEBUG + printk + (KERN_DEBUG "i2c-i801.o: Failed reset at end of transaction (%02x)\n", + temp); +#endif + } +#ifdef DEBUG + printk + (KERN_DEBUG "i2c-i801.o: Transaction (post): CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); +#endif + return result; +} + +/* All-inclusive block transaction function */ +int i801_block_transaction(union i2c_smbus_data *data, char read_write, + int command) +{ + int i, len; + int smbcmd; + int temp; + int result = 0; + int timeout; + unsigned char hostc, errmask; + + if (command == I2C_SMBUS_I2C_BLOCK_DATA) { + if (read_write == I2C_SMBUS_WRITE) { + /* set I2C_EN bit in configuration register */ + pci_read_config_byte(I801_dev, SMBHSTCFG, &hostc); + pci_write_config_byte(I801_dev, SMBHSTCFG, + hostc | SMBHSTCFG_I2C_EN); + } else { + printk("i2c-i801.o: " + "I2C_SMBUS_I2C_BLOCK_READ not supported!\n"); + return -1; + } + } + + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 1) + len = 1; + if (len > 32) + len = 32; + outb_p(len, SMBHSTDAT0); + outb_p(data->block[1], SMBBLKDAT); + } else { + len = 32; /* max for reads */ + } + + if(isich4 && command != I2C_SMBUS_I2C_BLOCK_DATA) { + /* set 32 byte buffer */ + } + + for (i = 1; i <= len; i++) { + if (i == len && read_write == I2C_SMBUS_READ) + smbcmd = I801_BLOCK_LAST; + else + smbcmd = I801_BLOCK_DATA; +#if 0 /* now using HW PEC */ + if(isich4 && command == I2C_SMBUS_BLOCK_DATA_PEC) + smbcmd |= I801_PEC_EN; +#endif + outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT); + +#ifdef DEBUG + printk + (KERN_DEBUG "i2c-i801.o: Block (pre %d): CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, BLKDAT=%02x\n", i, inb_p(SMBHSTCNT), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBBLKDAT)); +#endif + + /* Make sure the SMBus host is ready to start transmitting */ + temp = inb_p(SMBHSTSTS); + if (i == 1) { + /* Erronenous conditions before transaction: + * Byte_Done, Failed, Bus_Err, Dev_Err, Intr, Host_Busy */ + errmask=0x9f; + } else { + /* Erronenous conditions during transaction: + * Failed, Bus_Err, Dev_Err, Intr */ + errmask=0x1e; + } + if (temp & errmask) { +#ifdef DEBUG + printk + (KERN_DEBUG "i2c-i801.o: SMBus busy (%02x). Resetting... \n", + temp); +#endif + outb_p(temp, SMBHSTSTS); + if (((temp = inb_p(SMBHSTSTS)) & errmask) != 0x00) { + printk + (KERN_ERR "i2c-i801.o: Reset failed! (%02x)\n", + temp); + result = -1; + goto END; + } + if (i != 1) { + result = -1; /* if die in middle of block transaction, fail */ + goto END; + } + } + + if (i == 1) { +#if 0 /* #ifdef HAVE_PEC (now using HW PEC) */ + if(isich4 && command == I2C_SMBUS_BLOCK_DATA_PEC) { + if(read_write == I2C_SMBUS_WRITE) + outb_p(data->block[len + 1], SMBPEC); + } +#endif + outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT); + } + + /* We will always wait for a fraction of a second! */ + timeout = 0; + do { + temp = inb_p(SMBHSTSTS); + i801_do_pause(1); + } + while ((!(temp & 0x80)) + && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { + result = -1; +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: SMBus Timeout!\n"); +#endif + } + + if (temp & 0x10) { + result = -1; +#ifdef DEBUG + printk + (KERN_DEBUG "i2c-i801.o: Error: Failed bus transaction\n"); +#endif + } else if (temp & 0x08) { + result = -1; + printk(KERN_ERR "i2c-i801.o: Bus collision!\n"); + } else if (temp & 0x04) { + result = -1; +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: Error: no response!\n"); +#endif + } + + if (i == 1 && read_write == I2C_SMBUS_READ) { + len = inb_p(SMBHSTDAT0); + if (len < 1) + len = 1; + if (len > 32) + len = 32; + data->block[0] = len; + } + + /* Retrieve/store value in SMBBLKDAT */ + if (read_write == I2C_SMBUS_READ) + data->block[i] = inb_p(SMBBLKDAT); + if (read_write == I2C_SMBUS_WRITE && i+1 <= len) + outb_p(data->block[i+1], SMBBLKDAT); + if ((temp & 0x9e) != 0x00) + outb_p(temp, SMBHSTSTS); /* signals SMBBLKDAT ready */ + +#ifdef DEBUG + if ((temp = (0x1e & inb_p(SMBHSTSTS))) != 0x00) { + printk + (KERN_DEBUG "i2c-i801.o: Bad status (%02x) at end of transaction\n", + temp); + } + printk + (KERN_DEBUG "i2c-i801.o: Block (post %d): CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, BLKDAT=%02x\n", i, inb_p(SMBHSTCNT), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBBLKDAT)); +#endif + + if (result < 0) + goto END; + } + +#ifdef HAVE_PEC + if(isich4 && command == I2C_SMBUS_BLOCK_DATA_PEC) { + /* wait for INTR bit as advised by Intel */ + timeout = 0; + do { + temp = inb_p(SMBHSTSTS); + i801_do_pause(1); + } while ((!(temp & 0x02)) + && (timeout++ < MAX_TIMEOUT)); + + if (timeout >= MAX_TIMEOUT) { + printk(KERN_DEBUG "i2c-i801.o: PEC Timeout!\n"); + } +#if 0 /* now using HW PEC */ + if(read_write == I2C_SMBUS_READ) { + data->block[len + 1] = inb_p(SMBPEC); + } +#endif + outb_p(temp, SMBHSTSTS); + } +#endif + result = 0; +END: + if (command == I2C_SMBUS_I2C_BLOCK_DATA) { + /* restore saved configuration register value */ + pci_write_config_byte(I801_dev, SMBHSTCFG, hostc); + } + return result; +} + +/* Return -1 on error. */ +s32 i801_access(struct i2c_adapter * adap, u16 addr, unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data * data) +{ + int hwpec = 0; + int block = 0; + int ret, xact = 0; + +#ifdef HAVE_PEC + if(isich4) + hwpec = (flags & I2C_CLIENT_PEC) != 0; +#endif + + switch (size) { + case I2C_SMBUS_QUICK: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + xact = I801_QUICK; + break; + case I2C_SMBUS_BYTE: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD); + xact = I801_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0); + xact = I801_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + xact = I801_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_I2C_BLOCK_DATA: +#ifdef HAVE_PEC + case I2C_SMBUS_BLOCK_DATA_PEC: + if(hwpec && size == I2C_SMBUS_BLOCK_DATA) + size = I2C_SMBUS_BLOCK_DATA_PEC; +#endif + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + block = 1; + break; + case I2C_SMBUS_PROC_CALL: + default: + printk(KERN_ERR "i2c-i801.o: Unsupported transaction %d\n", size); + return -1; + } + +#ifdef HAVE_PEC + if(isich4 && hwpec) { + if(size != I2C_SMBUS_QUICK && + size != I2C_SMBUS_I2C_BLOCK_DATA) + outb_p(1, SMBAUXCTL); /* enable HW PEC */ + } +#endif + if(block) + ret = i801_block_transaction(data, read_write, size); + else { + outb_p(xact | ENABLE_INT9, SMBHSTCNT); + ret = i801_transaction(); + } + +#ifdef HAVE_PEC + if(isich4 && hwpec) { + if(size != I2C_SMBUS_QUICK && + size != I2C_SMBUS_I2C_BLOCK_DATA) + outb_p(0, SMBAUXCTL); + } +#endif + + if(block) + return ret; + if(ret) + return -1; + if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK)) + return 0; + + switch (xact & 0x7f) { + case I801_BYTE: /* Result put in SMBHSTDAT0 */ + case I801_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case I801_WORD_DATA: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + } + return 0; +} + + +u32 i801_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK +#ifdef HAVE_PEC + | (isich4 ? I2C_FUNC_SMBUS_BLOCK_DATA_PEC | + I2C_FUNC_SMBUS_HWPEC_CALC + : 0) +#endif + ; +} + +static struct i2c_algorithm smbus_algorithm = { + .name = "Non-I2C SMBus adapter", + .id = I2C_ALGO_SMBUS, + .smbus_xfer = i801_access, + .functionality = i801_func, +}; + +static struct i2c_adapter i801_adapter = { + .owner = THIS_MODULE, + .name = "unset", + .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_I801, + .algo = &smbus_algorithm, +}; + + + +static struct pci_device_id i801_ids[] __devinitdata = { + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82801AA_3, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82801AB_3, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82801BA_2, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82801CA_SMBUS, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82801DB_SMBUS, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { 0, } +}; + +static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + + if (i801_setup(dev)) { + printk + (KERN_WARNING "i2c-i801.o: I801 not detected, module not inserted.\n"); + return -ENODEV; + } + + /* set up the driverfs linkage to our parent device */ + i801_adapter.dev.parent = &dev->dev; + + sprintf(i801_adapter.name, "SMBus I801 adapter at %04x", + i801_smba); + return i2c_add_adapter(&i801_adapter); +} + +static void __devexit i801_remove(struct pci_dev *dev) +{ + i2c_del_adapter(&i801_adapter); +} + +static struct pci_driver i801_driver = { + .name = "i801 smbus", + .id_table = i801_ids, + .probe = i801_probe, + .remove = __devexit_p(i801_remove), +}; + +static int __init i2c_i801_init(void) +{ + printk(KERN_INFO "i2c-i801.o version %s (%s)\n", I2C_VERSION, I2C_DATE); + return pci_module_init(&i801_driver); +} + + +static void __exit i2c_i801_exit(void) +{ + pci_unregister_driver(&i801_driver); + release_region(i801_smba, (isich4 ? 16 : 8)); +} + + + +MODULE_AUTHOR + ("Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com>, and Mark D. Studebaker <mdsxyz123@yahoo.com>"); +MODULE_DESCRIPTION("I801 SMBus driver"); + +module_init(i2c_i801_init); +module_exit(i2c_i801_exit); diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c new file mode 100644 index 000000000000..5acd8211b1d6 --- /dev/null +++ b/drivers/i2c/busses/i2c-piix4.c @@ -0,0 +1,521 @@ +/* + piix4.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl> and + Philip Edelbrock <phil@netroedge.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + Supports: + Intel PIIX4, 440MX + Serverworks OSB4, CSB5 + SMSC Victory66 + + Note: we assume there can only be one device, with one SMBus interface. +*/ + +#include <linux/module.h> +#include <linux/config.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/stddef.h> +#include <linux/sched.h> +#include <linux/ioport.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/apm_bios.h> +#include <asm/io.h> + + +struct sd { + const unsigned short mfr; + const unsigned short dev; + const unsigned char fn; + const char *name; +}; + +/* PIIX4 SMBus address offsets */ +#define SMBHSTSTS (0 + piix4_smba) +#define SMBHSLVSTS (1 + piix4_smba) +#define SMBHSTCNT (2 + piix4_smba) +#define SMBHSTCMD (3 + piix4_smba) +#define SMBHSTADD (4 + piix4_smba) +#define SMBHSTDAT0 (5 + piix4_smba) +#define SMBHSTDAT1 (6 + piix4_smba) +#define SMBBLKDAT (7 + piix4_smba) +#define SMBSLVCNT (8 + piix4_smba) +#define SMBSHDWCMD (9 + piix4_smba) +#define SMBSLVEVT (0xA + piix4_smba) +#define SMBSLVDAT (0xC + piix4_smba) + +/* PCI Address Constants */ +#define SMBBA 0x090 +#define SMBHSTCFG 0x0D2 +#define SMBSLVC 0x0D3 +#define SMBSHDW1 0x0D4 +#define SMBSHDW2 0x0D5 +#define SMBREV 0x0D6 + +/* Other settings */ +#define MAX_TIMEOUT 500 +#define ENABLE_INT9 0 + +/* PIIX4 constants */ +#define PIIX4_QUICK 0x00 +#define PIIX4_BYTE 0x04 +#define PIIX4_BYTE_DATA 0x08 +#define PIIX4_WORD_DATA 0x0C +#define PIIX4_BLOCK_DATA 0x14 + +/* insmod parameters */ + +/* If force is set to anything different from 0, we forcibly enable the + PIIX4. DANGEROUS! */ +static int force = 0; +MODULE_PARM(force, "i"); +MODULE_PARM_DESC(force, "Forcibly enable the PIIX4. DANGEROUS!"); + +/* If force_addr is set to anything different from 0, we forcibly enable + the PIIX4 at the given address. VERY DANGEROUS! */ +static int force_addr = 0; +MODULE_PARM(force_addr, "i"); +MODULE_PARM_DESC(force_addr, + "Forcibly enable the PIIX4 at the given address. " + "EXTREMELY DANGEROUS!"); + +static void piix4_do_pause(unsigned int amount); +static int piix4_transaction(void); + + +static unsigned short piix4_smba = 0; + +/* + * Get DMI information. + */ +static int ibm_dmi_probe(void) +{ +#ifdef CONFIG_X86 + extern int is_unsafe_smbus; + return is_unsafe_smbus; +#else + return 0; +#endif +} + +static int piix4_setup(struct pci_dev *PIIX4_dev, const struct pci_device_id *id) +{ + int error_return = 0; + unsigned char temp; + + /* match up the function */ + if (PCI_FUNC(PIIX4_dev->devfn) != id->driver_data) + return -ENODEV; + + printk(KERN_INFO "i2c-piix4.o: Found %s device\n", PIIX4_dev->dev.name); + + if(ibm_dmi_probe()) { + printk + (KERN_ERR "i2c-piix4.o: IBM Laptop detected; this module may corrupt\n"); + printk + (KERN_ERR " your serial eeprom! Refusing to load module!\n"); + error_return = -EPERM; + goto END; + } + +/* Determine the address of the SMBus areas */ + if (force_addr) { + piix4_smba = force_addr & 0xfff0; + force = 0; + } else { + pci_read_config_word(PIIX4_dev, SMBBA, &piix4_smba); + piix4_smba &= 0xfff0; + if(piix4_smba == 0) { + printk(KERN_ERR "i2c-piix4.o: SMB base address uninitialized - upgrade BIOS or use force_addr=0xaddr\n"); + return -ENODEV; + } + } + + if (check_region(piix4_smba, 8)) { + printk + (KERN_ERR "i2c-piix4.o: SMB region 0x%x already in use!\n", + piix4_smba); + error_return = -ENODEV; + goto END; + } + + pci_read_config_byte(PIIX4_dev, SMBHSTCFG, &temp); +/* If force_addr is set, we program the new address here. Just to make + sure, we disable the PIIX4 first. */ + if (force_addr) { + pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp & 0xfe); + pci_write_config_word(PIIX4_dev, SMBBA, piix4_smba); + pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp | 0x01); + printk + (KERN_INFO "i2c-piix4.o: WARNING: SMBus interface set to new " + "address %04x!\n", piix4_smba); + } else if ((temp & 1) == 0) { + if (force) { +/* This should never need to be done, but has been noted that + many Dell machines have the SMBus interface on the PIIX4 + disabled!? NOTE: This assumes I/O space and other allocations WERE + done by the Bios! Don't complain if your hardware does weird + things after enabling this. :') Check for Bios updates before + resorting to this. */ + pci_write_config_byte(PIIX4_dev, SMBHSTCFG, + temp | 1); + printk + (KERN_NOTICE "i2c-piix4.o: WARNING: SMBus interface has been FORCEFULLY " + "ENABLED!\n"); + } else { + printk + (KERN_ERR "i2c-piix4.o: Host SMBus controller not enabled!\n"); + error_return = -ENODEV; + goto END; + } + } + + /* Everything is happy, let's grab the memory and set things up. */ + request_region(piix4_smba, 8, "piix4-smbus"); + +#ifdef DEBUG + if ((temp & 0x0E) == 8) + printk + (KERN_DEBUG "i2c-piix4.o: Using Interrupt 9 for SMBus.\n"); + else if ((temp & 0x0E) == 0) + printk + (KERN_DEBUG "i2c-piix4.o: Using Interrupt SMI# for SMBus.\n"); + else + printk + (KERN_ERR "i2c-piix4.o: Illegal Interrupt configuration (or code out " + "of date)!\n"); + + pci_read_config_byte(PIIX4_dev, SMBREV, &temp); + printk(KERN_DEBUG "i2c-piix4.o: SMBREV = 0x%X\n", temp); + printk(KERN_DEBUG "i2c-piix4.o: SMBA = 0x%X\n", piix4_smba); +#endif /* DEBUG */ + + END: + return error_return; +} + + +/* Internally used pause function */ +static void piix4_do_pause(unsigned int amount) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(amount); +} + +/* Another internally used function */ +static int piix4_transaction(void) +{ + int temp; + int result = 0; + int timeout = 0; + +#ifdef DEBUG + printk + (KERN_DEBUG "i2c-piix4.o: Transaction (pre): CNT=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, " + "DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); +#endif + + /* Make sure the SMBus host is ready to start transmitting */ + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { +#ifdef DEBUG + printk(KERN_DEBUG "i2c-piix4.o: SMBus busy (%02x). Resetting... \n", + temp); +#endif + outb_p(temp, SMBHSTSTS); + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { +#ifdef DEBUG + printk(KERN_ERR "i2c-piix4.o: Failed! (%02x)\n", temp); +#endif + return -1; + } else { +#ifdef DEBUG + printk(KERN_DEBUG "i2c-piix4.o: Successfull!\n"); +#endif + } + } + + /* start the transaction by setting bit 6 */ + outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT); + + /* We will always wait for a fraction of a second! (See PIIX4 docs errata) */ + do { + piix4_do_pause(1); + temp = inb_p(SMBHSTSTS); + } while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT)); + +#ifdef DEBUG + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { + printk(KERN_ERR "i2c-piix4.o: SMBus Timeout!\n"); + result = -1; + } +#endif + + if (temp & 0x10) { + result = -1; +#ifdef DEBUG + printk(KERN_ERR "i2c-piix4.o: Error: Failed bus transaction\n"); +#endif + } + + if (temp & 0x08) { + result = -1; + printk + (KERN_ERR "i2c-piix4.o: Bus collision! SMBus may be locked until next hard\n" + "reset. (sorry!)\n"); + /* Clock stops and slave is stuck in mid-transmission */ + } + + if (temp & 0x04) { + result = -1; +#ifdef DEBUG + printk(KERN_ERR "i2c-piix4.o: Error: no response!\n"); +#endif + } + + if (inb_p(SMBHSTSTS) != 0x00) + outb_p(inb(SMBHSTSTS), SMBHSTSTS); + +#ifdef DEBUG + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { + printk + (KERN_ERR "i2c-piix4.o: Failed reset at end of transaction (%02x)\n", + temp); + } + printk + (KERN_DEBUG "i2c-piix4.o: Transaction (post): CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); +#endif + return result; +} + +/* Return -1 on error. */ +static s32 piix4_access(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data * data) +{ + int i, len; + + switch (size) { + case I2C_SMBUS_PROC_CALL: + printk + (KERN_ERR "i2c-piix4.o: I2C_SMBUS_PROC_CALL not supported!\n"); + return -1; + case I2C_SMBUS_QUICK: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = PIIX4_QUICK; + break; + case I2C_SMBUS_BYTE: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD); + size = PIIX4_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0); + size = PIIX4_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + size = PIIX4_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 0) + len = 0; + if (len > 32) + len = 32; + outb_p(len, SMBHSTDAT0); + i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= len; i++) + outb_p(data->block[i], SMBBLKDAT); + } + size = PIIX4_BLOCK_DATA; + break; + } + + outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT); + + if (piix4_transaction()) /* Error in transaction */ + return -1; + + if ((read_write == I2C_SMBUS_WRITE) || (size == PIIX4_QUICK)) + return 0; + + + switch (size) { + case PIIX4_BYTE: /* Where is the result put? I assume here it is in + SMBHSTDAT0 but it might just as well be in the + SMBHSTCMD. No clue in the docs */ + + data->byte = inb_p(SMBHSTDAT0); + break; + case PIIX4_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case PIIX4_WORD_DATA: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + case PIIX4_BLOCK_DATA: + data->block[0] = inb_p(SMBHSTDAT0); + i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= data->block[0]; i++) + data->block[i] = inb_p(SMBBLKDAT); + break; + } + return 0; +} + + +static u32 piix4_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; +} + +static struct i2c_algorithm smbus_algorithm = { + .name = "Non-I2C SMBus adapter", + .id = I2C_ALGO_SMBUS, + .smbus_xfer = piix4_access, + .functionality = piix4_func, +}; + +static struct i2c_adapter piix4_adapter = { + .owner = THIS_MODULE, + .name = "unset", + .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_PIIX4, + .algo = &smbus_algorithm, +}; + + + +static struct pci_device_id piix4_ids[] __devinitdata = { + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82371AB_3, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = 3 + }, + { + .vendor = PCI_VENDOR_ID_SERVERWORKS, + .device = PCI_DEVICE_ID_SERVERWORKS_OSB4, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = 0, + }, + { + .vendor = PCI_VENDOR_ID_SERVERWORKS, + .device = PCI_DEVICE_ID_SERVERWORKS_CSB5, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = 0, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82443MX_3, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = 3, + }, + { + .vendor = PCI_VENDOR_ID_EFAR, + .device = PCI_DEVICE_ID_EFAR_SLC90E66_3, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = 0, + }, + { 0, } +}; + +static int __devinit piix4_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + int retval; + + retval = piix4_setup(dev, id); + if (retval) + return retval; + + /* set up the driverfs linkage to our parent device */ + piix4_adapter.dev.parent = &dev->dev; + + sprintf(piix4_adapter.name, "SMBus PIIX4 adapter at %04x", + piix4_smba); + + retval = i2c_add_adapter(&piix4_adapter); + + return retval; +} + +static void __devexit piix4_remove(struct pci_dev *dev) +{ + i2c_del_adapter(&piix4_adapter); +} + + +static struct pci_driver piix4_driver = { + .name = "piix4 smbus", + .id_table = piix4_ids, + .probe = piix4_probe, + .remove = __devexit_p(piix4_remove), +}; + +static int __init i2c_piix4_init(void) +{ + printk("i2c-piix4.o version %s (%s)\n", I2C_VERSION, I2C_DATE); + return pci_module_init(&piix4_driver); +} + + +static void __exit i2c_piix4_exit(void) +{ + pci_unregister_driver(&piix4_driver); + release_region(piix4_smba, 8); +} + + + +MODULE_AUTHOR + ("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>"); +MODULE_DESCRIPTION("PIIX4 SMBus driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_piix4_init); +module_exit(i2c_piix4_exit); diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 238c5a31e4f3..28cc3ec02277 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -30,6 +30,7 @@ #include <linux/proc_fs.h> #include <linux/i2c.h> #include <linux/init.h> +#include <linux/seq_file.h> #include <asm/uaccess.h> @@ -87,6 +88,16 @@ int i2c_add_adapter(struct i2c_adapter *adap) init_MUTEX(&adap->bus); init_MUTEX(&adap->list); + /* Add the adapter to the driver core. + * If the parent pointer is not set up, + * we add this adapter to the legacy bus. + */ + if (adap->dev.parent == NULL) + adap->dev.parent = &legacy_bus; + sprintf(adap->dev.bus_id, "i2c-%d", i); + strcpy(adap->dev.name, "i2c controller"); + device_register(&adap->dev); + /* inform drivers of new adapters */ for (j=0;j<I2C_DRIVER_MAX;j++) if (drivers[j]!=NULL && @@ -154,6 +165,9 @@ int i2c_del_adapter(struct i2c_adapter *adap) i2cproc_remove(i); + /* clean up the sysfs representation */ + device_unregister(&adap->dev); + adapters[i] = NULL; DEB(printk(KERN_DEBUG "i2c-core.o: adapter unregistered: %s\n",adap->name)); @@ -313,42 +327,45 @@ int i2c_check_addr(struct i2c_adapter *adapter, int addr) int i2c_attach_client(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; - int res = -EBUSY, i; + int i; down(&adapter->list); - if (__i2c_check_addr(client->adapter,client->addr)) + if (__i2c_check_addr(client->adapter, client->addr)) goto out_unlock_list; - for (i = 0; i < I2C_CLIENT_MAX; i++) - if (NULL == adapter->clients[i]) - break; - if (I2C_CLIENT_MAX == i) { - printk(KERN_WARNING - " i2c-core.o: attach_client(%s) - enlarge I2C_CLIENT_MAX.\n", - client->name); - res = -ENOMEM; - goto out_unlock_list; + for (i = 0; i < I2C_CLIENT_MAX; i++) { + if (!adapter->clients[i]) + goto free_slot; } + printk(KERN_WARNING + " i2c-core.o: attach_client(%s) - enlarge I2C_CLIENT_MAX.\n", + client->name); + + out_unlock_list: + up(&adapter->list); + return -EBUSY; + + free_slot: adapter->clients[i] = client; up(&adapter->list); - if (adapter->client_register) - if (adapter->client_register(client)) - printk(KERN_DEBUG "i2c-core.o: warning: client_register seems " + if (adapter->client_register) { + if (adapter->client_register(client)) { + printk(KERN_DEBUG + "i2c-core.o: warning: client_register seems " "to have failed for client %02x at adapter %s\n", - client->addr,adapter->name); - DEB(printk(KERN_DEBUG "i2c-core.o: client [%s] registered to adapter [%s](pos. %d).\n", - client->name, adapter->name,i)); + client->addr, adapter->name); + } + } + + DEB(printk(KERN_DEBUG + "i2c-core.o: client [%s] registered to adapter [%s] " + "(pos. %d).\n", client->name, adapter->name, i)); - if(client->flags & I2C_CLIENT_ALLOW_USE) + if (client->flags & I2C_CLIENT_ALLOW_USE) client->usage_count = 0; - return 0; - - out_unlock_list: - up(&adapter->list); - return res; } @@ -363,28 +380,30 @@ int i2c_detach_client(struct i2c_client *client) if (adapter->client_unregister) { res = adapter->client_unregister(client); if (res) { - printk(KERN_ERR "i2c-core.o: client_unregister [%s] failed, " - "client not detached",client->name); - return res; + printk(KERN_ERR + "i2c-core.o: client_unregister [%s] failed, " + "client not detached", client->name); + goto out; } } down(&adapter->list); for (i = 0; i < I2C_CLIENT_MAX; i++) { - if (client == adapter->clients[i]) - break; + if (client == adapter->clients[i]) { + adapter->clients[i] = NULL; + goto out_unlock; + } } - if (I2C_CLIENT_MAX == i) { - printk(KERN_WARNING " i2c-core.o: unregister_client " - "[%s] not found\n", - client->name); - return -ENODEV; - } else - adapter->clients[i] = NULL; - up(&adapter->list); + printk(KERN_WARNING + " i2c-core.o: unregister_client [%s] not found\n", + client->name); + res = -ENODEV; - return 0; + out_unlock: + up(&adapter->list); + out: + return res; } static int i2c_inc_use_client(struct i2c_client *client) @@ -443,45 +462,7 @@ int i2c_release_client(struct i2c_client *client) return 0; } -/* ---------------------------------------------------- - * The /proc functions - * ---------------------------------------------------- - */ - #ifdef CONFIG_PROC_FS -/* This function generates the output for /proc/bus/i2c */ -static int read_bus_i2c(char *buf, char **start, off_t offset, - int len, int *eof, void *private) -{ - int i; - int nr = 0; - - /* Note that it is safe to write a `little' beyond len. Yes, really. */ - /* Fuck you. Will convert this to seq_file later. --hch */ - - down(&core_lists); - for (i = 0; (i < I2C_ADAP_MAX) && (nr < len); i++) { - if (adapters[i]) { - nr += sprintf(buf+nr, "i2c-%d\t", i); - if (adapters[i]->algo->smbus_xfer) { - if (adapters[i]->algo->master_xfer) - nr += sprintf(buf+nr,"smbus/i2c"); - else - nr += sprintf(buf+nr,"smbus "); - } else if (adapters[i]->algo->master_xfer) - nr += sprintf(buf+nr,"i2c "); - else - nr += sprintf(buf+nr,"dummy "); - nr += sprintf(buf+nr,"\t%-32s\t%-32s\n", - adapters[i]->name, - adapters[i]->algo->name); - } - } - up(&core_lists); - - return nr; -} - /* This function generates the output for /proc/bus/i2c-? */ static ssize_t i2cproc_bus_read(struct file *file, char *buf, size_t count, loff_t *ppos) @@ -551,6 +532,50 @@ static struct file_operations i2cproc_operations = { .read = i2cproc_bus_read, }; +/* This function generates the output for /proc/bus/i2c */ +static int bus_i2c_show(struct seq_file *s, void *p) +{ + int i; + + down(&core_lists); + for (i = 0; i < I2C_ADAP_MAX; i++) { + struct i2c_adapter *adapter = adapters[i]; + + if (!adapter) + continue; + + seq_printf(s, "i2c-%d\t", i); + + if (adapter->algo->smbus_xfer) { + if (adapter->algo->master_xfer) + seq_printf(s, "smbus/i2c"); + else + seq_printf(s, "smbus "); + } else if (adapter->algo->master_xfer) + seq_printf(s ,"i2c "); + else + seq_printf(s, "dummy "); + + seq_printf(s, "\t%-32s\t%-32s\n", + adapter->name, adapter->algo->name); + } + up(&core_lists); + + return 0; +} + +static int bus_i2c_open(struct inode *inode, struct file *file) +{ + return single_open(file, bus_i2c_show, NULL); +} + +static struct file_operations bus_i2c_fops = { + .open = bus_i2c_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + }; + static int i2cproc_register(struct i2c_adapter *adap, int bus) { struct proc_dir_entry *proc_entry; @@ -563,7 +588,7 @@ static int i2cproc_register(struct i2c_adapter *adap, int bus) goto fail; proc_entry->proc_fops = &i2cproc_operations; - proc_entry->owner = THIS_MODULE; + proc_entry->owner = adap->owner; adap->inode = proc_entry->low_ino; return 0; fail: @@ -583,25 +608,53 @@ static int __init i2cproc_init(void) { struct proc_dir_entry *proc_bus_i2c; - proc_bus_i2c = create_proc_entry("i2c",0,proc_bus); - if (!proc_bus_i2c) { - printk(KERN_ERR "i2c-core.o: Could not create /proc/bus/i2c"); - return -ENOENT; - } + proc_bus_i2c = create_proc_entry("i2c", 0, proc_bus); + if (!proc_bus_i2c) + goto fail; + proc_bus_i2c->proc_fops = &bus_i2c_fops; + proc_bus_i2c->owner = THIS_MODULE; + return 0; - proc_bus_i2c->read_proc = &read_bus_i2c; - proc_bus_i2c->owner = THIS_MODULE; - return 0; + fail: + printk(KERN_ERR "i2c-core.o: Could not create /proc/bus/i2c"); + return -ENOENT; } static void __exit i2cproc_cleanup(void) { remove_proc_entry("i2c",proc_bus); } +#else +static int __init i2cproc_init(void) { return 0; } +static void __exit i2cproc_cleanup(void) { } +#endif /* CONFIG_PROC_FS */ + +/* match always succeeds, as we want the probe() to tell if we really accept this match */ +static int i2c_device_match(struct device *dev, struct device_driver *drv) +{ + return 1; +} + +struct bus_type i2c_bus_type = { + .name = "i2c", + .match = i2c_device_match, +}; + + +static int __init i2c_init(void) +{ + bus_register(&i2c_bus_type); + return i2cproc_init(); +} + +static void __exit i2c_exit(void) +{ + i2cproc_cleanup(); + bus_unregister(&i2c_bus_type); +} -module_init(i2cproc_init); -module_exit(i2cproc_cleanup); -#endif /* def CONFIG_PROC_FS */ +module_init(i2c_init); +module_exit(i2c_exit); /* ---------------------------------------------------- * the functional interface to the i2c busses. diff --git a/drivers/i2c/i2c-proc.c b/drivers/i2c/i2c-proc.c index 2b2e752e0769..73bb33815907 100644 --- a/drivers/i2c/i2c-proc.c +++ b/drivers/i2c/i2c-proc.c @@ -35,8 +35,6 @@ #include <linux/i2c-proc.h> #include <asm/uaccess.h> -static int i2c_create_name(char **name, const char *prefix, - struct i2c_adapter *adapter, int addr); static int i2c_parse_reals(int *nrels, void *buffer, int bufsize, long *results, int magnitude); static int i2c_write_reals(int nrels, void *buffer, size_t *bufsize, @@ -54,15 +52,6 @@ static struct ctl_table_header *i2c_entries[SENSORS_ENTRY_MAX]; static struct i2c_client *i2c_clients[SENSORS_ENTRY_MAX]; -static ctl_table sysctl_table[] = { - {CTL_DEV, "dev", NULL, 0, 0555}, - {0}, - {DEV_SENSORS, "sensors", NULL, 0, 0555}, - {0}, - {0, NULL, NULL, 0, 0555}, - {0} -}; - static ctl_table i2c_proc_dev_sensors[] = { {SENSORS_CHIPS, "chips", NULL, 0, 0644, NULL, &i2c_proc_chips, &i2c_sysctl_chips}, @@ -87,36 +76,40 @@ static struct ctl_table_header *i2c_proc_header; (for a LM78 chip on the ISA bus at port 0x310), or lm75-i2c-3-4e (for a LM75 chip on the third i2c bus at address 0x4e). name is allocated first. */ -static int i2c_create_name(char **name, const char *prefix, - struct i2c_adapter *adapter, int addr) +static char *generate_name(struct i2c_client *client, const char *prefix) { - char name_buffer[50]; - int id, i, end; - if (i2c_is_isa_adapter(adapter)) + struct i2c_adapter *adapter = client->adapter; + int addr = client->addr; + char name_buffer[50], *name; + + if (i2c_is_isa_adapter(adapter)) { sprintf(name_buffer, "%s-isa-%04x", prefix, addr); - else if (!adapter->algo->smbus_xfer && !adapter->algo->master_xfer) { - /* dummy adapter, generate prefix */ + } else if (adapter->algo->smbus_xfer || adapter->algo->master_xfer) { + int id = i2c_adapter_id(adapter); + if (id < 0) + return ERR_PTR(-ENOENT); + sprintf(name_buffer, "%s-i2c-%d-%02x", prefix, id, addr); + } else { /* dummy adapter, generate prefix */ + int end, i; + sprintf(name_buffer, "%s-", prefix); end = strlen(name_buffer); - for(i = 0; i < 32; i++) { - if(adapter->algo->name[i] == ' ') + + for (i = 0; i < 32; i++) { + if (adapter->algo->name[i] == ' ') break; name_buffer[end++] = tolower(adapter->algo->name[i]); } + name_buffer[end] = 0; sprintf(name_buffer + end, "-%04x", addr); - } else { - if ((id = i2c_adapter_id(adapter)) < 0) - return -ENOENT; - sprintf(name_buffer, "%s-i2c-%d-%02x", prefix, id, addr); - } - *name = kmalloc(strlen(name_buffer) + 1, GFP_KERNEL); - if (!*name) { - printk (KERN_WARNING "i2c_create_name: not enough memory\n"); - return -ENOMEM; } - strcpy(*name, name_buffer); - return 0; + + name = kmalloc(strlen(name_buffer) + 1, GFP_KERNEL); + if (unlikely(!name)) + return ERR_PTR(-ENOMEM); + strcpy(name, name_buffer); + return name; } /* This rather complex function must be called when you want to add an entry @@ -127,93 +120,80 @@ static int i2c_create_name(char **name, const char *prefix, If any driver wants subdirectories within the newly created directory, this function must be updated! */ int i2c_register_entry(struct i2c_client *client, const char *prefix, - ctl_table * ctl_template) + struct ctl_table *leaf) { - int i, res, len, id; - ctl_table *new_table, *client_tbl, *tbl; - char *name; - struct ctl_table_header *new_header; + struct { struct ctl_table root[2], dev[2], sensors[2]; } *tbl; + struct ctl_table_header *hdr; + struct ctl_table *tmp; + const char *name; + int id; + + name = generate_name(client, prefix); + if (IS_ERR(name)) + return PTR_ERR(name); + + for (id = 0; id < SENSORS_ENTRY_MAX; id++) { + if (!i2c_entries[id]) + goto free_slot; + } - if ((res = i2c_create_name(&name, prefix, client->adapter, - client->addr))) return res; + goto out_free_name; - for (id = 0; id < SENSORS_ENTRY_MAX; id++) - if (!i2c_entries[id]) { - break; - } - if (id == SENSORS_ENTRY_MAX) { - kfree(name); - return -ENOMEM; - } + free_slot: + tbl = kmalloc(sizeof(*tbl), GFP_KERNEL); + if (unlikely(!tbl)) + goto out_free_name; + memset(tbl, 0, sizeof(*tbl)); - id += 256; - - len = 0; - while (ctl_template[len].procname) - len++; - if (!(new_table = kmalloc(sizeof(sysctl_table) + sizeof(ctl_table) * (len + 1), - GFP_KERNEL))) { - kfree(name); - return -ENOMEM; - } + for (tmp = leaf; tmp->ctl_name; tmp++) + tmp->extra2 = client; - memcpy(new_table, sysctl_table, sizeof(sysctl_table)); - tbl = new_table; /* sys/ */ - tbl = tbl->child = tbl + 2; /* dev/ */ - tbl = tbl->child = tbl + 2; /* sensors/ */ - client_tbl = tbl->child = tbl + 2; /* XX-chip-YY-ZZ/ */ + tbl->sensors->ctl_name = id+256; + tbl->sensors->procname = name; + tbl->sensors->mode = 0555; + tbl->sensors->child = leaf; - client_tbl->procname = name; - client_tbl->ctl_name = id; - client_tbl->child = client_tbl + 2; + tbl->dev->ctl_name = DEV_SENSORS; + tbl->dev->procname = "sensors"; + tbl->dev->mode = 0555; + tbl->dev->child = tbl->sensors; - /* Next the client sysctls. --km */ - tbl = client_tbl->child; - memcpy(tbl, ctl_template, sizeof(ctl_table) * (len+1)); - for (i = 0; i < len; i++) - tbl[i].extra2 = client; + tbl->root->ctl_name = CTL_DEV; + tbl->root->procname = "dev"; + tbl->root->mode = 0555; + tbl->root->child = tbl->dev; - if (!(new_header = register_sysctl_table(new_table, 0))) { - printk(KERN_ERR "i2c-proc.o: error: sysctl interface not supported by kernel!\n"); - kfree(new_table); - kfree(name); - return -EPERM; - } + hdr = register_sysctl_table(tbl->root, 0); + if (unlikely(!hdr)) + goto out_free_tbl; - i2c_entries[id - 256] = new_header; + i2c_entries[id] = hdr; + i2c_clients[id] = client; - i2c_clients[id - 256] = client; + return (id + 256); /* XXX(hch) why?? */ -#ifdef DEBUG - if (!new_header || !new_header->ctl_table || - !new_header->ctl_table->child || - !new_header->ctl_table->child->child || - !new_header->ctl_table->child->child->de ) { - printk - (KERN_ERR "i2c-proc.o: NULL pointer when trying to install fill_inode fix!\n"); - return id; - } -#endif /* DEBUG */ - client_tbl->de->owner = client->driver->owner; - return id; + out_free_tbl: + kfree(tbl); + out_free_name: + kfree(name); + return -ENOMEM; } void i2c_deregister_entry(int id) { - ctl_table *table; - char *temp; + id -= 256; - id -= 256; if (i2c_entries[id]) { - table = i2c_entries[id]->ctl_table; - unregister_sysctl_table(i2c_entries[id]); - /* 2-step kfree needed to keep gcc happy about const points */ - (const char *) temp = table[4].procname; - kfree(temp); - kfree(table); - i2c_entries[id] = NULL; - i2c_clients[id] = NULL; + struct ctl_table_header *hdr = i2c_entries[id]; + struct ctl_table *tbl = hdr->ctl_table; + + unregister_sysctl_table(hdr); + kfree(tbl->child->child->procname); + kfree(tbl); /* actually the whole anonymous struct */ } + + i2c_entries[id] = NULL; + i2c_clients[id] = NULL; } static int i2c_proc_chips(ctl_table * ctl, int write, struct file *filp, diff --git a/drivers/md/linear.c b/drivers/md/linear.c index 1c94ad6ff2a5..00e9abe0f015 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -203,36 +203,34 @@ static int linear_make_request (request_queue_t *q, struct bio *bio) return 0; } bio->bi_bdev = tmp_dev->rdev->bdev; - bio->bi_sector = bio->bi_sector - (tmp_dev->offset << 1); + bio->bi_sector = bio->bi_sector - (tmp_dev->offset << 1) + tmp_dev->rdev->data_offset; return 1; } -static int linear_status (char *page, mddev_t *mddev) +static void linear_status (struct seq_file *seq, mddev_t *mddev) { - int sz = 0; #undef MD_DEBUG #ifdef MD_DEBUG int j; linear_conf_t *conf = mddev_to_conf(mddev); - sz += sprintf(page+sz, " "); + seq_printf(seq, " "); for (j = 0; j < conf->nr_zones; j++) { - sz += sprintf(page+sz, "[%s", + seq_printf(seq, "[%s", bdev_partition_name(conf->hash_table[j].dev0->rdev->bdev)); if (conf->hash_table[j].dev1) - sz += sprintf(page+sz, "/%s] ", + seq_printf(seq, "/%s] ", bdev_partition_name(conf->hash_table[j].dev1->rdev->bdev)); else - sz += sprintf(page+sz, "] "); + seq_printf(seq, "] "); } - sz += sprintf(page+sz, "\n"); + seq_printf(seq, "\n"); #endif - sz += sprintf(page+sz, " %dk rounding", mddev->chunk_size/1024); - return sz; + seq_printf(seq, " %dk rounding", mddev->chunk_size/1024); } diff --git a/drivers/md/md.c b/drivers/md/md.c index cd65d8960c39..cefd2423f1c7 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -124,9 +124,6 @@ static ctl_table raid_root_table[] = { { .ctl_name = 0 } }; -static void md_recover_arrays(void); -static mdk_thread_t *md_recovery_thread; - sector_t md_size[MAX_MD_DEVS]; static struct block_device_operations md_fops; @@ -222,6 +219,7 @@ static mddev_t * mddev_find(int unit) init_MUTEX(&new->reconfig_sem); INIT_LIST_HEAD(&new->disks); INIT_LIST_HEAD(&new->all_mddevs); + init_timer(&new->safemode_timer); atomic_set(&new->active, 1); blk_queue_make_request(&new->queue, md_fail_request); @@ -272,40 +270,35 @@ static mdk_rdev_t * find_rdev(mddev_t * mddev, dev_t dev) return NULL; } -static sector_t calc_dev_sboffset(struct block_device *bdev) +inline static sector_t calc_dev_sboffset(struct block_device *bdev) { sector_t size = bdev->bd_inode->i_size >> BLOCK_SIZE_BITS; return MD_NEW_SIZE_BLOCKS(size); } -static sector_t calc_dev_size(struct block_device *bdev, mddev_t *mddev) +static sector_t calc_dev_size(mdk_rdev_t *rdev, unsigned chunk_size) { sector_t size; - if (mddev->persistent) - size = calc_dev_sboffset(bdev); - else - size = bdev->bd_inode->i_size >> BLOCK_SIZE_BITS; - if (mddev->chunk_size) - size &= ~((sector_t)mddev->chunk_size/1024 - 1); + size = rdev->sb_offset; + + if (chunk_size) + size &= ~((sector_t)chunk_size/1024 - 1); return size; } static sector_t zoned_raid_size(mddev_t *mddev) { - sector_t mask; mdk_rdev_t * rdev; struct list_head *tmp; /* * do size and offset calculations. */ - mask = ~((sector_t)mddev->chunk_size/1024 - 1); - ITERATE_RDEV(mddev,rdev,tmp) { - rdev->size &= mask; + ITERATE_RDEV(mddev,rdev,tmp) md_size[mdidx(mddev)] += rdev->size; - } + return 0; } @@ -389,7 +382,6 @@ static int sync_page_io(struct block_device *bdev, sector_t sector, int size, static int read_disk_sb(mdk_rdev_t * rdev) { - sector_t sb_offset; if (!rdev->sb_page) { MD_BUG(); @@ -398,16 +390,8 @@ static int read_disk_sb(mdk_rdev_t * rdev) if (rdev->sb_loaded) return 0; - /* - * Calculate the position of the superblock, - * it's at the end of the disk. - * - * It also happens to be a multiple of 4Kb. - */ - sb_offset = calc_dev_sboffset(rdev->bdev); - rdev->sb_offset = sb_offset; - if (!sync_page_io(rdev->bdev, sb_offset<<1, MD_SB_BYTES, rdev->sb_page, READ)) + if (!sync_page_io(rdev->bdev, rdev->sb_offset<<1, MD_SB_BYTES, rdev->sb_page, READ)) goto fail; rdev->sb_loaded = 1; return 0; @@ -486,7 +470,7 @@ static unsigned int calc_sb_csum(mdp_super_t * sb) * We rely on user-space to write the initial superblock, and support * reading and updating of superblocks. * Interface methods are: - * int load_super(mdk_rdev_t *dev, mdk_rdev_t *refdev) + * int load_super(mdk_rdev_t *dev, mdk_rdev_t *refdev, int minor_version) * loads and validates a superblock on dev. * if refdev != NULL, compare superblocks on both devices * Return: @@ -511,7 +495,7 @@ static unsigned int calc_sb_csum(mdp_super_t * sb) struct super_type { char *name; struct module *owner; - int (*load_super)(mdk_rdev_t *rdev, mdk_rdev_t *refdev); + int (*load_super)(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version); int (*validate_super)(mddev_t *mddev, mdk_rdev_t *rdev); void (*sync_super)(mddev_t *mddev, mdk_rdev_t *rdev); }; @@ -519,10 +503,20 @@ struct super_type { /* * load_super for 0.90.0 */ -static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev) +static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) { mdp_super_t *sb; int ret; + sector_t sb_offset; + + /* + * Calculate the position of the superblock, + * it's at the end of the disk. + * + * It also happens to be a multiple of 4Kb. + */ + sb_offset = calc_dev_sboffset(rdev->bdev); + rdev->sb_offset = sb_offset; ret = read_disk_sb(rdev); if (ret) return ret; @@ -557,6 +551,12 @@ static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev) } rdev->preferred_minor = sb->md_minor; + rdev->data_offset = 0; + + if (sb->level == MULTIPATH) + rdev->desc_nr = -1; + else + rdev->desc_nr = sb->this_disk.number; if (refdev == 0) ret = 1; @@ -582,7 +582,7 @@ static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev) else ret = 0; } - + rdev->size = calc_dev_size(rdev, sb->chunk_size); abort: return ret; @@ -597,7 +597,7 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev) mdp_super_t *sb = (mdp_super_t *)page_address(rdev->sb_page); if (mddev->raid_disks == 0) { - mddev->major_version = sb->major_version; + mddev->major_version = 0; mddev->minor_version = sb->minor_version; mddev->patch_version = sb->patch_version; mddev->persistent = ! sb->not_persistent; @@ -634,7 +634,6 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev) return -EINVAL; } if (mddev->level != LEVEL_MULTIPATH) { - rdev->desc_nr = sb->this_disk.number; rdev->raid_disk = -1; rdev->in_sync = rdev->faulty = 0; desc = sb->disks + rdev->desc_nr; @@ -704,10 +703,8 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev) sb->recovery_cp = mddev->recovery_cp; sb->cp_events_hi = (mddev->events>>32); sb->cp_events_lo = (u32)mddev->events; - if (mddev->recovery_cp == MaxSector) { - printk(KERN_INFO "md: marking sb clean...\n"); + if (mddev->recovery_cp == MaxSector) sb->state = (1<< MD_SB_CLEAN); - } } else sb->recovery_cp = 0; @@ -717,7 +714,7 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev) sb->disks[0].state = (1<<MD_DISK_REMOVED); ITERATE_RDEV(mddev,rdev2,tmp) { mdp_disk_t *d; - if (rdev2->raid_disk >= 0) + if (rdev2->raid_disk >= 0 && rdev2->in_sync && !rdev2->faulty) rdev2->desc_nr = rdev2->raid_disk; else rdev2->desc_nr = next_spare++; @@ -726,7 +723,7 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev) d->number = rdev2->desc_nr; d->major = MAJOR(rdev2->bdev->bd_dev); d->minor = MINOR(rdev2->bdev->bd_dev); - if (rdev2->raid_disk >= 0) + if (rdev2->raid_disk >= 0 && rdev->in_sync && !rdev2->faulty) d->raid_disk = rdev2->raid_disk; else d->raid_disk = rdev2->desc_nr; /* compatibility */ @@ -766,6 +763,210 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev) sb->sb_csum = calc_sb_csum(sb); } +/* + * version 1 superblock + */ + +static unsigned int calc_sb_1_csum(struct mdp_superblock_1 * sb) +{ + unsigned int disk_csum, csum; + int size = 256 + sb->max_dev*2; + + disk_csum = sb->sb_csum; + sb->sb_csum = 0; + csum = csum_partial((void *)sb, size, 0); + sb->sb_csum = disk_csum; + return csum; +} + +static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) +{ + struct mdp_superblock_1 *sb; + int ret; + sector_t sb_offset; + + /* + * Calculate the position of the superblock. + * It is always aligned to a 4K boundary and + * depeding on minor_version, it can be: + * 0: At least 8K, but less than 12K, from end of device + * 1: At start of device + * 2: 4K from start of device. + */ + switch(minor_version) { + case 0: + sb_offset = rdev->bdev->bd_inode->i_size >> 9; + sb_offset -= 8*2; + sb_offset &= ~(4*2); + /* convert from sectors to K */ + sb_offset /= 2; + break; + case 1: + sb_offset = 0; + break; + case 2: + sb_offset = 4; + break; + default: + return -EINVAL; + } + rdev->sb_offset = sb_offset; + + ret = read_disk_sb(rdev); + if (ret) return ret; + + + sb = (struct mdp_superblock_1*)page_address(rdev->sb_page); + + if (sb->magic != cpu_to_le32(MD_SB_MAGIC) || + sb->major_version != cpu_to_le32(1) || + le32_to_cpu(sb->max_dev) > (4096-256)/2 || + le64_to_cpu(sb->super_offset) != (rdev->sb_offset<<1) || + sb->feature_map != 0) + return -EINVAL; + + if (calc_sb_1_csum(sb) != sb->sb_csum) { + printk(BAD_CSUM, bdev_partition_name(rdev->bdev)); + return -EINVAL; + } + rdev->preferred_minor = 0xffff; + rdev->data_offset = le64_to_cpu(sb->data_offset); + + if (refdev == 0) + return 1; + else { + __u64 ev1, ev2; + struct mdp_superblock_1 *refsb = + (struct mdp_superblock_1*)page_address(refdev->sb_page); + + if (memcmp(sb->set_uuid, refsb->set_uuid, 16) != 0 || + sb->level != refsb->level || + sb->layout != refsb->layout || + sb->chunksize != refsb->chunksize) { + printk(KERN_WARNING "md: %s has strangely different superblock to %s\n", + bdev_partition_name(rdev->bdev), + bdev_partition_name(refdev->bdev)); + return -EINVAL; + } + ev1 = le64_to_cpu(sb->events); + ev2 = le64_to_cpu(refsb->events); + + if (ev1 > ev2) + return 1; + } + if (minor_version) + rdev->size = ((rdev->bdev->bd_inode->i_size>>9) - le64_to_cpu(sb->data_offset)) / 2; + else + rdev->size = rdev->sb_offset; + if (rdev->size < le64_to_cpu(sb->data_size)/2) + return -EINVAL; + rdev->size = le64_to_cpu(sb->data_size)/2; + if (le32_to_cpu(sb->chunksize)) + rdev->size &= ~((sector_t)le32_to_cpu(sb->chunksize)/2 - 1); + return 0; +} + +static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev) +{ + struct mdp_superblock_1 *sb = (struct mdp_superblock_1*)page_address(rdev->sb_page); + + if (mddev->raid_disks == 0) { + mddev->major_version = 1; + mddev->minor_version = 0; + mddev->patch_version = 0; + mddev->persistent = 1; + mddev->chunk_size = le32_to_cpu(sb->chunksize) << 9; + mddev->ctime = le64_to_cpu(sb->ctime) & ((1ULL << 32)-1); + mddev->utime = le64_to_cpu(sb->utime) & ((1ULL << 32)-1); + mddev->level = le32_to_cpu(sb->level); + mddev->layout = le32_to_cpu(sb->layout); + mddev->raid_disks = le32_to_cpu(sb->raid_disks); + mddev->size = (u32)le64_to_cpu(sb->size); + mddev->events = le64_to_cpu(sb->events); + + mddev->recovery_cp = le64_to_cpu(sb->resync_offset); + memcpy(mddev->uuid, sb->set_uuid, 16); + + mddev->max_disks = (4096-256)/2; + } else { + __u64 ev1; + ev1 = le64_to_cpu(sb->events); + ++ev1; + if (ev1 < mddev->events) + return -EINVAL; + } + + if (mddev->level != LEVEL_MULTIPATH) { + int role; + rdev->desc_nr = le32_to_cpu(sb->dev_number); + role = le16_to_cpu(sb->dev_roles[rdev->desc_nr]); + switch(role) { + case 0xffff: /* spare */ + rdev->in_sync = 0; + rdev->faulty = 0; + rdev->raid_disk = -1; + break; + case 0xfffe: /* faulty */ + rdev->in_sync = 0; + rdev->faulty = 1; + rdev->raid_disk = -1; + break; + default: + rdev->in_sync = 1; + rdev->faulty = 0; + rdev->raid_disk = role; + break; + } + } + return 0; +} + +static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev) +{ + struct mdp_superblock_1 *sb; + struct list_head *tmp; + mdk_rdev_t *rdev2; + int max_dev, i; + /* make rdev->sb match mddev and rdev data. */ + + sb = (struct mdp_superblock_1*)page_address(rdev->sb_page); + + sb->feature_map = 0; + sb->pad0 = 0; + memset(sb->pad1, 0, sizeof(sb->pad1)); + memset(sb->pad2, 0, sizeof(sb->pad2)); + memset(sb->pad3, 0, sizeof(sb->pad3)); + + sb->utime = cpu_to_le64((__u64)mddev->utime); + sb->events = cpu_to_le64(mddev->events); + if (mddev->in_sync) + sb->resync_offset = cpu_to_le64(mddev->recovery_cp); + else + sb->resync_offset = cpu_to_le64(0); + + max_dev = 0; + ITERATE_RDEV(mddev,rdev2,tmp) + if (rdev2->desc_nr > max_dev) + max_dev = rdev2->desc_nr; + + sb->max_dev = max_dev; + for (i=0; i<max_dev;i++) + sb->dev_roles[max_dev] = cpu_to_le16(0xfffe); + + ITERATE_RDEV(mddev,rdev2,tmp) { + i = rdev2->desc_nr; + if (rdev2->faulty) + sb->dev_roles[i] = cpu_to_le16(0xfffe); + else if (rdev2->in_sync) + sb->dev_roles[i] = cpu_to_le16(rdev2->raid_disk); + else + sb->dev_roles[i] = cpu_to_le16(0xffff); + } + + sb->recovery_offset = cpu_to_le64(0); /* not supported yet */ +} + + struct super_type super_types[] = { [0] = { .name = "0.90.0", @@ -774,9 +975,14 @@ struct super_type super_types[] = { .validate_super = super_90_validate, .sync_super = super_90_sync, }, + [1] = { + .name = "md-1", + .owner = THIS_MODULE, + .load_super = super_1_load, + .validate_super = super_1_validate, + .sync_super = super_1_sync, + }, }; - - static mdk_rdev_t * match_dev_unit(mddev_t *mddev, mdk_rdev_t *dev) { @@ -804,13 +1010,13 @@ static int match_mddev_units(mddev_t *mddev1, mddev_t *mddev2) static LIST_HEAD(pending_raid_disks); -static void bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) +static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) { mdk_rdev_t *same_pdev; if (rdev->mddev) { MD_BUG(); - return; + return -EINVAL; } same_pdev = match_dev_unit(mddev, rdev); if (same_pdev) @@ -820,9 +1026,25 @@ static void bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) mdidx(mddev), bdev_partition_name(rdev->bdev), bdev_partition_name(same_pdev->bdev)); + /* Verify rdev->desc_nr is unique. + * If it is -1, assign a free number, else + * check number is not in use + */ + if (rdev->desc_nr < 0) { + int choice = 0; + if (mddev->pers) choice = mddev->raid_disks; + while (find_rdev_nr(mddev, choice)) + choice++; + rdev->desc_nr = choice; + } else { + if (find_rdev_nr(mddev, rdev->desc_nr)) + return -EBUSY; + } + list_add(&rdev->same_set, &mddev->disks); rdev->mddev = mddev; printk(KERN_INFO "md: bind<%s>\n", bdev_partition_name(rdev->bdev)); + return 0; } static void unbind_rdev_from_array(mdk_rdev_t * rdev) @@ -910,6 +1132,7 @@ static void export_array(mddev_t *mddev) if (!list_empty(&mddev->disks)) MD_BUG(); mddev->raid_disks = 0; + mddev->major_version = 0; } #undef BAD_CSUM @@ -994,8 +1217,6 @@ void md_print_devices(void) static int write_disk_sb(mdk_rdev_t * rdev) { - sector_t sb_offset; - sector_t size; if (!rdev->sb_loaded) { MD_BUG(); @@ -1006,35 +1227,12 @@ static int write_disk_sb(mdk_rdev_t * rdev) return 1; } - sb_offset = calc_dev_sboffset(rdev->bdev); - if (rdev->sb_offset != sb_offset) { - printk(KERN_INFO "%s's sb offset has changed from %llu to %llu, skipping\n", - bdev_partition_name(rdev->bdev), - (unsigned long long)rdev->sb_offset, - (unsigned long long)sb_offset); - goto skip; - } - /* - * If the disk went offline meanwhile and it's just a spare, then - * its size has changed to zero silently, and the MD code does - * not yet know that it's faulty. - */ - size = calc_dev_size(rdev->bdev, rdev->mddev); - if (size != rdev->size) { - printk(KERN_INFO "%s's size has changed from %llu to %llu since import, skipping\n", - bdev_partition_name(rdev->bdev), - (unsigned long long)rdev->size, - (unsigned long long)size); - goto skip; - } - - printk(KERN_INFO "(write) %s's sb offset: %llu\n", bdev_partition_name(rdev->bdev), (unsigned long long)sb_offset); + dprintk(KERN_INFO "(write) %s's sb offset: %llu\n", bdev_partition_name(rdev->bdev), + (unsigned long long)rdev->sb_offset); + + if (sync_page_io(rdev->bdev, rdev->sb_offset<<1, MD_SB_BYTES, rdev->sb_page, WRITE)) + return 0; - if (!sync_page_io(rdev->bdev, sb_offset<<1, MD_SB_BYTES, rdev->sb_page, WRITE)) - goto fail; -skip: - return 0; -fail: printk("md: write_disk_sb failed for device %s\n", bdev_partition_name(rdev->bdev)); return 1; } @@ -1045,7 +1243,8 @@ static void sync_sbs(mddev_t * mddev) struct list_head *tmp; ITERATE_RDEV(mddev,rdev,tmp) { - super_90_sync(mddev, rdev); + super_types[mddev->major_version]. + sync_super(mddev, rdev); rdev->sb_loaded = 1; } } @@ -1079,20 +1278,20 @@ repeat: if (!mddev->persistent) return; - printk(KERN_INFO "md: updating md%d RAID superblock on device (in sync %d)\n", + dprintk(KERN_INFO "md: updating md%d RAID superblock on device (in sync %d)\n", mdidx(mddev),mddev->in_sync); err = 0; ITERATE_RDEV(mddev,rdev,tmp) { - printk(KERN_INFO "md: "); + dprintk(KERN_INFO "md: "); if (rdev->faulty) - printk("(skipping faulty "); + dprintk("(skipping faulty "); - printk("%s ", bdev_partition_name(rdev->bdev)); + dprintk("%s ", bdev_partition_name(rdev->bdev)); if (!rdev->faulty) { err += write_disk_sb(rdev); } else - printk(")\n"); + dprintk(")\n"); if (!err && mddev->level == LEVEL_MULTIPATH) /* only need to write one superblock... */ break; @@ -1107,7 +1306,7 @@ repeat: } /* - * Import a device. If 'on_disk', then sanity check the superblock + * Import a device. If 'super_format' >= 0, then sanity check the superblock * * mark the device faulty if: * @@ -1116,7 +1315,7 @@ repeat: * * a faulty rdev _never_ has rdev->sb set. */ -static mdk_rdev_t *md_import_device(dev_t newdev, int on_disk) +static mdk_rdev_t *md_import_device(dev_t newdev, int super_format, int super_minor) { int err; mdk_rdev_t *rdev; @@ -1141,6 +1340,7 @@ static mdk_rdev_t *md_import_device(dev_t newdev, int on_disk) rdev->desc_nr = -1; rdev->faulty = 0; rdev->in_sync = 0; + rdev->data_offset = 0; atomic_set(&rdev->nr_pending, 0); size = rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS; @@ -1152,8 +1352,9 @@ static mdk_rdev_t *md_import_device(dev_t newdev, int on_disk) goto abort_free; } - if (on_disk) { - err = super_90_load(rdev, NULL); + if (super_format >= 0) { + err = super_types[super_format]. + load_super(rdev, NULL, super_minor); if (err == -EINVAL) { printk(KERN_WARNING "md: %s has invalid sb, not importing!\n", bdev_partition_name(rdev->bdev)); @@ -1206,7 +1407,8 @@ static int analyze_sbs(mddev_t * mddev) freshest = NULL; ITERATE_RDEV(mddev,rdev,tmp) - switch (super_90_load(rdev, freshest)) { + switch (super_types[mddev->major_version]. + load_super(rdev, freshest, mddev->minor_version)) { case 1: freshest = rdev; break; @@ -1218,12 +1420,14 @@ static int analyze_sbs(mddev_t * mddev) } - super_90_validate(mddev, freshest); + super_types[mddev->major_version]. + validate_super(mddev, freshest); i = 0; ITERATE_RDEV(mddev,rdev,tmp) { if (rdev != freshest) - if (super_90_validate(mddev, rdev)) { + if (super_types[mddev->major_version]. + validate_super(mddev, rdev)) { printk(KERN_WARNING "md: kicking non-fresh %s from array!\n", bdev_partition_name(rdev->bdev)); kick_rdev_from_array(rdev); @@ -1278,11 +1482,6 @@ static int device_size_calculation(mddev_t * mddev) ITERATE_RDEV(mddev,rdev,tmp) { if (rdev->faulty) continue; - if (rdev->size) { - MD_BUG(); - continue; - } - rdev->size = calc_dev_size(rdev->bdev, mddev); if (rdev->size < mddev->chunk_size / 1024) { printk(KERN_WARNING "md: Dev %s smaller than chunk_size: %lluk < %dk\n", @@ -1380,6 +1579,16 @@ static struct gendisk *md_probe(dev_t dev, int *part, void *data) return NULL; } +void md_wakeup_thread(mdk_thread_t *thread); + +static void md_safemode_timeout(unsigned long data) +{ + mddev_t *mddev = (mddev_t *) data; + + mddev->safemode = 1; + md_wakeup_thread(mddev->thread); +} + #define TOO_BIG_CHUNKSIZE KERN_ERR \ "too big chunk_size: %d > %d\n" @@ -1521,13 +1730,14 @@ static int do_md_run(mddev_t * mddev) } atomic_set(&mddev->writes_pending,0); mddev->safemode = 0; - if (mddev->pers->sync_request) - mddev->in_sync = 0; - else - mddev->in_sync = 1; + mddev->safemode_timer.function = md_safemode_timeout; + mddev->safemode_timer.data = (unsigned long) mddev; + mddev->safemode_delay = (20 * HZ)/1000 +1; /* 20 msec delay */ + mddev->in_sync = 1; md_update_sb(mddev); - md_recover_arrays(); + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + md_wakeup_thread(mddev->thread); set_capacity(disk, md_size[mdidx(mddev)]<<1); return (0); } @@ -1553,7 +1763,6 @@ static int restart_array(mddev_t *mddev) goto out; mddev->safemode = 0; - mddev->in_sync = 0; md_update_sb(mddev); mddev->ro = 0; set_disk_ro(disk, 0); @@ -1563,7 +1772,8 @@ static int restart_array(mddev_t *mddev) /* * Kick recovery or resync if necessary */ - md_recover_arrays(); + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + md_wakeup_thread(mddev->thread); err = 0; } else { printk(KERN_ERR "md: md%d has no personality assigned.\n", @@ -1593,12 +1803,13 @@ static int do_md_stop(mddev_t * mddev, int ro) if (mddev->pers) { if (mddev->sync_thread) { - if (mddev->recovery_running > 0) - mddev->recovery_running = -1; + set_bit(MD_RECOVERY_INTR, &mddev->recovery); md_unregister_thread(mddev->sync_thread); mddev->sync_thread = NULL; } + del_timer_sync(&mddev->safemode_timer); + invalidate_device(mk_kdev(disk->major, disk->first_minor), 1); if (ro) { @@ -1699,7 +1910,7 @@ static void autorun_devices(void) printk(KERN_INFO "md: considering %s ...\n", bdev_partition_name(rdev0->bdev)); INIT_LIST_HEAD(&candidates); ITERATE_RDEV_PENDING(rdev,tmp) - if (super_90_load(rdev, rdev0) >= 0) { + if (super_90_load(rdev, rdev0, 0) >= 0) { printk(KERN_INFO "md: adding %s ...\n", bdev_partition_name(rdev->bdev)); list_move(&rdev->same_set, &candidates); } @@ -1717,7 +1928,8 @@ static void autorun_devices(void) if (mddev_lock(mddev)) printk(KERN_WARNING "md: md%d locked, cannot run\n", mdidx(mddev)); - else if (mddev->raid_disks || !list_empty(&mddev->disks)) { + else if (mddev->raid_disks || mddev->major_version + || !list_empty(&mddev->disks)) { printk(KERN_WARNING "md: md%d already running, cannot run %s\n", mdidx(mddev), bdev_partition_name(rdev0->bdev)); mddev_unlock(mddev); @@ -1725,7 +1937,8 @@ static void autorun_devices(void) printk(KERN_INFO "md: created md%d\n", mdidx(mddev)); ITERATE_RDEV_GENERIC(candidates,rdev,tmp) { list_del_init(&rdev->same_set); - bind_rdev_to_array(rdev, mddev); + if (bind_rdev_to_array(rdev, mddev)) + export_rdev(rdev); } autorun_array(mddev); mddev_unlock(mddev); @@ -1778,7 +1991,7 @@ static int autostart_array(dev_t startdev) mdp_super_t *sb = NULL; mdk_rdev_t *start_rdev = NULL, *rdev; - start_rdev = md_import_device(startdev, 1); + start_rdev = md_import_device(startdev, 0, 0); if (IS_ERR(start_rdev)) { printk(KERN_WARNING "md: could not import %s!\n", partition_name(startdev)); return err; @@ -1812,7 +2025,7 @@ static int autostart_array(dev_t startdev) continue; if (dev == startdev) continue; - rdev = md_import_device(dev, 1); + rdev = md_import_device(dev, 0, 0); if (IS_ERR(rdev)) { printk(KERN_WARNING "md: could not import %s, trying to run array nevertheless.\n", partition_name(dev)); @@ -1875,9 +2088,8 @@ static int get_array_info(mddev_t * mddev, void * arg) } info.major_version = mddev->major_version; - info.major_version = mddev->major_version; info.minor_version = mddev->minor_version; - info.patch_version = mddev->patch_version; + info.patch_version = 1; info.ctime = mddev->ctime; info.level = mddev->level; info.size = mddev->size; @@ -1888,7 +2100,7 @@ static int get_array_info(mddev_t * mddev, void * arg) info.utime = mddev->utime; info.state = 0; - if (mddev->recovery_cp == MaxSector) + if (mddev->in_sync) info.state = (1<<MD_SB_CLEAN); info.active_disks = active; info.working_disks = working; @@ -1943,13 +2155,13 @@ static int get_disk_info(mddev_t * mddev, void * arg) static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info) { - sector_t size; mdk_rdev_t *rdev; dev_t dev; dev = MKDEV(info->major,info->minor); if (!mddev->raid_disks) { + int err; /* expecting a device which has a superblock */ - rdev = md_import_device(dev, 1); + rdev = md_import_device(dev, mddev->major_version, mddev->minor_version); if (IS_ERR(rdev)) { printk(KERN_WARNING "md: md_import_device returned %ld\n", PTR_ERR(rdev)); return PTR_ERR(rdev); @@ -1957,7 +2169,8 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info) if (!list_empty(&mddev->disks)) { mdk_rdev_t *rdev0 = list_entry(mddev->disks.next, mdk_rdev_t, same_set); - int err = super_90_load(rdev, rdev0); + int err = super_types[mddev->major_version] + .load_super(rdev, rdev0, mddev->minor_version); if (err < 0) { printk(KERN_WARNING "md: %s has different UUID to %s\n", bdev_partition_name(rdev->bdev), bdev_partition_name(rdev0->bdev)); @@ -1965,12 +2178,52 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info) return -EINVAL; } } - bind_rdev_to_array(rdev, mddev); - return 0; + err = bind_rdev_to_array(rdev, mddev); + if (err) + export_rdev(rdev); + return err; + } + + /* + * add_new_disk can be used once the array is assembled + * to add "hot spares". They must already have a superblock + * written + */ + if (mddev->pers) { + int err; + if (!mddev->pers->hot_add_disk) { + printk(KERN_WARNING "md%d: personality does not support diskops!\n", + mdidx(mddev)); + return -EINVAL; + } + rdev = md_import_device(dev, mddev->major_version, + mddev->minor_version); + if (IS_ERR(rdev)) { + printk(KERN_WARNING "md: md_import_device returned %ld\n", PTR_ERR(rdev)); + return PTR_ERR(rdev); + } + rdev->in_sync = 0; /* just to be sure */ + rdev->raid_disk = -1; + err = bind_rdev_to_array(rdev, mddev); + if (err) + export_rdev(rdev); + if (mddev->thread) + md_wakeup_thread(mddev->thread); + return err; + } + + /* otherwise, add_new_disk is only allowed + * for major_version==0 superblocks + */ + if (mddev->major_version != 0) { + printk(KERN_WARNING "md%d: ADD_NEW_DISK not supported\n", + mdidx(mddev)); + return -EINVAL; } if (!(info->state & (1<<MD_DISK_FAULTY))) { - rdev = md_import_device (dev, 0); + int err; + rdev = md_import_device (dev, -1, 0); if (IS_ERR(rdev)) { printk(KERN_WARNING "md: error, md_import_device() returned %ld\n", PTR_ERR(rdev)); return PTR_ERR(rdev); @@ -1987,16 +2240,21 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info) else rdev->in_sync = 0; - bind_rdev_to_array(rdev, mddev); + err = bind_rdev_to_array(rdev, mddev); + if (err) { + export_rdev(rdev); + return err; + } - if (!mddev->persistent) + if (!mddev->persistent) { printk(KERN_INFO "md: nonpersistent superblock ...\n"); + rdev->sb_offset = rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS; + } else + rdev->sb_offset = calc_dev_sboffset(rdev->bdev); + rdev->size = calc_dev_size(rdev, mddev->chunk_size); - size = calc_dev_size(rdev->bdev, mddev); - rdev->sb_offset = calc_dev_sboffset(rdev->bdev); - - if (!mddev->size || (mddev->size > size)) - mddev->size = size; + if (!mddev->size || (mddev->size > rdev->size)) + mddev->size = rdev->size; } return 0; @@ -2066,7 +2324,7 @@ busy: static int hot_add_disk(mddev_t * mddev, dev_t dev) { - int i, err; + int err; unsigned int size; mdk_rdev_t *rdev; @@ -2076,19 +2334,26 @@ static int hot_add_disk(mddev_t * mddev, dev_t dev) printk(KERN_INFO "md: trying to hot-add %s to md%d ... \n", partition_name(dev), mdidx(mddev)); + if (mddev->major_version != 0) { + printk(KERN_WARNING "md%d: HOT_ADD may only be used with version-0 superblocks.\n", + mdidx(mddev)); + return -EINVAL; + } if (!mddev->pers->hot_add_disk) { printk(KERN_WARNING "md%d: personality does not support diskops!\n", mdidx(mddev)); return -EINVAL; } - rdev = md_import_device (dev, 0); + rdev = md_import_device (dev, -1, 0); if (IS_ERR(rdev)) { printk(KERN_WARNING "md: error, md_import_device() returned %ld\n", PTR_ERR(rdev)); return -EINVAL; } - size = calc_dev_size(rdev->bdev, mddev); + rdev->sb_offset = calc_dev_sboffset(rdev->bdev); + size = calc_dev_size(rdev, mddev->chunk_size); + rdev->size = size; if (size < mddev->size) { printk(KERN_WARNING "md%d: disk size %llu blocks < array size %llu\n", @@ -2105,27 +2370,21 @@ static int hot_add_disk(mddev_t * mddev, dev_t dev) goto abort_export; } rdev->in_sync = 0; + rdev->desc_nr = -1; bind_rdev_to_array(rdev, mddev); /* * The rest should better be atomic, we can have disk failures * noticed in interrupt contexts ... */ - rdev->size = size; - rdev->sb_offset = calc_dev_sboffset(rdev->bdev); - for (i = mddev->raid_disks; i < mddev->max_disks; i++) - if (find_rdev_nr(mddev,i)==NULL) - break; - - if (i == mddev->max_disks) { + if (rdev->desc_nr == mddev->max_disks) { printk(KERN_WARNING "md%d: can not hot-add to full array!\n", mdidx(mddev)); err = -EBUSY; goto abort_unbind_export; } - rdev->desc_nr = i; rdev->raid_disk = -1; md_update_sb(mddev); @@ -2134,7 +2393,8 @@ static int hot_add_disk(mddev_t * mddev, dev_t dev) * Kick recovery, maybe this spare has to be added to the * array immediately. */ - md_recover_arrays(); + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + md_wakeup_thread(mddev->thread); return 0; @@ -2146,9 +2406,37 @@ abort_export: return err; } +/* + * set_array_info is used two different ways + * The original usage is when creating a new array. + * In this usage, raid_disks is > = and it together with + * level, size, not_persistent,layout,chunksize determine the + * shape of the array. + * This will always create an array with a type-0.90.0 superblock. + * The newer usage is when assembling an array. + * In this case raid_disks will be 0, and the major_version field is + * use to determine which style super-blocks are to be found on the devices. + * The minor and patch _version numbers are also kept incase the + * super_block handler wishes to interpret them. + */ static int set_array_info(mddev_t * mddev, mdu_array_info_t *info) { + if (info->raid_disks == 0) { + /* just setting version number for superblock loading */ + if (info->major_version < 0 || + info->major_version >= sizeof(super_types)/sizeof(super_types[0]) || + super_types[info->major_version].name == NULL) { + /* maybe try to auto-load a module? */ + printk(KERN_INFO "md: superblock version %d not known\n", + info->major_version); + return -EINVAL; + } + mddev->major_version = info->major_version; + mddev->minor_version = info->minor_version; + mddev->patch_version = info->patch_version; + return 0; + } mddev->major_version = MD_MAJOR_VERSION; mddev->minor_version = MD_MINOR_VERSION; mddev->patch_version = MD_PATCHLEVEL_VERSION; @@ -2169,6 +2457,7 @@ static int set_array_info(mddev_t * mddev, mdu_array_info_t *info) mddev->layout = info->layout; mddev->chunk_size = info->chunk_size; + mddev->max_disks = MD_SB_DISKS; /* @@ -2282,9 +2571,11 @@ static int md_ioctl(struct inode *inode, struct file *file, err = -EBUSY; goto abort_unlock; } - if (arg) { + { mdu_array_info_t info; - if (copy_from_user(&info, (void*)arg, sizeof(info))) { + if (!arg) + memset(&info, 0, sizeof(info)); + else if (copy_from_user(&info, (void*)arg, sizeof(info))) { err = -EFAULT; goto abort_unlock; } @@ -2473,12 +2764,6 @@ static struct block_device_operations md_fops = .ioctl = md_ioctl, }; - -static inline void flush_curr_signals(void) -{ - flush_signals(current); -} - int md_thread(void * arg) { mdk_thread_t *thread = arg; @@ -2489,7 +2774,7 @@ int md_thread(void * arg) * Detach thread */ - daemonize(thread->name); + daemonize(thread->name, mdidx(thread->mddev)); current->exit_signal = SIGCHLD; allow_signal(SIGKILL); @@ -2510,7 +2795,7 @@ int md_thread(void * arg) complete(thread->event); while (thread->run) { - void (*run)(void *data); + void (*run)(mddev_t *); wait_event_interruptible(thread->wqueue, test_bit(THREAD_WAKEUP, &thread->flags)); @@ -2521,11 +2806,11 @@ int md_thread(void * arg) run = thread->run; if (run) { - run(thread->data); + run(thread->mddev); blk_run_queues(); } if (signal_pending(current)) - flush_curr_signals(); + flush_signals(current); } complete(thread->event); return 0; @@ -2533,13 +2818,15 @@ int md_thread(void * arg) void md_wakeup_thread(mdk_thread_t *thread) { - dprintk("md: waking up MD thread %p.\n", thread); - set_bit(THREAD_WAKEUP, &thread->flags); - wake_up(&thread->wqueue); + if (thread) { + dprintk("md: waking up MD thread %p.\n", thread); + set_bit(THREAD_WAKEUP, &thread->flags); + wake_up(&thread->wqueue); + } } -mdk_thread_t *md_register_thread(void (*run) (void *), - void *data, const char *name) +mdk_thread_t *md_register_thread(void (*run) (mddev_t *), mddev_t *mddev, + const char *name) { mdk_thread_t *thread; int ret; @@ -2556,7 +2843,7 @@ mdk_thread_t *md_register_thread(void (*run) (void *), init_completion(&event); thread->event = &event; thread->run = run; - thread->data = data; + thread->mddev = mddev; thread->name = name; ret = kernel_thread(md_thread, thread, 0); if (ret < 0) { @@ -2591,16 +2878,6 @@ void md_unregister_thread(mdk_thread_t *thread) kfree(thread); } -static void md_recover_arrays(void) -{ - if (!md_recovery_thread) { - MD_BUG(); - return; - } - md_wakeup_thread(md_recovery_thread); -} - - void md_error(mddev_t *mddev, mdk_rdev_t *rdev) { dprintk("md_error dev:(%d:%d), rdev:(%d:%d), (caller: %p,%p,%p,%p).\n", @@ -2618,33 +2895,34 @@ void md_error(mddev_t *mddev, mdk_rdev_t *rdev) if (!mddev->pers->error_handler) return; mddev->pers->error_handler(mddev,rdev); - md_recover_arrays(); + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + md_wakeup_thread(mddev->thread); } -static int status_unused(char * page) +/* seq_file implementation /proc/mdstat */ + +static void status_unused(struct seq_file *seq) { - int sz = 0, i = 0; + int i = 0; mdk_rdev_t *rdev; struct list_head *tmp; - sz += sprintf(page + sz, "unused devices: "); + seq_printf(seq, "unused devices: "); ITERATE_RDEV_PENDING(rdev,tmp) { i++; - sz += sprintf(page + sz, "%s ", + seq_printf(seq, "%s ", bdev_partition_name(rdev->bdev)); } if (!i) - sz += sprintf(page + sz, "<none>"); + seq_printf(seq, "<none>"); - sz += sprintf(page + sz, "\n"); - return sz; + seq_printf(seq, "\n"); } -static int status_resync(char * page, mddev_t * mddev) +static void status_resync(struct seq_file *seq, mddev_t * mddev) { - int sz = 0; unsigned long max_blocks, resync, res, dt, db, rt; resync = (mddev->curr_resync - atomic_read(&mddev->recovery_active))/2; @@ -2655,21 +2933,22 @@ static int status_resync(char * page, mddev_t * mddev) */ if (!max_blocks) { MD_BUG(); - return 0; + return; } res = (resync/1024)*1000/(max_blocks/1024 + 1); { int i, x = res/50, y = 20-x; - sz += sprintf(page + sz, "["); + seq_printf(seq, "["); for (i = 0; i < x; i++) - sz += sprintf(page + sz, "="); - sz += sprintf(page + sz, ">"); + seq_printf(seq, "="); + seq_printf(seq, ">"); for (i = 0; i < y; i++) - sz += sprintf(page + sz, "."); - sz += sprintf(page + sz, "] "); + seq_printf(seq, "."); + seq_printf(seq, "] "); } - sz += sprintf(page + sz, " %s =%3lu.%lu%% (%lu/%lu)", - (mddev->spares ? "recovery" : "resync"), + seq_printf(seq, " %s =%3lu.%lu%% (%lu/%lu)", + (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) ? + "resync" : "recovery"), res/10, res % 10, resync, max_blocks); /* @@ -2686,44 +2965,110 @@ static int status_resync(char * page, mddev_t * mddev) db = resync - (mddev->resync_mark_cnt/2); rt = (dt * ((max_blocks-resync) / (db/100+1)))/100; - sz += sprintf(page + sz, " finish=%lu.%lumin", rt / 60, (rt % 60)/6); + seq_printf(seq, " finish=%lu.%lumin", rt / 60, (rt % 60)/6); - sz += sprintf(page + sz, " speed=%ldK/sec", db/dt); + seq_printf(seq, " speed=%ldK/sec", db/dt); +} - return sz; +static void *md_seq_start(struct seq_file *seq, loff_t *pos) +{ + struct list_head *tmp; + loff_t l = *pos; + mddev_t *mddev; + + if (l > 0x10000) + return NULL; + if (!l--) + /* header */ + return (void*)1; + + spin_lock(&all_mddevs_lock); + list_for_each(tmp,&all_mddevs) + if (!l--) { + mddev = list_entry(tmp, mddev_t, all_mddevs); + mddev_get(mddev); + spin_unlock(&all_mddevs_lock); + return mddev; + } + spin_unlock(&all_mddevs_lock); + return (void*)2;/* tail */ +} + +static void *md_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct list_head *tmp; + mddev_t *next_mddev, *mddev = v; + + ++*pos; + if (v == (void*)2) + return NULL; + + spin_lock(&all_mddevs_lock); + if (v == (void*)1) + tmp = all_mddevs.next; + else + tmp = mddev->all_mddevs.next; + if (tmp != &all_mddevs) + next_mddev = mddev_get(list_entry(tmp,mddev_t,all_mddevs)); + else { + next_mddev = (void*)2; + *pos = 0x10000; + } + spin_unlock(&all_mddevs_lock); + + if (v != (void*)1) + mddev_put(mddev); + return next_mddev; + +} + +static void md_seq_stop(struct seq_file *seq, void *v) +{ + mddev_t *mddev = v; + + if (mddev && v != (void*)1 && v != (void*)2) + mddev_put(mddev); } -static int md_status_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int md_seq_show(struct seq_file *seq, void *v) { - int sz = 0, j; + mddev_t *mddev = v; sector_t size; - struct list_head *tmp, *tmp2; + struct list_head *tmp2; mdk_rdev_t *rdev; - mddev_t *mddev; + int i; - sz += sprintf(page + sz, "Personalities : "); - for (j = 0; j < MAX_PERSONALITY; j++) - if (pers[j]) - sz += sprintf(page+sz, "[%s] ", pers[j]->name); + if (v == (void*)1) { + seq_printf(seq, "Personalities : "); + for (i = 0; i < MAX_PERSONALITY; i++) + if (pers[i]) + seq_printf(seq, "[%s] ", pers[i]->name); - sz += sprintf(page+sz, "\n"); + seq_printf(seq, "\n"); + return 0; + } + if (v == (void*)2) { + status_unused(seq); + return 0; + } - ITERATE_MDDEV(mddev,tmp) if (mddev_lock(mddev)==0) { - sz += sprintf(page + sz, "md%d : %sactive", mdidx(mddev), + if (mddev_lock(mddev)!=0) + return -EINTR; + if (mddev->pers || mddev->raid_disks || !list_empty(&mddev->disks)) { + seq_printf(seq, "md%d : %sactive", mdidx(mddev), mddev->pers ? "" : "in"); if (mddev->pers) { if (mddev->ro) - sz += sprintf(page + sz, " (read-only)"); - sz += sprintf(page + sz, " %s", mddev->pers->name); + seq_printf(seq, " (read-only)"); + seq_printf(seq, " %s", mddev->pers->name); } size = 0; ITERATE_RDEV(mddev,rdev,tmp2) { - sz += sprintf(page + sz, " %s[%d]", + seq_printf(seq, " %s[%d]", bdev_partition_name(rdev->bdev), rdev->desc_nr); if (rdev->faulty) { - sz += sprintf(page + sz, "(F)"); + seq_printf(seq, "(F)"); continue; } size += rdev->size; @@ -2731,34 +3076,50 @@ static int md_status_read_proc(char *page, char **start, off_t off, if (!list_empty(&mddev->disks)) { if (mddev->pers) - sz += sprintf(page + sz, "\n %llu blocks", + seq_printf(seq, "\n %llu blocks", (unsigned long long)md_size[mdidx(mddev)]); else - sz += sprintf(page + sz, "\n %llu blocks", (unsigned long long)size); + seq_printf(seq, "\n %llu blocks", (unsigned long long)size); } - if (!mddev->pers) { - sz += sprintf(page+sz, "\n"); - mddev_unlock(mddev); - continue; + if (mddev->pers) { + mddev->pers->status (seq, mddev); + seq_printf(seq, "\n "); + if (mddev->curr_resync > 2) + status_resync (seq, mddev); + else if (mddev->curr_resync == 1 || mddev->curr_resync == 2) + seq_printf(seq, " resync=DELAYED"); } - sz += mddev->pers->status (page+sz, mddev); + seq_printf(seq, "\n"); + } + mddev_unlock(mddev); + + return 0; +} - sz += sprintf(page+sz, "\n "); - if (mddev->curr_resync > 2) - sz += status_resync (page+sz, mddev); - else if (mddev->curr_resync == 1 || mddev->curr_resync == 2) - sz += sprintf(page + sz, " resync=DELAYED"); +static struct seq_operations md_seq_ops = { + .start = md_seq_start, + .next = md_seq_next, + .stop = md_seq_stop, + .show = md_seq_show, +}; - sz += sprintf(page + sz, "\n"); - mddev_unlock(mddev); - } - sz += status_unused(page + sz); +static int md_seq_open(struct inode *inode, struct file *file) +{ + int error; - return sz; + error = seq_open(file, &md_seq_ops); + return error; } +static struct file_operations md_seq_fops = { + .open = md_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + int register_md_personality(int pnum, mdk_personality_t *p) { if (pnum >= MAX_PERSONALITY) { @@ -2820,9 +3181,8 @@ void md_done_sync(mddev_t *mddev, int blocks, int ok) atomic_sub(blocks, &mddev->recovery_active); wake_up(&mddev->recovery_wait); if (!ok) { - mddev->recovery_error = -EIO; - mddev->recovery_running = -1; - md_recover_arrays(); + set_bit(MD_RECOVERY_ERR, &mddev->recovery); + md_wakeup_thread(mddev->thread); // stop recovery, signal do_sync .... } } @@ -2830,40 +3190,49 @@ void md_done_sync(mddev_t *mddev, int blocks, int ok) void md_write_start(mddev_t *mddev) { - if (mddev->safemode && !atomic_read(&mddev->writes_pending)) { + if (!atomic_read(&mddev->writes_pending)) { mddev_lock_uninterruptible(mddev); - atomic_inc(&mddev->writes_pending); if (mddev->in_sync) { mddev->in_sync = 0; + del_timer(&mddev->safemode_timer); md_update_sb(mddev); } + atomic_inc(&mddev->writes_pending); mddev_unlock(mddev); } else atomic_inc(&mddev->writes_pending); } -void md_write_end(mddev_t *mddev, mdk_thread_t *thread) +void md_write_end(mddev_t *mddev) { - if (atomic_dec_and_test(&mddev->writes_pending) && mddev->safemode) - md_wakeup_thread(thread); + if (atomic_dec_and_test(&mddev->writes_pending)) { + if (mddev->safemode == 2) + md_wakeup_thread(mddev->thread); + else + mod_timer(&mddev->safemode_timer, jiffies + mddev->safemode_delay); + } } + static inline void md_enter_safemode(mddev_t *mddev) { - mddev_lock_uninterruptible(mddev); - if (mddev->safemode && !atomic_read(&mddev->writes_pending) && !mddev->in_sync && !mddev->recovery_running) { + if (mddev->safemode && !atomic_read(&mddev->writes_pending) && + !mddev->in_sync && mddev->recovery_cp == MaxSector) { mddev->in_sync = 1; md_update_sb(mddev); } mddev_unlock(mddev); + + if (mddev->safemode == 1) + mddev->safemode = 0; } void md_handle_safemode(mddev_t *mddev) { if (signal_pending(current)) { - printk(KERN_INFO "md: md%d in safe mode\n",mdidx(mddev)); - mddev->safemode= 1; - flush_curr_signals(); + printk(KERN_INFO "md: md%d in immediate safe mode\n",mdidx(mddev)); + mddev->safemode = 2; + flush_signals(current); } if (mddev->safemode) md_enter_safemode(mddev); @@ -2874,9 +3243,8 @@ DECLARE_WAIT_QUEUE_HEAD(resync_wait); #define SYNC_MARKS 10 #define SYNC_MARK_STEP (3*HZ) -static void md_do_sync(void *data) +static void md_do_sync(mddev_t *mddev) { - mddev_t *mddev = data; mddev_t *mddev2; unsigned int max_sectors, currspeed = 0, j, window, err; @@ -2887,7 +3255,7 @@ static void md_do_sync(void *data) unsigned long last_check; /* just incase thread restarts... */ - if (mddev->recovery_running <= 0) + if (test_bit(MD_RECOVERY_DONE, &mddev->recovery)) return; /* we overload curr_resync somewhat here. @@ -2914,14 +3282,16 @@ static void md_do_sync(void *data) } if (wait_event_interruptible(resync_wait, mddev2->curr_resync < mddev->curr_resync)) { - flush_curr_signals(); + flush_signals(current); err = -EINTR; mddev_put(mddev2); goto skip; } } - if (mddev->curr_resync == 1) + if (mddev->curr_resync == 1) { + mddev_put(mddev2); break; + } } } while (mddev->curr_resync < 2); @@ -2934,9 +3304,13 @@ static void md_do_sync(void *data) sysctl_speed_limit_max); is_mddev_idle(mddev); /* this also initializes IO event counters */ + if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) + j = mddev->recovery_cp; + else + j = 0; for (m = 0; m < SYNC_MARKS; m++) { mark[m] = jiffies; - mark_cnt[m] = mddev->recovery_cp; + mark_cnt[m] = j; } last_mark = 0; mddev->resync_mark = mark[last_mark]; @@ -2953,12 +3327,10 @@ static void md_do_sync(void *data) init_waitqueue_head(&mddev->recovery_wait); last_check = 0; - mddev->recovery_error = 0; - - if (mddev->recovery_cp) + if (j) printk(KERN_INFO "md: resuming recovery of md%d from checkpoint.\n", mdidx(mddev)); - for (j = mddev->recovery_cp; j < max_sectors;) { + while (j < max_sectors) { int sectors; sectors = mddev->pers->sync_request(mddev, j, currspeed < sysctl_speed_limit_min); @@ -2975,6 +3347,10 @@ static void md_do_sync(void *data) last_check = j; + if (test_bit(MD_RECOVERY_INTR, &mddev->recovery) || + test_bit(MD_RECOVERY_ERR, &mddev->recovery)) + break; + blk_run_queues(); repeat: @@ -2995,7 +3371,7 @@ static void md_do_sync(void *data) * got a signal, exit. */ printk(KERN_INFO "md: md_do_sync() got signal ... exiting\n"); - flush_curr_signals(); + flush_signals(current); err = -EINTR; goto out; } @@ -3029,39 +3405,42 @@ static void md_do_sync(void *data) out: wait_event(mddev->recovery_wait, !atomic_read(&mddev->recovery_active)); - if (mddev->recovery_running < 0 && - !mddev->recovery_error && mddev->curr_resync > 2) - { - /* interrupted but no write errors */ - printk(KERN_INFO "md: checkpointing recovery of md%d.\n", mdidx(mddev)); - mddev->recovery_cp = mddev->curr_resync; - } - /* tell personality that we are finished */ mddev->pers->sync_request(mddev, max_sectors, 1); - skip: - mddev->curr_resync = 0; + if (err) - mddev->recovery_running = -1; - if (mddev->recovery_running > 0) - mddev->recovery_running = 0; - if (mddev->recovery_running == 0) - mddev->recovery_cp = MaxSector; + set_bit(MD_RECOVERY_ERR, &mddev->recovery); + if (!test_bit(MD_RECOVERY_ERR, &mddev->recovery) && + mddev->curr_resync > 2 && + mddev->curr_resync > mddev->recovery_cp) { + if (test_bit(MD_RECOVERY_INTR, &mddev->recovery)) { + printk(KERN_INFO "md: checkpointing recovery of md%d.\n", mdidx(mddev)); + mddev->recovery_cp = mddev->curr_resync; + } else + mddev->recovery_cp = MaxSector; + } + if (mddev->safemode) md_enter_safemode(mddev); - md_recover_arrays(); + skip: + mddev->curr_resync = 0; + set_bit(MD_RECOVERY_DONE, &mddev->recovery); + md_wakeup_thread(mddev->thread); } /* - * This is the kernel thread that watches all md arrays for re-sync and other - * action that might be needed. + * This routine is regularly called by all per-raid-array threads to + * deal with generic issues like resync and super-block update. + * Raid personalities that don't have a thread (linear/raid0) do not + * need this as they never do any recovery or update the superblock. + * * It does not do any resync itself, but rather "forks" off other threads * to do that as needed. - * When it is determined that resync is needed, we set "->recovery_running" and - * create a thread at ->sync_thread. - * When the thread finishes it clears recovery_running (or sets an error) - * and wakeup up this thread which will reap the thread and finish up. + * When it is determined that resync is needed, we set MD_RECOVERY_RUNNING in + * "->recovery" and create a thread at ->sync_thread. + * When the thread finishes it sets MD_RECOVERY_DONE (and might set MD_RECOVERY_ERR) + * and wakeups up this thread which will reap the thread and finish up. * This thread also removes any faulty devices (with nr_pending == 0). * * The overall approach is: @@ -3072,41 +3451,47 @@ static void md_do_sync(void *data) * 5/ If array is degraded, try to add spares devices * 6/ If array has spares or is not in-sync, start a resync thread. */ -void md_do_recovery(void *data) +void md_check_recovery(mddev_t *mddev) { - mddev_t *mddev; mdk_rdev_t *rdev; - struct list_head *tmp, *rtmp; + struct list_head *rtmp; dprintk(KERN_INFO "md: recovery thread got woken up ...\n"); - ITERATE_MDDEV(mddev,tmp) if (mddev_lock(mddev)==0) { - if (!mddev->raid_disks || !mddev->pers || mddev->ro) - goto unlock; + if (mddev->ro) + return; + if ( ! ( + mddev->sb_dirty || + test_bit(MD_RECOVERY_NEEDED, &mddev->recovery) || + test_bit(MD_RECOVERY_DONE, &mddev->recovery) + )) + return; + if (mddev_trylock(mddev)==0) { + int spares =0; if (mddev->sb_dirty) md_update_sb(mddev); - if (mddev->recovery_running > 0) + if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) && + !test_bit(MD_RECOVERY_DONE, &mddev->recovery)) /* resync/recovery still happening */ goto unlock; if (mddev->sync_thread) { /* resync has finished, collect result */ md_unregister_thread(mddev->sync_thread); mddev->sync_thread = NULL; - if (mddev->recovery_running == 0) { + if (!test_bit(MD_RECOVERY_ERR, &mddev->recovery)) { /* success...*/ /* activate any spares */ mddev->pers->spare_active(mddev); - mddev->spares = 0; } md_update_sb(mddev); - mddev->recovery_running = 0; + mddev->recovery = 0; wake_up(&resync_wait); goto unlock; } - if (mddev->recovery_running) { + if (mddev->recovery) { /* that's odd.. */ - mddev->recovery_running = 0; + mddev->recovery = 0; wake_up(&resync_wait); } @@ -3114,7 +3499,6 @@ void md_do_recovery(void *data) * remove any failed drives, then * add spares if possible */ - mddev->spares = 0; ITERATE_RDEV(mddev,rdev,rtmp) { if (rdev->raid_disk >= 0 && rdev->faulty && @@ -3123,43 +3507,41 @@ void md_do_recovery(void *data) rdev->raid_disk = -1; } if (!rdev->faulty && rdev->raid_disk >= 0 && !rdev->in_sync) - mddev->spares++; + spares++; } if (mddev->degraded) { ITERATE_RDEV(mddev,rdev,rtmp) if (rdev->raid_disk < 0 && !rdev->faulty) { - if (mddev->pers->hot_add_disk(mddev,rdev)) { - mddev->spares++; - mddev->recovery_cp = 0; - } + if (mddev->pers->hot_add_disk(mddev,rdev)) + spares++; else break; } } - if (!mddev->spares && (mddev->recovery_cp == MaxSector )) { + if (!spares && (mddev->recovery_cp == MaxSector )) { /* nothing we can do ... */ goto unlock; } if (mddev->pers->sync_request) { + set_bit(MD_RECOVERY_RUNNING, &mddev->recovery); + if (!spares) + set_bit(MD_RECOVERY_SYNC, &mddev->recovery); mddev->sync_thread = md_register_thread(md_do_sync, mddev, - "md_resync"); + "md%d_resync"); if (!mddev->sync_thread) { printk(KERN_ERR "md%d: could not start resync thread...\n", mdidx(mddev)); /* leave the spares where they are, it shouldn't hurt */ - mddev->recovery_running = 0; + mddev->recovery = 0; } else { - mddev->recovery_running = 1; md_wakeup_thread(mddev->sync_thread); } } unlock: mddev_unlock(mddev); } - dprintk(KERN_INFO "md: recovery thread finished ...\n"); - } int md_notify_reboot(struct notifier_block *this, @@ -3194,6 +3576,7 @@ struct notifier_block md_notifier = { static void md_geninit(void) { + struct proc_dir_entry *p; int i; for(i = 0; i < MAX_MD_DEVS; i++) { @@ -3203,13 +3586,14 @@ static void md_geninit(void) dprintk("md: sizeof(mdp_super_t) = %d\n", (int)sizeof(mdp_super_t)); #ifdef CONFIG_PROC_FS - create_proc_read_entry("mdstat", 0, NULL, md_status_read_proc, NULL); + p = create_proc_entry("mdstat", S_IRUGO, NULL); + if (p) + p->proc_fops = &md_seq_fops; #endif } int __init md_init(void) { - static char * name = "mdrecoveryd"; int minor; printk(KERN_INFO "md: md driver %d.%d.%d MAX_MD_DEVS=%d, MD_SB_DISKS=%d\n", @@ -3229,11 +3613,6 @@ int __init md_init(void) S_IFBLK | S_IRUSR | S_IWUSR, &md_fops, NULL); } - md_recovery_thread = md_register_thread(md_do_recovery, NULL, name); - if (!md_recovery_thread) - printk(KERN_ALERT - "md: bug: couldn't allocate md_recovery_thread\n"); - register_reboot_notifier(&md_notifier); raid_table_header = register_sysctl_table(raid_root_table, 1); @@ -3268,7 +3647,7 @@ static void autostart_arrays(void) for (i = 0; i < dev_cnt; i++) { dev_t dev = detected_devices[i]; - rdev = md_import_device(dev,1); + rdev = md_import_device(dev,0, 0); if (IS_ERR(rdev)) { printk(KERN_ALERT "md: could not import %s!\n", partition_name(dev)); @@ -3291,7 +3670,6 @@ static __exit void md_exit(void) { int i; blk_unregister_region(MKDEV(MAJOR_NR,0), MAX_MD_DEVS); - md_unregister_thread(md_recovery_thread); for (i=0; i < MAX_MD_DEVS; i++) devfs_remove("md/%d", i); devfs_remove("md"); @@ -3331,4 +3709,5 @@ EXPORT_SYMBOL(md_unregister_thread); EXPORT_SYMBOL(md_wakeup_thread); EXPORT_SYMBOL(md_print_devices); EXPORT_SYMBOL(md_interrupt_thread); +EXPORT_SYMBOL(md_check_recovery); MODULE_LICENSE("GPL"); diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c index 5b96e4a42d21..bff6174d8782 100644 --- a/drivers/md/multipath.c +++ b/drivers/md/multipath.c @@ -86,7 +86,6 @@ static void multipath_reschedule_retry (struct multipath_bh *mp_bh) { unsigned long flags; mddev_t *mddev = mp_bh->mddev; - multipath_conf_t *conf = mddev_to_conf(mddev); spin_lock_irqsave(&retry_list_lock, flags); if (multipath_retry_list == NULL) @@ -95,7 +94,7 @@ static void multipath_reschedule_retry (struct multipath_bh *mp_bh) multipath_retry_tail = &mp_bh->next_mp; mp_bh->next_mp = NULL; spin_unlock_irqrestore(&retry_list_lock, flags); - md_wakeup_thread(conf->thread); + md_wakeup_thread(mddev->thread); } @@ -185,19 +184,18 @@ static int multipath_make_request (request_queue_t *q, struct bio * bio) return 0; } -static int multipath_status (char *page, mddev_t *mddev) +static void multipath_status (struct seq_file *seq, mddev_t *mddev) { multipath_conf_t *conf = mddev_to_conf(mddev); - int sz = 0, i; + int i; - sz += sprintf (page+sz, " [%d/%d] [", conf->raid_disks, + seq_printf (seq, " [%d/%d] [", conf->raid_disks, conf->working_disks); for (i = 0; i < conf->raid_disks; i++) - sz += sprintf (page+sz, "%s", + seq_printf (seq, "%s", conf->multipaths[i].rdev && conf->multipaths[i].rdev->in_sync ? "U" : "_"); - sz += sprintf (page+sz, "]"); - return sz; + seq_printf (seq, "]"); } #define LAST_DISK KERN_ALERT \ @@ -334,14 +332,14 @@ abort: * 3. Performs writes following reads for array syncronising. */ -static void multipathd (void *data) +static void multipathd (mddev_t *mddev) { struct multipath_bh *mp_bh; struct bio *bio; unsigned long flags; - mddev_t *mddev; mdk_rdev_t *rdev; + md_check_recovery(mddev); for (;;) { spin_lock_irqsave(&retry_list_lock, flags); mp_bh = multipath_retry_list; @@ -471,10 +469,10 @@ static int multipath_run (mddev_t *mddev) } { - const char * name = "multipathd"; + const char * name = "md%d_multipath"; - conf->thread = md_register_thread(multipathd, conf, name); - if (!conf->thread) { + mddev->thread = md_register_thread(multipathd, mddev, name); + if (!mddev->thread) { printk(THREAD_ERROR, mdidx(mddev)); goto out_free_conf; } @@ -513,7 +511,7 @@ static int multipath_stop (mddev_t *mddev) { multipath_conf_t *conf = mddev_to_conf(mddev); - md_unregister_thread(conf->thread); + md_unregister_thread(mddev->thread); mempool_destroy(conf->pool); kfree(conf); mddev->private = NULL; diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index 0d83f3647f93..99de04b380a3 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -349,7 +349,7 @@ static int raid0_make_request (request_queue_t *q, struct bio *bio) * is the only IO operation happening on this bh. */ bio->bi_bdev = tmp_dev->bdev; - bio->bi_sector = rsect; + bio->bi_sector = rsect + tmp_dev->data_offset; /* * Let the main block layer submit the IO and resolve recursion: @@ -372,41 +372,40 @@ bad_zone1: return 0; } -static int raid0_status (char *page, mddev_t *mddev) +static void raid0_status (struct seq_file *seq, mddev_t *mddev) { - int sz = 0; #undef MD_DEBUG #ifdef MD_DEBUG int j, k; raid0_conf_t *conf = mddev_to_conf(mddev); - sz += sprintf(page + sz, " "); + seq_printf(seq, " "); for (j = 0; j < conf->nr_zones; j++) { - sz += sprintf(page + sz, "[z%d", + seq_printf(seq, "[z%d", conf->hash_table[j].zone0 - conf->strip_zone); if (conf->hash_table[j].zone1) - sz += sprintf(page+sz, "/z%d] ", + seq_printf(seq, "/z%d] ", conf->hash_table[j].zone1 - conf->strip_zone); else - sz += sprintf(page+sz, "] "); + seq_printf(seq, "] "); } - sz += sprintf(page + sz, "\n"); + seq_printf(seq, "\n"); for (j = 0; j < conf->nr_strip_zones; j++) { - sz += sprintf(page + sz, " z%d=[", j); + seq_printf(seq, " z%d=[", j); for (k = 0; k < conf->strip_zone[j].nb_dev; k++) - sz += sprintf (page+sz, "%s/", bdev_partition_name( + seq_printf (seq, "%s/", bdev_partition_name( conf->strip_zone[j].dev[k]->bdev)); - sz--; - sz += sprintf (page+sz, "] zo=%d do=%d s=%d\n", + + seq_printf (seq, "] zo=%d do=%d s=%d\n", conf->strip_zone[j].zone_offset, conf->strip_zone[j].dev_offset, conf->strip_zone[j].size); } #endif - sz += sprintf(page + sz, " %dk chunks", mddev->chunk_size/1024); - return sz; + seq_printf(seq, " %dk chunks", mddev->chunk_size/1024); + return; } static mdk_personality_t raid0_personality= diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 83ee73ef172f..0dd391e3d3b1 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -225,13 +225,12 @@ static void reschedule_retry(r1bio_t *r1_bio) { unsigned long flags; mddev_t *mddev = r1_bio->mddev; - conf_t *conf = mddev_to_conf(mddev); spin_lock_irqsave(&retry_list_lock, flags); list_add(&r1_bio->retry_list, &retry_list_head); spin_unlock_irqrestore(&retry_list_lock, flags); - md_wakeup_thread(conf->thread); + md_wakeup_thread(mddev->thread); } /* @@ -320,7 +319,7 @@ static int end_request(struct bio *bio, unsigned int bytes_done, int error) * already. */ if (atomic_dec_and_test(&r1_bio->remaining)) { - md_write_end(r1_bio->mddev,conf->thread); + md_write_end(r1_bio->mddev); raid_end_bio_io(r1_bio, uptodate); } } @@ -494,7 +493,7 @@ static int make_request(request_queue_t *q, struct bio * bio) BUG(); r1_bio->read_bio = read_bio; - read_bio->bi_sector = r1_bio->sector; + read_bio->bi_sector = r1_bio->sector + mirror->rdev->data_offset; read_bio->bi_bdev = mirror->rdev->bdev; read_bio->bi_end_io = end_request; read_bio->bi_rw = r1_bio->cmd; @@ -529,7 +528,7 @@ static int make_request(request_queue_t *q, struct bio * bio) mbio = bio_clone(bio, GFP_NOIO); r1_bio->write_bios[i] = mbio; - mbio->bi_sector = r1_bio->sector; + mbio->bi_sector = r1_bio->sector + conf->mirrors[i].rdev->data_offset; mbio->bi_bdev = conf->mirrors[i].rdev->bdev; mbio->bi_end_io = end_request; mbio->bi_rw = r1_bio->cmd; @@ -542,7 +541,7 @@ static int make_request(request_queue_t *q, struct bio * bio) * If all mirrors are non-operational * then return an IO error: */ - md_write_end(mddev,conf->thread); + md_write_end(mddev); raid_end_bio_io(r1_bio, 0); return 0; } @@ -571,19 +570,18 @@ static int make_request(request_queue_t *q, struct bio * bio) return 0; } -static int status(char *page, mddev_t *mddev) +static void status(struct seq_file *seq, mddev_t *mddev) { conf_t *conf = mddev_to_conf(mddev); - int sz = 0, i; + int i; - sz += sprintf(page+sz, " [%d/%d] [", conf->raid_disks, + seq_printf(seq, " [%d/%d] [", conf->raid_disks, conf->working_disks); for (i = 0; i < conf->raid_disks; i++) - sz += sprintf(page+sz, "%s", + seq_printf(seq, "%s", conf->mirrors[i].rdev && conf->mirrors[i].rdev->in_sync ? "U" : "_"); - sz += sprintf (page+sz, "]"); - return sz; + seq_printf(seq, "]"); } #define LAST_DISK KERN_ALERT \ @@ -624,10 +622,9 @@ static void error(mddev_t *mddev, mdk_rdev_t *rdev) mddev->degraded++; conf->working_disks--; /* - * if recovery was running, stop it now. + * if recovery is running, make sure it aborts. */ - if (mddev->recovery_running) - mddev->recovery_running = -EIO; + set_bit(MD_RECOVERY_ERR, &mddev->recovery); } rdev->in_sync = 0; rdev->faulty = 1; @@ -859,7 +856,7 @@ static void sync_request_write(mddev_t *mddev, r1bio_t *r1_bio) mbio = bio_clone(bio, GFP_NOIO); r1_bio->write_bios[i] = mbio; mbio->bi_bdev = conf->mirrors[i].rdev->bdev; - mbio->bi_sector = r1_bio->sector; + mbio->bi_sector = r1_bio->sector | conf->mirrors[i].rdev->data_offset; mbio->bi_end_io = end_sync_write; mbio->bi_rw = WRITE; mbio->bi_private = r1_bio; @@ -900,17 +897,17 @@ static void sync_request_write(mddev_t *mddev, r1bio_t *r1_bio) * 3. Performs writes following reads for array syncronising. */ -static void raid1d(void *data) +static void raid1d(mddev_t *mddev) { struct list_head *head = &retry_list_head; r1bio_t *r1_bio; struct bio *bio; unsigned long flags; - mddev_t *mddev; - conf_t *conf = data; + conf_t *conf = mddev_to_conf(mddev); mdk_rdev_t *rdev; - md_handle_safemode(conf->mddev); + md_check_recovery(mddev); + md_handle_safemode(mddev); for (;;) { spin_lock_irqsave(&retry_list_lock, flags); @@ -937,7 +934,7 @@ static void raid1d(void *data) printk(REDIRECT_SECTOR, bdev_partition_name(rdev->bdev), (unsigned long long)r1_bio->sector); bio->bi_bdev = rdev->bdev; - bio->bi_sector = r1_bio->sector; + bio->bi_sector = r1_bio->sector + rdev->data_offset; bio->bi_rw = r1_bio->cmd; generic_make_request(bio); @@ -1048,7 +1045,7 @@ static int sync_request(mddev_t *mddev, sector_t sector_nr, int go_faster) read_bio = bio_clone(r1_bio->master_bio, GFP_NOIO); - read_bio->bi_sector = sector_nr; + read_bio->bi_sector = sector_nr + mirror->rdev->data_offset; read_bio->bi_bdev = mirror->rdev->bdev; read_bio->bi_end_io = end_sync_read; read_bio->bi_rw = READ; @@ -1190,10 +1187,8 @@ static int run(mddev_t *mddev) { - snprintf(conf->thread_name,MD_THREAD_NAME_MAX,"raid1d_md%d",mdidx(mddev)); - - conf->thread = md_register_thread(raid1d, conf, conf->thread_name); - if (!conf->thread) { + mddev->thread = md_register_thread(raid1d, mddev, "md%d_raid1"); + if (!mddev->thread) { printk(THREAD_ERROR, mdidx(mddev)); goto out_free_conf; } @@ -1219,7 +1214,8 @@ static int stop(mddev_t *mddev) { conf_t *conf = mddev_to_conf(mddev); - md_unregister_thread(conf->thread); + md_unregister_thread(mddev->thread); + mddev->thread = NULL; if (conf->r1bio_pool) mempool_destroy(conf->r1bio_pool); kfree(conf); diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 7949de5f8e87..51a8e2d721d6 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -71,12 +71,12 @@ static inline void __release_stripe(raid5_conf_t *conf, struct stripe_head *sh) list_add_tail(&sh->lru, &conf->delayed_list); else list_add_tail(&sh->lru, &conf->handle_list); - md_wakeup_thread(conf->thread); + md_wakeup_thread(conf->mddev->thread); } else { if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) { atomic_dec(&conf->preread_active_stripes); if (atomic_read(&conf->preread_active_stripes) < IO_THRESHOLD) - md_wakeup_thread(conf->thread); + md_wakeup_thread(conf->mddev->thread); } list_add_tail(&sh->lru, &conf->inactive_list); atomic_dec(&conf->active_stripes); @@ -463,10 +463,9 @@ static void error(mddev_t *mddev, mdk_rdev_t *rdev) conf->failed_disks++; rdev->in_sync = 0; /* - * if recovery was running, stop it now. + * if recovery was running, make sure it aborts. */ - if (mddev->recovery_running) - mddev->recovery_running = -EIO; + set_bit(MD_RECOVERY_ERR, &mddev->recovery); } rdev->faulty = 1; printk (KERN_ALERT @@ -913,7 +912,7 @@ static void handle_stripe(struct stripe_head *sh) struct bio *nextbi = bi->bi_next; clear_bit(BIO_UPTODATE, &bi->bi_flags); if (--bi->bi_phys_segments == 0) { - md_write_end(conf->mddev, conf->thread); + md_write_end(conf->mddev); bi->bi_next = return_bi; return_bi = bi; } @@ -970,7 +969,7 @@ static void handle_stripe(struct stripe_head *sh) while (wbi && wbi->bi_sector < dev->sector + STRIPE_SECTORS) { wbi2 = wbi->bi_next; if (--wbi->bi_phys_segments == 0) { - md_write_end(conf->mddev, conf->thread); + md_write_end(conf->mddev); wbi->bi_next = return_bi; return_bi = wbi; } @@ -1113,7 +1112,7 @@ static void handle_stripe(struct stripe_head *sh) if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) { atomic_dec(&conf->preread_active_stripes); if (atomic_read(&conf->preread_active_stripes) < IO_THRESHOLD) - md_wakeup_thread(conf->thread); + md_wakeup_thread(conf->mddev->thread); } } } @@ -1207,7 +1206,7 @@ static void handle_stripe(struct stripe_head *sh) bi->bi_bdev = rdev->bdev; PRINTK("for %llu schedule op %ld on disc %d\n", (unsigned long long)sh->sector, bi->bi_rw, i); atomic_inc(&sh->count); - bi->bi_sector = sh->sector; + bi->bi_sector = sh->sector + rdev->data_offset; bi->bi_flags = 1 << BIO_UPTODATE; bi->bi_vcnt = 1; bi->bi_idx = 0; @@ -1251,7 +1250,7 @@ static void raid5_unplug_device(void *data) if (blk_remove_plug(q)) raid5_activate_delayed(conf); - md_wakeup_thread(conf->thread); + md_wakeup_thread(mddev->thread); spin_unlock_irqrestore(&conf->device_lock, flags); } @@ -1304,7 +1303,7 @@ static int make_request (request_queue_t *q, struct bio * bi) int bytes = bi->bi_size; if ( bio_data_dir(bi) == WRITE ) - md_write_end(mddev,conf->thread); + md_write_end(mddev); bi->bi_size = 0; bi->bi_end_io(bi, bytes, 0); } @@ -1356,16 +1355,17 @@ static int sync_request (mddev_t *mddev, sector_t sector_nr, int go_faster) * During the scan, completed stripes are saved for us by the interrupt * handler, so that they will not have to wait for our next wakeup. */ -static void raid5d (void *data) +static void raid5d (mddev_t *mddev) { struct stripe_head *sh; - raid5_conf_t *conf = data; - mddev_t *mddev = conf->mddev; + raid5_conf_t *conf = mddev_to_conf(mddev); int handled; PRINTK("+++ raid5d active\n"); + md_check_recovery(mddev); md_handle_safemode(mddev); + handled = 0; spin_lock_irq(&conf->device_lock); while (1) { @@ -1486,10 +1486,8 @@ static int run (mddev_t *mddev) } { - snprintf(conf->thread_name,MD_THREAD_NAME_MAX,"raid5d_md%d",mdidx(mddev)); - - conf->thread = md_register_thread(raid5d, conf, conf->thread_name); - if (!conf->thread) { + mddev->thread = md_register_thread(raid5d, mddev, "md%d_raid5"); + if (!mddev->thread) { printk(KERN_ERR "raid5: couldn't allocate thread for md%d\n", mdidx(mddev)); goto abort; } @@ -1500,7 +1498,7 @@ static int run (mddev_t *mddev) if (grow_stripes(conf, conf->max_nr_stripes)) { printk(KERN_ERR "raid5: couldn't allocate %dkB for buffers\n", memory); shrink_stripes(conf); - md_unregister_thread(conf->thread); + md_unregister_thread(mddev->thread); goto abort; } else printk(KERN_INFO "raid5: allocated %dkB for md%d\n", memory, mdidx(mddev)); @@ -1536,7 +1534,8 @@ static int stop (mddev_t *mddev) { raid5_conf_t *conf = (raid5_conf_t *) mddev->private; - md_unregister_thread(conf->thread); + md_unregister_thread(mddev->thread); + mddev->thread = NULL; shrink_stripes(conf); free_pages((unsigned long) conf->stripe_hashtbl, HASH_PAGES_ORDER); kfree(conf); @@ -1574,29 +1573,26 @@ static void printall (raid5_conf_t *conf) } } spin_unlock_irq(&conf->device_lock); - - PRINTK("--- raid5d inactive\n"); } #endif -static int status (char *page, mddev_t *mddev) +static void status (struct seq_file *seq, mddev_t *mddev) { raid5_conf_t *conf = (raid5_conf_t *) mddev->private; - int sz = 0, i; + int i; - sz += sprintf (page+sz, " level %d, %dk chunk, algorithm %d", mddev->level, mddev->chunk_size >> 10, mddev->layout); - sz += sprintf (page+sz, " [%d/%d] [", conf->raid_disks, conf->working_disks); + seq_printf (seq, " level %d, %dk chunk, algorithm %d", mddev->level, mddev->chunk_size >> 10, mddev->layout); + seq_printf (seq, " [%d/%d] [", conf->raid_disks, conf->working_disks); for (i = 0; i < conf->raid_disks; i++) - sz += sprintf (page+sz, "%s", + seq_printf (seq, "%s", conf->disks[i].rdev && conf->disks[i].rdev->in_sync ? "U" : "_"); - sz += sprintf (page+sz, "]"); + seq_printf (seq, "]"); #if RAID5_DEBUG #define D(x) \ - sz += sprintf (page+sz, "<"#x":%d>", atomic_read(&conf->x)) + seq_printf (seq, "<"#x":%d>", atomic_read(&conf->x)) printall(conf); #endif - return sz; } static void print_raid5_conf (raid5_conf_t *conf) diff --git a/drivers/media/radio/miropcm20-rds.c b/drivers/media/radio/miropcm20-rds.c index fa2eebe2ecd0..dd08c51cae80 100644 --- a/drivers/media/radio/miropcm20-rds.c +++ b/drivers/media/radio/miropcm20-rds.c @@ -113,7 +113,7 @@ static struct file_operations rds_fops = { static struct miscdevice rds_miscdev = { .minor = MISC_DYNAMIC_MINOR, - .name = "radiotext" + .name = "radiotext", .fops = &rds_fops, }; @@ -128,7 +128,7 @@ static int __init miropcm20_rds_init(void) error = devfs_mk_symlink(NULL, "v4l/rds/radiotext", 0, "../misc/radiotext", NULL, NULL); if (error) - misc_deregister(&rds_miscdev) + misc_deregister(&rds_miscdev); return error; } @@ -136,7 +136,7 @@ static int __init miropcm20_rds_init(void) static void __exit miropcm20_rds_cleanup(void) { devfs_remove("v4l/rds/radiotext"); - misc_deregister(&rds_miscdev) + misc_deregister(&rds_miscdev); } module_init(miropcm20_rds_init); diff --git a/drivers/media/video/audiochip.h b/drivers/media/video/audiochip.h deleted file mode 100644 index b161f921d303..000000000000 --- a/drivers/media/video/audiochip.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef AUDIOCHIP_H -#define AUDIOCHIP_H - -/* ---------------------------------------------------------------------- */ - -#define MIN(a,b) (((a)>(b))?(b):(a)) -#define MAX(a,b) (((a)>(b))?(a):(b)) - -/* v4l device was opened in Radio mode */ -#define AUDC_SET_RADIO _IO('m',2) -/* select from TV,radio,extern,MUTE */ -#define AUDC_SET_INPUT _IOW('m',17,int) - -/* audio inputs */ -#define AUDIO_TUNER 0x00 -#define AUDIO_RADIO 0x01 -#define AUDIO_EXTERN 0x02 -#define AUDIO_INTERN 0x03 -#define AUDIO_OFF 0x04 -#define AUDIO_ON 0x05 -#define AUDIO_MUTE 0x80 -#define AUDIO_UNMUTE 0x81 - -/* all the stuff below is obsolete and just here for reference. I'll - * remove it once the driver is tested and works fine. - * - * Instead creating alot of tiny API's for all kinds of different - * chips, we'll just pass throuth the v4l ioctl structs (v4l2 not - * yet...). It is a bit less flexible, but most/all used i2c chips - * make sense in v4l context only. So I think that's acceptable... - */ - -/* misc stuff to pass around config info to i2c chips */ -#define AUDC_CONFIG_PINNACLE _IOW('m',32,int) - -#endif /* AUDIOCHIP_H */ diff --git a/drivers/media/video/bttv-cards.c b/drivers/media/video/bttv-cards.c index 2ed590fc87c1..0f823e82d5e3 100644 --- a/drivers/media/video/bttv-cards.c +++ b/drivers/media/video/bttv-cards.c @@ -36,7 +36,6 @@ #include <asm/io.h> #include "bttvp.h" -#include "tuner.h" #include "bt832.h" /* fwd decl */ diff --git a/drivers/media/video/bttv-driver.c b/drivers/media/video/bttv-driver.c index b9d3949f9857..ed8d3b0f03f6 100644 --- a/drivers/media/video/bttv-driver.c +++ b/drivers/media/video/bttv-driver.c @@ -37,7 +37,6 @@ #include <asm/io.h> #include "bttvp.h" -#include "tuner.h" int bttv_num; /* number of Bt848s in use */ struct bttv bttvs[BTTV_MAX]; diff --git a/drivers/media/video/bttv-if.c b/drivers/media/video/bttv-if.c index 01ea37beac5f..1b3c83e16fe0 100644 --- a/drivers/media/video/bttv-if.c +++ b/drivers/media/video/bttv-if.c @@ -34,7 +34,6 @@ #include <asm/io.h> #include "bttvp.h" -#include "tuner.h" static struct i2c_algo_bit_data bttv_i2c_algo_template; static struct i2c_adapter bttv_i2c_adap_template; diff --git a/drivers/media/video/bttvp.h b/drivers/media/video/bttvp.h index 49882b03769e..65f8d54e1816 100644 --- a/drivers/media/video/bttvp.h +++ b/drivers/media/video/bttvp.h @@ -34,10 +34,12 @@ #include <linux/pci.h> #include <asm/scatterlist.h> +#include <media/video-buf.h> +#include <media/audiochip.h> +#include <media/tuner.h> + #include "bt848.h" #include "bttv.h" -#include "video-buf.h" -#include "audiochip.h" #ifdef __KERNEL__ diff --git a/drivers/media/video/id.h b/drivers/media/video/id.h deleted file mode 100644 index f9360b0665bb..000000000000 --- a/drivers/media/video/id.h +++ /dev/null @@ -1,35 +0,0 @@ -/* FIXME: this temporarely, until these are included in linux/i2c-id.h */ - -/* drivers */ -#ifndef I2C_DRIVERID_TVMIXER -# define I2C_DRIVERID_TVMIXER I2C_DRIVERID_EXP0 -#endif -#ifndef I2C_DRIVERID_TVAUDIO -# define I2C_DRIVERID_TVAUDIO I2C_DRIVERID_EXP1 -#endif - -/* chips */ -#ifndef I2C_DRIVERID_DPL3518 -# define I2C_DRIVERID_DPL3518 I2C_DRIVERID_EXP2 -#endif -#ifndef I2C_DRIVERID_TDA9873 -# define I2C_DRIVERID_TDA9873 I2C_DRIVERID_EXP3 -#endif -#ifndef I2C_DRIVERID_TDA9875 -# define I2C_DRIVERID_TDA9875 I2C_DRIVERID_EXP0+4 -#endif -#ifndef I2C_DRIVERID_PIC16C54_PV951 -# define I2C_DRIVERID_PIC16C54_PV951 I2C_DRIVERID_EXP0+5 -#endif -#ifndef I2C_DRIVERID_TDA7432 -# define I2C_DRIVERID_TDA7432 I2C_DRIVERID_EXP0+6 -#endif -#ifndef I2C_DRIVERID_TDA9874 -# define I2C_DRIVERID_TDA9874 I2C_DRIVERID_EXP0+7 -#endif - -/* algorithms */ -#ifndef I2C_ALGO_SAA7134 -# define I2C_ALGO_SAA7134 0x090000 -#endif - diff --git a/drivers/media/video/msp3400.c b/drivers/media/video/msp3400.c index d91d04f97f4f..9000e6f107c3 100644 --- a/drivers/media/video/msp3400.c +++ b/drivers/media/video/msp3400.c @@ -56,7 +56,7 @@ #define __KERNEL_SYSCALLS__ #include <linux/unistd.h> -#include "audiochip.h" +#include <media/audiochip.h> #include "msp3400.h" /* Addresses to scan */ @@ -1495,8 +1495,8 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) VIDEO_AUDIO_MUTABLE; if (msp->muted) va->flags |= VIDEO_AUDIO_MUTE; - va->volume=MAX(msp->left,msp->right); - va->balance=(32768*MIN(msp->left,msp->right))/ + va->volume=max(msp->left,msp->right); + va->balance=(32768*min(msp->left,msp->right))/ (va->volume ? va->volume : 1); va->balance=(msp->left<msp->right)? (65535-va->balance) : va->balance; @@ -1517,9 +1517,9 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) dprintk(KERN_DEBUG "msp34xx: VIDIOCSAUDIO\n"); msp->muted = (va->flags & VIDEO_AUDIO_MUTE); - msp->left = (MIN(65536 - va->balance,32768) * + msp->left = (min(65536 - va->balance,32768) * va->volume) / 32768; - msp->right = (MIN(va->balance,32768) * + msp->right = (min(va->balance,(__u16)32768) * va->volume) / 32768; msp->bass = va->bass; msp->treble = va->treble; diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index bc8b98bfd062..d2d58ec70da1 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -24,7 +24,6 @@ #include "saa7134-reg.h" #include "saa7134.h" -#include "tuner.h" /* commly used strings */ static char name_mute[] = "mute"; diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index 07b31bc2f848..e88367522cdb 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -30,7 +30,6 @@ #include "saa7134-reg.h" #include "saa7134.h" -#include "tuner.h" MODULE_DESCRIPTION("v4l2 driver module for saa7130/34 based TV cards"); MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c index 438ee56ae0c3..72463f5cb7b3 100644 --- a/drivers/media/video/saa7134/saa7134-i2c.c +++ b/drivers/media/video/saa7134/saa7134-i2c.c @@ -30,8 +30,6 @@ #include "saa7134-reg.h" #include "saa7134.h" -#include "tuner.h" -#include "id.h" /* ----------------------------------------------------------- */ diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index 562ebf3332ee..9d22bf8a348b 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -29,8 +29,6 @@ #include "saa7134-reg.h" #include "saa7134.h" -#include "tuner.h" -#include "audiochip.h" /* ------------------------------------------------------------------ */ diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index aa3ef2596b62..57f1fc42ed0a 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -22,7 +22,11 @@ #include <linux/i2c.h> #include <linux/videodev.h> #include <linux/kdev_t.h> -#include "video-buf.h" + +#include <media/video-buf.h> +#include <media/tuner.h> +#include <media/audiochip.h> +#include <media/id.h> #define SAA7134_VERSION_CODE KERNEL_VERSION(0,2,2) diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c index 4b96ec8af1f7..8ba55c0a6644 100644 --- a/drivers/media/video/tda7432.c +++ b/drivers/media/video/tda7432.c @@ -49,8 +49,8 @@ #include <linux/i2c-algo-bit.h> #include "bttv.h" -#include "audiochip.h" -#include "id.h" +#include <media/audiochip.h> +#include <media/id.h> #ifndef VIDEO_AUDIO_BALANCE # define VIDEO_AUDIO_BALANCE 32 diff --git a/drivers/media/video/tda9875.c b/drivers/media/video/tda9875.c index d2d0917ececf..28a9c2f60c7d 100644 --- a/drivers/media/video/tda9875.c +++ b/drivers/media/video/tda9875.c @@ -31,8 +31,8 @@ #include <linux/init.h> #include "bttv.h" -#include "audiochip.h" -#include "id.h" +#include <media/audiochip.h> +#include <media/id.h> MODULE_PARM(debug,"i"); MODULE_LICENSE("GPL"); @@ -316,17 +316,15 @@ static int tda9875_command(struct i2c_client *client, /* min is -84 max is 24 */ left = (t->lvol+84)*606; right = (t->rvol+84)*606; - va->volume=MAX(left,right); - va->balance=(32768*MIN(left,right))/ + va->volume=max(left,right); + va->balance=(32768*min(left,right))/ (va->volume ? va->volume : 1); va->balance=(left<right)? (65535-va->balance) : va->balance; va->bass = (t->bass+12)*2427; /* min -12 max +15 */ va->treble = (t->treble+12)*2730;/* min -12 max +12 */ - va->mode |= VIDEO_SOUND_MONO; - break; /* VIDIOCGAUDIO case */ } @@ -336,9 +334,9 @@ static int tda9875_command(struct i2c_client *client, int left,right; dprintk("VIDEOCSAUDIO...\n"); - left = (MIN(65536 - va->balance,32768) * + left = (min(65536 - va->balance,32768) * va->volume) / 32768; - right = (MIN(va->balance,32768) * + right = (min(va->balance,(__u16)32768) * va->volume) / 32768; t->lvol = ((left/606)-84) & 0xff; if (t->lvol > 24) diff --git a/drivers/media/video/tda9887.c b/drivers/media/video/tda9887.c index 3d8c6fa0b20f..fb20ba56c24e 100644 --- a/drivers/media/video/tda9887.c +++ b/drivers/media/video/tda9887.c @@ -7,8 +7,8 @@ #include <linux/errno.h> #include <linux/slab.h> -#include "id.h" -#include "audiochip.h" +#include <media/audiochip.h> +#include <media/id.h> /* Chips: TDA9885 (PAL, NTSC) diff --git a/drivers/media/video/tuner-3036.c b/drivers/media/video/tuner-3036.c index 4319f317cb30..5c4328d6a1ab 100644 --- a/drivers/media/video/tuner-3036.c +++ b/drivers/media/video/tuner-3036.c @@ -27,7 +27,7 @@ #include <linux/i2c.h> #include <linux/videodev.h> -#include "tuner.h" +#include <media/tuner.h> static int debug; /* insmod parameter */ static int this_adap; diff --git a/drivers/media/video/tuner.c b/drivers/media/video/tuner.c index 0638747257c5..f83309c257d8 100644 --- a/drivers/media/video/tuner.c +++ b/drivers/media/video/tuner.c @@ -12,8 +12,8 @@ #include <linux/videodev.h> #include <linux/init.h> -#include "tuner.h" -#include "audiochip.h" +#include <media/tuner.h> +#include <media/audiochip.h> /* Addresses to scan */ static unsigned short normal_i2c[] = {I2C_CLIENT_END}; diff --git a/drivers/media/video/tuner.h b/drivers/media/video/tuner.h deleted file mode 100644 index f716e23dda8e..000000000000 --- a/drivers/media/video/tuner.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - tuner.h - definition for different tuners - - Copyright (C) 1997 Markus Schroeder (schroedm@uni-duesseldorf.de) - minor modifications by Ralph Metzler (rjkm@thp.uni-koeln.de) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef _TUNER_H -#define _TUNER_H - -#include "id.h" - -#define TUNER_TEMIC_PAL 0 /* 4002 FH5 (3X 7756, 9483) */ -#define TUNER_PHILIPS_PAL_I 1 -#define TUNER_PHILIPS_NTSC 2 -#define TUNER_PHILIPS_SECAM 3 /* you must actively select B/G, L, L` */ -#define TUNER_ABSENT 4 -#define TUNER_PHILIPS_PAL 5 -#define TUNER_TEMIC_NTSC 6 /* 4032 FY5 (3X 7004, 9498, 9789) */ -#define TUNER_TEMIC_PAL_I 7 /* 4062 FY5 (3X 8501, 9957) */ -#define TUNER_TEMIC_4036FY5_NTSC 8 /* 4036 FY5 (3X 1223, 1981, 7686) */ -#define TUNER_ALPS_TSBH1_NTSC 9 -#define TUNER_ALPS_TSBE1_PAL 10 -#define TUNER_ALPS_TSBB5_PAL_I 11 -#define TUNER_ALPS_TSBE5_PAL 12 -#define TUNER_ALPS_TSBC5_PAL 13 -#define TUNER_TEMIC_4006FH5_PAL 14 /* 4006 FH5 (3X 9500, 9501, 7291) */ -#define TUNER_ALPS_TSHC6_NTSC 15 -#define TUNER_TEMIC_PAL_DK 16 /* 4016 FY5 (3X 1392, 1393) */ -#define TUNER_PHILIPS_NTSC_M 17 -#define TUNER_TEMIC_4066FY5_PAL_I 18 /* 4066 FY5 (3X 7032, 7035) */ -#define TUNER_TEMIC_4006FN5_MULTI_PAL 19 /* B/G, I and D/K autodetected (3X 7595, 7606, 7657)*/ -#define TUNER_TEMIC_4009FR5_PAL 20 /* incl. FM radio (3X 7607, 7488, 7711)*/ -#define TUNER_TEMIC_4039FR5_NTSC 21 /* incl. FM radio (3X 7246, 7578, 7732)*/ -#define TUNER_TEMIC_4046FM5 22 /* you must actively select B/G, D/K, I, L, L` ! (3X 7804, 7806, 8103, 8104)*/ -#define TUNER_PHILIPS_PAL_DK 23 -#define TUNER_PHILIPS_FQ1216ME 24 /* you must actively select B/G/D/K, I, L, L` */ -#define TUNER_LG_PAL_I_FM 25 -#define TUNER_LG_PAL_I 26 -#define TUNER_LG_NTSC_FM 27 -#define TUNER_LG_PAL_FM 28 -#define TUNER_LG_PAL 29 -#define TUNER_TEMIC_4009FN5_MULTI_PAL_FM 30 /* B/G, I and D/K autodetected (3X 8155, 8160, 8163)*/ -#define TUNER_SHARP_2U5JF5540_NTSC 31 -#define TUNER_Samsung_PAL_TCPM9091PD27 32 -#define TUNER_MT2032 33 -#define TUNER_TEMIC_4106FH5 34 /* 4106 FH5 (3X 7808, 7865)*/ -#define TUNER_TEMIC_4012FY5 35 /* 4012 FY5 (3X 0971, 1099)*/ -#define TUNER_TEMIC_4136FY5 36 /* 4136 FY5 (3X 7708, 7746)*/ -#define TUNER_LG_PAL_NEW_TAPC 37 -#define TUNER_PHILIPS_FM1216ME_MK3 38 -#define TUNER_LG_NTSC_NEW_TAPC 39 - - - - -#define NOTUNER 0 -#define PAL 1 /* PAL_BG */ -#define PAL_I 2 -#define NTSC 3 -#define SECAM 4 - -#define NoTuner 0 -#define Philips 1 -#define TEMIC 2 -#define Sony 3 -#define Alps 4 -#define LGINNOTEK 5 -#define SHARP 6 -#define Samsung 7 -#define Microtune 8 - -#define TUNER_SET_TYPE _IOW('t',1,int) /* set tuner type */ -#define TUNER_SET_TVFREQ _IOW('t',2,int) /* set tv freq */ -#if 0 /* obsolete */ -# define TUNER_SET_RADIOFREQ _IOW('t',3,int) /* set radio freq */ -# define TUNER_SET_MODE _IOW('t',4,int) /* set tuner mode */ -#endif - -#endif diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index b077e42ef792..bf68fc0d6874 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -29,9 +29,10 @@ #include <linux/init.h> #include <linux/smp_lock.h> -#include "audiochip.h" +#include <media/audiochip.h> +#include <media/id.h> + #include "tvaudio.h" -#include "id.h" /* ---------------------------------------------------------------------- */ @@ -1477,8 +1478,8 @@ static int chip_command(struct i2c_client *client, if (desc->flags & CHIP_HAS_VOLUME) { va->flags |= VIDEO_AUDIO_VOLUME; - va->volume = MAX(chip->left,chip->right); - va->balance = (32768*MIN(chip->left,chip->right))/ + va->volume = max(chip->left,chip->right); + va->balance = (32768*min(chip->left,chip->right))/ (va->volume ? va->volume : 1); } if (desc->flags & CHIP_HAS_BASSTREBLE) { @@ -1500,9 +1501,9 @@ static int chip_command(struct i2c_client *client, struct video_audio *va = arg; if (desc->flags & CHIP_HAS_VOLUME) { - chip->left = (MIN(65536 - va->balance,32768) * + chip->left = (min(65536 - va->balance,32768) * va->volume) / 32768; - chip->right = (MIN(va->balance,32768) * + chip->right = (min(va->balance,(__u16)32768) * va->volume) / 32768; chip_write(chip,desc->leftreg,desc->volfunc(chip->left)); chip_write(chip,desc->rightreg,desc->volfunc(chip->right)); diff --git a/drivers/media/video/tvmixer.c b/drivers/media/video/tvmixer.c index 73aba5038825..ff50ba8bb249 100644 --- a/drivers/media/video/tvmixer.c +++ b/drivers/media/video/tvmixer.c @@ -16,8 +16,6 @@ #include <linux/soundcard.h> #include <asm/uaccess.h> -#include "audiochip.h" -#include "id.h" #define DEV_MAX 4 @@ -136,16 +134,16 @@ static int tvmixer_ioctl(struct inode *inode, struct file *file, unsigned int cm case MIXER_WRITE(SOUND_MIXER_VOLUME): left = mix_to_v4l(val); right = mix_to_v4l(val >> 8); - va.volume = MAX(left,right); - va.balance = (32768*MIN(left,right)) / (va.volume ? va.volume : 1); + va.volume = max(left,right); + va.balance = (32768*min(left,right)) / (va.volume ? va.volume : 1); va.balance = (left<right) ? (65535-va.balance) : va.balance; client->driver->command(client,VIDIOCSAUDIO,&va); client->driver->command(client,VIDIOCGAUDIO,&va); /* fall throuth */ case MIXER_READ(SOUND_MIXER_VOLUME): - left = (MIN(65536 - va.balance,32768) * + left = (min(65536 - va.balance,32768) * va.volume) / 32768; - right = (MIN(va.balance,32768) * + right = (min(va.balance,32768) * va.volume) / 32768; ret = v4l_to_mix2(left,right); break; diff --git a/drivers/media/video/video-buf.c b/drivers/media/video/video-buf.c index 20e2c2e6f692..36c155bfd42f 100644 --- a/drivers/media/video/video-buf.c +++ b/drivers/media/video/video-buf.c @@ -31,7 +31,7 @@ # define TryLockPage TestSetPageLocked #endif -#include "video-buf.h" +#include <media/video-buf.h> static int debug = 0; diff --git a/drivers/media/video/video-buf.h b/drivers/media/video/video-buf.h deleted file mode 100644 index fceea3e24f75..000000000000 --- a/drivers/media/video/video-buf.h +++ /dev/null @@ -1,234 +0,0 @@ -/* - * generic helper functions for video4linux capture buffers, to handle - * memory management and PCI DMA. Right now bttv + saa7134 use it. - * - * The functions expect the hardware being able to scatter gatter - * (i.e. the buffers are not linear in physical memory, but fragmented - * into PAGE_SIZE chunks). They also assume the driver does not need - * to touch the video data (thus it is probably not useful for USB as - * data often must be uncompressed by the drivers). - * - * (c) 2001,02 Gerd Knorr <kraxel@bytesex.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. - */ - -#include <linux/videodev.h> - -/* --------------------------------------------------------------------- */ - -/* - * Return a scatterlist for some page-aligned vmalloc()'ed memory - * block (NULL on errors). Memory for the scatterlist is allocated - * using kmalloc. The caller must free the memory. - */ -struct scatterlist* videobuf_vmalloc_to_sg(unsigned char *virt, int nr_pages); - -/* - * 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_pages_to_sg(struct page **pages, int nr_pages, - int offset); -int videobuf_lock(struct page **pages, int nr_pages); -int videobuf_unlock(struct page **pages, int nr_pages); - -/* --------------------------------------------------------------------- */ - -/* - * A small set of helper functions to manage buffers (both userland - * and kernel) for DMA. - * - * videobuf_init_*_dmabuf() - * creates a buffer. The userland version takes a userspace - * pointer + length. The kernel version just wants the size and - * does memory allocation too using vmalloc_32(). - * - * videobuf_pci_*_dmabuf() - * see Documentation/DMA-mapping.txt, these functions to - * basically the same. The map function does also build a - * scatterlist for the buffer (and unmap frees it ...) - * - * videobuf_free_dmabuf() - * no comment ... - * - */ - -struct videobuf_dmabuf { - /* for userland buffer */ - int offset; - struct page **pages; - - /* for kernel buffers */ - void *vmalloc; - - /* common */ - struct scatterlist *sglist; - int sglen; - int nr_pages; - int direction; -}; - -int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction, - unsigned long data, unsigned long size); -int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, - int nr_pages); -int videobuf_dma_pci_map(struct pci_dev *dev, struct videobuf_dmabuf *dma); -int videobuf_dma_pci_sync(struct pci_dev *dev, - struct videobuf_dmabuf *dma); -int videobuf_dma_pci_unmap(struct pci_dev *dev, struct videobuf_dmabuf *dma); -int videobuf_dma_free(struct videobuf_dmabuf *dma); - -/* --------------------------------------------------------------------- */ - -/* - * A small set of helper functions to manage video4linux buffers. - * - * struct videobuf_buffer holds the data structures used by the helper - * functions, additionally some commonly used fields for v4l buffers - * (width, height, lists, waitqueue) are in there. That struct should - * be used as first element in the drivers buffer struct. - * - * about the mmap helpers (videobuf_mmap_*): - * - * The mmaper function allows to map any subset of contingous buffers. - * This includes one mmap() call for all buffers (which the original - * video4linux API uses) as well as one mmap() for every single buffer - * (which v4l2 uses). - * - * If there is a valid mapping for a buffer, buffer->baddr/bsize holds - * userspace address + size which can be feeded into the - * videobuf_dma_init_user function listed above. - * - */ - -struct videobuf_buffer; -struct videobuf_queue; - -struct videobuf_mapping { - unsigned int count; - int highmem_ok; - unsigned long start; - unsigned long end; - struct videobuf_queue *q; -}; - -enum videobuf_state { - STATE_NEEDS_INIT = 0, - STATE_PREPARED = 1, - STATE_QUEUED = 2, - STATE_ACTIVE = 3, - STATE_DONE = 4, - STATE_ERROR = 5, - STATE_IDLE = 6, -}; - -struct videobuf_buffer { - unsigned int i; - - /* info about the buffer */ - unsigned int width; - unsigned int height; - unsigned long size; - enum v4l2_field field; - enum videobuf_state state; - struct videobuf_dmabuf dma; - struct list_head stream; /* QBUF/DQBUF list */ - - /* for mmap'ed buffers */ - size_t boff; /* buffer offset (mmap) */ - size_t bsize; /* buffer size */ - unsigned long baddr; /* buffer addr (userland ptr!) */ - struct videobuf_mapping *map; - - /* touched by irq handler */ - struct list_head queue; - wait_queue_head_t done; - unsigned int field_count; - struct timeval ts; -}; - -struct videobuf_queue_ops { - int (*buf_setup)(struct file *file, - unsigned int *count, unsigned int *size); - int (*buf_prepare)(struct file *file,struct videobuf_buffer *vb, - enum v4l2_field field); - void (*buf_queue)(struct file *file,struct videobuf_buffer *vb); - void (*buf_release)(struct file *file,struct videobuf_buffer *vb); -}; - -struct videobuf_queue { - struct semaphore lock; - spinlock_t *irqlock; - struct pci_dev *pci; - - enum v4l2_buf_type type; - unsigned int msize; - enum v4l2_field field; - enum v4l2_field last; /* for field=V4L2_FIELD_ALTERNATE */ - struct videobuf_buffer *bufs[VIDEO_MAX_FRAME]; - struct videobuf_queue_ops *ops; - - /* capture via mmap() + ioctl(QBUF/DQBUF) */ - unsigned int streaming; - struct list_head stream; - - /* capture via read() */ - unsigned int reading; - unsigned int read_off; - struct videobuf_buffer *read_buf; -}; - -void* videobuf_alloc(unsigned int size); -int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr); -int videobuf_iolock(struct pci_dev *pci, struct videobuf_buffer *vb); - -void videobuf_queue_init(struct videobuf_queue *q, - struct videobuf_queue_ops *ops, - struct pci_dev *pci, spinlock_t *irqlock, - enum v4l2_buf_type type, - enum v4l2_field field, - unsigned int msize); -int videobuf_queue_is_busy(struct videobuf_queue *q); -void videobuf_queue_cancel(struct file *file, struct videobuf_queue *q); - -void videobuf_status(struct v4l2_buffer *b, struct videobuf_buffer *vb, - enum v4l2_buf_type type); -int videobuf_reqbufs(struct file *file, struct videobuf_queue *q, - struct v4l2_requestbuffers *req); -int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b); -int videobuf_qbuf(struct file *file, struct videobuf_queue *q, - struct v4l2_buffer *b); -int videobuf_dqbuf(struct file *file, struct videobuf_queue *q, - struct v4l2_buffer *b); -int videobuf_streamon(struct file *file, struct videobuf_queue *q); -int videobuf_streamoff(struct file *file, struct videobuf_queue *q); - -int videobuf_read_start(struct file *file, struct videobuf_queue *q); -void videobuf_read_stop(struct file *file, struct videobuf_queue *q); -ssize_t videobuf_read_stream(struct file *file, struct videobuf_queue *q, - char *data, size_t count, loff_t *ppos, - int vbihack); -ssize_t videobuf_read_one(struct file *file, struct videobuf_queue *q, - char *data, size_t count, loff_t *ppos); -unsigned int videobuf_poll_stream(struct file *file, - struct videobuf_queue *q, - poll_table *wait); - -int videobuf_mmap_setup(struct file *file, struct videobuf_queue *q, - unsigned int bcount, unsigned int bsize); -int videobuf_mmap_free(struct file *file, struct videobuf_queue *q); -int videobuf_mmap_mapper(struct vm_area_struct *vma, - struct videobuf_queue *q); - -/* --------------------------------------------------------------------- */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/net/e100/e100_main.c b/drivers/net/e100/e100_main.c index 523e83f771da..1f1f70caca21 100644 --- a/drivers/net/e100/e100_main.c +++ b/drivers/net/e100/e100_main.c @@ -3784,11 +3784,15 @@ static int e100_ethtool_gstrings(struct net_device *dev, struct ifreq *ifr) return -EOPNOTSUPP; } - if (copy_to_user(ifr->ifr_data, &info, sizeof (info))) + if (copy_to_user(ifr->ifr_data, &info, sizeof (info))) { + kfree(strings); return -EFAULT; + } - if (copy_to_user(usr_strings, strings, info.len * ETH_GSTRING_LEN)) + if (copy_to_user(usr_strings, strings, info.len * ETH_GSTRING_LEN)) { + kfree(strings); return -EFAULT; + } kfree(strings); return 0; diff --git a/drivers/oprofile/buffer_sync.c b/drivers/oprofile/buffer_sync.c index c2b0b6a833a2..9162f38d8395 100644 --- a/drivers/oprofile/buffer_sync.c +++ b/drivers/oprofile/buffer_sync.c @@ -82,9 +82,16 @@ static struct notifier_block exit_mmap_nb = { int sync_start(void) { - int err = profile_event_register(EXIT_TASK, &exit_task_nb); + int err; + + init_timer(&sync_timer); + sync_timer.function = timer_ping; + sync_timer.expires = jiffies + DEFAULT_EXPIRE; + add_timer(&sync_timer); + + err = profile_event_register(EXIT_TASK, &exit_task_nb); if (err) - goto out; + goto out1; err = profile_event_register(EXIT_MMAP, &exit_mmap_nb); if (err) goto out2; @@ -92,16 +99,14 @@ int sync_start(void) if (err) goto out3; - init_timer(&sync_timer); - sync_timer.function = timer_ping; - sync_timer.expires = jiffies + DEFAULT_EXPIRE; - add_timer(&sync_timer); out: return err; out3: profile_event_unregister(EXIT_MMAP, &exit_mmap_nb); out2: profile_event_unregister(EXIT_TASK, &exit_task_nb); +out1: + del_timer_sync(&sync_timer); goto out; } diff --git a/drivers/pci/pci.ids b/drivers/pci/pci.ids index 60d3a51f93a3..0ee6b158705c 100644 --- a/drivers/pci/pci.ids +++ b/drivers/pci/pci.ids @@ -5440,6 +5440,11 @@ 14f1 2004 Dynalink 56PMi 8234 RS8234 ATM SAR Controller [ServiceSAR Plus] 14f2 MOBILITY Electronics + 0120 EV1000 bridge + 0121 EV1000 Parallel port + 0122 EV1000 Serial port + 0123 EV1000 Keyboard controller + 0124 EV1000 Mouse controller 14f3 BROADLOGIC 14f4 TOKYO Electronic Industry CO Ltd 14f5 SOPAC Ltd diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 1c66d319f420..88b30de4726a 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -15,6 +15,9 @@ #define DBG(x...) #endif +#define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */ +#define CARDBUS_RESERVE_BUSNR 3 + LIST_HEAD(pci_root_buses); LIST_HEAD(pci_devices); @@ -291,12 +294,15 @@ static unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus); */ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int max, int pass) { - unsigned int buses; struct pci_bus *child; int is_cardbus = (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS); + u32 buses; pci_read_config_dword(dev, PCI_PRIMARY_BUS, &buses); - DBG("Scanning behind PCI bridge %s, config %06x, pass %d\n", dev->slot_name, buses & 0xffffff, pass); + + DBG("Scanning behind PCI bridge %s, config %06x, pass %d\n", + dev->slot_name, buses & 0xffffff, pass); + if ((buses & 0xffff00) && !pcibios_assign_all_busses() && !is_cardbus) { unsigned int cmax; /* @@ -333,8 +339,10 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int max * yenta.c forces a secondary latency timer of 176. * Copy that behaviour here. */ - if (is_cardbus) - buses = (buses & 0x00ffffff) | (176 << 24); + if (is_cardbus) { + buses &= ~0xff000000; + buses |= CARDBUS_LATENCY_TIMER << 24; + } /* * We need to blast all three values with a single write. @@ -350,7 +358,7 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int max * as cards with a PCI-to-PCI bridge can be * inserted later. */ - max += 3; + max += CARDBUS_RESERVE_BUSNR; } /* * Set the subordinate bus number to its real value. @@ -358,6 +366,7 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int max child->subordinate = max; pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max); } + sprintf(child->name, (is_cardbus ? "PCI CardBus #%02x" : "PCI Bus #%02x"), child->number); return max; @@ -391,17 +400,20 @@ int pci_setup_device(struct pci_dev * dev) { u32 class; - sprintf(dev->slot_name, "%02x:%02x.%d", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); - sprintf(dev->dev.name, "PCI device %04x:%04x", dev->vendor, dev->device); + sprintf(dev->slot_name, "%02x:%02x.%d", dev->bus->number, + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + sprintf(dev->dev.name, "PCI device %04x:%04x", + dev->vendor, dev->device); + INIT_LIST_HEAD(&dev->pools); - + pci_read_config_dword(dev, PCI_CLASS_REVISION, &class); class >>= 8; /* upper 3 bytes */ dev->class = class; class >>= 8; - DBG("Found %02x:%02x [%04x/%04x] %06x %02x\n", dev->bus->number, dev->devfn, - dev->vendor, dev->device, class, dev->hdr_type); + DBG("Found %02x:%02x [%04x/%04x] %06x %02x\n", dev->bus->number, + dev->devfn, dev->vendor, dev->device, class, dev->hdr_type); /* "Unknown power state" */ dev->current_state = 4; @@ -468,7 +480,8 @@ pci_scan_device(struct pci_bus *bus, int devfn) return NULL; /* some broken boards return 0 or ~0 if a slot is empty: */ - if (l == 0xffffffff || l == 0x00000000 || l == 0x0000ffff || l == 0xffff0000) + if (l == 0xffffffff || l == 0x00000000 || + l == 0x0000ffff || l == 0xffff0000) return NULL; dev = kmalloc(sizeof(struct pci_dev), GFP_KERNEL); @@ -520,11 +533,14 @@ int __devinit pci_scan_slot(struct pci_bus *bus, int devfn) struct pci_dev *dev; dev = pci_scan_device(bus, devfn); - if (!dev) - continue; - - if (func != 0) + if (func == 0) { + if (!dev) + break; + } else { + if (!dev) + continue; dev->multifunction = 1; + } /* Fix up broken headers */ pci_fixup_device(PCI_FIXUP_HEADER, dev); @@ -647,7 +663,8 @@ struct pci_bus * __devinit pci_scan_bus_parented(struct device *parent, int bus, if (b) { b->sysdata = sysdata; b->ops = ops; - b->subordinate = pci_do_scan_bus(b); + b->subordinate = pci_scan_child_bus(b); + pci_bus_add_devices(b); } return b; } diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index b796e2270a65..172174f9c61e 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -72,40 +72,29 @@ pbus_assign_resources_sorted(struct pci_bus *bus) requires that if there is no I/O ports or memory behind the bridge, corresponding range must be turned off by writing base value greater than limit to the bridge's base/limit registers. */ -static void __devinit -pci_setup_bridge(struct pci_bus *bus) +static void __devinit pci_setup_bridge(struct pci_bus *bus) { - struct pbus_set_ranges_data ranges; struct pci_dev *bridge = bus->self; + struct pci_bus_region region; u32 l; - if (!bridge || (bridge->class >> 8) != PCI_CLASS_BRIDGE_PCI) - return; - - ranges.io_start = bus->resource[0]->start; - ranges.io_end = bus->resource[0]->end; - ranges.mem_start = bus->resource[1]->start; - ranges.mem_end = bus->resource[1]->end; - ranges.prefetch_start = bus->resource[2]->start; - ranges.prefetch_end = bus->resource[2]->end; - pcibios_fixup_pbus_ranges(bus, &ranges); - DBGC((KERN_INFO "PCI: Bus %d, bridge: %s\n", bus->number, bridge->dev.name)); /* Set up the top and bottom of the PCI I/O segment for this bus. */ + pcibios_resource_to_bus(bridge, ®ion, bus->resource[0]); if (bus->resource[0]->flags & IORESOURCE_IO) { pci_read_config_dword(bridge, PCI_IO_BASE, &l); l &= 0xffff0000; - l |= (ranges.io_start >> 8) & 0x00f0; - l |= ranges.io_end & 0xf000; + l |= (region.start >> 8) & 0x00f0; + l |= region.end & 0xf000; /* Set up upper 16 bits of I/O base/limit. */ pci_write_config_word(bridge, PCI_IO_BASE_UPPER16, - ranges.io_start >> 16); + region.start >> 16); pci_write_config_word(bridge, PCI_IO_LIMIT_UPPER16, - ranges.io_end >> 16); + region.end >> 16); DBGC((KERN_INFO " IO window: %04lx-%04lx\n", - ranges.io_start, ranges.io_end)); + region.start, region.end)); } else { /* Clear upper 16 bits of I/O base/limit. */ @@ -117,11 +106,12 @@ pci_setup_bridge(struct pci_bus *bus) /* Set up the top and bottom of the PCI Memory segment for this bus. */ + pcibios_resource_to_bus(bridge, ®ion, bus->resource[1]); if (bus->resource[1]->flags & IORESOURCE_MEM) { - l = (ranges.mem_start >> 16) & 0xfff0; - l |= ranges.mem_end & 0xfff00000; + l = (region.start >> 16) & 0xfff0; + l |= region.end & 0xfff00000; DBGC((KERN_INFO " MEM window: %08lx-%08lx\n", - ranges.mem_start, ranges.mem_end)); + region.start, region.end)); } else { l = 0x0000fff0; @@ -134,11 +124,12 @@ pci_setup_bridge(struct pci_bus *bus) pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, 0); /* Set up PREF base/limit. */ + pcibios_resource_to_bus(bridge, ®ion, bus->resource[2]); if (bus->resource[2]->flags & IORESOURCE_PREFETCH) { - l = (ranges.prefetch_start >> 16) & 0xfff0; - l |= ranges.prefetch_end & 0xfff00000; + l = (region.start >> 16) & 0xfff0; + l |= region.end & 0xfff00000; DBGC((KERN_INFO " PREFETCH window: %08lx-%08lx\n", - ranges.prefetch_start, ranges.prefetch_end)); + region.start, region.end)); } else { l = 0x0000fff0; @@ -284,7 +275,7 @@ pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long type) order = 0; /* Exclude ranges with size > align from calculation of the alignment. */ - if (size == align) + if (r_size == align) aligns[order] += align; if (order > max_order) max_order = order; diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 3331a411db7b..4989b88b45d3 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -33,6 +33,59 @@ #endif +static void +pci_update_resource(struct pci_dev *dev, struct resource *res, int resno) +{ + struct pci_bus_region region; + u32 new, check, mask; + int reg; + + pcibios_resource_to_bus(dev, ®ion, res); + + DBGC((KERN_ERR " got res [%lx:%lx] bus [%lx:%lx] for " + "resource %d of %s\n", res->start, res->end, + region.start, region.end, resno, dev->dev.name)); + + new = region.start | (res->flags & PCI_REGION_FLAG_MASK); + if (res->flags & IORESOURCE_IO) + mask = (u32)PCI_BASE_ADDRESS_IO_MASK; + else + mask = (u32)PCI_BASE_ADDRESS_MEM_MASK; + + if (resno < 6) { + reg = PCI_BASE_ADDRESS_0 + 4 * resno; + } else if (resno == PCI_ROM_RESOURCE) { + new |= res->flags & PCI_ROM_ADDRESS_ENABLE; + reg = dev->rom_base_reg; + } else { + /* Hmm, non-standard resource. */ + printk("PCI: trying to set non-standard region %s/%d\n", + dev->slot_name, resno); + return; + } + + pci_write_config_dword(dev, reg, new); + pci_read_config_dword(dev, reg, &check); + + if ((new ^ check) & mask) { + printk(KERN_ERR "PCI: Error while updating region " + "%s/%d (%08x != %08x)\n", dev->slot_name, resno, + new, check); + } + + if ((new & (PCI_BASE_ADDRESS_SPACE|PCI_BASE_ADDRESS_MEM_TYPE_MASK)) == + (PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64)) { + new = 0; /* currently everyone zeros the high address */ + pci_write_config_dword(dev, reg + 4, new); + pci_read_config_dword(dev, reg + 4, &check); + if (check != new) { + printk(KERN_ERR "PCI: Error updating region " + "%s/%d (high %08x != %08x)\n", + dev->slot_name, resno, new, check); + } + } +} + int __init pci_claim_resource(struct pci_dev *dev, int resource) { @@ -89,10 +142,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno) printk(KERN_ERR "PCI: Failed to allocate resource %d(%lx-%lx) for %s\n", resno, res->start, res->end, dev->slot_name); } else { - DBGC((KERN_ERR " got res[%lx:%lx] for resource %d of %s\n", - res->start, res->end, i, dev->dev.name)); - /* Update PCI config space. */ - pcibios_update_resource(dev, res->parent, res, resno); + pci_update_resource(dev, res, resno); } return ret; diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c index 95af5d5f81d0..9f5ace7ae222 100644 --- a/drivers/scsi/aacraid/linit.c +++ b/drivers/scsi/aacraid/linit.c @@ -679,27 +679,26 @@ static int aac_cfg_ioctl(struct inode * inode, struct file * file, unsigned int */ static Scsi_Host_Template driver_template = { - module: THIS_MODULE, - name: "AAC", - proc_info: aac_procinfo, - detect: aac_detect, - release: aac_release, - info: aac_driverinfo, - ioctl: aac_ioctl, - queuecommand: aac_queuecommand, - bios_param: aac_biosparm, - slave_configure: aac_slave_configure, - can_queue: AAC_NUM_IO_FIB, - this_id: 16, - sg_tablesize: 16, - max_sectors: 128, - cmd_per_lun: AAC_NUM_IO_FIB, - eh_abort_handler: aac_eh_abort, - eh_device_reset_handler:aac_eh_device_reset, - eh_bus_reset_handler: aac_eh_bus_reset, - eh_host_reset_handler: aac_eh_reset, - - use_clustering: ENABLE_CLUSTERING, + .module = THIS_MODULE, + .name = "AAC", + .proc_info = aac_procinfo, + .detect = aac_detect, + .release = aac_release, + .info = aac_driverinfo, + .ioctl = aac_ioctl, + .queuecommand = aac_queuecommand, + .bios_param = aac_biosparm, + .slave_configure = aac_slave_configure, + .can_queue = AAC_NUM_IO_FIB, + .this_id = 16, + .sg_tablesize = 16, + .max_sectors = 128, + .cmd_per_lun = 1, + .eh_abort_handler = aac_eh_abort, + .eh_device_reset_handler = aac_eh_device_reset, + .eh_bus_reset_handler = aac_eh_bus_reset, + .eh_host_reset_handler = aac_eh_reset, + .use_clustering = ENABLE_CLUSTERING, }; #include "scsi_module.c" diff --git a/drivers/scsi/hosts.h b/drivers/scsi/hosts.h index c8098010e86b..f5f428711b34 100644 --- a/drivers/scsi/hosts.h +++ b/drivers/scsi/hosts.h @@ -549,6 +549,7 @@ struct Scsi_Device_Template void (*detach)(Scsi_Device *); int (*init_command)(Scsi_Cmnd *); /* Used by new queueing code. Selects command for blkdevs */ + void (*rescan)(Scsi_Device *); struct device_driver scsi_driverfs_driver; }; diff --git a/drivers/scsi/osst.c b/drivers/scsi/osst.c index 1c5ca7e8ad02..8ccfb9e3642b 100644 --- a/drivers/scsi/osst.c +++ b/drivers/scsi/osst.c @@ -606,7 +606,7 @@ static int osst_wait_ready(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, unsigned { unsigned char cmd[MAX_COMMAND_SIZE]; Scsi_Request * SRpnt; - long startwait = jiffies; + unsigned long startwait = jiffies; #if DEBUG int dbg = debugging; char * name = tape_name(STp); @@ -673,7 +673,7 @@ static int osst_wait_for_medium(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, unsi { unsigned char cmd[MAX_COMMAND_SIZE]; Scsi_Request * SRpnt; - long startwait = jiffies; + unsigned long startwait = jiffies; #if DEBUG int dbg = debugging; char * name = tape_name(STp); @@ -777,8 +777,8 @@ static int osst_flush_drive_buffer(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt) #define OSST_POLL_PER_SEC 10 static int osst_wait_frame(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, int curr, int minlast, int to) { - long startwait = jiffies; - char * name = tape_name(STp); + unsigned long startwait = jiffies; + char * name = tape_name(STp); #if DEBUG char notyetprinted = 1; #endif @@ -1288,7 +1288,7 @@ static int osst_read_back_buffer_and_rewrite(OS_Scsi_Tape * STp, Scsi_Request ** int logical_blk_num = ntohl(STp->buffer->aux->logical_blk_num) - (nframes + pending - 1) * blks_per_frame; char * name = tape_name(STp); - long startwait = jiffies; + unsigned long startwait = jiffies; #if DEBUG int dbg = debugging; #endif @@ -1477,7 +1477,7 @@ static int osst_reposition_and_retry(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, int expected = 0; int attempts = 1000 / skip; int flag = 1; - long startwait = jiffies; + unsigned long startwait = jiffies; #if DEBUG int dbg = debugging; #endif diff --git a/drivers/scsi/qlogicfc.c b/drivers/scsi/qlogicfc.c index 7b966c63bb9e..3cf04a66dc5d 100644 --- a/drivers/scsi/qlogicfc.c +++ b/drivers/scsi/qlogicfc.c @@ -836,14 +836,22 @@ static int isp2x00_make_portdb(struct Scsi_Host *host) short param[8]; int i, j; - struct id_name_map temp[QLOGICFC_MAX_ID + 1]; + struct id_name_map *map; /* base of array [QLOGICFC_MAX_ID + 1] */ + struct id_name_map *mapx; /* array entry pointer */ struct isp2x00_hostdata *hostdata; - isp2x00_disable_irqs(host); - - memset(temp, 0, sizeof(temp)); hostdata = (struct isp2x00_hostdata *) host->hostdata; + map = kmalloc((QLOGICFC_MAX_ID + 1) * sizeof(struct id_name_map), GFP_ATOMIC); + if (!map) { + printk("qlogicfc%d : error getting memory -- cannot make port database.\n", + hostdata->host_id); + goto fini; + } + memset(map, 0, (QLOGICFC_MAX_ID + 1) * sizeof(struct id_name_map)); + + isp2x00_disable_irqs(host); + #if ISP2x00_FABRIC for (i = 0x81; i < QLOGICFC_MAX_ID; i++) { param[0] = MBOX_PORT_LOGOUT; @@ -868,72 +876,74 @@ static int isp2x00_make_portdb(struct Scsi_Host *host) if (param[0] == MBOX_COMMAND_COMPLETE) { hostdata->port_id = ((u_int) param[3]) << 16; hostdata->port_id |= param[2]; - temp[0].loop_id = param[1]; - temp[0].wwn = hostdata->wwn; + map->loop_id = param[1]; + map->wwn = hostdata->wwn; } else { printk("qlogicfc%d : error getting scsi id.\n", hostdata->host_id); } - for (i = 0; i <=QLOGICFC_MAX_ID; i++) - temp[i].loop_id = temp[0].loop_id; + for (i = 0, mapx = map; i <= QLOGICFC_MAX_ID; i++, mapx++) + mapx->loop_id = map->loop_id; - for (i = 0, j = 1; i <= QLOGICFC_MAX_LOOP_ID; i++) { + for (i = 0, j = 1, mapx = map + 1; i <= QLOGICFC_MAX_LOOP_ID; i++) { param[0] = MBOX_GET_PORT_NAME; param[1] = (i << 8) & 0xff00; isp2x00_mbox_command(host, param); if (param[0] == MBOX_COMMAND_COMPLETE) { - temp[j].loop_id = i; - temp[j].wwn = ((u64) (param[2] & 0xff)) << 56; - temp[j].wwn |= ((u64) ((param[2] >> 8) & 0xff)) << 48; - temp[j].wwn |= ((u64) (param[3] & 0xff)) << 40; - temp[j].wwn |= ((u64) ((param[3] >> 8) & 0xff)) << 32; - temp[j].wwn |= ((u64) (param[6] & 0xff)) << 24; - temp[j].wwn |= ((u64) ((param[6] >> 8) & 0xff)) << 16; - temp[j].wwn |= ((u64) (param[7] & 0xff)) << 8; - temp[j].wwn |= ((u64) ((param[7] >> 8) & 0xff)); + mapx->loop_id = i; + mapx->wwn = ((u64) (param[2] & 0xff)) << 56; + mapx->wwn |= ((u64) ((param[2] >> 8) & 0xff)) << 48; + mapx->wwn |= ((u64) (param[3] & 0xff)) << 40; + mapx->wwn |= ((u64) ((param[3] >> 8) & 0xff)) << 32; + mapx->wwn |= ((u64) (param[6] & 0xff)) << 24; + mapx->wwn |= ((u64) ((param[6] >> 8) & 0xff)) << 16; + mapx->wwn |= ((u64) (param[7] & 0xff)) << 8; + mapx->wwn |= ((u64) ((param[7] >> 8) & 0xff)); j++; - + mapx++; } } #if ISP2x00_FABRIC - isp2x00_init_fabric(host, temp, j); + isp2x00_init_fabric(host, map, j); #endif - for (i = 0; i <= QLOGICFC_MAX_ID; i++) { - if (temp[i].wwn != hostdata->port_db[i].wwn) { - for (j = 0; j <= QLOGICFC_MAX_ID; j++) { - if (temp[j].wwn == hostdata->port_db[i].wwn) { - hostdata->port_db[i].loop_id = temp[j].loop_id; + for (i = 0, mapx = map; i <= QLOGICFC_MAX_ID; i++, mapx++) { + struct id_name_map *tmap; /* second array entry pointer */ + if (mapx->wwn != hostdata->port_db[i].wwn) { + for (j = 0, tmap = map; j <= QLOGICFC_MAX_ID; j++, tmap++) { + if (tmap->wwn == hostdata->port_db[i].wwn) { + hostdata->port_db[i].loop_id = tmap->loop_id; break; } } if (j == QLOGICFC_MAX_ID + 1) - hostdata->port_db[i].loop_id = temp[0].loop_id; + hostdata->port_db[i].loop_id = map->loop_id; for (j = 0; j <= QLOGICFC_MAX_ID; j++) { - if (hostdata->port_db[j].wwn == temp[i].wwn || !hostdata->port_db[j].wwn) { + if (hostdata->port_db[j].wwn == mapx->wwn || !hostdata->port_db[j].wwn) { break; } } if (j == QLOGICFC_MAX_ID + 1) printk("qlogicfc%d : Too many scsi devices, no more room in port map.\n", hostdata->host_id); if (!hostdata->port_db[j].wwn) { - hostdata->port_db[j].loop_id = temp[i].loop_id; - hostdata->port_db[j].wwn = temp[i].wwn; + hostdata->port_db[j].loop_id = mapx->loop_id; + hostdata->port_db[j].wwn = mapx->wwn; } } else - hostdata->port_db[i].loop_id = temp[i].loop_id; + hostdata->port_db[i].loop_id = mapx->loop_id; } isp2x00_enable_irqs(host); - + kfree(map); +fini: return 0; } diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 5632d669ca7a..78577c181884 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -96,12 +96,7 @@ unsigned long scsi_pid; Scsi_Cmnd *last_cmnd; static unsigned long serial_number; -struct softscsi_data { - Scsi_Cmnd *head; - Scsi_Cmnd *tail; -}; - -static struct softscsi_data softscsi_data[NR_CPUS] __cacheline_aligned; +static struct list_head done_q[NR_CPUS] __cacheline_aligned; /* * List of all highlevel drivers. @@ -637,79 +632,60 @@ void scsi_init_cmd_from_req(Scsi_Cmnd * SCpnt, Scsi_Request * SRpnt) } /** - * scsi_done - Mark this command as done - * @SCpnt: The SCSI Command which we think we've completed. + * scsi_done - Enqueue the finished SCSI command into the done queue. + * @cmd: The SCSI Command for which a low-level device driver (LLDD) gives + * ownership back to SCSI Core -- i.e. the LLDD has finished with it. * - * This function is the mid-level interrupt routine, which decides how - * to handle error conditions. Each invocation of this function must - * do one and *only* one of the following: + * This function is the mid-level's (SCSI Core) interrupt routine, which + * regains ownership of the SCSI command (de facto) from a LLDD, and enqueues + * the command to the done queue for further processing. * - * 1) Insert command in BH queue. - * 2) Activate error handler for host. + * This is the producer of the done queue who enqueues at the tail. * - * There is no longer a problem with stack overflow. Interrupts queue - * Scsi_Cmnd on a per-CPU queue and the softirq handler removes them - * from the queue one at a time. - * - * This function is sometimes called from interrupt context, but sometimes - * from task context. + * This function is interrupt context safe. */ -void scsi_done(Scsi_Cmnd * SCpnt) +void scsi_done(struct scsi_cmnd *cmd) { + int cpu; unsigned long flags; - int cpu, tstatus; - struct softscsi_data *queue; + struct list_head *pdone_q; /* * We don't have to worry about this one timing out any more. - */ - tstatus = scsi_delete_timer(SCpnt); - - /* - * If we are unable to remove the timer, it means that the command - * has already timed out. In this case, we have no choice but to + * If we are unable to remove the timer, then the command + * has already timed out. In which case, we have no choice but to * let the timeout function run, as we have no idea where in fact * that function could really be. It might be on another processor, * etc, etc. */ - if (!tstatus) { + if (!scsi_delete_timer(cmd)) return; - } /* Set the serial numbers back to zero */ - SCpnt->serial_number = 0; - SCpnt->serial_number_at_timeout = 0; - SCpnt->state = SCSI_STATE_BHQUEUE; - SCpnt->owner = SCSI_OWNER_BH_HANDLER; - SCpnt->bh_next = NULL; + cmd->serial_number = 0; + cmd->serial_number_at_timeout = 0; + cmd->state = SCSI_STATE_BHQUEUE; + cmd->owner = SCSI_OWNER_BH_HANDLER; /* - * Next, put this command in the softirq queue. - * - * This is a per-CPU queue, so we just disable local interrupts + * Next, enqueue the command into the done queue. + * It is a per-CPU queue, so we just disable local interrupts * and need no spinlock. */ - local_irq_save(flags); cpu = smp_processor_id(); - queue = &softscsi_data[cpu]; - - if (!queue->head) { - queue->head = SCpnt; - queue->tail = SCpnt; - } else { - queue->tail->bh_next = SCpnt; - queue->tail = SCpnt; - } - + pdone_q = &done_q[cpu]; + list_add_tail(&cmd->eh_entry, pdone_q); cpu_raise_softirq(cpu, SCSI_SOFTIRQ); local_irq_restore(flags); } /** - * scsi_softirq - Perform post-interrupt handling for completed commands + * scsi_softirq - Perform post-interrupt processing of finished SCSI commands. + * + * This is the consumer of the done queue. * * This is called with all interrupts enabled. This should reduce * interrupt latency, stack depth, and reentrancy of the low-level @@ -717,88 +693,92 @@ void scsi_done(Scsi_Cmnd * SCpnt) */ static void scsi_softirq(struct softirq_action *h) { - int cpu = smp_processor_id(); - struct softscsi_data *queue = &softscsi_data[cpu]; + LIST_HEAD(local_q); - while (queue->head) { - Scsi_Cmnd *SCpnt, *SCnext; + local_irq_disable(); + list_splice_init(&done_q[smp_processor_id()], &local_q); + local_irq_enable(); - local_irq_disable(); - SCpnt = queue->head; - queue->head = NULL; - local_irq_enable(); + while (!list_empty(&local_q)) { + struct scsi_cmnd *cmd = list_entry(local_q.next, + struct scsi_cmnd, eh_entry); + list_del_init(&cmd->eh_entry); - for (; SCpnt; SCpnt = SCnext) { - SCnext = SCpnt->bh_next; + switch (scsi_decide_disposition(cmd)) { + case SUCCESS: + /* + * Add to BH queue. + */ + SCSI_LOG_MLCOMPLETE(3, + printk("Command finished %d %d " + "0x%x\n", + cmd->device->host->host_busy, + cmd->device->host->host_failed, + cmd->result)); + + scsi_finish_command(cmd); + break; + case NEEDS_RETRY: + /* + * We only come in here if we want to retry a + * command. The test to see whether the + * command should be retried should be keeping + * track of the number of tries, so we don't + * end up looping, of course. + */ + SCSI_LOG_MLCOMPLETE(3, printk("Command needs retry " + "%d %d 0x%x\n", + cmd->device->host->host_busy, + cmd->device->host->host_failed, + cmd->result)); - switch (scsi_decide_disposition(SCpnt)) { - case SUCCESS: - /* - * Add to BH queue. - */ - SCSI_LOG_MLCOMPLETE(3, printk("Command finished %d %d 0x%x\n", SCpnt->device->host->host_busy, - SCpnt->device->host->host_failed, - SCpnt->result)); + scsi_retry_command(cmd); + break; + case ADD_TO_MLQUEUE: + /* + * This typically happens for a QUEUE_FULL + * message - typically only when the queue + * depth is only approximate for a given + * device. Adding a command to the queue for + * the device will prevent further commands + * from being sent to the device, so we + * shouldn't end up with tons of things being + * sent down that shouldn't be. + */ + SCSI_LOG_MLCOMPLETE(3, printk("Command rejected as " + "device queue full, " + "put on ml queue %p\n", + cmd)); + scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY); + break; + default: + /* + * Here we have a fatal error of some sort. + * Turn it over to the error handler. + */ + SCSI_LOG_MLCOMPLETE(3, + printk("Command failed %p %x " + "busy=%d failed=%d\n", + cmd, cmd->result, + cmd->device->host->host_busy, + cmd->device->host->host_failed)); - scsi_finish_command(SCpnt); - break; - case NEEDS_RETRY: - /* - * We only come in here if we want to retry a - * command. The test to see whether the - * command should be retried should be keeping - * track of the number of tries, so we don't - * end up looping, of course. - */ - SCSI_LOG_MLCOMPLETE(3, printk("Command needs retry %d %d 0x%x\n", SCpnt->device->host->host_busy, - SCpnt->device->host->host_failed, SCpnt->result)); - - scsi_retry_command(SCpnt); - break; - case ADD_TO_MLQUEUE: - /* - * This typically happens for a QUEUE_FULL - * message - typically only when the queue - * depth is only approximate for a given - * device. Adding a command to the queue for - * the device will prevent further commands - * from being sent to the device, so we - * shouldn't end up with tons of things being - * sent down that shouldn't be. - */ - SCSI_LOG_MLCOMPLETE(3, printk("Command rejected as device queue full, put on ml queue %p\n", - SCpnt)); - scsi_queue_insert(SCpnt, SCSI_MLQUEUE_DEVICE_BUSY); - break; - default: - /* - * Here we have a fatal error of some sort. - * Turn it over to the error handler. - */ - SCSI_LOG_MLCOMPLETE(3, - printk("Command failed %p %x busy=%d failed=%d\n", - SCpnt, SCpnt->result, - SCpnt->device->host->host_busy, - SCpnt->device->host->host_failed)); + /* + * Dump the sense information too. + */ + if ((status_byte(cmd->result)&CHECK_CONDITION) != 0) { + SCSI_LOG_MLCOMPLETE(3, print_sense("bh", cmd)); + } + if (!scsi_eh_scmd_add(cmd, 0)) { /* - * Dump the sense information too. + * We only get here if the error + * recovery thread has died. */ - if ((status_byte(SCpnt->result) & CHECK_CONDITION) != 0) { - SCSI_LOG_MLCOMPLETE(3, print_sense("bh", SCpnt)); - } - - if (!scsi_eh_scmd_add(SCpnt, 0)) - { - /* - * We only get here if the error - * recovery thread has died. - */ - scsi_finish_command(SCpnt); - } - } /* switch */ - } /* for(; SCpnt...) */ - } /* while(queue->head) */ + scsi_finish_command(cmd); + } + } + } } /* @@ -1284,6 +1264,21 @@ void scsi_detach_device(struct scsi_device *sdev) up_read(&scsi_devicelist_mutex); } +void scsi_rescan_device(struct scsi_device *sdev) +{ + struct Scsi_Device_Template *sdt; + + down_read(&scsi_devicelist_mutex); + list_for_each_entry(sdt, &scsi_devicelist, list) { + if (!try_module_get(sdt->module)) + continue; + if (*sdt->rescan) + (*sdt->rescan)(sdev); + module_put(sdt->module); + } + up_read(&scsi_devicelist_mutex); +} + int scsi_device_get(struct scsi_device *sdev) { if (!try_module_get(sdev->host->hostt->module)) @@ -1481,7 +1476,7 @@ __setup("scsi_default_dev_flags=", setup_scsi_default_dev_flags); static int __init init_scsi(void) { - int error; + int error, i; error = scsi_init_queue(); if (error) @@ -1496,6 +1491,9 @@ static int __init init_scsi(void) if (error) goto cleanup_devlist; + for (i = 0; i < NR_CPUS; i++) + INIT_LIST_HEAD(&done_q[i]); + scsi_host_init(); devfs_mk_dir(NULL, "scsi", NULL); open_softirq(SCSI_SOFTIRQ, scsi_softirq, NULL); diff --git a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h index 12d9783b9c77..96f92ae21c32 100644 --- a/drivers/scsi/scsi.h +++ b/drivers/scsi/scsi.h @@ -741,8 +741,7 @@ struct scsi_cmnd { * abort, etc are in process. */ unsigned volatile char internal_timeout; - struct scsi_cmnd *bh_next; /* To enumerate the commands waiting - to be processed. */ + unsigned char cmd_len; unsigned char old_cmd_len; unsigned char sc_data_direction; diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index e16e70bcad90..eed0e0583619 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -11,7 +11,7 @@ * (or disk like devices) sharing a common amount of RAM * * - * For documentation see http://www.torque.net/sg/sdebug.html + * For documentation see http://www.torque.net/sg/sdebug25.html * * D. Gilbert (dpg) work for Magneto-Optical device test [20010421] * dpg: work for devfs large number of disks [20010809] @@ -54,38 +54,37 @@ #include "scsi_debug.h" -static const char * scsi_debug_version_str = "Version: 1.67 (20021221)"; - -#ifndef SCSI_CMD_READ_16 -#define SCSI_CMD_READ_16 0x88 -#endif -#ifndef SCSI_CMD_WRITE_16 -#define SCSI_CMD_WRITE_16 0x8a -#endif +static const char * scsi_debug_version_str = "Version: 1.68 (20030314)"; #define SDEBUG_TAGGED_QUEUING 0 /* 0 | MSG_SIMPLE_TAG | MSG_ORDERED_TAG */ /* Default values for driver parameters */ #define DEF_NUM_DEVS 1 #define DEF_DEV_SIZE_MB 8 -#define DEF_EVERY_NTH 100 +#define DEF_EVERY_NTH 0 #define DEF_DELAY 1 #define DEF_MAX_LUNS 2 #define DEF_SCSI_LEVEL 3 #define DEF_NUM_HOST 1 #define DEF_OPTS 0 -#define MAX_NUM_HOSTS 128 - /* bit mask values for scsi_debug_opts */ #define SCSI_DEBUG_OPT_NOISE 1 #define SCSI_DEBUG_OPT_MEDIUM_ERR 2 -#define SCSI_DEBUG_OPT_EVERY_NTH 4 +#define SCSI_DEBUG_OPT_TIMEOUT 4 +#define SCSI_DEBUG_OPT_RECOVERED_ERR 8 +/* When "every_nth" > 0 then modulo "every_nth" commands: + * - a no response is simulated if SCSI_DEBUG_OPT_TIMEOUT is set + * - a RECOVERED_ERROR is simulated on successful read and write + * commands if SCSI_DEBUG_OPT_RECOVERED_ERR is set. + */ -#define OPT_MEDIUM_ERR_ADDR 0x1234 +/* when 1==SCSI_DEBUG_OPT_MEDIUM_ERR, a medium error is simulated at this + * sector on read commands: */ +#define OPT_MEDIUM_ERR_ADDR 0x1234 /* that's sector 4660 in decimal */ static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB; -static int scsi_debug_num_devs = DEF_NUM_DEVS; +static int scsi_debug_num_devs = DEF_NUM_DEVS; /* max devs per host */ static int scsi_debug_opts = DEF_OPTS; static int scsi_debug_every_nth = DEF_EVERY_NTH; static int scsi_debug_cmnd_count = 0; @@ -112,25 +111,28 @@ static int sdebug_sectors_per; /* sectors per cylinder */ #define SECT_SIZE (1 << POW2_SECT_SIZE) #define SECT_SIZE_PER(TGT) SECT_SIZE -struct sdebug_host_info { - struct Scsi_Host *shost; - struct device *dev; -}; - -struct sdebug_host_info * scsi_debug_hosts; - #define SDEBUG_SENSE_LEN 32 struct sdebug_dev_info { + struct list_head dev_list; unsigned char sense_buff[SDEBUG_SENSE_LEN]; /* weak nexus */ unsigned int channel; unsigned int target; unsigned int lun; - struct Scsi_Host *host; + struct sdebug_host_info *sdbg_host; char reset; char used; }; -static struct sdebug_dev_info * devInfop; + +struct sdebug_host_info { + struct list_head host_list; + struct Scsi_Host *shost; + struct device *dev; + struct list_head dev_info_list; +}; + +static LIST_HEAD(sdebug_host_list); +static spinlock_t sdebug_host_list_lock = SPIN_LOCK_UNLOCKED; typedef void (* done_funct_t) (struct scsi_cmnd *); @@ -156,11 +158,20 @@ static spinlock_t queued_arr_lock = SPIN_LOCK_UNLOCKED; static rwlock_t atomic_rw = RW_LOCK_UNLOCKED; static char sdebug_proc_name[] = "scsi_debug"; + +static int sdebug_driver_probe(struct device *); +static int sdebug_driver_remove(struct device *); + static struct device_driver sdebug_driverfs_driver = { - .name = sdebug_proc_name, - .devclass = &shost_devclass, + .name = sdebug_proc_name, + .probe = sdebug_driver_probe, + .remove = sdebug_driver_remove, + .devclass = &shost_devclass, }; +static const int check_condition_result = + (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION; + /* function declarations */ static int resp_inquiry(unsigned char * cmd, int target, unsigned char * buff, int bufflen, struct sdebug_dev_info * devip); @@ -174,7 +185,7 @@ static int resp_write(struct scsi_cmnd * SCpnt, int upper_blk, int block, static int resp_report_luns(unsigned char * cmd, unsigned char * buff, int bufflen, struct sdebug_dev_info * devip); static void timer_intr_handler(unsigned long); -static struct sdebug_dev_info * devInfoReg(struct scsi_cmnd *scmd); +static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev); static void mk_sense_buffer(struct sdebug_dev_info * devip, int key, int asc, int asq, int inbandLen); static int check_reset(struct scsi_cmnd * SCpnt, @@ -189,15 +200,29 @@ static int inquiry_evpd_83(unsigned char * arr, int dev_id_num, const char * dev_id_str, int dev_id_str_len); static void do_create_driverfs_files(void); static void do_remove_driverfs_files(void); -static void sdebug_add_shost(int num); -static void sdebug_remove_shost(int num); -static int sdebug_add_adapter(int num); -static void sdebug_remove_adapter(int num); +static int sdebug_add_adapter(void); +static void sdebug_remove_adapter(void); static struct device pseudo_primary; static struct bus_type pseudo_lld_bus; -int scsi_debug_register_driver(struct device_driver *); -int scsi_debug_unregister_driver(struct device_driver *); +static int scsi_debug_register_driver(struct device_driver *); +static int scsi_debug_unregister_driver(struct device_driver *); + +static struct sdebug_host_info * + sdebug_shost_to_host_info(struct Scsi_Host *shost) +{ + struct sdebug_host_info * sdbg_host, * found = NULL; + + spin_lock(&sdebug_host_list_lock); + list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) { + if (sdbg_host->shost == shost) { + found = sdbg_host; + break; + } + } + spin_unlock(&sdebug_host_list_lock); + return found; +} static unsigned char * scatg2virt(const struct scatterlist * sclp) { @@ -222,6 +247,7 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) unsigned long capac; struct sdebug_dev_info * devip = NULL; unsigned char * sbuff; + int inj_recovered = 0; if (done == NULL) return 0; /* assume mid level reprocessing command */ @@ -250,16 +276,18 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) if (SCpnt->device->lun >= scsi_debug_max_luns) return schedule_resp(SCpnt, NULL, done, DID_NO_CONNECT << 16, 0); - devip = devInfoReg(SCpnt); + devip = devInfoReg(SCpnt->device); if (NULL == devip) return schedule_resp(SCpnt, NULL, done, DID_NO_CONNECT << 16, 0); - if ((SCSI_DEBUG_OPT_EVERY_NTH & scsi_debug_opts) && - (scsi_debug_every_nth > 0) && + if ((scsi_debug_every_nth > 0) && (++scsi_debug_cmnd_count >= scsi_debug_every_nth)) { scsi_debug_cmnd_count =0; - return 0; /* ignore command causing timeout */ + if (SCSI_DEBUG_OPT_TIMEOUT & scsi_debug_opts) + return 0; /* ignore command causing timeout */ + else if (SCSI_DEBUG_OPT_RECOVERED_ERR & scsi_debug_opts) + inj_recovered = 1; /* to reads and writes below */ } switch (*cmd) { @@ -269,8 +297,8 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) case REQUEST_SENSE: /* mandatory */ /* Since this driver indicates autosense by placing the * sense buffer in the scsi_cmnd structure in the response - * (when CHECK_CONDITION is set), the mid level shouldn't - * need to call REQUEST_SENSE */ + * (when SAM_STAT_CHECK_CONDITION is set), the mid level + * shouldn't need to call REQUEST_SENSE */ if (devip) { sbuff = devip->sense_buff; memcpy(buff, sbuff, (bufflen < SDEBUG_SENSE_LEN) ? @@ -326,14 +354,14 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) buff[7] = SECT_SIZE_PER(target) & 0xff; } break; - case SCSI_CMD_READ_16: /* SBC-2 */ + case READ_16: case READ_12: case READ_10: case READ_6: if ((errsts = check_reset(SCpnt, devip))) break; upper_blk = 0; - if ((*cmd) == SCSI_CMD_READ_16) { + if ((*cmd) == READ_16) { upper_blk = cmd[5] + (cmd[4] << 8) + (cmd[3] << 16) + (cmd[2] << 24); block = cmd[9] + (cmd[8] << 8) + @@ -355,18 +383,22 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) num = cmd[4]; } errsts = resp_read(SCpnt, upper_blk, block, num, devip); + if (inj_recovered && (0 == errsts)) { + mk_sense_buffer(devip, RECOVERED_ERROR, 0x5d, 0, 14); + errsts = check_condition_result; + } break; case REPORT_LUNS: errsts = resp_report_luns(cmd, buff, bufflen, devip); break; - case SCSI_CMD_WRITE_16: /* SBC-2 */ + case WRITE_16: case WRITE_12: case WRITE_10: case WRITE_6: if ((errsts = check_reset(SCpnt, devip))) break; upper_blk = 0; - if ((*cmd) == SCSI_CMD_WRITE_16) { + if ((*cmd) == WRITE_16) { upper_blk = cmd[5] + (cmd[4] << 8) + (cmd[3] << 16) + (cmd[2] << 24); block = cmd[9] + (cmd[8] << 8) + @@ -388,6 +420,10 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) num = cmd[4]; } errsts = resp_write(SCpnt, upper_blk, block, num, devip); + if (inj_recovered && (0 == errsts)) { + mk_sense_buffer(devip, RECOVERED_ERROR, 0x5d, 0, 14); + errsts = check_condition_result; + } break; case MODE_SENSE: case MODE_SENSE_10: @@ -400,7 +436,7 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) if ((errsts = check_reset(SCpnt, devip))) break; mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x20, 0, 14); - errsts = (DRIVER_SENSE << 24) | (CHECK_CONDITION << 1); + errsts = check_condition_result; break; } return schedule_resp(SCpnt, devip, done, errsts, scsi_debug_delay); @@ -420,7 +456,7 @@ static int check_reset(struct scsi_cmnd * SCpnt, struct sdebug_dev_info * devip) if (devip->reset) { devip->reset = 0; mk_sense_buffer(devip, UNIT_ATTENTION, 0x29, 0, 14); - return (DRIVER_SENSE << 24) | (CHECK_CONDITION << 1); + return check_condition_result; } return 0; } @@ -481,12 +517,12 @@ static int resp_inquiry(unsigned char * cmd, int target, unsigned char * buff, arr[0] = pq_pdt; if (0x2 & cmd[1]) { /* CMDDT bit set */ mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x24, 0, 14); - return (DRIVER_SENSE << 24) | (CHECK_CONDITION << 1); + return check_condition_result; } else if (0x1 & cmd[1]) { /* EVPD bit set */ int dev_id_num, len; char dev_id_str[6]; - dev_id_num = ((devip->host->host_no + 1) * 2000) + + dev_id_num = ((devip->sdbg_host->shost->host_no + 1) * 2000) + (devip->target * 1000) + devip->lun; len = snprintf(dev_id_str, 6, "%d", dev_id_num); len = (len > 6) ? 6 : len; @@ -506,7 +542,7 @@ static int resp_inquiry(unsigned char * cmd, int target, unsigned char * buff, } else { /* Illegal request, invalid field in cdb */ mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x24, 0, 14); - return (DRIVER_SENSE << 24) | (CHECK_CONDITION << 1); + return check_condition_result; } memcpy(buff, arr, min_len); return 0; @@ -619,7 +655,7 @@ static int resp_mode_sense(unsigned char * cmd, int target, memset(arr, 0, SDEBUG_MAX_MSENSE_SZ); if (0x3 == pcontrol) { /* Saving values not supported */ mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x39, 0, 14); - return (DRIVER_SENSE << 24) | (CHECK_CONDITION << 1); + return check_condition_result; } dev_spec = DEV_READONLY(target) ? 0x80 : 0x0; if (msense_6) { @@ -662,7 +698,7 @@ static int resp_mode_sense(unsigned char * cmd, int target, break; default: mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x24, 0, 14); - return (DRIVER_SENSE << 24) | (CHECK_CONDITION << 1); + return check_condition_result; } if (msense_6) arr[0] = offset - 1; @@ -686,14 +722,14 @@ static int resp_read(struct scsi_cmnd * SCpnt, int upper_blk, int block, if (upper_blk || (block + num > sdebug_capacity)) { mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x21, 0, 14); - return (DRIVER_SENSE << 24) | (CHECK_CONDITION << 1); + return check_condition_result; } if ((SCSI_DEBUG_OPT_MEDIUM_ERR & scsi_debug_opts) && - (block >= OPT_MEDIUM_ERR_ADDR) && - (block < (OPT_MEDIUM_ERR_ADDR + num))) { + (block <= OPT_MEDIUM_ERR_ADDR) && + ((block + num) > OPT_MEDIUM_ERR_ADDR)) { mk_sense_buffer(devip, MEDIUM_ERROR, 0x11, 0, 14); /* claim unrecoverable read error */ - return (DRIVER_SENSE << 24) | (CHECK_CONDITION << 1); + return check_condition_result; } read_lock_irqsave(&atomic_rw, iflags); sgcount = 0; @@ -735,7 +771,7 @@ static int resp_write(struct scsi_cmnd * SCpnt, int upper_blk, int block, if (upper_blk || (block + num > sdebug_capacity)) { mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x21, 0, 14); - return (DRIVER_SENSE << 24) | (CHECK_CONDITION << 1); + return check_condition_result; } write_lock_irqsave(&atomic_rw, iflags); @@ -776,7 +812,7 @@ static int resp_report_luns(unsigned char * cmd, unsigned char * buff, alloc_len = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24); if ((alloc_len < 16) || (select_report > 2)) { mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x24, 0, 14); - return (DRIVER_SENSE << 24) | (CHECK_CONDITION << 1); + return check_condition_result; } if (bufflen > 3) { lun_cnt = min((int)(bufflen / sizeof(ScsiLun)), @@ -810,15 +846,24 @@ static void timer_intr_handler(unsigned long indx) return; } sqcp->in_use = 0; - if (sqcp->done_funct) + if (sqcp->done_funct) { + sqcp->a_cmnd->result = sqcp->scsi_result; sqcp->done_funct(sqcp->a_cmnd); /* callback to mid level */ + } sqcp->done_funct = NULL; spin_unlock_irqrestore(&queued_arr_lock, iflags); } +static int scsi_debug_slave_alloc(struct scsi_device * sdp) +{ + if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) + printk(KERN_INFO "scsi_debug: slave_alloc <%u %u %u %u>\n", + sdp->host->host_no, sdp->channel, sdp->id, sdp->lun); + return 0; +} + static int scsi_debug_slave_configure(struct scsi_device * sdp) { - int k; struct sdebug_dev_info * devip; if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) @@ -826,16 +871,8 @@ static int scsi_debug_slave_configure(struct scsi_device * sdp) sdp->host->host_no, sdp->channel, sdp->id, sdp->lun); if (sdp->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN) sdp->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN; - for (k = 0; k < scsi_debug_num_devs; ++k) { - devip = &devInfop[k]; - if ((devip->channel == sdp->channel) && - (devip->target == sdp->id) && - (devip->lun == sdp->lun) && - (devip->host == sdp->host)) { - sdp->hostdata = devip; - break; - } - } + devip = devInfoReg(sdp); + sdp->hostdata = devip; if (sdp->host->cmd_per_lun) scsi_adjust_queue_depth(sdp, SDEBUG_TAGGED_QUEUING, sdp->host->cmd_per_lun); @@ -852,43 +889,47 @@ static void scsi_debug_slave_destroy(struct scsi_device * sdp) sdp->host->host_no, sdp->channel, sdp->id, sdp->lun); if (devip) { /* make this slot avaliable for re-use */ - memset(devip, 0, sizeof(struct sdebug_dev_info)); + devip->used = 0; sdp->hostdata = NULL; } } -static struct sdebug_dev_info * devInfoReg(struct scsi_cmnd *scmd) +static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev) { - int k; - struct scsi_device * sdp = scmd->device; + struct sdebug_host_info * sdbg_host; + struct sdebug_dev_info * open_devip = NULL; struct sdebug_dev_info * devip = - (struct sdebug_dev_info *)sdp->hostdata; + (struct sdebug_dev_info *)sdev->hostdata; if (devip) return devip; - for (k = 0; k < scsi_debug_num_devs; ++k) { - devip = &devInfop[k]; - if ((devip->channel == scmd->device->channel) && - (devip->target == scmd->device->id) && - (devip->lun == scmd->device->lun) && - (devip->host == scmd->device->host)) - return devip; - } - for (k = 0; k < scsi_debug_num_devs; ++k) { - devip = &devInfop[k]; - if (!devip->used) { - devip->channel = scmd->device->channel; - devip->target = scmd->device->id; - devip->lun = scmd->device->lun; - devip->host = scmd->device->host; - devip->reset = 1; - devip->used = 1; - memset(devip->sense_buff, 0, SDEBUG_SENSE_LEN); - devip->sense_buff[0] = 0x70; - return devip; + sdbg_host = sdebug_shost_to_host_info(sdev->host); + if(! sdbg_host) { + printk(KERN_ERR "Unable to locate host info\n"); + return NULL; + } + list_for_each_entry(devip, &sdbg_host->dev_info_list, dev_list) { + if ((devip->used) && (devip->channel == sdev->channel) && + (devip->target == sdev->id) && + (devip->lun == sdev->lun)) + return devip; + else { + if ((!devip->used) && (!open_devip)) + open_devip = devip; } } - return NULL; + if (open_devip) { + open_devip->channel = sdev->channel; + open_devip->target = sdev->id; + open_devip->lun = sdev->lun; + open_devip->sdbg_host = sdbg_host; + open_devip->reset = 1; + open_devip->used = 1; + memset(open_devip->sense_buff, 0, SDEBUG_SENSE_LEN); + open_devip->sense_buff[0] = 0x70; + return open_devip; + } + return NULL; } static void mk_sense_buffer(struct sdebug_dev_info * devip, int key, @@ -946,7 +987,7 @@ static int scsi_debug_device_reset(struct scsi_cmnd * SCpnt) printk(KERN_INFO "scsi_debug: device_reset\n"); ++num_dev_resets; if (SCpnt) { - devip = devInfoReg(SCpnt); + devip = devInfoReg(SCpnt->device); if (devip) devip->reset = 1; } @@ -955,17 +996,21 @@ static int scsi_debug_device_reset(struct scsi_cmnd * SCpnt) static int scsi_debug_bus_reset(struct scsi_cmnd * SCpnt) { - struct scsi_device * sdp; - struct Scsi_Host * hp; - int k; + struct sdebug_host_info *sdbg_host; + struct sdebug_dev_info * dev_info; + struct scsi_device * sdp; + struct Scsi_Host * hp; if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) printk(KERN_INFO "scsi_debug: bus_reset\n"); ++num_bus_resets; - if (SCpnt && ((sdp = SCpnt->device)) && ((hp = SCpnt->device->host))) { - for (k = 0; k < scsi_debug_num_devs; ++k) { - if (hp == devInfop[k].host) - devInfop[k].reset = 1; + if (SCpnt && ((sdp = SCpnt->device)) && ((hp = sdp->host))) { + sdbg_host = sdebug_shost_to_host_info(hp); + if (sdbg_host) { + list_for_each_entry(dev_info, + &sdbg_host->dev_info_list, + dev_list) + dev_info->reset = 1; } } return SUCCESS; @@ -973,15 +1018,20 @@ static int scsi_debug_bus_reset(struct scsi_cmnd * SCpnt) static int scsi_debug_host_reset(struct scsi_cmnd * SCpnt) { - int k; + struct sdebug_host_info * sdbg_host; + struct sdebug_dev_info * dev_info; if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) printk(KERN_INFO "scsi_debug: host_reset\n"); ++num_host_resets; - for (k = 0; k < scsi_debug_num_devs; ++k) - devInfop[k].reset = 1; + spin_lock(&sdebug_host_list_lock); + list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) { + list_for_each_entry(dev_info, &sdbg_host->dev_info_list, + dev_list) + dev_info->reset = 1; + } + spin_unlock(&sdebug_host_list_lock); stop_all_queued(); - return SUCCESS; } @@ -1063,7 +1113,7 @@ static int schedule_resp(struct scsi_cmnd * cmnd, } if (cmnd && devip) { /* simulate autosense by this driver */ - if (CHECK_CONDITION == status_byte(scsi_result)) + if (SAM_STAT_CHECK_CONDITION == (scsi_result & 0xff)) memcpy(cmnd->sense_buffer, devip->sense_buff, (SCSI_SENSE_BUFFERSIZE > SDEBUG_SENSE_LEN) ? SDEBUG_SENSE_LEN : SCSI_SENSE_BUFFERSIZE); @@ -1099,6 +1149,8 @@ static int schedule_resp(struct scsi_cmnd * cmnd, sqcp->cmnd_timer.expires = jiffies + delta_jiff; add_timer(&sqcp->cmnd_timer); spin_unlock_irqrestore(&queued_arr_lock, iflags); + if (cmnd) + cmnd->result = 0; return 0; } } @@ -1120,7 +1172,7 @@ MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert"); MODULE_DESCRIPTION("SCSI debug adapter driver"); MODULE_LICENSE("GPL"); -MODULE_PARM_DESC(num_devs, "number of SCSI devices to simulate"); +MODULE_PARM_DESC(num_devs, "number of SCSI devices per host to simulate"); MODULE_PARM_DESC(max_luns, "number of SCSI LUNs per target to simulate"); MODULE_PARM_DESC(scsi_level, "SCSI level to simulate"); MODULE_PARM_DESC(dev_size_mb, "size in MB of ram shared by devs"); @@ -1163,7 +1215,7 @@ static int scsi_debug_proc_info(char *buffer, char **start, off_t offset, if (1 != sscanf(arr, "%d", &pos)) return -EINVAL; scsi_debug_opts = pos; - if (SCSI_DEBUG_OPT_EVERY_NTH & scsi_debug_opts) + if (scsi_debug_every_nth > 0) scsi_debug_cmnd_count = 0; return length; } @@ -1192,12 +1244,12 @@ static int scsi_debug_proc_info(char *buffer, char **start, off_t offset, return len; } -static ssize_t sdebug_delay_read(struct device_driver * ddp, char * buf) +static ssize_t sdebug_delay_show(struct device_driver * ddp, char * buf) { - return sprintf(buf, "%d\n", scsi_debug_delay); + return snprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_delay); } -static ssize_t sdebug_delay_write(struct device_driver * ddp, +static ssize_t sdebug_delay_store(struct device_driver * ddp, const char * buf, size_t count) { int delay; @@ -1211,15 +1263,15 @@ static ssize_t sdebug_delay_write(struct device_driver * ddp, } return -EINVAL; } -DRIVER_ATTR(delay, S_IRUGO | S_IWUSR, sdebug_delay_read, - sdebug_delay_write) +DRIVER_ATTR(delay, S_IRUGO | S_IWUSR, sdebug_delay_show, + sdebug_delay_store) -static ssize_t sdebug_opts_read(struct device_driver * ddp, char * buf) +static ssize_t sdebug_opts_show(struct device_driver * ddp, char * buf) { - return sprintf(buf, "0x%x\n", scsi_debug_opts); + return snprintf(buf, PAGE_SIZE, "0x%x\n", scsi_debug_opts); } -static ssize_t sdebug_opts_write(struct device_driver * ddp, +static ssize_t sdebug_opts_store(struct device_driver * ddp, const char * buf, size_t count) { int opts; @@ -1237,50 +1289,76 @@ static ssize_t sdebug_opts_write(struct device_driver * ddp, return -EINVAL; opts_done: scsi_debug_opts = opts; + scsi_debug_cmnd_count = 0; return count; } -DRIVER_ATTR(opts, S_IRUGO | S_IWUSR, sdebug_opts_read, - sdebug_opts_write) +DRIVER_ATTR(opts, S_IRUGO | S_IWUSR, sdebug_opts_show, + sdebug_opts_store) -static ssize_t sdebug_num_devs_read(struct device_driver * ddp, char * buf) +static ssize_t sdebug_num_devs_show(struct device_driver * ddp, char * buf) { - return sprintf(buf, "%d\n", scsi_debug_num_devs); + return snprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_devs); } -DRIVER_ATTR(num_devs, S_IRUGO, sdebug_num_devs_read, NULL) +static ssize_t sdebug_num_devs_store(struct device_driver * ddp, + const char * buf, size_t count) +{ + int n; + + if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) { + scsi_debug_num_devs = n; + return count; + } + return -EINVAL; +} +DRIVER_ATTR(num_devs, S_IRUGO | S_IWUSR, sdebug_num_devs_show, + sdebug_num_devs_store) -static ssize_t sdebug_dev_size_mb_read(struct device_driver * ddp, char * buf) +static ssize_t sdebug_dev_size_mb_show(struct device_driver * ddp, char * buf) { - return sprintf(buf, "%d\n", scsi_debug_dev_size_mb); + return snprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dev_size_mb); } -DRIVER_ATTR(dev_size_mb, S_IRUGO, sdebug_dev_size_mb_read, NULL) +DRIVER_ATTR(dev_size_mb, S_IRUGO, sdebug_dev_size_mb_show, NULL) -static ssize_t sdebug_every_nth_read(struct device_driver * ddp, char * buf) +static ssize_t sdebug_every_nth_show(struct device_driver * ddp, char * buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_every_nth); +} +static ssize_t sdebug_every_nth_store(struct device_driver * ddp, + const char * buf, size_t count) { - return sprintf(buf, "%d\n", scsi_debug_every_nth); + int nth; + + if ((count > 0) && (1 == sscanf(buf, "%d", &nth)) && (nth >= 0)) { + scsi_debug_every_nth = nth; + scsi_debug_cmnd_count = 0; + return count; + } + return -EINVAL; } -DRIVER_ATTR(every_nth, S_IRUGO, sdebug_every_nth_read, NULL) +DRIVER_ATTR(every_nth, S_IRUGO | S_IWUSR, sdebug_every_nth_show, + sdebug_every_nth_store) -static ssize_t sdebug_max_luns_read(struct device_driver * ddp, char * buf) +static ssize_t sdebug_max_luns_show(struct device_driver * ddp, char * buf) { - return sprintf(buf, "%d\n", scsi_debug_max_luns); + return snprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_max_luns); } -DRIVER_ATTR(max_luns, S_IRUGO, sdebug_max_luns_read, NULL) +DRIVER_ATTR(max_luns, S_IRUGO, sdebug_max_luns_show, NULL) -static ssize_t sdebug_scsi_level_read(struct device_driver * ddp, char * buf) +static ssize_t sdebug_scsi_level_show(struct device_driver * ddp, char * buf) { - return sprintf(buf, "%d\n", scsi_debug_scsi_level); + return snprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_scsi_level); } -DRIVER_ATTR(scsi_level, S_IRUGO, sdebug_scsi_level_read, NULL) +DRIVER_ATTR(scsi_level, S_IRUGO, sdebug_scsi_level_show, NULL) -static ssize_t sdebug_add_host_read(struct device_driver * ddp, char * buf) +static ssize_t sdebug_add_host_show(struct device_driver * ddp, char * buf) { - return sprintf(buf, "%d\n", scsi_debug_add_host); + return snprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_add_host); } -static ssize_t sdebug_add_host_write(struct device_driver * ddp, - const char * buf, size_t count) +static ssize_t sdebug_add_host_store(struct device_driver * ddp, + const char * buf, size_t count) { - int delta_hosts, k; + int delta_hosts; char work[20]; if (1 != sscanf(buf, "%10s", work)) @@ -1297,33 +1375,17 @@ static ssize_t sdebug_add_host_write(struct device_driver * ddp, } if (delta_hosts > 0) { do { - for (k = 0; k < MAX_NUM_HOSTS; ++k) { - if (NULL == scsi_debug_hosts[k].shost) { - sdebug_add_shost(k); - break; - } - } - if (k == MAX_NUM_HOSTS) - break; - ++scsi_debug_add_host; + sdebug_add_adapter(); } while (--delta_hosts); } else if (delta_hosts < 0) { do { - for (k = MAX_NUM_HOSTS - 1; k >= 0; --k) { - if (scsi_debug_hosts[k].shost) { - sdebug_remove_shost(k); - break; - } - } - if (k < 0) - break; - --scsi_debug_add_host; + sdebug_remove_adapter(); } while (++delta_hosts); } return count; } -DRIVER_ATTR(add_host, S_IRUGO | S_IWUSR, sdebug_add_host_read, - sdebug_add_host_write) +DRIVER_ATTR(add_host, S_IRUGO | S_IWUSR, sdebug_add_host_show, + sdebug_add_host_store) static void do_create_driverfs_files() { @@ -1349,44 +1411,10 @@ static void do_remove_driverfs_files() driver_remove_file(&sdebug_driverfs_driver, &driver_attr_delay); } -static void sdebug_add_shost(int num) -{ - struct Scsi_Host * hpnt; - int err; - - if (sdebug_add_adapter(num)){ - printk(KERN_ERR "sdebug_add_shost: sdebug_add_adapter failed\n"); - return; - } - hpnt = scsi_register(&sdebug_driver_template, 0); - if (NULL == hpnt) { - sdebug_remove_adapter(num); - printk(KERN_ERR "sdebug_add_shost: scsi_register failed\n"); - return; - } - err = scsi_add_host(hpnt, scsi_debug_hosts[num].dev); - if (err) { - printk(KERN_ERR "sdebug_add_shost: scsi_add_host failed\n"); - scsi_unregister(hpnt); - sdebug_remove_adapter(num); - return; - } - hpnt->max_lun = scsi_debug_max_luns; - - scsi_debug_hosts[num].shost = hpnt; -} - -static void sdebug_remove_shost(int num) -{ - scsi_remove_host(scsi_debug_hosts[num].shost); - scsi_unregister(scsi_debug_hosts[num].shost); - sdebug_remove_adapter(num); - scsi_debug_hosts[num].shost = NULL; -} - static int __init scsi_debug_init(void) { unsigned long sz; + int host_to_add; int k; sdebug_store_size = (unsigned long)scsi_debug_dev_size_mb * 1048576; @@ -1409,30 +1437,10 @@ static int __init scsi_debug_init(void) (sdebug_sectors_per * sdebug_heads); } - if (scsi_debug_num_devs > 0) { - sz = sizeof(struct sdebug_dev_info) * scsi_debug_num_devs; - devInfop = vmalloc(sz); - if (NULL == devInfop) { - printk(KERN_ERR "scsi_debug_init: out of memory\n"); - return -ENOMEM; - } - memset(devInfop, 0, sz); - } - - sz = sizeof(struct sdebug_host_info) * MAX_NUM_HOSTS; - scsi_debug_hosts = vmalloc(sz); - if (NULL == scsi_debug_hosts) { - printk(KERN_ERR "scsi_debug_init: out of memory 1\n"); - return -ENOMEM; - } - memset(scsi_debug_hosts, 0, sz); - sz = sdebug_store_size; fake_storep = vmalloc(sz); if (NULL == fake_storep) { printk(KERN_ERR "scsi_debug_init: out of memory, 1\n"); - if (devInfop) - vfree(devInfop); return -ENOMEM; } memset(fake_storep, 0, sz); @@ -1446,15 +1454,16 @@ static int __init scsi_debug_init(void) sdebug_driver_template.proc_name = (char *)sdebug_proc_name; - for (k = 0; (k < scsi_debug_add_host) && (k < MAX_NUM_HOSTS); k++) { - sdebug_add_shost(k); - if (NULL == scsi_debug_hosts[k].shost) { - printk(KERN_ERR "scsi_debug_init: " - "sdebug_add_shost failed k=%d\n", k); - break; - } - } - scsi_debug_add_host = k; // number of hosts actually present + host_to_add = scsi_debug_add_host; + scsi_debug_add_host = 0; + + for (k = 0; k < host_to_add; k++) { + if (sdebug_add_adapter()) { + printk(KERN_ERR "scsi_debug_init: " + "sdebug_add_adapter failed k=%d\n", k); + break; + } + } if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) { printk(KERN_INFO "scsi_debug: ... built %d host(s)\n", @@ -1465,13 +1474,7 @@ static int __init scsi_debug_init(void) static void __exit scsi_debug_exit(void) { - int k; - - for (k = MAX_NUM_HOSTS - 1; k >= 0; --k) { - if (scsi_debug_hosts[k].shost) { - sdebug_remove_shost(k); - } - } + /* free up adapters here ?? */ stop_all_queued(); do_remove_driverfs_files(); scsi_debug_unregister_driver(&sdebug_driverfs_driver); @@ -1479,8 +1482,6 @@ static void __exit scsi_debug_exit(void) device_unregister(&pseudo_primary); vfree(fake_storep); - if (devInfop) - vfree(devInfop); } device_initcall(scsi_debug_init); @@ -1502,7 +1503,7 @@ static struct bus_type pseudo_lld_bus = { .match = pseudo_lld_bus_match, }; -int scsi_debug_register_driver(struct device_driver *dev_driver) +static int scsi_debug_register_driver(struct device_driver *dev_driver) { dev_driver->bus = &pseudo_lld_bus; driver_register(dev_driver); @@ -1510,36 +1511,168 @@ int scsi_debug_register_driver(struct device_driver *dev_driver) return 0; } -int scsi_debug_unregister_driver(struct device_driver *dev_driver) +static int scsi_debug_unregister_driver(struct device_driver *dev_driver) { driver_unregister(dev_driver); return 0; } -static int sdebug_add_adapter(int num) +static void sdebug_release_adapter(struct device * dev) { - struct device * dev; + kfree(dev); +} - dev = kmalloc(sizeof(*dev),GFP_KERNEL); - if (NULL == dev) { - printk(KERN_ERR "%s: out of memory\n", __FUNCTION__); - return 1; - } +static int sdebug_add_adapter() +{ + struct device * dev; + int error; + + dev = kmalloc(sizeof(*dev),GFP_KERNEL); + if (NULL == dev) { + printk(KERN_ERR "%s: out of memory at line %d\n", + __FUNCTION__, __LINE__); + return -ENOMEM; + } + memset(dev, 0, sizeof(*dev)); - memset(dev, 0, sizeof(*dev)); - dev->bus = &pseudo_lld_bus; - dev->parent = &pseudo_primary; - sprintf(dev->name, "scsi debug adapter"); - sprintf(dev->bus_id, "adapter%d", num); + dev->bus = &pseudo_lld_bus; + dev->parent = &pseudo_primary; + dev->release = &sdebug_release_adapter; + sprintf(dev->name, "scsi debug adapter"); + sprintf(dev->bus_id, "adapter%d", scsi_debug_add_host); - device_register(dev); + error = device_register(dev); - scsi_debug_hosts[num].dev = dev; + if (error) + kfree(dev); + else + ++scsi_debug_add_host; - return 0; + return error; } -static void sdebug_remove_adapter(int num) +static void sdebug_remove_adapter() { - device_unregister(scsi_debug_hosts[num].dev); + struct sdebug_host_info * sdbg_host = NULL; + + spin_lock(&sdebug_host_list_lock); + if (!list_empty(&sdebug_host_list)) + sdbg_host = list_entry(sdebug_host_list.prev, + struct sdebug_host_info, host_list); + spin_unlock(&sdebug_host_list_lock); + + device_unregister(sdbg_host->dev); + --scsi_debug_add_host; +} + +static int sdebug_driver_probe(struct device * dev) +{ + int k; + int error = 0; + struct sdebug_host_info *sdbg_host; + struct sdebug_dev_info *sdbg_devinfo; + struct list_head *lh, *lh_sf; + struct Scsi_Host *hpnt; + + sdbg_host = kmalloc(sizeof(*sdbg_host),GFP_KERNEL); + if (NULL == sdbg_host) { + printk(KERN_ERR "%s: out of memory at line %d\n", + __FUNCTION__, __LINE__); + return -ENOMEM; + } + memset(sdbg_host, 0, sizeof(*sdbg_host)); + + INIT_LIST_HEAD(&sdbg_host->dev_info_list); + + for (k = 0; k < scsi_debug_num_devs; k++) { + sdbg_devinfo = kmalloc(sizeof(*sdbg_devinfo),GFP_KERNEL); + if (NULL == sdbg_devinfo) { + printk(KERN_ERR "%s: out of memory at line %d\n", + __FUNCTION__, __LINE__); + error = -ENOMEM; + } + memset(sdbg_devinfo, 0, sizeof(*sdbg_devinfo)); + sdbg_devinfo->sdbg_host = sdbg_host; + list_add_tail(&sdbg_devinfo->dev_list, + &sdbg_host->dev_info_list); + } + + list_add_tail(&sdbg_host->host_list, &sdebug_host_list); + + hpnt = scsi_register(&sdebug_driver_template, 0); + if (NULL == hpnt) { + printk(KERN_ERR "%s: scsi_register failed\n", __FUNCTION__); + error = -ENODEV; + goto clean1; + } + + sdbg_host->shost = hpnt; + sdbg_host->dev = dev; + hpnt->max_lun = scsi_debug_max_luns; + + error = scsi_add_host(hpnt, sdbg_host->dev); + if (error) { + printk(KERN_ERR "%s: scsi_add_host failed\n", __FUNCTION__); + error = -ENODEV; + goto clean2; + } + + + return 0; + +clean2: + scsi_unregister(hpnt); +clean1: + list_for_each_safe(lh, lh_sf, &sdbg_host->dev_info_list) { + sdbg_devinfo = list_entry(lh, struct sdebug_dev_info, + dev_list); + list_del(&sdbg_devinfo->dev_list); + kfree(sdbg_devinfo); + } + + kfree(sdbg_host); + + return error; +} + +static int sdebug_driver_remove(struct device * dev) +{ + struct list_head *lh, *lh_sf; + struct sdebug_dev_info *sdbg_devinfo; + struct sdebug_host_info *sdbg_host, *found = NULL; + + spin_lock(&sdebug_host_list_lock); + list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) { + if (sdbg_host->dev == dev) { + list_del(&sdbg_host->host_list); + found = sdbg_host; + break; + } + } + spin_unlock(&sdebug_host_list_lock); + + if (!found) { + printk(KERN_ERR "%s: sdebug_host_info not found\n", + __FUNCTION__); + return -ENODEV; + } + + + if (scsi_remove_host(sdbg_host->shost)) { + printk(KERN_ERR "%s: scsi_remove_host failed\n", __FUNCTION__); + return -EBUSY; + } + + scsi_unregister(sdbg_host->shost); + + list_for_each_safe(lh, lh_sf, &sdbg_host->dev_info_list) { + sdbg_devinfo = list_entry(lh, struct sdebug_dev_info, + dev_list); + list_del(&sdbg_devinfo->dev_list); + kfree(sdbg_devinfo); + } + + kfree(sdbg_host); + + return 0; } diff --git a/drivers/scsi/scsi_debug.h b/drivers/scsi/scsi_debug.h index 36d7bbebc9cd..2d9ab66993e2 100644 --- a/drivers/scsi/scsi_debug.h +++ b/drivers/scsi/scsi_debug.h @@ -2,6 +2,7 @@ #include <linux/types.h> +static int scsi_debug_slave_alloc(struct scsi_device *); static int scsi_debug_slave_configure(struct scsi_device *); static void scsi_debug_slave_destroy(struct scsi_device *); static int scsi_debug_queuecommand(struct scsi_cmnd *, @@ -27,8 +28,9 @@ static Scsi_Host_Template sdebug_driver_template = { .proc_info = scsi_debug_proc_info, .name = "SCSI DEBUG", .info = scsi_debug_info, + .slave_alloc = scsi_debug_slave_alloc, .slave_configure = scsi_debug_slave_configure, - .slave_destroy = scsi_debug_slave_destroy, + .slave_destroy = scsi_debug_slave_destroy, .ioctl = scsi_debug_ioctl, .queuecommand = scsi_debug_queuecommand, .eh_abort_handler = scsi_debug_abort, diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index eec12d6a61ba..b9c826ed12eb 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -597,28 +597,6 @@ static int scsi_request_sense(struct scsi_cmnd *scmd) } /** - * scsi_eh_retry_cmd - Retry the original command - * @scmd: Original failed SCSI cmd. - * - * Notes: - * This function will *not* return until the command either times out, - * or it completes. - **/ -static int scsi_eh_retry_cmd(struct scsi_cmnd *scmd) -{ - int rtn = SUCCESS; - - for (; scmd->retries < scmd->allowed; scmd->retries++) { - scsi_setup_cmd_retry(scmd); - rtn = scsi_send_eh_cmnd(scmd, scmd->timeout_per_command); - if (rtn != NEEDS_RETRY) - break; - } - - return rtn; -} - -/** * scsi_eh_finish_cmd - Handle a cmd that eh is finished with. * @scmd: Original SCSI cmd that eh has finished. * @done_q: Queue for processed commands. diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 5ee88b47014b..a06341945483 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -125,7 +125,6 @@ int scsi_queue_insert(struct scsi_cmnd *cmd, int reason) */ cmd->state = SCSI_STATE_MLQUEUE; cmd->owner = SCSI_OWNER_MIDLEVEL; - cmd->bh_next = NULL; /* * Decrement the counters, since these commands are no longer diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 185fec2e14c8..20115e69dc05 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -385,83 +385,92 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost, struct scsi_device *sdev, *device; sdev = kmalloc(sizeof(*sdev), GFP_ATOMIC); - if (sdev != NULL) { - memset(sdev, 0, sizeof(Scsi_Device)); - sdev->vendor = scsi_null_device_strs; - sdev->model = scsi_null_device_strs; - sdev->rev = scsi_null_device_strs; - sdev->host = shost; - sdev->id = id; - sdev->lun = lun; - sdev->channel = channel; - sdev->online = TRUE; - INIT_LIST_HEAD(&sdev->siblings); - INIT_LIST_HEAD(&sdev->same_target_siblings); - INIT_LIST_HEAD(&sdev->cmd_list); - spin_lock_init(&sdev->list_lock); - /* - * Some low level driver could use device->type - */ - sdev->type = -1; - /* - * Assume that the device will have handshaking problems, - * and then fix this field later if it turns out it - * doesn't - */ - sdev->borken = 1; + if (!sdev) + goto out; - if (!q || *q == NULL) { - sdev->request_queue = scsi_alloc_queue(shost); - if (!sdev->request_queue) - goto out_bail; - } else { - sdev->request_queue = *q; - *q = NULL; - } + memset(sdev, 0, sizeof(*sdev)); + sdev->vendor = scsi_null_device_strs; + sdev->model = scsi_null_device_strs; + sdev->rev = scsi_null_device_strs; + sdev->host = shost; + sdev->id = id; + sdev->lun = lun; + sdev->channel = channel; + sdev->online = TRUE; + INIT_LIST_HEAD(&sdev->siblings); + INIT_LIST_HEAD(&sdev->same_target_siblings); + INIT_LIST_HEAD(&sdev->cmd_list); + spin_lock_init(&sdev->list_lock); - sdev->request_queue->queuedata = sdev; - scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun); - init_waitqueue_head(&sdev->scpnt_wait); + /* + * Some low level driver could use device->type + */ + sdev->type = -1; - if (shost->hostt->slave_alloc) - if (shost->hostt->slave_alloc(sdev)) { - goto out_bail; - } - /* - * If there are any same target siblings, add this to the - * sibling list - */ - list_for_each_entry(device, &shost->my_devices, siblings) { - if(device->id == sdev->id && - device->channel == sdev->channel) { - list_add_tail(&sdev->same_target_siblings, - &device->same_target_siblings); - sdev->scsi_level = device->scsi_level; - break; - } + /* + * Assume that the device will have handshaking problems, + * and then fix this field later if it turns out it + * doesn't + */ + sdev->borken = 1; + + if (!q || *q == NULL) { + sdev->request_queue = scsi_alloc_queue(shost); + if (!sdev->request_queue) + goto out_free_dev; + } else { + sdev->request_queue = *q; + *q = NULL; + } + + sdev->request_queue->queuedata = sdev; + scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun); + init_waitqueue_head(&sdev->scpnt_wait); + + if (shost->hostt->slave_alloc) { + if (shost->hostt->slave_alloc(sdev)) + goto out_free_queue; + } + + /* + * If there are any same target siblings, add this to the + * sibling list + */ + list_for_each_entry(device, &shost->my_devices, siblings) { + if (device->id == sdev->id && + device->channel == sdev->channel) { + list_add_tail(&sdev->same_target_siblings, + &device->same_target_siblings); + sdev->scsi_level = device->scsi_level; + break; } - /* - * If there wasn't another lun already configured at this - * target, then default this device to SCSI_2 until we - * know better - */ - if(!sdev->scsi_level) - sdev->scsi_level = SCSI_2; - /* - * Add it to the end of the shost->my_devices list. - */ - list_add_tail(&sdev->siblings, &shost->my_devices); - return (sdev); } -out_bail: - printk(ALLOC_FAILURE_MSG, __FUNCTION__); + + /* + * If there wasn't another lun already configured at this + * target, then default this device to SCSI_2 until we + * know better + */ + if (!sdev->scsi_level) + sdev->scsi_level = SCSI_2; + + /* + * Add it to the end of the shost->my_devices list. + */ + list_add_tail(&sdev->siblings, &shost->my_devices); + return sdev; + +out_free_queue: if (q && sdev->request_queue) { *q = sdev->request_queue; sdev->request_queue = NULL; } else if (sdev->request_queue) scsi_free_queue(sdev->request_queue); +out_free_dev: kfree(sdev); +out: + printk(ALLOC_FAILURE_MSG, __FUNCTION__); return NULL; } diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index eade9fd9b026..4ecaa390ebdc 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -266,6 +266,25 @@ sdev_rd_attr (model, "%.16s\n"); sdev_rd_attr (rev, "%.4s\n"); sdev_rw_attr_bit (online); +static ssize_t +show_rescan_field (struct device *dev, char *buf) +{ + return 0; +} + +static ssize_t +store_rescan_field (struct device *dev, const char *buf, size_t count) +{ + int ret = ENODEV; + struct scsi_device *sdev; + sdev = to_scsi_device(dev); + if (sdev) + ret = scsi_rescan_device(sdev); + return ret; +} + +static DEVICE_ATTR(rescan, S_IRUGO | S_IWUSR, show_rescan_field, store_rescan_field) + static struct device_attribute * const sdev_attrs[] = { &dev_attr_device_blocked, &dev_attr_queue_depth, @@ -276,6 +295,7 @@ static struct device_attribute * const sdev_attrs[] = { &dev_attr_model, &dev_attr_rev, &dev_attr_online, + &dev_attr_rescan, }; /** diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 12d4a6839639..eadc8315551a 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -93,10 +93,12 @@ static void sd_rw_intr(struct scsi_cmnd * SCpnt); static int sd_attach(struct scsi_device *); static void sd_detach(struct scsi_device *); +static void sd_rescan(struct scsi_device *); static int sd_init_command(struct scsi_cmnd *); static int sd_synchronize_cache(struct scsi_disk *, int); static int sd_notifier(struct notifier_block *, unsigned long, void *); - +static void sd_read_capacity(struct scsi_disk *sdkp, char *diskname, + struct scsi_request *SRpnt, unsigned char *buffer); static struct notifier_block sd_notifier_block = {sd_notifier, NULL, 0}; static struct Scsi_Device_Template sd_template = { @@ -106,6 +108,7 @@ static struct Scsi_Device_Template sd_template = { .scsi_type = TYPE_DISK, .attach = sd_attach, .detach = sd_detach, + .rescan = sd_rescan, .init_command = sd_init_command, .scsi_driverfs_driver = { .name = "sd", @@ -629,6 +632,38 @@ not_present: return 1; } +static void sd_rescan(struct scsi_device * sdp) +{ + unsigned char *buffer; + struct scsi_disk *sdkp = sd_find_by_sdev(sdp); + struct gendisk *gd; + struct scsi_request *SRpnt; + + if (!sdkp || sdp->online == FALSE || !sdkp->media_present) + return; + + gd = sdkp->disk; + + SCSI_LOG_HLQUEUE(3, printk("sd_rescan: disk=%s\n", gd->disk_name)); + + SRpnt = scsi_allocate_request(sdp); + if (!SRpnt) { + printk(KERN_WARNING "(sd_rescan:) Request allocation " + "failure.\n"); + return; + } + + if (sdkp->device->host->unchecked_isa_dma) + buffer = kmalloc(512, GFP_DMA); + else + buffer = kmalloc(512, GFP_KERNEL); + + sd_read_capacity(sdkp, gd->disk_name, SRpnt, buffer); + set_capacity(gd, sdkp->capacity); + scsi_release_request(SRpnt); + kfree(buffer); +} + static int sd_revalidate_disk(struct gendisk *disk) { struct scsi_disk *sdkp = scsi_disk(disk); diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 91dace681ba1..bd6d50025126 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -18,10 +18,8 @@ * */ #include <linux/config.h> -#ifdef CONFIG_PROC_FS -static char *sg_version_str = "Version: 3.5.27 (20030130)"; -#endif -static int sg_version_num = 30527; /* 2 digits for each component */ +static char *sg_version_str = "3.5.28 [20030308]"; +static int sg_version_num = 30528; /* 2 digits for each component */ /* * D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au), notes: * - scsi logging is available via SCSI_LOG_TIMEOUT macros. First @@ -56,6 +54,7 @@ static int sg_version_num = 30527; /* 2 digits for each component */ #include <linux/poll.h> #include <linux/vmalloc.h> #include <linux/smp_lock.h> +#include <linux/moduleparam.h> #include <asm/io.h> #include <asm/uaccess.h> @@ -1327,27 +1326,6 @@ static struct file_operations sg_fops = { .fasync = sg_fasync, }; -#ifndef MODULE -static int __init -sg_def_reserved_size_setup(char *str) -{ - int tmp; - - if (get_option(&str, &tmp) == 1) { - def_reserved_size = tmp; - if (tmp >= 0) - sg_big_buff = tmp; - return 1; - } else { - printk(KERN_WARNING "sg_def_reserved_size : usage " - "sg_def_reserved_size=n (n could be 65536, 131072 or 262144)\n"); - return 0; - } -} - -__setup("sg_def_reserved_size=", sg_def_reserved_size_setup); -#endif - /* Driverfs file support */ static ssize_t sg_device_kdev_read(struct device *driverfs_dev, char *page) @@ -1564,16 +1542,77 @@ sg_detach(Scsi_Device * scsidp) scsi_sleep(2); /* dirty detach so delay device destruction */ } +/* Set 'perm' (4th argument) to 0 to disable module_param's definition + * of sysfs parameters (which module_param doesn't yet support). + * Sysfs parameters defined explicitly below. + */ +module_param_named(def_reserved_size, def_reserved_size, int, 0); +module_param_named(allow_dio, sg_allow_dio, int, 0); + MODULE_AUTHOR("Douglas Gilbert"); MODULE_DESCRIPTION("SCSI generic (sg) driver"); - -#ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); -#endif -MODULE_PARM(def_reserved_size, "i"); MODULE_PARM_DESC(def_reserved_size, "size of buffer reserved for each fd"); +static ssize_t sg_allow_dio_show(struct device_driver * ddp, char * buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", sg_allow_dio); +} +static ssize_t sg_allow_dio_store(struct device_driver * ddp, + const char * buf, size_t count) +{ + if (1 == sscanf(buf, "%d", &sg_allow_dio)) { + sg_allow_dio = sg_allow_dio ? 1 : 0; + return count; + } + return -EINVAL; +} +DRIVER_ATTR(allow_dio, S_IRUGO | S_IWUSR, sg_allow_dio_show, + sg_allow_dio_store) + +static ssize_t sg_def_reserved_show(struct device_driver * ddp, char * buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", sg_big_buff); +} +static ssize_t sg_def_reserved_store(struct device_driver * ddp, + const char * buf, size_t count) +{ + if (1 == sscanf(buf, "%d", &def_reserved_size)) { + if (def_reserved_size >= 0) { + sg_big_buff = def_reserved_size; + return count; + } + } + return -EINVAL; +} +DRIVER_ATTR(def_reserved_size, S_IRUGO | S_IWUSR, sg_def_reserved_show, + sg_def_reserved_store) + +static ssize_t sg_version_show(struct device_driver * ddp, char * buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", sg_version_str); +} +DRIVER_ATTR(version, S_IRUGO, sg_version_show, NULL) + +static void do_create_driverfs_files(void) +{ + struct device_driver * driverfs = &sg_template.scsi_driverfs_driver; + + driver_create_file(driverfs, &driver_attr_allow_dio); + driver_create_file(driverfs, &driver_attr_def_reserved_size); + driver_create_file(driverfs, &driver_attr_version); +} + +static void do_remove_driverfs_files(void) +{ + struct device_driver * driverfs = &sg_template.scsi_driverfs_driver; + + driver_remove_file(driverfs, &driver_attr_version); + driver_remove_file(driverfs, &driver_attr_def_reserved_size); + driver_remove_file(driverfs, &driver_attr_allow_dio); +} + static int __init init_sg(void) { @@ -1591,12 +1630,14 @@ init_sg(void) #ifdef CONFIG_PROC_FS sg_proc_init(); #endif /* CONFIG_PROC_FS */ + do_create_driverfs_files(); return 0; } static void __exit exit_sg(void) { + do_remove_driverfs_files(); #ifdef CONFIG_PROC_FS sg_proc_cleanup(); #endif /* CONFIG_PROC_FS */ @@ -2656,10 +2697,6 @@ sg_get_dev(int dev) static struct proc_dir_entry *sg_proc_sgp = NULL; static char sg_proc_sg_dirname[] = "sg"; -static const char *sg_proc_leaf_names[] = { "allow_dio", "def_reserved_size", - "debug", "devices", "device_hdr", "device_strs", - "hosts", "host_hdr", "host_strs", "version" -}; static int sg_proc_adio_read(char *buffer, char **start, off_t offset, int size, int *eof, void *data); @@ -2693,13 +2730,21 @@ static int sg_proc_version_read(char *buffer, char **start, off_t offset, int size, int *eof, void *data); static int sg_proc_version_info(char *buffer, int *len, off_t * begin, off_t offset, int size); -static read_proc_t *sg_proc_leaf_reads[] = { - sg_proc_adio_read, sg_proc_dressz_read, sg_proc_debug_read, - sg_proc_dev_read, sg_proc_devhdr_read, sg_proc_devstrs_read, - sg_proc_version_read + +struct sg_proc_leaf { + const char * name; + read_proc_t * rf; + write_proc_t * wf; }; -static write_proc_t *sg_proc_leaf_writes[] = { - sg_proc_adio_write, sg_proc_dressz_write, 0, 0, 0, 0, 0, 0, 0, 0 + +static struct sg_proc_leaf sg_proc_leaf_arr[] = { + {"allow_dio", sg_proc_adio_read, sg_proc_adio_write}, + {"def_reserved_size", sg_proc_dressz_read, sg_proc_dressz_write}, + {"debug", sg_proc_debug_read, NULL}, + {"devices", sg_proc_dev_read, NULL}, + {"device_hdr", sg_proc_devhdr_read, NULL}, + {"device_strs", sg_proc_devstrs_read, NULL}, + {"version", sg_proc_version_read, NULL} }; #define PRINT_PROC(fmt,args...) \ @@ -2729,9 +2774,10 @@ static int sg_proc_init() { int k, mask; - int leaves = - sizeof (sg_proc_leaf_names) / sizeof (sg_proc_leaf_names[0]); + int num_leaves = + sizeof (sg_proc_leaf_arr) / sizeof (sg_proc_leaf_arr[0]); struct proc_dir_entry *pdep; + struct sg_proc_leaf * leaf; if (!proc_scsi) return 1; @@ -2739,14 +2785,14 @@ sg_proc_init() S_IFDIR | S_IRUGO | S_IXUGO, proc_scsi); if (!sg_proc_sgp) return 1; - for (k = 0; k < leaves; ++k) { - mask = sg_proc_leaf_writes[k] ? S_IRUGO | S_IWUSR : S_IRUGO; - pdep = - create_proc_entry(sg_proc_leaf_names[k], mask, sg_proc_sgp); + for (k = 0; k < num_leaves; ++k) { + leaf = &sg_proc_leaf_arr[k]; + mask = leaf->wf ? S_IRUGO | S_IWUSR : S_IRUGO; + pdep = create_proc_entry(leaf->name, mask, sg_proc_sgp); if (pdep) { - pdep->read_proc = sg_proc_leaf_reads[k]; - if (sg_proc_leaf_writes[k]) - pdep->write_proc = sg_proc_leaf_writes[k]; + pdep->read_proc = leaf->rf; + if (leaf->wf) + pdep->write_proc = leaf->wf; } } return 0; @@ -2756,13 +2802,13 @@ static void sg_proc_cleanup() { int k; - int leaves = - sizeof (sg_proc_leaf_names) / sizeof (sg_proc_leaf_names[0]); + int num_leaves = + sizeof (sg_proc_leaf_arr) / sizeof (sg_proc_leaf_arr[0]); if ((!proc_scsi) || (!sg_proc_sgp)) return; - for (k = 0; k < leaves; ++k) - remove_proc_entry(sg_proc_leaf_names[k], sg_proc_sgp); + for (k = 0; k < num_leaves; ++k) + remove_proc_entry(sg_proc_leaf_arr[k].name, sg_proc_sgp); remove_proc_entry(sg_proc_sg_dirname, proc_scsi); } diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index d0949c6b6a1e..09e0165f45a9 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -93,13 +93,15 @@ unsigned int share_irqs = SERIAL8250_SHARE_IRQS; #ifdef CONFIG_SERIAL_8250_MULTIPORT #define CONFIG_SERIAL_MULTIPORT 1 #endif +#ifdef CONFIG_SERIAL_8250_MANY_PORTS +#define CONFIG_SERIAL_MANY_PORTS 1 +#endif /* * HUB6 is always on. This will be removed once the header * files have been cleaned. */ #define CONFIG_HUB6 1 -#define CONFIG_SERIAL_MANY_PORTS 1 #include <asm/serial.h> @@ -2095,6 +2097,28 @@ void serial8250_get_irq_map(unsigned int *map) } } +/** + * serial8250_suspend_port - suspend one serial port + * @line: serial line number + * + * Suspend one serial port. + */ +void serial8250_suspend_port(int line, u32 level) +{ + uart_suspend_port(&serial8250_reg, &serial8250_ports[line].port, level); +} + +/** + * serial8250_resume_port - resume one serial port + * @line: serial line number + * + * Resume one serial port. + */ +void serial8250_resume_port(int line, u32 level) +{ + uart_resume_port(&serial8250_reg, &serial8250_ports[line].port, level); +} + static int __init serial8250_init(void) { int ret, i; @@ -2128,6 +2152,8 @@ module_exit(serial8250_exit); EXPORT_SYMBOL(register_serial); EXPORT_SYMBOL(unregister_serial); EXPORT_SYMBOL(serial8250_get_irq_map); +EXPORT_SYMBOL(serial8250_suspend_port); +EXPORT_SYMBOL(serial8250_resume_port); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Generic 8250/16x50 serial driver $Revision: 1.90 $"); diff --git a/drivers/serial/8250.h b/drivers/serial/8250.h index 030116e6ebf6..cc206f9476ac 100644 --- a/drivers/serial/8250.h +++ b/drivers/serial/8250.h @@ -27,6 +27,8 @@ struct serial8250_probe { int serial8250_register_probe(struct serial8250_probe *probe); void serial8250_unregister_probe(struct serial8250_probe *probe); void serial8250_get_irq_map(unsigned int *map); +void serial8250_suspend_port(int line, u32 level); +void serial8250_resume_port(int line, u32 level); struct old_serial_port { unsigned int uart; diff --git a/drivers/serial/8250_acorn.c b/drivers/serial/8250_acorn.c index 0065a31a418a..db4666f894e5 100644 --- a/drivers/serial/8250_acorn.c +++ b/drivers/serial/8250_acorn.c @@ -1,7 +1,7 @@ /* * linux/drivers/serial/acorn.c * - * Copyright (C) 1996-2002 Russell King. + * Copyright (C) 1996-2003 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 @@ -9,6 +9,8 @@ */ #include <linux/module.h> #include <linux/types.h> +#include <linux/tty.h> +#include <linux/serial_core.h> #include <linux/serial.h> #include <linux/errno.h> #include <linux/ioport.h> @@ -16,6 +18,7 @@ #include <linux/device.h> #include <linux/init.h> +#include <asm/io.h> #include <asm/ecard.h> #include <asm/string.h> @@ -24,36 +27,41 @@ struct serial_card_type { unsigned int num_ports; unsigned int baud_base; - int type; - int speed; - int offset[MAX_PORTS]; + unsigned int type; + unsigned int offset[MAX_PORTS]; }; struct serial_card_info { unsigned int num_ports; int ports[MAX_PORTS]; - unsigned long base[MAX_PORTS]; }; -static inline int serial_register_onedev(unsigned long port, int irq, unsigned int baud_base) +static inline int +serial_register_onedev(unsigned long baddr, void *vaddr, int irq, unsigned int baud_base) { struct serial_struct req; memset(&req, 0, sizeof(req)); - req.baud_base = baud_base; - req.irq = irq; - req.port = port; - req.flags = 0; + req.irq = irq; + req.flags = UPF_AUTOPROBE | UPF_RESOURCES | + UPF_SHARE_IRQ; + req.baud_base = baud_base; + req.io_type = UPIO_MEM; + req.iomem_base = vaddr; + req.iomem_reg_shift = 2; + req.iomap_base = baddr; return register_serial(&req); } -static int __devinit serial_card_probe(struct expansion_card *ec, const struct ecard_id *id) +static int __devinit +serial_card_probe(struct expansion_card *ec, const struct ecard_id *id) { struct serial_card_info *info; struct serial_card_type *type = id->data; - unsigned long cardaddr, address; - int port; + unsigned long bus_addr; + unsigned char *virt_addr; + unsigned int port; info = kmalloc(sizeof(struct serial_card_info), GFP_KERNEL); if (!info) @@ -64,20 +72,19 @@ static int __devinit serial_card_probe(struct expansion_card *ec, const struct e ecard_set_drvdata(ec, info); - cardaddr = ecard_address(ec, type->type, type->speed); + bus_addr = ec->resource[type->type].start; + virt_addr = ioremap(bus_addr, ec->resource[type->type].end - bus_addr + 1); + if (!virt_addr) { + kfree(info); + return -ENOMEM; + } for (port = 0; port < info->num_ports; port ++) { - address = cardaddr + type->offset[port]; - - info->ports[port] = -1; - info->base[port] = address; + unsigned long baddr = bus_addr + type->offset[port]; + unsigned char *vaddr = virt_addr + type->offset[port]; - if (!request_region(address, 8, "acornserial")) - continue; - - info->ports[port] = serial_register_onedev(address, ec->irq, type->baud_base); - if (info->ports[port] < 0) - break; + info->ports[port] = serial_register_onedev(baddr, vaddr, + ec->irq, type->baud_base); } return 0; @@ -90,12 +97,9 @@ static void __devexit serial_card_remove(struct expansion_card *ec) ecard_set_drvdata(ec, NULL); - for (i = 0; i < info->num_ports; i++) { - if (info->ports[i] > 0) { + for (i = 0; i < info->num_ports; i++) + if (info->ports[i] > 0) unregister_serial(info->ports[i]); - release_region(info->base[i], 8); - } - } kfree(info); } @@ -103,17 +107,15 @@ static void __devexit serial_card_remove(struct expansion_card *ec) static struct serial_card_type atomwide_type = { .num_ports = 3, .baud_base = 7372800 / 16, - .type = ECARD_IOC, - .speed = ECARD_SLOW, - .offset = { 0xa00, 0x900, 0x800 }, + .type = ECARD_RES_IOCSLOW, + .offset = { 0x2800, 0x2400, 0x2000 }, }; static struct serial_card_type serport_type = { .num_ports = 2, .baud_base = 3686400 / 16, - .type = ECARD_IOC, - .speed = ECARD_SLOW, - .offset = { 0x800, 0x808 }, + .type = ECARD_RES_IOCSLOW, + .offset = { 0x2000, 0x2020 }, }; static const struct ecard_id serial_cids[] = { @@ -127,7 +129,8 @@ static struct ecard_driver serial_card_driver = { .remove = __devexit_p(serial_card_remove), .id_table = serial_cids, .drv = { - .name = "acornserial", + .devclass = &tty_devclass, + .name = "8250_acorn", }, }; @@ -142,6 +145,7 @@ static void __exit serial_card_exit(void) } MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("Acorn 8250-compatible serial port expansion card driver"); MODULE_LICENSE("GPL"); module_init(serial_card_init); diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c index 240b79df5794..1123ebd9fe30 100644 --- a/drivers/serial/8250_pci.c +++ b/drivers/serial/8250_pci.c @@ -20,8 +20,9 @@ #include <linux/string.h> #include <linux/kernel.h> #include <linux/slab.h> +#include <linux/tty.h> #include <linux/serial.h> -#include <linux/serialP.h> +#include <linux/serial_core.h> #include <asm/bitops.h> #include <asm/byteorder.h> @@ -30,174 +31,213 @@ #include "8250.h" +/* + * Definitions for PCI support. + */ +#define FL_BASE_MASK 0x0007 +#define FL_BASE0 0x0000 +#define FL_BASE1 0x0001 +#define FL_BASE2 0x0002 +#define FL_BASE3 0x0003 +#define FL_BASE4 0x0004 +#define FL_GET_BASE(x) (x & FL_BASE_MASK) + +#define FL_IRQ_MASK (0x0007 << 4) +#define FL_IRQBASE0 (0x0000 << 4) +#define FL_IRQBASE1 (0x0001 << 4) +#define FL_IRQBASE2 (0x0002 << 4) +#define FL_IRQBASE3 (0x0003 << 4) +#define FL_IRQBASE4 (0x0004 << 4) +#define FL_GET_IRQBASE(x) ((x & FL_IRQ_MASK) >> 4) + +/* Use successive BARs (PCI base address registers), + else use offset into some specified BAR */ +#define FL_BASE_BARS 0x0008 + +/* Use the irq resource table instead of dev->irq */ +#define FL_IRQRESOURCE 0x0080 + +/* Use the Base address register size to cap number of ports */ +#define FL_REGION_SZ_CAP 0x0100 -#ifndef IS_PCI_REGION_IOPORT -#define IS_PCI_REGION_IOPORT(dev, r) (pci_resource_flags((dev), (r)) & \ - IORESOURCE_IO) -#endif -#ifndef IS_PCI_REGION_IOMEM -#define IS_PCI_REGION_IOMEM(dev, r) (pci_resource_flags((dev), (r)) & \ - IORESOURCE_MEM) -#endif -#ifndef PCI_IRQ_RESOURCE -#define PCI_IRQ_RESOURCE(dev, r) ((dev)->irq_resource[r].start) -#endif - -#ifndef pci_get_subvendor -#define pci_get_subvendor(dev) ((dev)->subsystem_vendor) -#define pci_get_subdevice(dev) ((dev)->subsystem_device) -#endif - -struct serial_private { - unsigned int nr; - struct pci_board *board; - int line[0]; +struct pci_board { + unsigned int flags; + unsigned int num_ports; + unsigned int base_baud; + unsigned int uart_offset; + unsigned int reg_shift; + unsigned int first_offset; }; /* - * init_fn returns: + * init function returns: * > 0 - number of ports * = 0 - use board->num_ports * < 0 - error */ -struct pci_board { - int flags; - int num_ports; - int base_baud; - int uart_offset; - int reg_shift; - int (*init_fn)(struct pci_dev *dev, int enable); - int first_uart_offset; +struct pci_serial_quirk { + u32 vendor; + u32 device; + u32 subvendor; + u32 subdevice; + int (*init)(struct pci_dev *dev); + int (*setup)(struct pci_dev *dev, struct pci_board *board, + struct serial_struct *req, int idx); + void (*exit)(struct pci_dev *dev); +}; + +#define PCI_NUM_BAR_RESOURCES 6 + +struct serial_private { + unsigned int nr; + void *remapped_bar[PCI_NUM_BAR_RESOURCES]; + struct pci_serial_quirk *quirk; + int line[0]; }; +static void moan_device(const char *str, struct pci_dev *dev) +{ + printk(KERN_WARNING "%s: %s\n" + KERN_WARNING "Please send the output of lspci -vv, this\n" + KERN_WARNING "message (0x%04x,0x%04x,0x%04x,0x%04x), the\n" + KERN_WARNING "manufacturer and name of serial board or\n" + KERN_WARNING "modem board to rmk+serial@arm.linux.org.uk.\n", + dev->slot_name, str, dev->vendor, dev->device, + dev->subsystem_vendor, dev->subsystem_device); +} + static int -get_pci_port(struct pci_dev *dev, struct pci_board *board, - struct serial_struct *req, int idx) +setup_port(struct pci_dev *dev, struct serial_struct *req, + int bar, int offset, int regshift) { - unsigned long port; - int base_idx; - int max_port; - int offset; + struct serial_private *priv = pci_get_drvdata(dev); + unsigned long port, len; - base_idx = SPCI_FL_GET_BASE(board->flags); - if (board->flags & SPCI_FL_BASE_TABLE) - base_idx += idx; + if (bar >= PCI_NUM_BAR_RESOURCES) + return -EINVAL; - if (board->flags & SPCI_FL_REGION_SZ_CAP) { - max_port = pci_resource_len(dev, base_idx) / 8; - if (idx >= max_port) - return 1; - } - - offset = board->first_uart_offset; + if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) { + port = pci_resource_start(dev, bar); + len = pci_resource_len(dev, bar); - /* - * Timedia/SUNIX uses a mixture of BARs and offsets - * Ugh, this is ugly as all hell --- TYT - */ - if (dev->vendor == PCI_VENDOR_ID_TIMEDIA) - switch(idx) { - case 0: - base_idx = 0; - break; - case 1: - base_idx = 0; - offset = 8; - break; - case 2: - base_idx = 1; - break; - case 3: - base_idx = 1; - offset = 8; - break; - case 4: /* BAR 2 */ - case 5: /* BAR 3 */ - case 6: /* BAR 4 */ - case 7: /* BAR 5 */ - base_idx = idx - 2; - } + if (!priv->remapped_bar[bar]) + priv->remapped_bar[bar] = ioremap(port, len); + if (!priv->remapped_bar[bar]) + return -ENOMEM; - /* AFAVLAB uses a different mixture of BARs and offsets */ - /* Not that ugly ;) -- HW */ - if (dev->vendor == PCI_VENDOR_ID_AFAVLAB && idx >= 4) { - base_idx = 4; - offset = (idx - 4) * 8; + req->io_type = UPIO_MEM; + req->iomap_base = port; + req->iomem_base = priv->remapped_bar[bar] + offset; + req->iomem_reg_shift = regshift; + } else { + port = pci_resource_start(dev, bar) + offset; + req->io_type = UPIO_PORT; + req->port = port; + if (HIGH_BITS_OFFSET) + req->port_high = port >> HIGH_BITS_OFFSET; } + return 0; +} - /* Some Titan cards are also a little weird */ - if (dev->vendor == PCI_VENDOR_ID_TITAN && - (dev->device == PCI_DEVICE_ID_TITAN_400L || - dev->device == PCI_DEVICE_ID_TITAN_800L)) { - switch (idx) { - case 0: base_idx = 1; - break; - case 1: base_idx = 2; - break; - default: - base_idx = 4; - offset = 8 * (idx - 2); - } - } +/* + * AFAVLAB uses a different mixture of BARs and offsets + * Not that ugly ;) -- HW + */ +static int +afavlab_setup(struct pci_dev *dev, struct pci_board *board, + struct serial_struct *req, int idx) +{ + unsigned int bar, offset = board->first_offset; + + bar = FL_GET_BASE(board->flags); + if (idx < 4) + bar += idx; + else + offset += (idx - 4) * board->uart_offset; - /* HP's Diva chip puts the 4th/5th serial port further out, and - * some serial ports are supposed to be hidden on certain models. - */ - if (dev->vendor == PCI_VENDOR_ID_HP && - dev->device == PCI_DEVICE_ID_HP_DIVA) { - switch (dev->subsystem_device) { - case PCI_DEVICE_ID_HP_DIVA_MAESTRO: - if (idx == 3) - idx++; - break; - case PCI_DEVICE_ID_HP_DIVA_EVEREST: - if (idx > 0) - idx++; - if (idx > 2) - idx++; - break; - } - if (idx > 2) { - offset = 0x18; - } + return setup_port(dev, req, bar, offset, board->reg_shift); +} + +/* + * HP's Remote Management Console. The Diva chip came in several + * different versions. N-class, L2000 and A500 have two Diva chips, each + * with 3 UARTs (the third UART on the second chip is unused). Superdome + * and Keystone have one Diva chip with 3 UARTs. Some later machines have + * one Diva chip, but it has been expanded to 5 UARTs. + */ +static int __devinit pci_hp_diva_init(struct pci_dev *dev) +{ + int rc = 0; + + switch (dev->subsystem_device) { + case PCI_DEVICE_ID_HP_DIVA_TOSCA1: + case PCI_DEVICE_ID_HP_DIVA_HALFDOME: + case PCI_DEVICE_ID_HP_DIVA_KEYSTONE: + case PCI_DEVICE_ID_HP_DIVA_EVEREST: + rc = 3; + break; + case PCI_DEVICE_ID_HP_DIVA_TOSCA2: + rc = 2; + break; + case PCI_DEVICE_ID_HP_DIVA_MAESTRO: + rc = 4; + break; + case PCI_DEVICE_ID_HP_DIVA_POWERBAR: + rc = 1; + break; } - port = pci_resource_start(dev, base_idx) + offset; + return rc; +} - if ((board->flags & SPCI_FL_BASE_TABLE) == 0) - port += idx * (board->uart_offset ? board->uart_offset : 8); +/* + * HP's Diva chip puts the 4th/5th serial port further out, and + * some serial ports are supposed to be hidden on certain models. + */ +static int +pci_hp_diva_setup(struct pci_dev *dev, struct pci_board *board, + struct serial_struct *req, int idx) +{ + unsigned int offset = board->first_offset; + unsigned int bar = FL_GET_BASE(board->flags); - if (IS_PCI_REGION_IOPORT(dev, base_idx)) { - req->port = port; - if (HIGH_BITS_OFFSET) - req->port_high = port >> HIGH_BITS_OFFSET; - else - req->port_high = 0; - return 0; + switch (dev->subsystem_device) { + case PCI_DEVICE_ID_HP_DIVA_MAESTRO: + if (idx == 3) + idx++; + break; + case PCI_DEVICE_ID_HP_DIVA_EVEREST: + if (idx > 0) + idx++; + if (idx > 2) + idx++; + break; } - req->io_type = SERIAL_IO_MEM; - req->iomap_base = port; - req->iomem_base = ioremap(port, board->uart_offset); - if (req->iomem_base == NULL) - return -ENOMEM; - req->iomem_reg_shift = board->reg_shift; - req->port = 0; - return 0; + if (idx > 2) + offset = 0x18; + + offset += idx * board->uart_offset; + + return setup_port(dev, req, bar, offset, board->reg_shift); } -static _INLINE_ int -get_pci_irq(struct pci_dev *dev, struct pci_board *board, int idx) +/* + * Added for EKF Intel i960 serial boards + */ +static int __devinit pci_inteli960ni_init(struct pci_dev *dev) { - int base_idx; + unsigned long oldval; - if ((board->flags & SPCI_FL_IRQRESOURCE) == 0) - return dev->irq; + if (!(dev->subsystem_device & 0x1000)) + return -ENODEV; - base_idx = SPCI_FL_GET_IRQBASE(board->flags); - if (board->flags & SPCI_FL_IRQ_TABLE) - base_idx += idx; - - return PCI_IRQ_RESOURCE(dev, base_idx); + /* is firmware started? */ + pci_read_config_dword(dev, 0x44, (void*) &oldval); + if (oldval == 0x00001000L) { /* RESET value */ + printk(KERN_DEBUG "Local i960 firmware missing"); + return -ENODEV; + } + return 0; } /* @@ -206,26 +246,29 @@ get_pci_irq(struct pci_dev *dev, struct pci_board *board, int idx) * seems to be mainly needed on card using the PLX which also use I/O * mapped memory. */ -static int __devinit pci_plx9050_fn(struct pci_dev *dev, int enable) +static int __devinit pci_plx9050_init(struct pci_dev *dev) { - u8 *p, irq_config = 0; - - if (enable) { - irq_config = 0x41; - if (dev->vendor == PCI_VENDOR_ID_PANACOM) - irq_config = 0x43; - if ((dev->vendor == PCI_VENDOR_ID_PLX) && - (dev->device == PCI_DEVICE_ID_PLX_ROMULUS)) { - /* - * As the megawolf cards have the int pins active - * high, and have 2 UART chips, both ints must be - * enabled on the 9050. Also, the UARTS are set in - * 16450 mode by default, so we have to enable the - * 16C950 'enhanced' mode so that we can use the - * deep FIFOs - */ - irq_config = 0x5b; - } + u8 *p, irq_config; + + if ((pci_resource_flags(dev, 0) & IORESOURCE_MEM) == 0) { + moan_device("no memory in bar 0", dev); + return 0; + } + + irq_config = 0x41; + if (dev->vendor == PCI_VENDOR_ID_PANACOM) + irq_config = 0x43; + if ((dev->vendor == PCI_VENDOR_ID_PLX) && + (dev->device == PCI_DEVICE_ID_PLX_ROMULUS)) { + /* + * As the megawolf cards have the int pins active + * high, and have 2 UART chips, both ints must be + * enabled on the 9050. Also, the UARTS are set in + * 16450 mode by default, so we have to enable the + * 16C950 'enhanced' mode so that we can use the + * deep FIFOs + */ + irq_config = 0x5b; } /* @@ -245,6 +288,27 @@ static int __devinit pci_plx9050_fn(struct pci_dev *dev, int enable) return 0; } +static void __devexit pci_plx9050_exit(struct pci_dev *dev) +{ + u8 *p; + + if ((pci_resource_flags(dev, 0) & IORESOURCE_MEM) == 0) + return; + + /* + * disable interrupts + */ + p = ioremap(pci_resource_start(dev, 0), 0x80); + if (p != NULL) { + writel(0, p + 0x4c); + + /* + * Read the register back to ensure that it took effect. + */ + readl(p + 0x4c); + iounmap(p); + } +} /* * SIIG serial cards have an PCI interface chip which also controls @@ -270,23 +334,20 @@ static int __devinit pci_plx9050_fn(struct pci_dev *dev, int enable) #define PCI_DEVICE_ID_SIIG_1S_10x (PCI_DEVICE_ID_SIIG_1S_10x_550 & 0xfffc) #define PCI_DEVICE_ID_SIIG_2S_10x (PCI_DEVICE_ID_SIIG_2S_10x_550 & 0xfff8) -int pci_siig10x_fn(struct pci_dev *dev, int enable) +static int pci_siig10x_init(struct pci_dev *dev) { u16 data, *p; - if (!enable) - return 0; - switch (dev->device & 0xfff8) { - case PCI_DEVICE_ID_SIIG_1S_10x: /* 1S */ - data = 0xffdf; - break; - case PCI_DEVICE_ID_SIIG_2S_10x: /* 2S, 2S1P */ - data = 0xf7ff; - break; - default: /* 1S1P, 4S */ - data = 0xfffb; - break; + case PCI_DEVICE_ID_SIIG_1S_10x: /* 1S */ + data = 0xffdf; + break; + case PCI_DEVICE_ID_SIIG_2S_10x: /* 2S, 2S1P */ + data = 0xf7ff; + break; + default: /* 1S1P, 4S */ + data = 0xfffb; + break; } p = ioremap(pci_resource_start(dev, 0), 0x80); @@ -294,22 +355,18 @@ int pci_siig10x_fn(struct pci_dev *dev, int enable) return -ENOMEM; writew(readw((unsigned long) p + 0x28) & data, (unsigned long) p + 0x28); + readw((unsigned long)p + 0x28); iounmap(p); return 0; } -EXPORT_SYMBOL(pci_siig10x_fn); - #define PCI_DEVICE_ID_SIIG_2S_20x (PCI_DEVICE_ID_SIIG_2S_20x_550 & 0xfffc) #define PCI_DEVICE_ID_SIIG_2S1P_20x (PCI_DEVICE_ID_SIIG_2S1P_20x_550 & 0xfffc) -int pci_siig20x_fn(struct pci_dev *dev, int enable) +static int pci_siig20x_init(struct pci_dev *dev) { u8 data; - if (!enable) - return 0; - /* Change clock frequency for the first UART. */ pci_read_config_byte(dev, 0x6f, &data); pci_write_config_byte(dev, 0x6f, data & 0xef); @@ -323,28 +380,25 @@ int pci_siig20x_fn(struct pci_dev *dev, int enable) return 0; } -EXPORT_SYMBOL(pci_siig20x_fn); - -/* Added for EKF Intel i960 serial boards */ -static int __devinit pci_inteli960ni_fn(struct pci_dev *dev, int enable) +int pci_siig10x_fn(struct pci_dev *dev, int enable) { - unsigned long oldval; - - if (!(pci_get_subdevice(dev) & 0x1000)) - return -ENODEV; + int ret = 0; + if (enable) + ret = pci_siig10x_init(dev); + return ret; +} - if (!enable) /* is there something to deinit? */ - return 0; - - /* is firmware started? */ - pci_read_config_dword(dev, 0x44, (void*) &oldval); - if (oldval == 0x00001000L) { /* RESET value */ - printk(KERN_DEBUG "Local i960 firmware missing"); - return -ENODEV; - } - return 0; +int pci_siig20x_fn(struct pci_dev *dev, int enable) +{ + int ret = 0; + if (enable) + ret = pci_siig20x_init(dev); + return ret; } +EXPORT_SYMBOL(pci_siig10x_fn); +EXPORT_SYMBOL(pci_siig20x_fn); + /* * Timedia has an explosion of boards, and to avoid the PCI table from * growing *huge*, we use this function to collapse some 70 entries @@ -385,78 +439,453 @@ static struct timedia_struct { { 0, 0 } }; -static int __devinit pci_timedia_fn(struct pci_dev *dev, int enable) +static int __devinit pci_timedia_init(struct pci_dev *dev) { - int i, j; unsigned short *ids; - - if (!enable) - return 0; + int i, j; for (i = 0; timedia_data[i].num; i++) { ids = timedia_data[i].ids; for (j = 0; ids[j]; j++) - if (pci_get_subdevice(dev) == ids[j]) + if (dev->subsystem_device == ids[j]) return timedia_data[i].num; } return 0; } /* - * HP's Remote Management Console. The Diva chip came in several - * different versions. N-class, L2000 and A500 have two Diva chips, each - * with 3 UARTs (the third UART on the second chip is unused). Superdome - * and Keystone have one Diva chip with 3 UARTs. Some later machines have - * one Diva chip, but it has been expanded to 5 UARTs. + * Timedia/SUNIX uses a mixture of BARs and offsets + * Ugh, this is ugly as all hell --- TYT */ -static int __devinit pci_hp_diva(struct pci_dev *dev, int enable) +static int +pci_timedia_setup(struct pci_dev *dev, struct pci_board *board, + struct serial_struct *req, int idx) { - int rc = 0; - - if (!enable) - return 0; + unsigned int bar = 0, offset = board->first_offset; - switch (dev->subsystem_device) { - case PCI_DEVICE_ID_HP_DIVA_TOSCA1: - case PCI_DEVICE_ID_HP_DIVA_HALFDOME: - case PCI_DEVICE_ID_HP_DIVA_KEYSTONE: - case PCI_DEVICE_ID_HP_DIVA_EVEREST: - rc = 3; + switch (idx) { + case 0: + bar = 0; break; - case PCI_DEVICE_ID_HP_DIVA_TOSCA2: - rc = 2; + case 1: + offset = board->uart_offset; + bar = 0; break; - case PCI_DEVICE_ID_HP_DIVA_MAESTRO: - rc = 4; - break; - case PCI_DEVICE_ID_HP_DIVA_POWERBAR: - rc = 1; + case 2: + bar = 1; break; + case 3: + offset = board->uart_offset; + bar = 1; + case 4: /* BAR 2 */ + case 5: /* BAR 3 */ + case 6: /* BAR 4 */ + case 7: /* BAR 5 */ + bar = idx - 2; } - return rc; + return setup_port(dev, req, bar, offset, board->reg_shift); } +/* + * Some Titan cards are also a little weird + */ +static int +titan_400l_800l_setup(struct pci_dev *dev, struct pci_board *board, + struct serial_struct *req, int idx) +{ + unsigned int bar, offset = board->first_offset; + + switch (idx) { + case 0: + bar = 1; + break; + case 1: + bar = 2; + break; + default: + bar = 4; + offset = (idx - 2) * board->uart_offset; + } + + return setup_port(dev, req, bar, offset, board->reg_shift); +} -static int __devinit pci_xircom_fn(struct pci_dev *dev, int enable) +static int __devinit pci_xircom_init(struct pci_dev *dev) { __set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ/10); return 0; } +static int +pci_default_setup(struct pci_dev *dev, struct pci_board *board, + struct serial_struct *req, int idx) +{ + unsigned int bar, offset = board->first_offset, maxnr; + + bar = FL_GET_BASE(board->flags); + if (board->flags & FL_BASE_BARS) + bar += idx; + else + offset += idx * board->uart_offset; + + maxnr = (pci_resource_len(dev, bar) - board->uart_offset) / + (8 << board->reg_shift); + + if (board->flags & FL_REGION_SZ_CAP && idx >= maxnr) + return 1; + + return setup_port(dev, req, bar, offset, board->reg_shift); +} + +/* + * Master list of serial port init/setup/exit quirks. + * This does not describe the general nature of the port. + * (ie, baud base, number and location of ports, etc) + * + * This list is ordered alphabetically by vendor then device. + * Specific entries must come before more generic entries. + */ +static struct pci_serial_quirk pci_serial_quirks[] = { + /* + * AFAVLAB cards. + * It is not clear whether this applies to all products. + */ + { + .vendor = PCI_VENDOR_ID_AFAVLAB, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = afavlab_setup, + }, + /* + * HP Diva + */ + { + .vendor = PCI_VENDOR_ID_HP, + .device = PCI_DEVICE_ID_HP_DIVA, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_hp_diva_init, + .setup = pci_hp_diva_setup, + }, + /* + * Intel + */ + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_80960_RP, + .subvendor = 0xe4bf, + .subdevice = PCI_ANY_ID, + .init = pci_inteli960ni_init, + .setup = pci_default_setup, + }, + /* + * Panacom + */ + { + .vendor = PCI_VENDOR_ID_PANACOM, + .device = PCI_DEVICE_ID_PANACOM_QUADMODEM, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_plx9050_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_plx9050_exit), + }, + { + .vendor = PCI_VENDOR_ID_PANACOM, + .device = PCI_DEVICE_ID_PANACOM_DUALMODEM, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_plx9050_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_plx9050_exit), + }, + /* + * PLX + */ + { + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_PLX_9050, + .subvendor = PCI_SUBVENDOR_ID_KEYSPAN, + .subdevice = PCI_SUBDEVICE_ID_KEYSPAN_SX2, + .init = pci_plx9050_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_plx9050_exit), + }, + { + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_PLX_ROMULUS, + .subvendor = PCI_VENDOR_ID_PLX, + .subdevice = PCI_DEVICE_ID_PLX_ROMULUS, + .init = pci_plx9050_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_plx9050_exit), + }, + /* + * SIIG cards. + * It is not clear whether these could be collapsed. + */ + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_1S_10x_550, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig10x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_1S_10x_650, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig10x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_1S_10x_850, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig10x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_2S_10x_550, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig10x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_2S_10x_650, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig10x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_2S_10x_850, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig10x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_4S_10x_550, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig10x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_4S_10x_650, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig10x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_4S_10x_850, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig10x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_1S_20x_550, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig20x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_1S_20x_650, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig20x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_1S_20x_850, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig20x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_2S_20x_550, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig20x_init, + .setup = pci_default_setup, + }, + { .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_2S_20x_650, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig20x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_2S_20x_850, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig20x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_4S_20x_550, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig20x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_4S_20x_650, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig20x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_4S_20x_850, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig20x_init, + .setup = pci_default_setup, + }, + /* + * Titan cards + */ + { + .vendor = PCI_VENDOR_ID_TITAN, + .device = PCI_DEVICE_ID_TITAN_400L, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = titan_400l_800l_setup, + }, + { + .vendor = PCI_VENDOR_ID_TITAN, + .device = PCI_DEVICE_ID_TITAN_800L, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = titan_400l_800l_setup, + }, + /* + * Timedia cards + */ + { + .vendor = PCI_VENDOR_ID_TIMEDIA, + .device = PCI_DEVICE_ID_TIMEDIA_1889, + .subvendor = PCI_VENDOR_ID_TIMEDIA, + .subdevice = PCI_ANY_ID, + .init = pci_timedia_init, + .setup = pci_timedia_setup, + }, + { + .vendor = PCI_VENDOR_ID_TIMEDIA, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_timedia_setup, + }, + /* + * Xircom cards + */ + { + .vendor = PCI_VENDOR_ID_XIRCOM, + .device = PCI_DEVICE_ID_XIRCOM_X3201_MDM, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_xircom_init, + .setup = pci_default_setup, + }, + /* + * Default "match everything" terminator entry + */ + { + .vendor = PCI_ANY_ID, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_default_setup, + } +}; + +static inline int quirk_id_matches(u32 quirk_id, u32 dev_id) +{ + return quirk_id == PCI_ANY_ID || quirk_id == dev_id; +} + +static struct pci_serial_quirk *find_quirk(struct pci_dev *dev) +{ + struct pci_serial_quirk *quirk; + + for (quirk = pci_serial_quirks; ; quirk++) + if (quirk_id_matches(quirk->vendor, dev->vendor) && + quirk_id_matches(quirk->device, dev->device) && + quirk_id_matches(quirk->subvendor, dev->subsystem_vendor) && + quirk_id_matches(quirk->subdevice, dev->subsystem_device)) + break; + return quirk; +} + +static _INLINE_ int +get_pci_irq(struct pci_dev *dev, struct pci_board *board, int idx) +{ + int base_idx; + + if ((board->flags & FL_IRQRESOURCE) == 0) + return dev->irq; + + base_idx = FL_GET_IRQBASE(board->flags); + + if (base_idx > DEVICE_COUNT_IRQ) + return 0; + + return dev->irq_resource[base_idx].start; +} + /* * This is the configuration table for all of the PCI serial boards * which we support. It is directly indexed by the pci_board_num_t enum * value, which is encoded in the pci_device_id PCI probe table's * driver_data member. + * + * The makeup of these names are: + * pbn_bn{_bt}_n_baud + * + * bn = PCI BAR number + * bt = Index using PCI BARs + * n = number of serial ports + * baud = baud rate + * + * Please note: in theory if n = 1, _bt infix should make no difference. + * ie, pbn_b0_1_115200 is the same as pbn_b0_bt_1_115200 */ enum pci_board_num_t { - pbn_b0_1_115200, pbn_default = 0, + pbn_b0_1_115200, pbn_b0_2_115200, pbn_b0_4_115200, + pbn_b0_5_115200, pbn_b0_1_921600, pbn_b0_2_921600, @@ -465,171 +894,465 @@ enum pci_board_num_t { pbn_b0_bt_1_115200, pbn_b0_bt_2_115200, pbn_b0_bt_8_115200, + pbn_b0_bt_1_460800, pbn_b0_bt_2_460800, + pbn_b0_bt_1_921600, + pbn_b0_bt_2_921600, + pbn_b0_bt_4_921600, + pbn_b0_bt_8_921600, + pbn_b1_1_115200, pbn_b1_2_115200, pbn_b1_4_115200, pbn_b1_8_115200, + pbn_b1_1_921600, pbn_b1_2_921600, pbn_b1_4_921600, pbn_b1_8_921600, + pbn_b1_bt_2_921600, + pbn_b1_2_1382400, pbn_b1_4_1382400, pbn_b1_8_1382400, pbn_b2_1_115200, pbn_b2_8_115200, + + pbn_b2_1_460800, pbn_b2_4_460800, pbn_b2_8_460800, pbn_b2_16_460800, + + pbn_b2_1_921600, pbn_b2_4_921600, pbn_b2_8_921600, pbn_b2_bt_1_115200, pbn_b2_bt_2_115200, pbn_b2_bt_4_115200, + pbn_b2_bt_2_921600, + pbn_b2_bt_4_921600, + + pbn_b3_4_115200, + pbn_b3_8_115200, + /* + * Board-specific versions. + */ pbn_panacom, pbn_panacom2, pbn_panacom4, pbn_plx_romulus, pbn_oxsemi, - pbn_timedia, pbn_intel_i960, pbn_sgi_ioc3, - pbn_hp_diva, pbn_nec_nile4, - - pbn_dci_pccom4, - pbn_dci_pccom8, - - pbn_xircom_combo, - - pbn_siig10x_0, - pbn_siig10x_1, - pbn_siig10x_2, - pbn_siig10x_4, - pbn_siig20x_0, - pbn_siig20x_2, - pbn_siig20x_4, - pbn_computone_4, pbn_computone_6, pbn_computone_8, }; static struct pci_board pci_boards[] __devinitdata = { + [pbn_default] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_1_115200] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_2_115200] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_4_115200] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_5_115200] = { + .flags = FL_BASE0, + .num_ports = 5, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b0_1_921600] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_2_921600] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_4_921600] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b0_bt_1_115200] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_bt_2_115200] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_bt_8_115200] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b0_bt_1_460800] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 460800, + .uart_offset = 8, + }, + [pbn_b0_bt_2_460800] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 460800, + .uart_offset = 8, + }, + + [pbn_b0_bt_1_921600] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_bt_2_921600] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_bt_4_921600] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_bt_8_921600] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b1_1_115200] = { + .flags = FL_BASE1, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b1_2_115200] = { + .flags = FL_BASE1, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b1_4_115200] = { + .flags = FL_BASE1, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b1_8_115200] = { + .flags = FL_BASE1, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b1_1_921600] = { + .flags = FL_BASE1, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b1_2_921600] = { + .flags = FL_BASE1, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b1_4_921600] = { + .flags = FL_BASE1, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b1_8_921600] = { + .flags = FL_BASE1, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b1_bt_2_921600] = { + .flags = FL_BASE1|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b1_2_1382400] = { + .flags = FL_BASE1, + .num_ports = 2, + .base_baud = 1382400, + .uart_offset = 8, + }, + [pbn_b1_4_1382400] = { + .flags = FL_BASE1, + .num_ports = 4, + .base_baud = 1382400, + .uart_offset = 8, + }, + [pbn_b1_8_1382400] = { + .flags = FL_BASE1, + .num_ports = 8, + .base_baud = 1382400, + .uart_offset = 8, + }, + + [pbn_b2_1_115200] = { + .flags = FL_BASE2, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b2_8_115200] = { + .flags = FL_BASE2, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b2_1_460800] = { + .flags = FL_BASE2, + .num_ports = 1, + .base_baud = 460800, + .uart_offset = 8, + }, + [pbn_b2_4_460800] = { + .flags = FL_BASE2, + .num_ports = 4, + .base_baud = 460800, + .uart_offset = 8, + }, + [pbn_b2_8_460800] = { + .flags = FL_BASE2, + .num_ports = 8, + .base_baud = 460800, + .uart_offset = 8, + }, + [pbn_b2_16_460800] = { + .flags = FL_BASE2, + .num_ports = 16, + .base_baud = 460800, + .uart_offset = 8, + }, + + [pbn_b2_1_921600] = { + .flags = FL_BASE2, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b2_4_921600] = { + .flags = FL_BASE2, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b2_8_921600] = { + .flags = FL_BASE2, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b2_bt_1_115200] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b2_bt_2_115200] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b2_bt_4_115200] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b2_bt_2_921600] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b2_bt_4_921600] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b3_4_115200] = { + .flags = FL_BASE3, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b3_8_115200] = { + .flags = FL_BASE3, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + }, + /* - * PCI Flags, Number of Ports, Base (Maximum) Baud Rate, - * Offset to get to next UART's registers, - * Register shift to use for memory-mapped I/O, - * Initialization function, first UART offset + * Entries following this are board-specific. */ - /* Generic serial board, pbn_b0_1_115200, pbn_default */ - { SPCI_FL_BASE0, 1, 115200 }, /* pbn_b0_1_115200, - pbn_default */ - - { SPCI_FL_BASE0, 2, 115200 }, /* pbn_b0_2_115200 */ - { SPCI_FL_BASE0, 4, 115200 }, /* pbn_b0_4_115200 */ - - { SPCI_FL_BASE0, 1, 921600 }, /* pbn_b0_1_921600 */ - { SPCI_FL_BASE0, 2, 921600 }, /* pbn_b0_2_921600 */ - { SPCI_FL_BASE0, 4, 921600 }, /* pbn_b0_4_921600 */ - - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b0_bt_1_115200 */ - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b0_bt_2_115200 */ - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 8, 115200 }, /* pbn_b0_bt_8_115200 */ - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 460800 }, /* pbn_b0_bt_1_460800 */ - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 460800 }, /* pbn_b0_bt_2_460800 */ - - { SPCI_FL_BASE1, 1, 115200 }, /* pbn_b1_1_115200 */ - { SPCI_FL_BASE1, 2, 115200 }, /* pbn_b1_2_115200 */ - { SPCI_FL_BASE1, 4, 115200 }, /* pbn_b1_4_115200 */ - { SPCI_FL_BASE1, 8, 115200 }, /* pbn_b1_8_115200 */ - - { SPCI_FL_BASE1, 2, 921600 }, /* pbn_b1_2_921600 */ - { SPCI_FL_BASE1, 4, 921600 }, /* pbn_b1_4_921600 */ - { SPCI_FL_BASE1, 8, 921600 }, /* pbn_b1_8_921600 */ - - { SPCI_FL_BASE1, 2, 1382400 }, /* pbn_b1_2_1382400 */ - { SPCI_FL_BASE1, 4, 1382400 }, /* pbn_b1_4_1382400 */ - { SPCI_FL_BASE1, 8, 1382400 }, /* pbn_b1_8_1382400 */ - - { SPCI_FL_BASE2, 1, 115200 }, /* pbn_b2_1_115200 */ - { SPCI_FL_BASE2, 8, 115200 }, /* pbn_b2_8_115200 */ - { SPCI_FL_BASE2, 4, 460800 }, /* pbn_b2_4_460800 */ - { SPCI_FL_BASE2, 8, 460800 }, /* pbn_b2_8_460800 */ - { SPCI_FL_BASE2, 16, 460800 }, /* pbn_b2_16_460800 */ - { SPCI_FL_BASE2, 4, 921600 }, /* pbn_b2_4_921600 */ - { SPCI_FL_BASE2, 8, 921600 }, /* pbn_b2_8_921600 */ - - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b2_bt_1_115200 */ - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b2_bt_2_115200 */ - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 115200 }, /* pbn_b2_bt_4_115200 */ - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600 }, /* pbn_b2_bt_2_921600 */ - - { SPCI_FL_BASE2, 2, 921600, /* IOMEM */ /* pbn_panacom */ - 0x400, 7, pci_plx9050_fn }, - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_panacom2 */ - 0x400, 7, pci_plx9050_fn }, - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_panacom4 */ - 0x400, 7, pci_plx9050_fn }, - { SPCI_FL_BASE2, 4, 921600, /* pbn_plx_romulus */ - 0x20, 2, pci_plx9050_fn, 0x03 }, + /* + * Panacom - IOMEM + */ + [pbn_panacom] = { + .flags = FL_BASE2, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 0x400, + .reg_shift = 7, + }, + [pbn_panacom2] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 0x400, + .reg_shift = 7, + }, + [pbn_panacom4] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 0x400, + .reg_shift = 7, + }, + + /* I think this entry is broken - the first_offset looks wrong --rmk */ + [pbn_plx_romulus] = { + .flags = FL_BASE2, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8 << 2, + .reg_shift = 2, + .first_offset = 0x03, + }, /* * This board uses the size of PCI Base region 0 to * signal now many ports are available */ - { SPCI_FL_BASE0 | SPCI_FL_REGION_SZ_CAP, 32, 115200 }, /* pbn_oxsemi */ - { SPCI_FL_BASE_TABLE, 1, 921600, /* pbn_timedia */ - 0, 0, pci_timedia_fn }, - /* EKF addition for i960 Boards form EKF with serial port */ - { SPCI_FL_BASE0, 32, 921600, /* max 256 ports */ /* pbn_intel_i960 */ - 8<<2, 2, pci_inteli960ni_fn, 0x10000}, - { SPCI_FL_BASE0 | SPCI_FL_IRQRESOURCE, /* pbn_sgi_ioc3 */ - 1, 458333, 0, 0, 0, 0x20178 }, - { SPCI_FL_BASE0, 5, 115200, 8, 0, pci_hp_diva, 0 },/* pbn_hp_diva */ + [pbn_oxsemi] = { + .flags = FL_BASE0|FL_REGION_SZ_CAP, + .num_ports = 32, + .base_baud = 115200, + .uart_offset = 8, + }, + + /* + * EKF addition for i960 Boards form EKF with serial port. + * Max 256 ports. + */ + [pbn_intel_i960] = { + .flags = FL_BASE0, + .num_ports = 32, + .base_baud = 921600, + .uart_offset = 8 << 2, + .reg_shift = 2, + .first_offset = 0x10000, + }, + [pbn_sgi_ioc3] = { + .flags = FL_BASE0|FL_IRQRESOURCE, + .num_ports = 1, + .base_baud = 458333, + .uart_offset = 8, + .reg_shift = 0, + .first_offset = 0x20178, + }, /* * NEC Vrc-5074 (Nile 4) builtin UART. */ - { SPCI_FL_BASE0, 1, 520833, /* pbn_nec_nile4 */ - 64, 3, NULL, 0x300 }, - - { SPCI_FL_BASE3, 4, 115200, 8 }, /* pbn_dci_pccom4 */ - { SPCI_FL_BASE3, 8, 115200, 8 }, /* pbn_dci_pccom8 */ - - { SPCI_FL_BASE0, 1, 115200, /* pbn_xircom_combo */ - 0, 0, pci_xircom_fn }, - - { SPCI_FL_BASE2, 1, 460800, /* pbn_siig10x_0 */ - 0, 0, pci_siig10x_fn }, - { SPCI_FL_BASE2, 1, 921600, /* pbn_siig10x_1 */ - 0, 0, pci_siig10x_fn }, - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_siig10x_2 */ - 0, 0, pci_siig10x_fn }, - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siig10x_4 */ - 0, 0, pci_siig10x_fn }, - { SPCI_FL_BASE0, 1, 921600, /* pbn_siix20x_0 */ - 0, 0, pci_siig20x_fn }, - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_siix20x_2 */ - 0, 0, pci_siig20x_fn }, - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siix20x_4 */ - 0, 0, pci_siig20x_fn }, - - { SPCI_FL_BASE0, 4, 921600, /* IOMEM */ /* pbn_computone_4 */ - 0x40, 2, NULL, 0x200 }, - { SPCI_FL_BASE0, 6, 921600, /* IOMEM */ /* pbn_computone_6 */ - 0x40, 2, NULL, 0x200 }, - { SPCI_FL_BASE0, 8, 921600, /* IOMEM */ /* pbn_computone_8 */ - 0x40, 2, NULL, 0x200 }, + [pbn_nec_nile4] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 520833, + .uart_offset = 8 << 3, + .reg_shift = 3, + .first_offset = 0x300, + }, + + /* + * Computone - uses IOMEM. + */ + [pbn_computone_4] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 0x40, + .reg_shift = 2, + .first_offset = 0x200, + }, + [pbn_computone_6] = { + .flags = FL_BASE0, + .num_ports = 6, + .base_baud = 921600, + .uart_offset = 0x40, + .reg_shift = 2, + .first_offset = 0x200, + }, + [pbn_computone_8] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 0x40, + .reg_shift = 2, + .first_offset = 0x200, + }, }; /* @@ -640,8 +1363,7 @@ static struct pci_board pci_boards[] __devinitdata = { static int __devinit serial_pci_guess_board(struct pci_dev *dev, struct pci_board *board) { - int num_iomem = 0, num_port = 0, first_port = -1; - int i; + int num_iomem, num_port, first_port = -1, i; /* * If it is not a communications device or the programming @@ -655,36 +1377,63 @@ serial_pci_guess_board(struct pci_dev *dev, struct pci_board *board) (dev->class & 0xff) > 6) return -ENODEV; - for (i = 0; i < 6; i++) { - if (IS_PCI_REGION_IOPORT(dev, i)) { + num_iomem = num_port = 0; + for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) { + if (pci_resource_flags(dev, i) & IORESOURCE_IO) { num_port++; if (first_port == -1) first_port = i; } - if (IS_PCI_REGION_IOMEM(dev, i)) + if (pci_resource_flags(dev, i) & IORESOURCE_MEM) num_iomem++; } /* - * If there is 1 or 0 iomem regions, and exactly one port, use - * it. + * If there is 1 or 0 iomem regions, and exactly one port, + * use it. We guess the number of ports based on the IO + * region size. */ if (num_iomem <= 1 && num_port == 1) { board->flags = first_port; + board->num_ports = pci_resource_len(dev, first_port) / 8; return 0; } + + /* + * Now guess if we've got a board which indexes by BARs. + * Each IO BAR should be 8 bytes, and they should follow + * consecutively. + */ + first_port = -1; + num_port = 0; + for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) { + if (pci_resource_flags(dev, i) & IORESOURCE_IO && + pci_resource_len(dev, i) == 8 && + (first_port == -1 || (first_port + num_port) == i)) { + num_port++; + if (first_port == -1) + first_port = i; + } + } + + if (num_port > 1) { + board->flags = first_port | FL_BASE_BARS; + board->num_ports = num_port; + return 0; + } + return -ENODEV; } static inline int -serial_pci_matches(struct pci_board *board, int index) +serial_pci_matches(struct pci_board *board, struct pci_board *guessed) { return - board->base_baud == pci_boards[index].base_baud && - board->num_ports == pci_boards[index].num_ports && - board->uart_offset == pci_boards[index].uart_offset && - board->reg_shift == pci_boards[index].reg_shift && - board->first_uart_offset == pci_boards[index].first_uart_offset; + board->num_ports == guessed->num_ports && + board->base_baud == guessed->base_baud && + board->uart_offset == guessed->uart_offset && + board->reg_shift == guessed->reg_shift && + board->first_offset == guessed->first_offset; } /* @@ -692,10 +1441,11 @@ serial_pci_matches(struct pci_board *board, int index) * to the arrangement of serial ports on a PCI card. */ static int __devinit -pci_init_one(struct pci_dev *dev, const struct pci_device_id *ent) +pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent) { struct serial_private *priv; struct pci_board *board, tmp; + struct pci_serial_quirk *quirk; struct serial_struct serial_req; int base_baud, rc, nr_ports, i; @@ -732,92 +1482,109 @@ pci_init_one(struct pci_dev *dev, const struct pci_device_id *ent) * detect this boards settings with our heuristic, * then we no longer need this entry. */ + memcpy(&tmp, &pci_boards[pbn_default], sizeof(struct pci_board)); rc = serial_pci_guess_board(dev, &tmp); - if (rc == 0 && serial_pci_matches(board, pbn_default)) { - printk(KERN_INFO - "Redundant entry in serial pci_table. Please send the output\n" - "of lspci -vv, this message (0x%04x,0x%04x,0x%04x,0x%04x),\n" - "the manufacturer and name of serial board or modem board to\n" - "rmk@arm.linux.org.uk.\n", - dev->vendor, dev->device, - pci_get_subvendor(dev), pci_get_subdevice(dev)); - } + if (rc == 0 && serial_pci_matches(board, &tmp)) + moan_device("Redundant entry in serial pci_table.", + dev); } nr_ports = board->num_ports; /* - * Run the initialization function, if any. The initialization - * function returns: + * Find an init and setup quirks. + */ + quirk = find_quirk(dev); + + /* + * Run the new-style initialization function. + * The initialization function returns: * <0 - error * 0 - use board->num_ports * >0 - number of ports */ - if (board->init_fn) { - rc = board->init_fn(dev, 1); + if (quirk->init) { + rc = quirk->init(dev); if (rc < 0) goto disable; - if (rc) nr_ports = rc; } priv = kmalloc(sizeof(struct serial_private) + - sizeof(unsigned int) * nr_ports, - GFP_KERNEL); + sizeof(unsigned int) * nr_ports, + GFP_KERNEL); if (!priv) { rc = -ENOMEM; goto deinit; } + memset(priv, 0, sizeof(struct serial_private) + + sizeof(unsigned int) * nr_ports); + + priv->quirk = quirk; + pci_set_drvdata(dev, priv); + base_baud = board->base_baud; - if (!base_baud) + if (!base_baud) { + moan_device("Board entry does not specify baud rate.", dev); base_baud = BASE_BAUD; - memset(&serial_req, 0, sizeof(serial_req)); + } for (i = 0; i < nr_ports; i++) { + memset(&serial_req, 0, sizeof(serial_req)); + serial_req.flags = UPF_SKIP_TEST | UPF_AUTOPROBE | + UPF_RESOURCES | UPF_SHARE_IRQ; + serial_req.baud_base = base_baud; serial_req.irq = get_pci_irq(dev, board, i); - if (get_pci_port(dev, board, &serial_req, i)) + if (quirk->setup(dev, board, &serial_req, i)) break; #ifdef SERIAL_DEBUG_PCI printk("Setup PCI port: port %x, irq %d, type %d\n", serial_req.port, serial_req.irq, serial_req.io_type); #endif - serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE; - serial_req.baud_base = base_baud; priv->line[i] = register_serial(&serial_req); if (priv->line[i] < 0) break; } - priv->board = board; priv->nr = i; - pci_set_drvdata(dev, priv); - return 0; deinit: - if (board->init_fn) - board->init_fn(dev, 0); + if (quirk->exit) + quirk->exit(dev); disable: pci_disable_device(dev); return rc; } -static void __devexit pci_remove_one(struct pci_dev *dev) +static void __devexit pciserial_remove_one(struct pci_dev *dev) { struct serial_private *priv = pci_get_drvdata(dev); - int i; pci_set_drvdata(dev, NULL); if (priv) { + struct pci_serial_quirk *quirk; + int i; + for (i = 0; i < priv->nr; i++) unregister_serial(priv->line[i]); - if (priv->board->init_fn) - priv->board->init_fn(dev, 0); + for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) { + if (priv->remapped_bar[i]) + iounmap(priv->remapped_bar[i]); + priv->remapped_bar[i] = NULL; + } + + /* + * Find the exit quirks. + */ + quirk = find_quirk(dev); + if (quirk->exit) + quirk->exit(dev); pci_disable_device(dev); @@ -825,6 +1592,53 @@ static void __devexit pci_remove_one(struct pci_dev *dev) } } +static int pciserial_save_state_one(struct pci_dev *dev, u32 state) +{ + struct serial_private *priv = pci_get_drvdata(dev); + + if (priv) { + int i; + + for (i = 0; i < priv->nr; i++) + serial8250_suspend_port(priv->line[i], SUSPEND_SAVE_STATE); + } + return 0; +} + +static int pciserial_suspend_one(struct pci_dev *dev, u32 state) +{ + struct serial_private *priv = pci_get_drvdata(dev); + + if (priv) { + int i; + + for (i = 0; i < priv->nr; i++) + serial8250_suspend_port(priv->line[i], SUSPEND_POWER_DOWN); + } + return 0; +} + +static int pciserial_resume_one(struct pci_dev *dev) +{ + struct serial_private *priv = pci_get_drvdata(dev); + + if (priv) { + int i; + + /* + * Ensure that the board is correctly configured. + */ + if (priv->quirk->init) + priv->quirk->init(dev); + + for (i = 0; i < priv->nr; i++) { + serial8250_resume_port(priv->line[i], RESUME_POWER_ON); + serial8250_resume_port(priv->line[i], RESUME_RESTORE_STATE); + } + } + return 0; +} + static struct pci_device_id serial_pci_tbl[] __devinitdata = { { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, PCI_SUBVENDOR_ID_CONNECT_TECH, @@ -908,7 +1722,9 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b2_bt_2_921600 }, - /* VScom SPCOM800, from sl@s.pl */ + /* + * VScom SPCOM800, from sl@s.pl + */ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b2_8_921600 }, @@ -949,8 +1765,10 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { PCI_SUBVENDOR_ID_CHASE_PCIRAS, PCI_SUBDEVICE_ID_CHASE_PCIRAS8, 0, 0, pbn_b2_8_460800 }, - /* Megawolf Romulus PCI Serial Card, from Mike Hudson */ - /* (Exoray@isys.ca) */ + /* + * Megawolf Romulus PCI Serial Card, from Mike Hudson + * (Exoray@isys.ca) + */ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS, 0x10b5, 0x106a, 0, 0, pbn_plx_romulus }, @@ -976,16 +1794,24 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_2_115200 }, - /* Digitan DS560-558, from jimd@esoft.com */ + /* + * Digitan DS560-558, from jimd@esoft.com + */ { PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b1_1_115200 }, - /* 3Com US Robotics 56k Voice Internal PCI model 5610 */ + /* + * 3Com US Robotics 56k Voice Internal PCI model 5610 + */ { PCI_VENDOR_ID_USR, 0x1008, - PCI_ANY_ID, PCI_ANY_ID, }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_115200 }, - /* Titan Electronic cards */ + /* + * Titan Electronic cards + * The 400L and 800L have a custom setup quirk. + */ { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_1_921600 }, @@ -999,120 +1825,76 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_4_921600 }, { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100L, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE1, 1, 921600 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_1_921600 }, { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200L, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE1 | SPCI_FL_BASE_TABLE, 2, 921600 }, - /* The 400L and 800L have a custom hack in get_pci_port */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_2_921600 }, { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400L, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE_TABLE, 4, 921600 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_4_921600 }, { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800L, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE_TABLE, 8, 921600 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_8_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_0 }, + pbn_b2_1_460800 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_0 }, + pbn_b2_1_460800 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_1 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_1 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_1 }, + pbn_b2_1_460800 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_2 }, + pbn_b2_bt_2_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_2 }, + pbn_b2_bt_2_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_2 }, + pbn_b2_bt_2_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_4 }, + pbn_b2_bt_4_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_4 }, + pbn_b2_bt_4_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_4 }, + pbn_b2_bt_4_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, + pbn_b0_1_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, + pbn_b0_1_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, + pbn_b0_1_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_2 }, + pbn_b0_bt_2_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_2 }, + pbn_b0_bt_2_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_2 }, + pbn_b0_bt_2_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_4 }, + pbn_b0_bt_4_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_4 }, + pbn_b0_bt_4_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_4 }, + pbn_b0_bt_4_921600 }, - /* Computone devices submitted by Doug McNash dmcnash@computone.com */ + /* + * Computone devices submitted by Doug McNash dmcnash@computone.com + */ { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4, 0, 0, pbn_computone_4 }, @@ -1124,18 +1906,22 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { 0, 0, pbn_computone_6 }, { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_oxsemi }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi }, { PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889, - PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, 0, 0, pbn_timedia }, + PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, 0, 0, + pbn_b0_bt_1_921600 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_115200 }, - /* AFAVLAB serial card, from Harald Welte <laforge@gnumonks.org> */ + /* + * AFAVLAB serial card, from Harald Welte <laforge@gnumonks.org> + */ { PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P028, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_8_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_2_115200 }, @@ -1158,26 +1944,40 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_1_460800 }, - /* RAStel 2 port modem, gerg@moreton.com.au */ + /* + * RAStel 2 port modem, gerg@moreton.com.au + */ { PCI_VENDOR_ID_MORETON, PCI_DEVICE_ID_RASTEL_2PORT, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b2_bt_2_115200 }, - /* EKF addition for i960 Boards form EKF with serial port */ - { PCI_VENDOR_ID_INTEL, 0x1960, + /* + * EKF addition for i960 Boards form EKF with serial port + */ + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80960_RP, 0xE4BF, PCI_ANY_ID, 0, 0, pbn_intel_i960 }, - /* Xircom Cardbus/Ethernet combos */ + /* + * Xircom Cardbus/Ethernet combos + */ { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_xircom_combo }, + pbn_b0_1_115200 }, + /* + * Xircom RBM56G cardbus modem - Dirk Arnold (temp entry) + */ + { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_RBM56G, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_115200 }, /* * Untested PCI modems, sent in from various folks... */ - /* Elsa Model 56K PCI Modem, from Andreas Rath <arh@01019freenet.de> */ + /* + * Elsa Model 56K PCI Modem, from Andreas Rath <arh@01019freenet.de> + */ { PCI_VENDOR_ID_ROCKWELL, 0x1004, 0x1048, 0x1500, 0, 0, pbn_b1_1_115200 }, @@ -1186,10 +1986,12 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { 0xFF00, 0, 0, 0, pbn_sgi_ioc3 }, - /* HP Diva card */ + /* + * HP Diva card + */ { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_hp_diva }, + pbn_b0_5_115200 }, { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA_AUX, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b2_1_115200 }, @@ -1203,15 +2005,14 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_dci_pccom4 }, + pbn_b3_4_115200 }, { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_dci_pccom8 }, + pbn_b3_8_115200 }, /* - * These entries match devices with class - * COMMUNICATION_SERIAL, COMMUNICATION_MODEM - * or COMMUNICATION_MULTISERIAL + * These entries match devices with class COMMUNICATION_SERIAL, + * COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL */ { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, @@ -1230,9 +2031,15 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { static struct pci_driver serial_pci_driver = { .name = "serial", - .probe = pci_init_one, - .remove = __devexit_p(pci_remove_one), + .probe = pciserial_init_one, + .remove = __devexit_p(pciserial_remove_one), + .save_state = pciserial_save_state_one, + .suspend = pciserial_suspend_one, + .resume = pciserial_resume_one, .id_table = serial_pci_tbl, + .driver = { + .devclass = &tty_devclass, + }, }; static int __init serial8250_pci_init(void) diff --git a/drivers/serial/core.c b/drivers/serial/core.c index 5b394b10cf2e..cd71c9e65fb0 100644 --- a/drivers/serial/core.c +++ b/drivers/serial/core.c @@ -21,9 +21,6 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * $Id: core.c,v 1.100 2002/07/28 10:03:28 rmk Exp $ - * */ #include <linux/config.h> #include <linux/module.h> @@ -31,9 +28,9 @@ #include <linux/slab.h> #include <linux/init.h> #include <linux/console.h> -#include <linux/pm.h> #include <linux/serial_core.h> #include <linux/smp_lock.h> +#include <linux/device.h> #include <linux/serial.h> /* for serial_state and serial_icounter_struct */ #include <asm/irq.h> @@ -46,11 +43,6 @@ #define DPRINTK(x...) do { } while (0) #endif -#ifndef CONFIG_PM -#define pm_access(pm) do { } while (0) -#define pm_unregister(pm) do { } while (0) -#endif - /* * This is used to lock changes in serial line configuration. */ @@ -58,8 +50,17 @@ static DECLARE_MUTEX(port_sem); #define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) -static void uart_change_speed(struct uart_info *info, struct termios *old_termios); +#define uart_users(state) ((state)->count + ((state)->info ? (state)->info->blocked_open : 0)) + +#ifdef CONFIG_SERIAL_CORE_CONSOLE +#define uart_console(port) ((port)->cons && (port)->cons->index == (port)->line) +#else +#define uart_console(port) (0) +#endif + +static void uart_change_speed(struct uart_state *state, struct termios *old_termios); static void uart_wait_until_sent(struct tty_struct *tty, int timeout); +static void uart_change_pm(struct uart_state *state, int pm_state); /* * This routine is used by the interrupt handler to schedule processing in @@ -73,8 +74,8 @@ void uart_write_wakeup(struct uart_port *port) static void uart_stop(struct tty_struct *tty) { - struct uart_info *info = tty->driver_data; - struct uart_port *port = info->port; + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; unsigned long flags; spin_lock_irqsave(&port->lock, flags); @@ -84,32 +85,31 @@ static void uart_stop(struct tty_struct *tty) static void __uart_start(struct tty_struct *tty) { - struct uart_info *info = tty->driver_data; - struct uart_port *port = info->port; + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; - if (!uart_circ_empty(&info->xmit) && info->xmit.buf && + if (!uart_circ_empty(&state->info->xmit) && state->info->xmit.buf && !tty->stopped && !tty->hw_stopped) port->ops->start_tx(port, 1); } static void uart_start(struct tty_struct *tty) { - struct uart_info *info = tty->driver_data; + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; unsigned long flags; - pm_access(info->state->pm); - - spin_lock_irqsave(&info->port->lock, flags); + spin_lock_irqsave(&port->lock, flags); __uart_start(tty); - spin_unlock_irqrestore(&info->port->lock, flags); + spin_unlock_irqrestore(&port->lock, flags); } static void uart_tasklet_action(unsigned long data) { - struct uart_info *info = (struct uart_info *)data; + struct uart_state *state = (struct uart_state *)data; struct tty_struct *tty; - tty = info->tty; + tty = state->info->tty; if (tty) { if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) @@ -137,11 +137,12 @@ uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) /* * Startup the port. This will be called once per open. All calls - * will be serialised by the global port semaphore. + * will be serialised by the per-port semaphore. */ -static int uart_startup(struct uart_info *info, int init_hw) +static int uart_startup(struct uart_state *state, int init_hw) { - struct uart_port *port = info->port; + struct uart_info *info = state->info; + struct uart_port *port = state->port; unsigned long page; int retval = 0; @@ -182,7 +183,7 @@ static int uart_startup(struct uart_info *info, int init_hw) /* * Initialise the hardware port settings. */ - uart_change_speed(info, NULL); + uart_change_speed(state, NULL); /* * Setup the RTS and DTR signals once the @@ -194,8 +195,7 @@ static int uart_startup(struct uart_info *info, int init_hw) info->flags |= UIF_INITIALIZED; - if (info->tty) - clear_bit(TTY_IO_ERROR, &info->tty->flags); + clear_bit(TTY_IO_ERROR, &info->tty->flags); } if (retval && capable(CAP_SYS_ADMIN)) @@ -207,11 +207,12 @@ static int uart_startup(struct uart_info *info, int init_hw) /* * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. Calls to - * uart_shutdown are serialised by port_sem. + * uart_shutdown are serialised by the per-port semaphore. */ -static void uart_shutdown(struct uart_info *info) +static void uart_shutdown(struct uart_state *state) { - struct uart_port *port = info->port; + struct uart_info *info = state->info; + struct uart_port *port = state->port; if (!(info->flags & UIF_INITIALIZED)) return; @@ -220,7 +221,7 @@ static void uart_shutdown(struct uart_info *info) * Turn off DTR and RTS early. */ if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) - uart_clear_mctrl(info->port, TIOCM_DTR | TIOCM_RTS); + uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); /* * clear delta_msr_wait queue to avoid mem leaks: we may free @@ -412,10 +413,10 @@ uart_get_divisor(struct uart_port *port, unsigned int baud) EXPORT_SYMBOL(uart_get_divisor); static void -uart_change_speed(struct uart_info *info, struct termios *old_termios) +uart_change_speed(struct uart_state *state, struct termios *old_termios) { - struct tty_struct *tty = info->tty; - struct uart_port *port = info->port; + struct tty_struct *tty = state->info->tty; + struct uart_port *port = state->port; struct termios *termios; /* @@ -431,14 +432,14 @@ uart_change_speed(struct uart_info *info, struct termios *old_termios) * Set flags based on termios cflag */ if (termios->c_cflag & CRTSCTS) - info->flags |= UIF_CTS_FLOW; + state->info->flags |= UIF_CTS_FLOW; else - info->flags &= ~UIF_CTS_FLOW; + state->info->flags &= ~UIF_CTS_FLOW; if (termios->c_cflag & CLOCAL) - info->flags &= ~UIF_CHECK_CD; + state->info->flags &= ~UIF_CHECK_CD; else - info->flags |= UIF_CHECK_CD; + state->info->flags |= UIF_CHECK_CD; port->ops->set_termios(port, termios, old_termios); } @@ -526,10 +527,10 @@ __uart_kern_write(struct uart_port *port, struct circ_buf *circ, static void uart_put_char(struct tty_struct *tty, unsigned char ch) { - struct uart_info *info = tty->driver_data; + struct uart_state *state = tty->driver_data; if (tty) - __uart_put_char(info->port, &info->xmit, ch); + __uart_put_char(state->port, &state->info->xmit, ch); } static void uart_flush_chars(struct tty_struct *tty) @@ -541,16 +542,16 @@ static int uart_write(struct tty_struct *tty, int from_user, const unsigned char * buf, int count) { - struct uart_info *info = tty->driver_data; + struct uart_state *state = tty->driver_data; int ret; - if (!tty || !info->xmit.buf) + if (!tty || !state->info->xmit.buf) return 0; if (from_user) - ret = __uart_user_write(info->port, &info->xmit, buf, count); + ret = __uart_user_write(state->port, &state->info->xmit, buf, count); else - ret = __uart_kern_write(info->port, &info->xmit, buf, count); + ret = __uart_kern_write(state->port, &state->info->xmit, buf, count); uart_start(tty); return ret; @@ -558,29 +559,30 @@ uart_write(struct tty_struct *tty, int from_user, const unsigned char * buf, static int uart_write_room(struct tty_struct *tty) { - struct uart_info *info = tty->driver_data; + struct uart_state *state = tty->driver_data; - return uart_circ_chars_free(&info->xmit); + return uart_circ_chars_free(&state->info->xmit); } static int uart_chars_in_buffer(struct tty_struct *tty) { - struct uart_info *info = tty->driver_data; + struct uart_state *state = tty->driver_data; - return uart_circ_chars_pending(&info->xmit); + return uart_circ_chars_pending(&state->info->xmit); } static void uart_flush_buffer(struct tty_struct *tty) { - struct uart_info *info = tty->driver_data; + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; unsigned long flags; DPRINTK("uart_flush_buffer(%d) called\n", minor(tty->device) - tty->driver.minor_start); - spin_lock_irqsave(&info->port->lock, flags); - uart_circ_clear(&info->xmit); - spin_unlock_irqrestore(&info->port->lock, flags); + spin_lock_irqsave(&port->lock, flags); + uart_circ_clear(&state->info->xmit); + spin_unlock_irqrestore(&port->lock, flags); wake_up_interruptible(&tty->write_wait); if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) @@ -593,8 +595,8 @@ static void uart_flush_buffer(struct tty_struct *tty) */ static void uart_send_xchar(struct tty_struct *tty, char ch) { - struct uart_info *info = tty->driver_data; - struct uart_port *port = info->port; + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; unsigned long flags; if (port->ops->send_xchar) @@ -611,19 +613,19 @@ static void uart_send_xchar(struct tty_struct *tty, char ch) static void uart_throttle(struct tty_struct *tty) { - struct uart_info *info = tty->driver_data; + struct uart_state *state = tty->driver_data; if (I_IXOFF(tty)) uart_send_xchar(tty, STOP_CHAR(tty)); if (tty->termios->c_cflag & CRTSCTS) - uart_clear_mctrl(info->port, TIOCM_RTS); + uart_clear_mctrl(state->port, TIOCM_RTS); } static void uart_unthrottle(struct tty_struct *tty) { - struct uart_info *info = tty->driver_data; - struct uart_port *port = info->port; + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; if (I_IXOFF(tty)) { if (port->x_char) @@ -636,10 +638,9 @@ static void uart_unthrottle(struct tty_struct *tty) uart_set_mctrl(port, TIOCM_RTS); } -static int uart_get_info(struct uart_info *info, struct serial_struct *retinfo) +static int uart_get_info(struct uart_state *state, struct serial_struct *retinfo) { - struct uart_state *state = info->state; - struct uart_port *port = info->port; + struct uart_port *port = state->port; struct serial_struct tmp; memset(&tmp, 0, sizeof(tmp)); @@ -649,7 +650,7 @@ static int uart_get_info(struct uart_info *info, struct serial_struct *retinfo) if (HIGH_BITS_OFFSET) tmp.port_high = (long) port->iobase >> HIGH_BITS_OFFSET; tmp.irq = port->irq; - tmp.flags = port->flags | info->flags; + tmp.flags = port->flags; tmp.xmit_fifo_size = port->fifosize; tmp.baud_base = port->uartclk / 16; tmp.close_delay = state->close_delay; @@ -666,11 +667,10 @@ static int uart_get_info(struct uart_info *info, struct serial_struct *retinfo) } static int -uart_set_info(struct uart_info *info, struct serial_struct *newinfo) +uart_set_info(struct uart_state *state, struct serial_struct *newinfo) { struct serial_struct new_serial; - struct uart_state *state = info->state; - struct uart_port *port = info->port; + struct uart_port *port = state->port; unsigned long new_port; unsigned int change_irq, change_port, old_flags; unsigned int old_custom_divisor; @@ -692,7 +692,7 @@ uart_set_info(struct uart_info *info, struct serial_struct *newinfo) * module insertion/removal doesn't change anything * under us. */ - down(&port_sem); + down(&state->sem); change_irq = new_serial.irq != port->irq; @@ -745,14 +745,14 @@ uart_set_info(struct uart_info *info, struct serial_struct *newinfo) /* * Make sure that we are the sole user of this port. */ - if (state->count > 1 || info->blocked_open != 0) + if (uart_users(state) > 1) goto exit; /* * We need to shutdown the serial port at the old * port/type/irq combination. */ - uart_shutdown(info); + uart_shutdown(state); } if (change_port) { @@ -813,18 +813,21 @@ uart_set_info(struct uart_info *info, struct serial_struct *newinfo) port->irq = new_serial.irq; port->uartclk = new_serial.baud_base * 16; - port->flags = new_serial.flags & UPF_CHANGE_MASK; + port->flags = (port->flags & ~UPF_CHANGE_MASK) | + (new_serial.flags & UPF_CHANGE_MASK); port->custom_divisor = new_serial.custom_divisor; state->close_delay = new_serial.close_delay * HZ / 100; state->closing_wait = new_serial.closing_wait * HZ / 100; port->fifosize = new_serial.xmit_fifo_size; - info->tty->low_latency = (port->flags & UPF_LOW_LATENCY) ? 1 : 0; + if (state->info->tty) + state->info->tty->low_latency = + (port->flags & UPF_LOW_LATENCY) ? 1 : 0; check_and_exit: retval = 0; if (port->type == PORT_UNKNOWN) goto exit; - if (info->flags & UIF_INITIALIZED) { + if (state->info->flags & UIF_INITIALIZED) { if (((old_flags ^ port->flags) & UPF_SPD_MASK) || old_custom_divisor != port->custom_divisor) { /* If they're setting up a custom divisor or speed, @@ -832,25 +835,26 @@ uart_set_info(struct uart_info *info, struct serial_struct *newinfo) * need to rate-limit; it's CAP_SYS_ADMIN only. */ if (port->flags & UPF_SPD_MASK) { printk(KERN_NOTICE "%s sets custom speed on %s%d. This is deprecated.\n", - current->comm, info->tty->driver.name, - info->port->line); + current->comm, state->info->tty->driver.name, + state->port->line); } - uart_change_speed(info, NULL); + uart_change_speed(state, NULL); } } else - retval = uart_startup(info, 1); + retval = uart_startup(state, 1); exit: - up(&port_sem); + up(&state->sem); return retval; } /* - * uart_get_lsr_info - get line status register info + * uart_get_lsr_info - get line status register info. + * Note: uart_ioctl protects us against hangups. */ -static int uart_get_lsr_info(struct uart_info *info, unsigned int *value) +static int uart_get_lsr_info(struct uart_state *state, unsigned int *value) { - struct uart_port *port = info->port; + struct uart_port *port = state->port; unsigned int result; result = port->ops->tx_empty(port); @@ -861,9 +865,9 @@ static int uart_get_lsr_info(struct uart_info *info, unsigned int *value) * avoid a race condition (depending on when the transmit * interrupt happens). */ - if (info->port->x_char || - ((uart_circ_chars_pending(&info->xmit) > 0) && - !info->tty->stopped && !info->tty->hw_stopped)) + if (port->x_char || + ((uart_circ_chars_pending(&state->info->xmit) > 0) && + !state->info->tty->stopped && !state->info->tty->hw_stopped)) result &= ~TIOCSER_TEMT; return put_user(result, value); @@ -888,6 +892,8 @@ uart_set_modem_info(struct uart_port *port, unsigned int cmd, if (get_user(arg, value)) return -EFAULT; + arg &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2; + set = clear = 0; switch (cmd) { case TIOCMBIS: @@ -911,8 +917,8 @@ uart_set_modem_info(struct uart_port *port, unsigned int cmd, static void uart_break_ctl(struct tty_struct *tty, int break_state) { - struct uart_info *info = tty->driver_data; - struct uart_port *port = info->port; + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; BUG_ON(!kernel_locked()); @@ -920,25 +926,25 @@ static void uart_break_ctl(struct tty_struct *tty, int break_state) port->ops->break_ctl(port, break_state); } -static int uart_do_autoconfig(struct uart_info *info) +static int uart_do_autoconfig(struct uart_state *state) { - struct uart_port *port = info->port; + struct uart_port *port = state->port; int flags, ret; if (!capable(CAP_SYS_ADMIN)) return -EPERM; /* - * Take the 'count' lock. This prevents count - * from incrementing, and hence any extra opens - * of the port while we're auto-configging. + * Take the per-port semaphore. This prevents count from + * changing, and hence any extra opens of the port while + * we're auto-configuring. */ - if (down_interruptible(&port_sem)) + if (down_interruptible(&state->sem)) return -ERESTARTSYS; ret = -EBUSY; - if (info->state->count == 1 && info->blocked_open == 0) { - uart_shutdown(info); + if (uart_users(state) == 1) { + uart_shutdown(state); /* * If we already have a port type configured, @@ -957,16 +963,22 @@ static int uart_do_autoconfig(struct uart_info *info) */ port->ops->config_port(port, flags); - ret = uart_startup(info, 1); + ret = uart_startup(state, 1); } - up(&port_sem); + up(&state->sem); return ret; } +/* + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change + * - mask passed in arg for lines of interest + * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) + * Caller should use TIOCGICOUNT to see which one it was + */ static int -uart_wait_modem_status(struct uart_info *info, unsigned long arg) +uart_wait_modem_status(struct uart_state *state, unsigned long arg) { - struct uart_port *port = info->port; + struct uart_port *port = state->port; DECLARE_WAITQUEUE(wait, current); struct uart_icount cprev, cnow; int ret; @@ -983,7 +995,7 @@ uart_wait_modem_status(struct uart_info *info, unsigned long arg) port->ops->enable_ms(port); spin_unlock_irq(&port->lock); - add_wait_queue(&info->delta_msr_wait, &wait); + add_wait_queue(&state->info->delta_msr_wait, &wait); for (;;) { spin_lock_irq(&port->lock); memcpy(&cnow, &port->icount, sizeof(struct uart_icount)); @@ -1011,117 +1023,144 @@ uart_wait_modem_status(struct uart_info *info, unsigned long arg) } current->state = TASK_RUNNING; - remove_wait_queue(&info->delta_msr_wait, &wait); + remove_wait_queue(&state->info->delta_msr_wait, &wait); return ret; } /* - * Called via sys_ioctl under the BKL. We can use spin_lock_irq() here. + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. */ static int -uart_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, - unsigned long arg) +uart_get_count(struct uart_state *state, struct serial_icounter_struct *icnt) { - struct uart_info *info = tty->driver_data; struct serial_icounter_struct icount; struct uart_icount cnow; + struct uart_port *port = state->port; + + spin_lock_irq(&port->lock); + memcpy(&cnow, &port->icount, sizeof(struct uart_icount)); + spin_unlock_irq(&port->lock); + + icount.cts = cnow.cts; + icount.dsr = cnow.dsr; + icount.rng = cnow.rng; + icount.dcd = cnow.dcd; + icount.rx = cnow.rx; + icount.tx = cnow.tx; + icount.frame = cnow.frame; + icount.overrun = cnow.overrun; + icount.parity = cnow.parity; + icount.brk = cnow.brk; + icount.buf_overrun = cnow.buf_overrun; + + return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0; +} + +/* + * Called via sys_ioctl under the BKL. We can use spin_lock_irq() here. + */ +static int +uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct uart_state *state = tty->driver_data; int ret = -ENOIOCTLCMD; BUG_ON(!kernel_locked()); - if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && - (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && - (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; + /* + * These ioctls don't rely on the hardware to be present. + */ + switch (cmd) { + case TIOCGSERIAL: + ret = uart_get_info(state, (struct serial_struct *)arg); + break; + + case TIOCSSERIAL: + ret = uart_set_info(state, (struct serial_struct *)arg); + break; + + case TIOCSERCONFIG: + ret = uart_do_autoconfig(state); + break; + + case TIOCSERGWILD: /* obsolete */ + case TIOCSERSWILD: /* obsolete */ + ret = 0; + break; } - switch (cmd) { - case TIOCMGET: - ret = uart_get_modem_info(info->port, - (unsigned int *)arg); - break; + if (ret != -ENOIOCTLCMD) + goto out; - case TIOCMBIS: - case TIOCMBIC: - case TIOCMSET: - ret = uart_set_modem_info(info->port, cmd, - (unsigned int *)arg); - break; + if (tty->flags & (1 << TTY_IO_ERROR)) { + ret = -EIO; + goto out; + } - case TIOCGSERIAL: - ret = uart_get_info(info, (struct serial_struct *)arg); - break; + /* + * The following should only be used when hardware is present. + */ + switch (cmd) { + case TIOCMIWAIT: + ret = uart_wait_modem_status(state, arg); + break; - case TIOCSSERIAL: - ret = uart_set_info(info, (struct serial_struct *)arg); - break; + case TIOCGICOUNT: + ret = uart_get_count(state, (struct serial_icounter_struct *)arg); + break; + } - case TIOCSERCONFIG: - ret = uart_do_autoconfig(info); - break; + if (ret != -ENOIOCTLCMD) + goto out; - case TIOCSERGETLSR: /* Get line status register */ - ret = uart_get_lsr_info(info, (unsigned int *)arg); - break; + down(&state->sem); - /* - * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change - * - mask passed in arg for lines of interest - * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) - * Caller should use TIOCGICOUNT to see which one it was - */ - case TIOCMIWAIT: - ret = uart_wait_modem_status(info, arg); - break; + if (tty_hung_up_p(filp)) { + ret = -EIO; + goto out_up; + } - /* - * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) - * Return: write counters to the user passed counter struct - * NB: both 1->0 and 0->1 transitions are counted except for - * RI where only 0->1 is counted. - */ - case TIOCGICOUNT: - spin_lock_irq(&info->port->lock); - memcpy(&cnow, &info->port->icount, - sizeof(struct uart_icount)); - spin_unlock_irq(&info->port->lock); - - icount.cts = cnow.cts; - icount.dsr = cnow.dsr; - icount.rng = cnow.rng; - icount.dcd = cnow.dcd; - icount.rx = cnow.rx; - icount.tx = cnow.tx; - icount.frame = cnow.frame; - icount.overrun = cnow.overrun; - icount.parity = cnow.parity; - icount.brk = cnow.brk; - icount.buf_overrun = cnow.buf_overrun; - - ret = copy_to_user((void *)arg, &icount, sizeof(icount)) - ? -EFAULT : 0; - break; + /* + * All these rely on hardware being present and need to be + * protected against the tty being hung up. + */ + switch (cmd) { + case TIOCMGET: + ret = uart_get_modem_info(state->port, (unsigned int *)arg); + break; - case TIOCSERGWILD: /* obsolete */ - case TIOCSERSWILD: /* obsolete */ - ret = 0; - break; + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + ret = uart_set_modem_info(state->port, cmd, + (unsigned int *)arg); + break; - default: { - struct uart_port *port = info->port; - if (port->ops->ioctl) - ret = port->ops->ioctl(port, cmd, arg); - break; - } + case TIOCSERGETLSR: /* Get line status register */ + ret = uart_get_lsr_info(state, (unsigned int *)arg); + break; + + default: { + struct uart_port *port = state->port; + if (port->ops->ioctl) + ret = port->ops->ioctl(port, cmd, arg); + break; + } } + out_up: + up(&state->sem); + out: return ret; } static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios) { - struct uart_info *info = tty->driver_data; + struct uart_state *state = tty->driver_data; unsigned long flags; unsigned int cflag = tty->termios->c_cflag; @@ -1137,11 +1176,11 @@ static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) return; - uart_change_speed(info, old_termios); + uart_change_speed(state, old_termios); /* Handle transition to B0 status */ if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) - uart_clear_mctrl(info->port, TIOCM_RTS | TIOCM_DTR); + uart_clear_mctrl(state->port, TIOCM_RTS | TIOCM_DTR); /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { @@ -1149,15 +1188,15 @@ static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios if (!(cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) mask |= TIOCM_RTS; - uart_set_mctrl(info->port, mask); + uart_set_mctrl(state->port, mask); } /* Handle turning off CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) { - spin_lock_irqsave(&info->port->lock, flags); + spin_lock_irqsave(&state->port->lock, flags); tty->hw_stopped = 0; __uart_start(tty); - spin_unlock_irqrestore(&info->port->lock, flags); + spin_unlock_irqrestore(&state->port->lock, flags); } #if 0 @@ -1169,7 +1208,7 @@ static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios */ if (!(old_termios->c_cflag & CLOCAL) && (tty->termios->c_cflag & CLOCAL)) - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&state->info->open_wait); #endif } @@ -1180,29 +1219,17 @@ static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios */ static void uart_close(struct tty_struct *tty, struct file *filp) { - struct uart_driver *drv = (struct uart_driver *)tty->driver.driver_state; - struct uart_info *info = tty->driver_data; - struct uart_port *port = info->port; - struct uart_state *state; - unsigned long flags; + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; BUG_ON(!kernel_locked()); + DPRINTK("uart_close(%d) called\n", port->line); - if (!info) - return; - - state = info->state; + down(&state->sem); - DPRINTK("uart_close() called\n"); - - /* - * This is safe, as long as the BKL exists in - * do_tty_hangup(), and we're protected by the BKL. - */ if (tty_hung_up_p(filp)) goto done; - spin_lock_irqsave(&info->port->lock, flags); if ((tty->count == 1) && (state->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty @@ -1217,34 +1244,28 @@ static void uart_close(struct tty_struct *tty, struct file *filp) } if (--state->count < 0) { printk("rs_close: bad serial port count for %s%d: %d\n", - tty->driver.name, info->port->line, state->count); + tty->driver.name, port->line, state->count); state->count = 0; } - if (state->count) { - spin_unlock_irqrestore(&info->port->lock, flags); + if (state->count) goto done; - } - - /* - * The UIF_CLOSING flag protects us against further opens - * of this port. - */ - info->flags |= UIF_CLOSING; - spin_unlock_irqrestore(&info->port->lock, flags); /* * Now we wait for the transmit buffer to clear; and we notify - * the line discipline to only process XON/XOFF characters. + * the line discipline to only process XON/XOFF characters by + * setting tty->closing. */ tty->closing = 1; - if (info->state->closing_wait != USF_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, info->state->closing_wait); + + if (state->closing_wait != USF_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, state->closing_wait); /* * At this point, we stop accepting input. To do this, we * disable the receive line status interrupts. */ - if (info->flags & UIF_INITIALIZED) { + if (state->info->flags & UIF_INITIALIZED) { + unsigned long flags; spin_lock_irqsave(&port->lock, flags); port->ops->stop_rx(port); spin_unlock_irqrestore(&port->lock, flags); @@ -1255,46 +1276,38 @@ static void uart_close(struct tty_struct *tty, struct file *filp) */ uart_wait_until_sent(tty, port->timeout); } - down(&port_sem); - uart_shutdown(info); - up(&port_sem); + + uart_shutdown(state); uart_flush_buffer(tty); if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); tty->closing = 0; - info->tty = NULL; - if (info->blocked_open) { - if (info->state->close_delay) { + state->info->tty = NULL; + + if (state->info->blocked_open) { + if (state->close_delay) { set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(info->state->close_delay); + schedule_timeout(state->close_delay); set_current_state(TASK_RUNNING); } - } else { -#ifdef CONFIG_PM - /* - * Put device into D3 state. - */ - pm_send(info->state->pm, PM_SUSPEND, (void *)3); -#else - if (port->ops->pm) - port->ops->pm(port, 3, 0); -#endif + } else if (!uart_console(port)) { + uart_change_pm(state, 3); } /* * Wake up anyone trying to open this port. */ - info->flags &= ~(UIF_NORMAL_ACTIVE|UIF_CLOSING); - wake_up_interruptible(&info->open_wait); + state->info->flags &= ~UIF_NORMAL_ACTIVE; + wake_up_interruptible(&state->info->open_wait); done: - module_put(drv->owner); + up(&state->sem); } static void uart_wait_until_sent(struct tty_struct *tty, int timeout) { - struct uart_info *info = tty->driver_data; - struct uart_port *port = info->port; + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; unsigned long char_time, expire; BUG_ON(!kernel_locked()); @@ -1358,23 +1371,22 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout) */ static void uart_hangup(struct tty_struct *tty) { - struct uart_info *info = tty->driver_data; - struct uart_state *state = info->state; + struct uart_state *state = tty->driver_data; BUG_ON(!kernel_locked()); + DPRINTK("uart_hangup(%d)\n", state->port->line); - uart_flush_buffer(tty); - down(&port_sem); - if (info->flags & UIF_CLOSING) { - up(&port_sem); - return; + down(&state->sem); + if (state->info && state->info->flags & UIF_NORMAL_ACTIVE) { + uart_flush_buffer(tty); + uart_shutdown(state); + state->count = 0; + state->info->flags &= ~UIF_NORMAL_ACTIVE; + state->info->tty = NULL; + wake_up_interruptible(&state->info->open_wait); + wake_up_interruptible(&state->info->delta_msr_wait); } - uart_shutdown(info); - state->count = 0; - info->flags &= ~UIF_NORMAL_ACTIVE; - info->tty = NULL; - up(&port_sem); - wake_up_interruptible(&info->open_wait); + up(&state->sem); } /* @@ -1383,18 +1395,15 @@ static void uart_hangup(struct tty_struct *tty) * kernel settings, and the settings init adopts when it opens the port * for the first time. */ -static void uart_update_termios(struct uart_info *info) +static void uart_update_termios(struct uart_state *state) { - struct tty_struct *tty = info->tty; - -#ifdef CONFIG_SERIAL_CORE_CONSOLE - struct console *c = info->port->cons; + struct tty_struct *tty = state->info->tty; + struct uart_port *port = state->port; - if (c && c->cflag && c->index == info->port->line) { - tty->termios->c_cflag = c->cflag; - c->cflag = 0; + if (uart_console(port) && port->cons->cflag) { + tty->termios->c_cflag = port->cons->cflag; + port->cons->cflag = 0; } -#endif /* * If the device failed to grab its irq resources, @@ -1405,22 +1414,26 @@ static void uart_update_termios(struct uart_info *info) /* * Make termios settings take effect. */ - uart_change_speed(info, NULL); + uart_change_speed(state, NULL); /* * And finally enable the RTS and DTR signals. */ if (tty->termios->c_cflag & CBAUD) - uart_set_mctrl(info->port, TIOCM_DTR | TIOCM_RTS); + uart_set_mctrl(port, TIOCM_DTR | TIOCM_RTS); } } +/* + * Block the open until the port is ready. We must be called with + * the per-port semaphore held. + */ static int -uart_block_til_ready(struct file *filp, struct uart_info *info) +uart_block_til_ready(struct file *filp, struct uart_state *state) { DECLARE_WAITQUEUE(wait, current); - struct uart_state *state = info->state; - struct uart_port *port = info->port; + struct uart_info *info = state->info; + struct uart_port *port = state->port; info->blocked_open++; state->count--; @@ -1432,18 +1445,10 @@ uart_block_til_ready(struct file *filp, struct uart_info *info) /* * If we have been hung up, tell userspace/restart open. */ - if (tty_hung_up_p(filp)) + if (tty_hung_up_p(filp) || info->tty == NULL) break; /* - * If the device is in the middle of being closed, block - * until it's done. We will need to re-initialise the - * port. Hmm, is it legal to block a non-blocking open? - */ - if (info->flags & UIF_CLOSING) - goto wait; - - /* * If the port has been closed, tell userspace/restart open. */ if (!(info->flags & UIF_INITIALIZED)) @@ -1470,7 +1475,7 @@ uart_block_til_ready(struct file *filp, struct uart_info *info) * the data from the modem. */ if (info->tty->termios->c_cflag & CBAUD) - uart_set_mctrl(info->port, TIOCM_DTR); + uart_set_mctrl(port, TIOCM_DTR); /* * and wait for the carrier to indicate that the @@ -1479,8 +1484,9 @@ uart_block_til_ready(struct file *filp, struct uart_info *info) if (port->ops->get_mctrl(port) & TIOCM_CAR) break; - wait: + up(&state->sem); schedule(); + down(&state->sem); if (signal_pending(current)) break; @@ -1494,52 +1500,56 @@ uart_block_til_ready(struct file *filp, struct uart_info *info) if (signal_pending(current)) return -ERESTARTSYS; - if (info->tty->flags & (1 << TTY_IO_ERROR)) - return 0; - - if (tty_hung_up_p(filp) || !(info->flags & UIF_INITIALIZED)) + if (!info->tty || tty_hung_up_p(filp)) return (port->flags & UPF_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS; return 0; } -static struct uart_info *uart_get(struct uart_driver *drv, int line) +static struct uart_state *uart_get(struct uart_driver *drv, int line) { - struct uart_state *state = drv->state + line; - struct uart_info *info = NULL; + struct uart_state *state; down(&port_sem); - if (!state->port) + state = drv->state + line; + if (down_interruptible(&state->sem)) { + state = ERR_PTR(-ERESTARTSYS); goto out; + } state->count++; - info = state->info; + if (!state->port) { + state->count--; + up(&state->sem); + state = ERR_PTR(-ENXIO); + goto out; + } - if (!info) { - info = kmalloc(sizeof(struct uart_info), GFP_KERNEL); - if (info) { - memset(info, 0, sizeof(struct uart_info)); - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->delta_msr_wait); + if (!state->info) { + state->info = kmalloc(sizeof(struct uart_info), GFP_KERNEL); + if (state->info) { + memset(state->info, 0, sizeof(struct uart_info)); + init_waitqueue_head(&state->info->open_wait); + init_waitqueue_head(&state->info->delta_msr_wait); /* * Link the info into the other structures. */ - info->port = state->port; - info->state = state; - state->port->info = info; - - tasklet_init(&info->tlet, uart_tasklet_action, - (unsigned long)info); - state->info = info; - } else + state->port->info = state->info; + + tasklet_init(&state->info->tlet, uart_tasklet_action, + (unsigned long)state); + } else { state->count--; + up(&state->sem); + state = ERR_PTR(-ENOMEM); + } } out: up(&port_sem); - return info; + return state; } /* @@ -1555,11 +1565,10 @@ static struct uart_info *uart_get(struct uart_driver *drv, int line) static int uart_open(struct tty_struct *tty, struct file *filp) { struct uart_driver *drv = (struct uart_driver *)tty->driver.driver_state; - struct uart_info *info; + struct uart_state *state; int retval, line = minor(tty->device) - tty->driver.minor_start; BUG_ON(!kernel_locked()); - DPRINTK("uart_open(%d) called\n", line); /* @@ -1572,92 +1581,66 @@ static int uart_open(struct tty_struct *tty, struct file *filp) goto fail; /* - * If we fail to increment the module use count, we can't have - * any other users of this tty (since this implies that the module - * is about to be unloaded). Therefore, it is safe to set - * tty->driver_data to be NULL, so uart_close() doesn't bite us. + * We take the semaphore inside uart_get to guarantee that we won't + * be re-entered while allocating the info structure, or while we + * request any IRQs that the driver may need. This also has the nice + * side-effect that it delays the action of uart_hangup, so we can + * guarantee that info->tty will always contain something reasonable. */ - if (!try_module_get(drv->owner)) { - tty->driver_data = NULL; + state = uart_get(drv, line); + if (IS_ERR(state)) { + retval = PTR_ERR(state); goto fail; } /* - * FIXME: This one isn't fun. We can't guarantee that the tty isn't - * already in open, nor can we guarantee the state of tty->driver_data - */ - info = uart_get(drv, line); - retval = -ENOMEM; - if (!info) { - if (tty->driver_data) - goto fail; - else - goto out; - } - - /* * Once we set tty->driver_data here, we are guaranteed that * uart_close() will decrement the driver module use count. * Any failures from here onwards should not touch the count. */ - tty->driver_data = info; - tty->low_latency = (info->port->flags & UPF_LOW_LATENCY) ? 1 : 0; + tty->driver_data = state; + tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0; tty->alt_speed = 0; - info->tty = tty; + state->info->tty = tty; /* * If the port is in the middle of closing, bail out now. */ - if (tty_hung_up_p(filp) || (info->flags & UIF_CLOSING)) { - wait_event_interruptible(info->open_wait, - !(info->flags & UIF_CLOSING)); - retval = (info->port->flags & UPF_HUP_NOTIFY) ? + if (tty_hung_up_p(filp)) { + retval = (state->port->flags & UPF_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS; + state->count--; + up(&state->sem); goto fail; } /* * Make sure the device is in D0 state. */ - if (info->state->count == 1) { -#ifdef CONFIG_PM - pm_send(info->state->pm, PM_RESUME, (void *)0); -#else - struct uart_port *port = info->port; - if (port->ops->pm) - port->ops->pm(port, 0, 3); -#endif - } + if (state->count == 1) + uart_change_pm(state, 0); /* - * Start up the serial port. We have this semaphore here to - * prevent uart_startup or uart_shutdown being re-entered if - * we sleep while requesting an IRQ. + * Start up the serial port. */ - down(&port_sem); - retval = uart_startup(info, 0); - up(&port_sem); - if (retval) - goto fail; + retval = uart_startup(state, 0); /* - * Wait until the port is ready. + * If we succeeded, wait until the port is ready. */ - retval = uart_block_til_ready(filp, info); + if (retval == 0) + retval = uart_block_til_ready(filp, state); + up(&state->sem); /* * If this is the first open to succeed, adjust things to suit. */ - if (retval == 0 && !(info->flags & UIF_NORMAL_ACTIVE)) { - info->flags |= UIF_NORMAL_ACTIVE; + if (retval == 0 && !(state->info->flags & UIF_NORMAL_ACTIVE)) { + state->info->flags |= UIF_NORMAL_ACTIVE; - uart_update_termios(info); + uart_update_termios(state); } - return retval; - - out: - module_put(drv->owner); fail: return retval; } @@ -1892,115 +1875,98 @@ uart_set_options(struct uart_port *port, struct console *co, } #endif /* CONFIG_SERIAL_CORE_CONSOLE */ -#ifdef CONFIG_PM -/* - * Serial port power management. - * - * This is pretty coarse at the moment - either all on or all off. We - * should probably some day do finer power management here some day. - * - * We don't actually save any state; the serial driver already has the - * state held internally to re-setup the port when we come out of D3. - */ -static int uart_pm_set_state(struct uart_state *state, int pm_state, int oldstate) +static void uart_change_pm(struct uart_state *state, int pm_state) { - struct uart_port *port; - struct uart_ops *ops; - int running = state->info && - state->info->flags & UIF_INITIALIZED; + struct uart_port *port = state->port; + if (port->ops->pm) + port->ops->pm(port, pm_state, state->pm_state); + state->pm_state = pm_state; +} - down(&port_sem); +int uart_suspend_port(struct uart_driver *drv, struct uart_port *port, u32 level) +{ + struct uart_state *state = drv->state + port->line; - if (!state->port || state->port->type == PORT_UNKNOWN) { - up(&port_sem); - return 0; - } + down(&state->sem); - port = state->port; - ops = port->ops; + switch (level) { + case SUSPEND_SAVE_STATE: + if (state->info && state->info->flags & UIF_INITIALIZED) { + struct uart_ops *ops = port->ops; - DPRINTK("pm: %08x: %d -> %d, %srunning\n", - port->iobase, dev->state, pm_state, running ? "" : "not "); + spin_lock_irq(&port->lock); + ops->stop_tx(port, 0); + ops->set_mctrl(port, 0); + ops->stop_rx(port); + spin_unlock_irq(&port->lock); - if (pm_state == 0) { - if (ops->pm) - ops->pm(port, pm_state, oldstate); - if (running) { /* - * The port lock isn't taken here - - * the port isn't initialised. + * Wait for the transmitter to empty. */ - ops->set_mctrl(port, 0); - ops->startup(port); - uart_change_speed(state->info, NULL); - spin_lock_irq(&port->lock); - ops->set_mctrl(port, port->mctrl); - ops->start_tx(port, 0); - spin_unlock_irq(&port->lock); + while (!ops->tx_empty(port)) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(10*HZ/1000); + } + set_current_state(TASK_RUNNING); + + ops->shutdown(port); } + break; - /* - * Re-enable the console device after suspending. - */ - if (port->cons && port->cons->index == port->line) - port->cons->flags |= CON_ENABLED; - } else if (pm_state == 1) { - if (ops->pm) - ops->pm(port, pm_state, oldstate); - } else { + case SUSPEND_POWER_DOWN: /* * Disable the console device before suspending. */ - if (port->cons && port->cons->index == port->line) + if (uart_console(port)) port->cons->flags &= ~CON_ENABLED; - if (running) { - spin_lock_irq(&port->lock); - ops->stop_tx(port, 0); - ops->set_mctrl(port, 0); - ops->stop_rx(port); - spin_unlock_irq(&port->lock); - ops->shutdown(port); - } - if (ops->pm) - ops->pm(port, pm_state, oldstate); + uart_change_pm(state, 3); + break; } - up(&port_sem); + + up(&state->sem); return 0; } -/* - * Wakeup support. - */ -static int uart_pm_set_wakeup(struct uart_state *state, int data) +int uart_resume_port(struct uart_driver *drv, struct uart_port *port, u32 level) { - int err = 0; - - if (state->port->ops->set_wake) - err = state->port->ops->set_wake(state->port, data); + struct uart_state *state = drv->state + port->line; - return err; -} + down(&state->sem); -static int uart_pm(struct pm_dev *dev, pm_request_t rqst, void *data) -{ - struct uart_state *state = dev->data; - int err = 0; + switch (level) { + case RESUME_POWER_ON: + uart_change_pm(state, 0); - switch (rqst) { - case PM_SUSPEND: - case PM_RESUME: - err = uart_pm_set_state(state, (int)(long)data, dev->state); + /* + * Re-enable the console device after suspending. + */ + if (uart_console(port)) { + uart_change_speed(state, NULL); + port->cons->flags |= CON_ENABLED; + } break; - case PM_SET_WAKEUP: - err = uart_pm_set_wakeup(state, (int)(long)data); + case RESUME_RESTORE_STATE: + if (state->info && state->info->flags & UIF_INITIALIZED) { + struct uart_ops *ops = port->ops; + + ops->set_mctrl(port, 0); + ops->startup(port); + uart_change_speed(state, NULL); + spin_lock_irq(&port->lock); + ops->set_mctrl(port, port->mctrl); + ops->start_tx(port, 0); + spin_unlock_irq(&port->lock); + } break; } - return err; + + up(&state->sem); + + return 0; } -#endif static inline void uart_report_port(struct uart_driver *drv, struct uart_port *port) @@ -2022,17 +1988,11 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port) } static void -__uart_register_port(struct uart_driver *drv, struct uart_state *state, - struct uart_port *port) +uart_configure_port(struct uart_driver *drv, struct uart_state *state, + struct uart_port *port) { unsigned int flags; - state->port = port; - - spin_lock_init(&port->lock); - port->cons = drv->cons; - port->info = state->info; - /* * If there isn't a port here, don't do anything further. */ @@ -2051,12 +2011,6 @@ __uart_register_port(struct uart_driver *drv, struct uart_state *state, port->ops->config_port(port, flags); } - /* - * Register the port whether it's detected or not. This allows - * setserial to be used to alter this ports parameters. - */ - tty_register_device(drv->tty_driver, drv->minor + port->line); - if (port->type != PORT_UNKNOWN) { unsigned long flags; @@ -2070,51 +2024,33 @@ __uart_register_port(struct uart_driver *drv, struct uart_state *state, port->ops->set_mctrl(port, 0); spin_unlock_irqrestore(&port->lock, flags); -#ifdef CONFIG_PM /* * Power down all ports by default, except the - * console if we have one. We need to drop the - * port semaphore here. + * console if we have one. */ - if (state->pm && (!drv->cons || port->line != drv->cons->index)) { - up(&port_sem); - pm_send(state->pm, PM_SUSPEND, (void *)3); - down(&port_sem); - } -#endif + if (!uart_console(port)) + uart_change_pm(state, 3); } } /* - * Hangup the port. This must be done outside the port_sem - * since uart_hangup() grabs this same semaphore. Grr. + * This reverses the affects of uart_configure_port, hanging up the + * port before removal. */ static void -__uart_hangup_port(struct uart_driver *drv, struct uart_state *state) +uart_unconfigure_port(struct uart_driver *drv, struct uart_state *state) { + struct uart_port *port = state->port; struct uart_info *info = state->info; if (info && info->tty) tty_vhangup(info->tty); -} -/* - * This reverses the affects of __uart_register_port. - */ -static void -__uart_unregister_port(struct uart_driver *drv, struct uart_state *state) -{ - struct uart_port *port = state->port; - struct uart_info *info = state->info; + down(&state->sem); state->info = NULL; /* - * Remove the devices from devfs - */ - tty_unregister_device(drv->tty_driver, drv->minor + port->line); - - /* * Free the port IO and memory resources, if any. */ if (port->type != PORT_UNKNOWN) @@ -2132,6 +2068,8 @@ __uart_unregister_port(struct uart_driver *drv, struct uart_state *state) tasklet_kill(&info->tlet); kfree(info); } + + up(&state->sem); } /** @@ -2186,6 +2124,7 @@ int uart_register_driver(struct uart_driver *drv) drv->tty_driver = normal; normal->magic = TTY_DRIVER_MAGIC; + normal->owner = drv->owner; normal->driver_name = drv->driver_name; normal->name = drv->dev_name; normal->major = drv->major; @@ -2232,20 +2171,13 @@ int uart_register_driver(struct uart_driver *drv) state->close_delay = 5 * HZ / 10; state->closing_wait = 30 * HZ; -#ifdef CONFIG_PM - state->pm = pm_register(PM_SYS_DEV, PM_SYS_COM, uart_pm); - if (state->pm) - state->pm->data = state; -#endif + + init_MUTEX(&state->sem); } retval = tty_register_driver(normal); out: if (retval < 0) { -#ifdef CONFIG_PM - for (i = 0; i < drv->nr; i++) - pm_unregister(drv->state[i].pm); -#endif kfree(normal); kfree(drv->state); kfree(termios); @@ -2264,11 +2196,6 @@ int uart_register_driver(struct uart_driver *drv) */ void uart_unregister_driver(struct uart_driver *drv) { - int i; - - for (i = 0; i < drv->nr; i++) - pm_unregister(drv->state[i].pm); - tty_unregister_driver(drv->tty_driver); kfree(drv->state); @@ -2289,6 +2216,7 @@ void uart_unregister_driver(struct uart_driver *drv) int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) { struct uart_state *state; + int ret = 0; BUG_ON(in_interrupt()); @@ -2298,10 +2226,29 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) state = drv->state + port->line; down(&port_sem); - __uart_register_port(drv, state, port); + if (state->port) { + ret = -EINVAL; + goto out; + } + + state->port = port; + + spin_lock_init(&port->lock); + port->cons = drv->cons; + port->info = state->info; + + uart_configure_port(drv, state, port); + + /* + * Register the port whether it's detected or not. This allows + * setserial to be used to alter this ports parameters. + */ + tty_register_device(drv->tty_driver, drv->minor + port->line); + + out: up(&port_sem); - return 0; + return ret; } /** @@ -2323,10 +2270,14 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) printk(KERN_ALERT "Removing wrong port: %p != %p\n", state->port, port); - __uart_hangup_port(drv, state); - down(&port_sem); - __uart_unregister_port(drv, state); + + /* + * Remove the devices from devfs + */ + tty_unregister_device(drv->tty_driver, drv->minor + port->line); + + uart_unconfigure_port(drv, state); state->port = NULL; up(&port_sem); @@ -2426,8 +2377,7 @@ int uart_register_port(struct uart_driver *drv, struct uart_port *port) * alter it underneath itself - the port may be open and * trying to do useful work. */ - if (state->count != 0 || - (state->info && state->info->blocked_open != 0)) { + if (uart_users(state) != 0) { ret = -EBUSY; goto out; } @@ -2447,7 +2397,7 @@ int uart_register_port(struct uart_driver *drv, struct uart_port *port) state->port->line = state - drv->state; state->port->mapbase = port->mapbase; - __uart_register_port(drv, state, state->port); + uart_configure_port(drv, state, state->port); } ret = state->port->line; @@ -2479,16 +2429,16 @@ void uart_unregister_port(struct uart_driver *drv, int line) state = drv->state + line; - __uart_hangup_port(drv, state); - down(&port_sem); - __uart_unregister_port(drv, state); + uart_unconfigure_port(drv, state); up(&port_sem); } EXPORT_SYMBOL(uart_write_wakeup); EXPORT_SYMBOL(uart_register_driver); EXPORT_SYMBOL(uart_unregister_driver); +EXPORT_SYMBOL(uart_suspend_port); +EXPORT_SYMBOL(uart_resume_port); EXPORT_SYMBOL(uart_register_port); EXPORT_SYMBOL(uart_unregister_port); EXPORT_SYMBOL(uart_add_one_port); diff --git a/drivers/serial/sa1100.c b/drivers/serial/sa1100.c index cf3160b26f57..b32608e75444 100644 --- a/drivers/serial/sa1100.c +++ b/drivers/serial/sa1100.c @@ -32,6 +32,7 @@ #include <linux/serial.h> #include <linux/console.h> #include <linux/sysrq.h> +#include <linux/device.h> #include <asm/io.h> #include <asm/irq.h> @@ -857,12 +858,54 @@ static struct uart_driver sa1100_reg = { .cons = SA1100_CONSOLE, }; +static int sa1100_serial_suspend(struct device *dev, u32 state, u32 level) +{ + int i; + + for (i = 0; i < NR_PORTS; i++) + uart_suspend_port(&sa1100_reg, &sa1100_ports[i].port, level); + + return 0; +} + +static int sa1100_serial_resume(struct device *dev, u32 level) +{ + int i; + + for (i = 0; i < NR_PORTS; i++) + uart_resume_port(&sa1100_reg, &sa1100_ports[i].port, level); + + return 0; +} + +static struct device_driver sa11x0_serial_driver = { + .name = "sa11x0_serial", + .bus = &system_bus_type, + .devclass = &tty_devclass, + .suspend = sa1100_serial_suspend, + .resume = sa1100_serial_resume, +}; + +/* + * This "device" covers _all_ ISA 8250-compatible serial devices. + */ +static struct sys_device sa11x0_serial_devs = { + .name = "sa11x0_serial", + .id = 0, + .dev = { + .driver = &sa11x0_serial_driver, + }, +}; + static int __init sa1100_serial_init(void) { int ret; printk(KERN_INFO "Serial: SA11x0 driver $Revision: 1.50 $\n"); + driver_register(&sa11x0_serial_driver); + sys_device_register(&sa11x0_serial_devs); + sa1100_init_ports(); ret = uart_register_driver(&sa1100_reg); if (ret == 0) { diff --git a/drivers/usb/class/bluetty.c b/drivers/usb/class/bluetty.c index d7cc97c9eff1..b83068f9f13a 100644 --- a/drivers/usb/class/bluetty.c +++ b/drivers/usb/class/bluetty.c @@ -168,7 +168,7 @@ struct usb_bluetooth { int magic; struct usb_device * dev; struct tty_driver * tty_driver; /* the tty_driver for this device */ - struct tty_struct * tty; /* the coresponding tty for this port */ + struct tty_struct * tty; /* the corresponding tty for this port */ unsigned char minor; /* the starting minor number for this device */ int throttle; /* throttled by tty layer */ @@ -1328,7 +1328,7 @@ int usb_bluetooth_init(void) int i; int result; - /* Initalize our global data */ + /* Initialize our global data */ for (i = 0; i < BLUETOOTH_TTY_MINORS; ++i) { bluetooth_table[i] = NULL; } diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 5cd38d761590..7a6b3d0204e8 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -138,9 +138,9 @@ struct acm_line { */ struct acm { - struct usb_device *dev; /* the coresponding usb device */ + struct usb_device *dev; /* the corresponding usb device */ struct usb_interface *iface; /* the interfaces - +0 control +1 data */ - struct tty_struct *tty; /* the coresponding tty */ + struct tty_struct *tty; /* the corresponding tty */ struct urb *ctrlurb, *readurb, *writeurb; /* urbs */ struct acm_line line; /* line coding (bits, stop, parity) */ struct work_struct work; /* work queue entry for line discipline waking up */ diff --git a/drivers/usb/class/usb-midi.c b/drivers/usb/class/usb-midi.c index 4e6d970299da..1361485d7629 100644 --- a/drivers/usb/class/usb-midi.c +++ b/drivers/usb/class/usb-midi.c @@ -1270,7 +1270,7 @@ static void *find_descriptor( void *descStart, unsigned int descLength, void *af return NULL; } -/** Utility to find a class-specfic interface descriptor. +/** Utility to find a class-specific interface descriptor. * dsubtype is a descriptor subtype * Called by parseDescriptor(); **/ diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index cd039053c127..b8f282d5524a 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -516,7 +516,7 @@ descriptor_error: endpoint = &desc->endpoint[0].desc; - /* Output endpoint? Curiousier and curiousier.. */ + /* Output endpoint? Curiouser and curiouser.. */ if (!(endpoint->bEndpointAddress & USB_DIR_IN)) { goto descriptor_error; } @@ -958,7 +958,7 @@ static void usb_hub_events(void) int i, ret; /* - * We restart the list everytime to avoid a deadlock with + * We restart the list every time to avoid a deadlock with * deleting hubs downstream from this one. This should be * safe since we delete the hub from the event list. * Not the most efficient, but avoids deadlocks. diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index a33c33b08082..8b2624844cb1 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -69,7 +69,7 @@ void usb_free_urb(struct urb *urb) * usb_get_urb - increments the reference count of the urb * @urb: pointer to the urb to modify * - * This must be called whenever a urb is transfered from a device driver to a + * This must be called whenever a urb is transferred from a device driver to a * host controller driver. This allows proper reference counting to happen * for urbs. * @@ -383,7 +383,7 @@ int usb_unlink_urb(struct urb *urb) { /* FIXME * We should not care about the state here, but the host controllers - * die a horrible death if we submit a urb for a device that has been + * die a horrible death if we unlink a urb for a device that has been * physically removed. */ if (urb && diff --git a/drivers/usb/host/hc_simple.c b/drivers/usb/host/hc_simple.c index 5d4cf5c02971..f970656f0810 100644 --- a/drivers/usb/host/hc_simple.c +++ b/drivers/usb/host/hc_simple.c @@ -605,7 +605,7 @@ static struct urb *qu_return_urb (hci_t * hci, struct urb * urb, int resub_ok) /*************************************************************************** * Function Name : sh_scan_iso_urb_list * - * This function goes throught the isochronous urb list and schedule the + * This function goes through the isochronous urb list and schedule the * the transfer. * * Note: This function has not tested yet diff --git a/drivers/usb/host/hc_simple.h b/drivers/usb/host/hc_simple.h index dfb67e7d964e..d0289f62f058 100644 --- a/drivers/usb/host/hc_simple.h +++ b/drivers/usb/host/hc_simple.h @@ -44,8 +44,8 @@ struct virt_root_hub { int devnum; /* Address of Root Hub endpoint */ void *urb; /* interrupt URB of root hub */ int send; /* active flag */ - int interval; /* intervall of roothub interrupt transfers */ - struct timer_list rh_int_timer; /* intervall timer for rh interrupt EP */ + int interval; /* interval of roothub interrupt transfers */ + struct timer_list rh_int_timer; /* interval timer for rh interrupt EP */ }; #if 1 diff --git a/drivers/usb/host/hc_sl811.h b/drivers/usb/host/hc_sl811.h index 1618d233f236..8b9eed235088 100644 --- a/drivers/usb/host/hc_sl811.h +++ b/drivers/usb/host/hc_sl811.h @@ -368,7 +368,7 @@ struct hci; #define SL11H_CTL1VAL_RESET 8 -/* Interrut enable (addr 6) and interrupt status register bits (addr 0xD) */ +/* Interrupt enable (addr 6) and interrupt status register bits (addr 0xD) */ #define SL11H_INTMASK_XFERDONE 1 #define SL11H_INTMASK_SOFINTR 0x10 #define SL11H_INTMASK_INSRMV 0x20 diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index a25daa6c899f..f531767a9ecd 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -650,7 +650,7 @@ static void td_submit_urb ( /* calculate transfer length/status and update the urb * PRECONDITION: irqsafe (only for urb->status locking) */ -static void td_done (struct urb *urb, struct td *td) +static void td_done (struct ohci_hcd *ohci, struct urb *urb, struct td *td) { u32 tdINFO = le32_to_cpup (&td->hwINFO); int cc = 0; @@ -908,7 +908,7 @@ rescan_this: *prev = td->hwNextTD | savebits; /* HC may have partly processed this TD */ - td_done (urb, td); + td_done (ohci, urb, td); urb_priv->td_cnt++; /* if URB is done, clean up */ @@ -991,7 +991,7 @@ dl_done_list (struct ohci_hcd *ohci, struct td *td, struct pt_regs *regs) struct ed *ed = td->ed; /* update URB's length and status from TD */ - td_done (urb, td); + td_done (ohci, urb, td); urb_priv->td_cnt++; /* If all this urb's TDs are done, call complete() */ diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index c3c0a7505765..06541479c29f 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -136,7 +136,7 @@ static inline void uhci_add_complete(struct uhci_hcd *uhci, struct urb *urb) unsigned long flags; spin_lock_irqsave(&uhci->complete_list_lock, flags); - list_add(&urbp->complete_list, &uhci->complete_list); + list_add_tail(&urbp->complete_list, &uhci->complete_list); spin_unlock_irqrestore(&uhci->complete_list_lock, flags); } diff --git a/drivers/usb/image/hpusbscsi.c b/drivers/usb/image/hpusbscsi.c index 9590371e8203..1b74264ee8e5 100644 --- a/drivers/usb/image/hpusbscsi.c +++ b/drivers/usb/image/hpusbscsi.c @@ -303,7 +303,7 @@ static int hpusbscsi_scsi_queuecommand (Scsi_Cmnd *srb, scsi_callback callback) } else { usb_callback = simple_payload_callback; } - /* Now we find out which direction data is to be transfered in */ + /* Now we find out which direction data is to be transferred in */ hpusbscsi->current_data_pipe = DIRECTION_IS_IN(srb->cmnd[0]) ? usb_rcvbulkpipe(hpusbscsi->dev, hpusbscsi->ep_in) : @@ -410,7 +410,7 @@ DEBUG("Getting status byte %d \n",hpusbscsi->scsi_state_byte); } if (hpusbscsi->scallback != NULL && hpusbscsi->state == HP_STATE_WAIT && scsi_state != CHECK_CONDITION <<1 ) - /* we do a callback to the scsi layer if and only if all data has been transfered */ + /* we do a callback to the scsi layer if and only if all data has been transferred */ hpusbscsi->scallback(hpusbscsi->srb); TRACE_STATE; diff --git a/drivers/usb/image/hpusbscsi.h b/drivers/usb/image/hpusbscsi.h index 6e83344dcaa9..b56f091cdd39 100644 --- a/drivers/usb/image/hpusbscsi.h +++ b/drivers/usb/image/hpusbscsi.h @@ -85,7 +85,7 @@ static Scsi_Host_Template hpusbscsi_scsi_host_template = { /* defines for internal driver state */ #define HP_STATE_FREE 0 /*ready for next request */ -#define HP_STATE_BEGINNING 1 /*command being transfered */ +#define HP_STATE_BEGINNING 1 /*command being transferred */ #define HP_STATE_WORKING 2 /* data transfer stage */ #define HP_STATE_ERROR 3 /* error has been reported */ #define HP_STATE_WAIT 4 /* waiting for status transfer */ diff --git a/drivers/usb/image/scanner.c b/drivers/usb/image/scanner.c index c8d1dd5e0835..6fb7c46362d6 100644 --- a/drivers/usb/image/scanner.c +++ b/drivers/usb/image/scanner.c @@ -1,7 +1,7 @@ /* -*- linux-c -*- */ /* - * Driver for USB Scanners (linux-2.5.64) + * Driver for USB Scanners (linux-2.5) * * Copyright (C) 1999, 2000, 2001, 2002 David E. Nelson * Copyright (C) 2002, 2003 Henning Meier-Geinitz @@ -33,8 +33,8 @@ * 0.1 8/31/1999 * * Developed/tested using linux-2.3.15 with minor ohci.c changes to - * support short packes during bulk xfer mode. Some testing was - * done with ohci-hcd but the performace was low. Very limited + * support short packets during bulk xfer mode. Some testing was + * done with ohci-hcd but the performance was low. Very limited * testing was performed with uhci but I was unable to get it to * work. Initial relase to the linux-usb development effort. * @@ -338,7 +338,7 @@ * Till Kamppeter <till.kamppeter@gmx.net> and others for all the ids. * - Cleaned up list of vendor/product ids. * - Print information about user-supplied ids only once at startup instead - * of everytime any USB device is plugged in. + * of every time any USB device is plugged in. * - Removed PV8630 ioctls. Use the standard ioctls instead. * - Made endpoint detection more generic. Basically, only one bulk-in * endpoint is required, everything else is optional. @@ -350,6 +350,9 @@ * - Added vendor/product ids for Artec, Avision, Brother, Medion, Primax, * Prolink, Fujitsu, Plustek, and SYSCAN scanners. * - Fixed generation of devfs names if dynamic minors are disabled. + * - Used kobject reference counting to free the scn struct when the device + * is closed and disconnected. Avoids crashes when writing to a + * disconnected device. (Thanks to Greg KH). * * TODO * - Performance @@ -427,6 +430,7 @@ irq_scanner(struct urb *urb, struct pt_regs *regs) return; default: dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); + return; } dbg("irq_scanner(%d): data:%x", scn->scn_minor, *data); @@ -461,6 +465,7 @@ open_scanner(struct inode * inode, struct file * file) return -ENODEV; } scn = usb_get_intfdata(intf); + kobject_get(&scn->kobj); dev = scn->scn_dev; @@ -521,6 +526,8 @@ close_scanner(struct inode * inode, struct file * file) up(&scn_mutex); up(&(scn->sem)); + kobject_put(&scn->kobj); + return 0; } @@ -813,6 +820,37 @@ ioctl_scanner(struct inode *inode, struct file *file, return retval; } +static void destroy_scanner (struct kobject *kobj) +{ + struct scn_usb_data *scn; + + dbg ("%s", __FUNCTION__); + + scn = to_scanner(kobj); + + down (&scn_mutex); + down (&(scn->sem)); + + usb_driver_release_interface(&scanner_driver, + &scn->scn_dev->actconfig->interface[scn->ifnum]); + + kfree(scn->ibuf); + kfree(scn->obuf); + + dbg("%s: De-allocating minor:%d", __FUNCTION__, scn->scn_minor); + devfs_unregister(scn->devfs); + usb_deregister_dev(1, scn->scn_minor); + usb_free_urb(scn->scn_irq); + usb_put_dev(scn->scn_dev); + up (&(scn->sem)); + kfree (scn); + up (&scn_mutex); +} + +static struct kobj_type scanner_kobj_type = { + .release = destroy_scanner, +}; + static struct file_operations usb_scanner_fops = { .owner = THIS_MODULE, @@ -982,6 +1020,8 @@ probe_scanner(struct usb_interface *intf, return -ENOMEM; } memset (scn, 0, sizeof(struct scn_usb_data)); + kobject_init(&scn->kobj); + scn->kobj.ktype = &scanner_kobj_type; scn->scn_irq = usb_alloc_urb(0, GFP_KERNEL); if (!scn->scn_irq) { @@ -1049,6 +1089,7 @@ probe_scanner(struct usb_interface *intf, } + usb_get_dev(dev); scn->bulk_in_ep = have_bulk_in; scn->bulk_out_ep = have_bulk_out; scn->intr_ep = have_intr; @@ -1089,28 +1130,13 @@ disconnect_scanner(struct usb_interface *intf) intf->kdev = NODEV; usb_set_intfdata(intf, NULL); - if (scn) { - down (&scn_mutex); - down (&(scn->sem)); - - if(scn->intr_ep) { - dbg("disconnect_scanner(%d): Unlinking IRQ URB", scn->scn_minor); - usb_unlink_urb(scn->scn_irq); - } - usb_driver_release_interface(&scanner_driver, - &scn->scn_dev->actconfig->interface[scn->ifnum]); - - kfree(scn->ibuf); - kfree(scn->obuf); - - dbg("disconnect_scanner: De-allocating minor:%d", scn->scn_minor); - devfs_unregister(scn->devfs); - usb_deregister_dev(1, scn->scn_minor); - usb_free_urb(scn->scn_irq); - up (&(scn->sem)); - kfree (scn); - up (&scn_mutex); + if(scn->intr_ep) { + dbg("%s(%d): Unlinking IRQ URB", __FUNCTION__, scn->scn_minor); + usb_unlink_urb(scn->scn_irq); } + + if (scn) + kobject_put(&scn->kobj); } /* we want to look at all devices, as the vendor/product id can change diff --git a/drivers/usb/image/scanner.h b/drivers/usb/image/scanner.h index 93be043c7a62..dc77eb090dec 100644 --- a/drivers/usb/image/scanner.h +++ b/drivers/usb/image/scanner.h @@ -1,5 +1,5 @@ /* - * Driver for USB Scanners (linux-2.5.64) + * Driver for USB Scanners (linux-2.5) * * Copyright (C) 1999, 2000, 2001, 2002 David E. Nelson * Previously maintained by Brian Beattie @@ -335,7 +335,9 @@ struct scn_usb_data { wait_queue_head_t rd_wait_q; /* read timeouts */ struct semaphore sem; /* lock to prevent concurrent reads or writes */ unsigned int rd_nak_timeout; /* Seconds to wait before read() timeout. */ + struct kobject kobj; /* Handles our reference counting */ }; +#define to_scanner(d) container_of(d, struct scn_usb_data, kobj) extern devfs_handle_t usb_devfs_handle; diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c index 0b222dd524c1..4da30222593a 100644 --- a/drivers/usb/input/hid-core.c +++ b/drivers/usb/input/hid-core.c @@ -954,7 +954,7 @@ void hid_output_report(struct hid_report *report, __u8 *data) /* * Set a field value. The report this field belongs to has to be - * created and transfered to the device, to set this value in the + * created and transferred to the device, to set this value in the * device. */ diff --git a/drivers/usb/input/hid-input.c b/drivers/usb/input/hid-input.c index c614338be50f..da0267893d06 100644 --- a/drivers/usb/input/hid-input.c +++ b/drivers/usb/input/hid-input.c @@ -480,7 +480,7 @@ static void hidinput_close(struct input_dev *dev) /* * Register the input device; print a message. * Configure the input layer interface - * Read all reports and initalize the absoulte field values. + * Read all reports and initialize the absolute field values. */ int hidinput_connect(struct hid_device *hid) diff --git a/drivers/usb/input/hid.h b/drivers/usb/input/hid.h index 70cab1643aa7..a6081d795493 100644 --- a/drivers/usb/input/hid.h +++ b/drivers/usb/input/hid.h @@ -209,8 +209,8 @@ struct hid_item { #define HID_QUIRK_BADPAD 0x20 /* - * This is the global enviroment of the parser. This information is - * persistent for main-items. The global enviroment can be saved and + * This is the global environment of the parser. This information is + * persistent for main-items. The global environment can be saved and * restored with PUSH/POP statements. */ @@ -228,7 +228,7 @@ struct hid_global { }; /* - * This is the local enviroment. It is resistent up the next main-item. + * This is the local environment. It is persistent up the next main-item. */ #define HID_MAX_DESCRIPTOR_SIZE 4096 diff --git a/drivers/usb/media/dsbr100.c b/drivers/usb/media/dsbr100.c index cc099cb8d91d..cabf38496d33 100644 --- a/drivers/usb/media/dsbr100.c +++ b/drivers/usb/media/dsbr100.c @@ -33,6 +33,12 @@ History: + Version 0.30: + Markus: Updates for 2.5.x kernel and more ISO compiant source + + Version 0.25: + PSL and Markus: Cleanup, radio now doesn't stop on device close + Version 0.24: Markus: Hope I got these silly VIDEO_TUNER_LOW issues finally right. Some minor cleanup, improved standalone compilation @@ -69,15 +75,22 @@ /* * Version Information */ -#define DRIVER_VERSION "v0.24" +#define DRIVER_VERSION "v0.25" #define DRIVER_AUTHOR "Markus Demleitner <msdemlei@tucana.harvard.edu>" -#define DRIVER_DESC "D-Link DSB-R100 USB radio driver" +#define DRIVER_DESC "D-Link DSB-R100 USB FM radio driver" #define DSB100_VENDOR 0x04b4 #define DSB100_PRODUCT 0x1002 #define TB_LEN 16 +/* Frequency limits in MHz -- these are European values. For Japanese +devices, that would be 76 and 91. */ +#define FREQ_MIN 87.5 +#define FREQ_MAX 108.0 +#define FREQ_MUL 16000 + + static int usb_dsbr100_probe(struct usb_interface *intf, const struct usb_device_id *id); static void usb_dsbr100_disconnect(struct usb_interface *intf); @@ -108,7 +121,7 @@ static struct file_operations usb_dsbr100_fops = { static struct video_device usb_dsbr100_radio= { .owner = THIS_MODULE, - .name = "D-Link DSB R-100 USB radio", + .name = "D-Link DSB-R 100", .type = VID_TYPE_TUNER, .hardware = VID_HARDWARE_AZTECH, .fops = &usb_dsbr100_fops, @@ -189,7 +202,7 @@ static int usb_dsbr100_probe(struct usb_interface *intf, return -ENOMEM; usb_dsbr100_radio.priv = radio; radio->dev = interface_to_usbdev (intf); - radio->curfreq = 1454000; + radio->curfreq = FREQ_MIN*FREQ_MUL; usb_set_intfdata (intf, radio); return 0; } @@ -225,11 +238,11 @@ static int usb_dsbr100_do_ioctl(struct inode *inode, struct file *file, { case VIDIOCGCAP: { struct video_capability *v = arg; - memset(v,0,sizeof(*v)); - v->type=VID_TYPE_TUNER; - v->channels=1; - v->audios=1; - strcpy(v->name, "D-Link R-100 USB Radio"); + memset(v, 0, sizeof(*v)); + v->type = VID_TYPE_TUNER; + v->channels = 1; + v->audios = 1; + strcpy(v->name, "D-Link R-100 USB FM Radio"); return 0; } case VIDIOCGTUNER: { @@ -237,8 +250,8 @@ static int usb_dsbr100_do_ioctl(struct inode *inode, struct file *file, dsbr100_getstat(radio); if(v->tuner) /* Only 1 tuner */ return -EINVAL; - v->rangelow = 87*16000; - v->rangehigh = 108*16000; + v->rangelow = FREQ_MIN*FREQ_MUL; + v->rangehigh = FREQ_MAX*FREQ_MUL; v->flags = VIDEO_TUNER_LOW; v->mode = VIDEO_MODE_AUTO; v->signal = radio->stereo*0x7000; @@ -268,31 +281,31 @@ static int usb_dsbr100_do_ioctl(struct inode *inode, struct file *file, radio->curfreq = *freq; if (dsbr100_setfreq(radio, radio->curfreq)==-1) - warn("set frequency failed"); + warn("Set frequency failed"); return 0; } case VIDIOCGAUDIO: { struct video_audio *v = arg; - memset(v,0, sizeof(*v)); - v->flags|=VIDEO_AUDIO_MUTABLE; - v->mode=VIDEO_SOUND_STEREO; - v->volume=1; - v->step=1; + memset(v, 0, sizeof(*v)); + v->flags |= VIDEO_AUDIO_MUTABLE; + v->mode = VIDEO_SOUND_STEREO; + v->volume = 1; + v->step = 1; strcpy(v->name, "Radio"); return 0; } case VIDIOCSAUDIO: { struct video_audio *v = arg; - if(v->audio) + if (v->audio) return -EINVAL; - if(v->flags&VIDEO_AUDIO_MUTE) { + if (v->flags&VIDEO_AUDIO_MUTE) { if (dsbr100_stop(radio)==-1) - warn("radio did not respond properly"); + warn("Radio did not respond properly"); } else if (dsbr100_start(radio)==-1) - warn("radio did not respond properly"); + warn("Radio did not respond properly"); return 0; } default: @@ -312,18 +325,18 @@ static int usb_dsbr100_open(struct inode *inode, struct file *file) usb_dsbr100 *radio=dev->priv; if (! radio) { - warn("radio not initialised"); + warn("Radio not initialised"); return -EAGAIN; } if(users) { - warn("radio in use"); + warn("Radio in use"); return -EBUSY; } users++; if (dsbr100_start(radio)<0) - warn("radio did not start up properly"); - dsbr100_setfreq(radio,radio->curfreq); + warn("Radio did not start up properly"); + dsbr100_setfreq(radio, radio->curfreq); return 0; } @@ -335,7 +348,6 @@ static int usb_dsbr100_close(struct inode *inode, struct file *file) if (!radio) return -ENODEV; users--; - dsbr100_stop(radio); return 0; } @@ -343,8 +355,9 @@ static int __init dsbr100_init(void) { usb_dsbr100_radio.priv = NULL; usb_register(&usb_dsbr100_driver); - if (video_register_device(&usb_dsbr100_radio,VFL_TYPE_RADIO,radio_nr)==-1) { - warn("couldn't register video device"); + if (video_register_device(&usb_dsbr100_radio, VFL_TYPE_RADIO, + radio_nr)==-1) { + warn("Couldn't register video device"); return -EINVAL; } info(DRIVER_VERSION ":" DRIVER_DESC); @@ -367,9 +380,3 @@ module_exit (dsbr100_exit); MODULE_AUTHOR( DRIVER_AUTHOR ); MODULE_DESCRIPTION( DRIVER_DESC ); MODULE_LICENSE("GPL"); - -/* -vi: ts=8 -Sigh. Of course, I am one of the ts=2 heretics, but Linus' wish is -my command. -*/ diff --git a/drivers/usb/media/konicawc.c b/drivers/usb/media/konicawc.c index 90049bc8993a..b79f0e816d4d 100644 --- a/drivers/usb/media/konicawc.c +++ b/drivers/usb/media/konicawc.c @@ -65,7 +65,7 @@ static const int debug = 0; #endif -/* Some default values for inital camera settings, +/* Some default values for initial camera settings, can be set by modprobe */ static enum frame_sizes size; diff --git a/drivers/usb/media/pwc-ctrl.c b/drivers/usb/media/pwc-ctrl.c index 92c5c8239913..a1af23e7b589 100644 --- a/drivers/usb/media/pwc-ctrl.c +++ b/drivers/usb/media/pwc-ctrl.c @@ -129,7 +129,7 @@ static struct Nala_table_entry Nala_table[PSZ_MAX][8] = 4 compression modi: none, low, medium, high When an uncompressed mode is not available, the next available compressed mode - will be choosen (unless the decompressor is absent). Sometimes there are only + will be chosen (unless the decompressor is absent). Sometimes there are only 1 or 2 compressed modes available; in that case entries are duplicated. */ struct Timon_table_entry diff --git a/drivers/usb/media/pwc-if.c b/drivers/usb/media/pwc-if.c index 6414b56defa4..19fe16084b90 100644 --- a/drivers/usb/media/pwc-if.c +++ b/drivers/usb/media/pwc-if.c @@ -255,7 +255,7 @@ static int pwc_allocate_buffers(struct pwc_device *pdev) return -ENXIO; } #endif - /* Allocate Isochronuous pipe buffers */ + /* Allocate Isochronous pipe buffers */ for (i = 0; i < MAX_ISO_BUFS; i++) { if (pdev->sbuf[i].data == NULL) { kbuf = kmalloc(ISO_BUFFER_SIZE, GFP_KERNEL); @@ -811,7 +811,7 @@ static int pwc_isoc_init(struct pwc_device *pdev) if (pdev->vmax_packet_size < 0 || pdev->vmax_packet_size > ISO_MAX_FRAME_SIZE) { Err("Failed to find packet size for video endpoint in current alternate setting.\n"); - return -ENFILE; /* Odd error, that should be noticable */ + return -ENFILE; /* Odd error, that should be noticeable */ } /* Set alternate interface */ @@ -2040,7 +2040,7 @@ static int __init usb_pwc_init(void) if (leds[1] >= 0) led_off = leds[1]; - /* Big device node whoopla. Basicly, it allows you to assign a + /* Big device node whoopla. Basically, it allows you to assign a device node (/dev/videoX) to a camera, based on its type & serial number. The format is [type[.serialnumber]:]node. diff --git a/drivers/usb/media/pwc-uncompress.c b/drivers/usb/media/pwc-uncompress.c index b34074e5d40d..42a65168dd5d 100644 --- a/drivers/usb/media/pwc-uncompress.c +++ b/drivers/usb/media/pwc-uncompress.c @@ -32,7 +32,7 @@ static LIST_HEAD(pwc_decompressor_list); /* Should the pwc_decompress structure ever change, we increase the version number so that we don't get nasty surprises, or can - dynamicly adjust our structure. + dynamically adjust our structure. */ const int pwc_decompressor_version = PWC_MAJOR; diff --git a/drivers/usb/media/pwc.h b/drivers/usb/media/pwc.h index 3ff42fc9f20e..9745effa4407 100644 --- a/drivers/usb/media/pwc.h +++ b/drivers/usb/media/pwc.h @@ -147,7 +147,7 @@ struct pwc_device 2. data is synchronized and packed into a frame buffer 3a. in case data is compressed, decompress it directly into image buffer 3b. in case data is uncompressed, copy into image buffer with viewport - 4. data is transfered to the user process + 4. data is transferred to the user process Note that MAX_ISO_BUFS != MAX_FRAMES != MAX_IMAGES.... We have in effect a back-to-back-double-buffer system. diff --git a/drivers/usb/media/se401.c b/drivers/usb/media/se401.c index cde8b307a2a1..f1f3210f3a48 100644 --- a/drivers/usb/media/se401.c +++ b/drivers/usb/media/se401.c @@ -692,7 +692,7 @@ static int se401_set_size(struct usb_se401 *se401, int width, int height) /* This shouldn't really be done in a v4l driver.... But it does make the image look a lot more usable. - Basicly it lifts the dark pixels more than the light pixels. + Basically it lifts the dark pixels more than the light pixels. */ static inline void enhance_picture(unsigned char *frame, int len) { diff --git a/drivers/usb/media/vicam.c b/drivers/usb/media/vicam.c index 831d932ea9e5..adb87516e8ab 100644 --- a/drivers/usb/media/vicam.c +++ b/drivers/usb/media/vicam.c @@ -531,7 +531,7 @@ vicam_ioctl(struct inode *inode, struct file *file, unsigned int ioctlnr, unsign return -ENODEV; switch (ioctlnr) { - /* query capabilites */ + /* query capabilities */ case VIDIOCGCAP: { struct video_capability b; diff --git a/drivers/usb/misc/auerswald.c b/drivers/usb/misc/auerswald.c index 28132e35a4a7..74ca6cacd589 100644 --- a/drivers/usb/misc/auerswald.c +++ b/drivers/usb/misc/auerswald.c @@ -687,10 +687,10 @@ static int auerchain_start_wait_urb (pauerchain_t acp, struct urb *urb, int time This function sends a simple control message to a specified endpoint and waits for the message to complete, or timeout. - If successful, it returns the transfered length, othwise a negative error number. + If successful, it returns the transferred length, otherwise a negative error number. Don't use this function from within an interrupt context, like a - bottom half handler. If you need a asyncronous message, or need to send + bottom half handler. If you need an asynchronous message, or need to send a message from within interrupt context, use auerchain_submit_urb() */ static int auerchain_control_msg (pauerchain_t acp, struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, @@ -814,7 +814,7 @@ static int auerbuf_setup (pauerbufctl_t bcp, unsigned int numElements, unsigned } return 0; -bl_fail:/* not enought memory. Free allocated elements */ +bl_fail:/* not enough memory. Free allocated elements */ dbg ("auerbuf_setup: no more memory"); auerbuf_free_buffers (bcp); return -ENOMEM; @@ -1083,7 +1083,7 @@ static void auerswald_int_complete (struct urb * urb, struct pt_regs *regs) /* can we do something more? This is a big problem: if this int packet is ignored, the device will wait forever and not signal any more data. - The only real solution is: having enought buffers! + The only real solution is: having enough buffers! Or perhaps temporary disabling the int endpoint? */ goto exit; @@ -1130,7 +1130,7 @@ static void auerswald_int_free (pauerswald_t cp) } /* This function is called to activate the interrupt - endpoint. This function returns 0 if successfull or an error code. + endpoint. This function returns 0 if successful or an error code. NOTE: no mutex please! */ static int auerswald_int_open (pauerswald_t cp) @@ -1181,7 +1181,7 @@ intoend: } /* This function is called to deactivate the interrupt - endpoint. This function returns 0 if successfull or an error code. + endpoint. This function returns 0 if successful or an error code. NOTE: no mutex please! */ static int auerswald_int_release (pauerswald_t cp) @@ -1713,7 +1713,7 @@ doreadlist: return -ERESTARTSYS; } - /* try to read the incomming data again */ + /* try to read the incoming data again */ goto doreadlist; } @@ -2075,7 +2075,7 @@ pfail: auerswald_delete (cp); The argument dev specifies the device context and the driver_context returns a pointer to the previously registered driver_context of the probe function. After returning from the disconnect function the USB - framework completly deallocates all data structures associated with + framework completely deallocates all data structures associated with this device. So especially the usb_device structure must not be used any longer by the usb driver. */ diff --git a/drivers/usb/misc/speedtouch.c b/drivers/usb/misc/speedtouch.c index 0c4ce9d19612..58bb47c64e6a 100644 --- a/drivers/usb/misc/speedtouch.c +++ b/drivers/usb/misc/speedtouch.c @@ -193,7 +193,7 @@ struct udsl_instance_data { static const char udsl_driver_name [] = "speedtch"; /* - * atm driver prototypes and stuctures + * atm driver prototypes and structures */ static void udsl_atm_dev_close (struct atm_dev *dev); @@ -273,39 +273,60 @@ static void udsl_groom_skb (struct atm_vcc *vcc, struct sk_buff *skb) { ctrl->aal5_trailer [7] = crc; } -static char *udsl_write_cell (struct sk_buff *skb, char *target) { +unsigned int udsl_write_cells (unsigned int howmany, struct sk_buff *skb, unsigned char **target_p) { struct udsl_control *ctrl = UDSL_SKB (skb); + unsigned char *target = *target_p; + unsigned int nc, ne, i; - ctrl->num_cells--; + dbg ("udsl_write_cells: howmany=%u, skb->len=%d, num_cells=%u, num_entire=%u, pdu_padding=%u", howmany, skb->len, ctrl->num_cells, ctrl->num_entire, ctrl->pdu_padding); - memcpy (target, ctrl->cell_header, ATM_CELL_HEADER); - target += ATM_CELL_HEADER; + nc = ctrl->num_cells; + ne = min (howmany, ctrl->num_entire); - if (ctrl->num_entire) { - ctrl->num_entire--; + for (i = 0; i < ne; i++) { + memcpy (target, ctrl->cell_header, ATM_CELL_HEADER); + target += ATM_CELL_HEADER; memcpy (target, skb->data, ATM_CELL_PAYLOAD); target += ATM_CELL_PAYLOAD; __skb_pull (skb, ATM_CELL_PAYLOAD); - return target; } + ctrl->num_entire -= ne; + + if (!(ctrl->num_cells -= ne) || !(howmany -= ne)) + goto out; + + memcpy (target, ctrl->cell_header, ATM_CELL_HEADER); + target += ATM_CELL_HEADER; memcpy (target, skb->data, skb->len); target += skb->len; __skb_pull (skb, skb->len); - memset (target, 0, ctrl->pdu_padding); target += ctrl->pdu_padding; - if (ctrl->num_cells) { - ctrl->pdu_padding = ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER; - } else { - memcpy (target, ctrl->aal5_trailer, ATM_AAL5_TRAILER); - target += ATM_AAL5_TRAILER; - /* set pti bit in last cell */ - *(target + 3 - ATM_CELL_SIZE) |= 0x2; + if (--ctrl->num_cells) { + if (!--howmany) { + ctrl->pdu_padding = ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER; + goto out; + } + + memcpy (target, ctrl->cell_header, ATM_CELL_HEADER); + target += ATM_CELL_HEADER; + memset (target, 0, ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER); + target += ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER; + + if (--ctrl->num_cells) + BUG(); } - return target; + memcpy (target, ctrl->aal5_trailer, ATM_AAL5_TRAILER); + target += ATM_AAL5_TRAILER; + /* set pti bit in last cell */ + *(target + 3 - ATM_CELL_SIZE) |= 0x2; + +out: + *target_p = target; + return nc - ctrl->num_cells; } @@ -500,14 +521,12 @@ static void udsl_complete_send (struct urb *urb, struct pt_regs *regs) static void udsl_process_send (unsigned long data) { struct udsl_send_buffer *buf; - unsigned int cells_to_write; int err; unsigned long flags; - unsigned int i; struct udsl_instance_data *instance = (struct udsl_instance_data *) data; + unsigned int num_written; struct sk_buff *skb; struct udsl_sender *snd; - unsigned char *target; dbg ("udsl_process_send entered"); @@ -577,16 +596,11 @@ made_progress: instance->current_buffer = buf; } - cells_to_write = min (buf->free_cells, UDSL_SKB (skb)->num_cells); - target = buf->free_start; - - dbg ("writing %u cells from skb 0x%p to buffer 0x%p", cells_to_write, skb, buf); + num_written = udsl_write_cells (buf->free_cells, skb, &buf->free_start); - for (i = 0; i < cells_to_write; i++) - target = udsl_write_cell (skb, target); + dbg ("wrote %u cells from skb 0x%p to buffer 0x%p", num_written, skb, buf); - buf->free_start = target; - if (!(buf->free_cells -= cells_to_write)) { + if (!(buf->free_cells -= num_written)) { list_add_tail (&buf->list, &instance->filled_buffers); instance->current_buffer = NULL; dbg ("queued filled buffer"); diff --git a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c index 398aa567d6ba..fec152c2f241 100644 --- a/drivers/usb/net/usbnet.c +++ b/drivers/usb/net/usbnet.c @@ -1707,7 +1707,7 @@ static int usbnet_stop (struct net_device *net) /*-------------------------------------------------------------------------*/ -// posts reads, and enables write queing +// posts reads, and enables write queuing // precondition: never called in_interrupt diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 7cf6b5847c1c..0c790ae6a4e8 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -215,7 +215,7 @@ static int belkin_sa_open (struct usb_serial_port *port, struct file *filp) dbg("%s port %d", __FUNCTION__, port->number); /*Start reading from the device*/ - /* TODO: Look at possibility of submitting mulitple URBs to device to + /* TODO: Look at possibility of submitting multiple URBs to device to * enhance buffering. Win trace shows 16 initial read URBs. */ port->read_urb->dev = port->serial->dev; diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index 8f6dcd730173..b0161705047e 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -302,7 +302,7 @@ static void cyberjack_read_int_callback( struct urb *urb, struct pt_regs *regs ) short old_rdtodo = priv->rdtodo; int result; - /* This is a announcement of comming bulk_ins. */ + /* This is a announcement of coming bulk_ins. */ unsigned short size = ((unsigned short)data[3]<<8)+data[2]+3; if( (size>259) || (size==0) ) { diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index af4a621ff524..37e266f52650 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -1086,7 +1086,7 @@ static int edge_open (struct usb_serial_port *port, struct file * filp) return -ENODEV; } - /* now wait for the port to be completly opened */ + /* now wait for the port to be completely opened */ timeout = OPEN_TIMEOUT; while (timeout && edge_port->openPending == TRUE) { timeout = interruptible_sleep_on_timeout (&edge_port->wait_open, timeout); diff --git a/drivers/usb/serial/io_ionsp.h b/drivers/usb/serial/io_ionsp.h index e5c344d033ec..944fc04f1946 100644 --- a/drivers/usb/serial/io_ionsp.h +++ b/drivers/usb/serial/io_ionsp.h @@ -19,7 +19,7 @@ The data to and from all ports on the peripheral is multiplexed through a single endpoint pair (EP1 since it supports 64-byte MaxPacketSize). Therefore, the data, commands, and status for -each port must be preceeded by a short header identifying the +each port must be preceded by a short header identifying the destination port. The header also identifies the bytes that follow as data or as command/status info. diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 7ba4d95425d4..b2368748b9de 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -468,7 +468,7 @@ static int TIIsTxActive (struct edgeport_port *port) { int status; struct out_endpoint_desc_block *oedb; - __u8 lsr; + __u8 *lsr; int bytes_left = 0; oedb = kmalloc (sizeof (* oedb), GFP_KERNEL); @@ -477,6 +477,13 @@ static int TIIsTxActive (struct edgeport_port *port) return -ENOMEM; } + lsr = kmalloc (1, GFP_KERNEL); /* Sigh, that's right, just one byte, + as not all platforms can do DMA + from stack */ + if (!lsr) { + kfree(oedb); + return -ENOMEM; + } /* Read the DMA Count Registers */ status = TIReadRam (port->port->serial->dev, port->dma_address, @@ -492,22 +499,25 @@ static int TIIsTxActive (struct edgeport_port *port) status = TIReadRam (port->port->serial->dev, port->uart_base + UMPMEM_OFFS_UART_LSR, 1, - &lsr); + lsr); if (status) goto exit_is_tx_active; - dbg ("%s - LSR = 0x%X", __FUNCTION__, lsr); + dbg ("%s - LSR = 0x%X", __FUNCTION__, *lsr); /* If either buffer has data or we are transmitting then return TRUE */ if ((oedb->XByteCount & 0x80 ) != 0 ) bytes_left += 64; - if ((lsr & UMP_UART_LSR_TX_MASK ) == 0 ) + if ((*lsr & UMP_UART_LSR_TX_MASK ) == 0 ) bytes_left += 1; /* We return Not Active if we get any kind of error */ exit_is_tx_active: dbg ("%s - return %d", __FUNCTION__, bytes_left ); + + kfree(lsr); + kfree(oedb); return bytes_left; } @@ -1151,8 +1161,12 @@ static int TIDownloadFirmware (struct edgeport_serial *serial) dbg ( "%s - HARDWARE RESET return %d", __FUNCTION__, status); /* return an error on purpose. */ + kfree (firmware_version); + kfree (rom_desc); + kfree (ti_manuf_desc); return -ENODEV; } + kfree (firmware_version); } // Search for type 0xF2 record (firmware blank record) else if ((start_address = TIGetDescriptorAddress (serial, I2C_DESC_TYPE_FIRMWARE_BLANK, rom_desc)) != 0) { diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 9381bc4f6023..332a56327123 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -471,7 +471,7 @@ static void ir_read_bulk_callback (struct urb *urb, struct pt_regs *regs) /* * Bypass flip-buffers, and feed the ldisc directly - * due to our potentally large buffer size. Since we + * due to our potentially large buffer size. Since we * used to set low_latency, this is exactly what the * tty layer did anyway :) */ @@ -553,7 +553,7 @@ static void ir_set_termios (struct usb_serial_port *port, struct termios *old_te /* * FIXME, we should compare the baud request against the * capability stated in the IR header that we got in the - * startup funtion. + * startup function. */ switch (cflag & CBAUD) { case B2400: ir_baud = SPEED_2400; break; diff --git a/drivers/usb/serial/keyspan_usa26msg.h b/drivers/usb/serial/keyspan_usa26msg.h index d3c0cb0277b9..cac3d501f523 100644 --- a/drivers/usb/serial/keyspan_usa26msg.h +++ b/drivers/usb/serial/keyspan_usa26msg.h @@ -23,7 +23,7 @@ notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - 3. The name of InnoSys Incorprated may not be used to endorse or promote + 3. The name of InnoSys Incorporated may not be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/drivers/usb/serial/keyspan_usa28msg.h b/drivers/usb/serial/keyspan_usa28msg.h index 4641abd7d682..d76eb72878e1 100644 --- a/drivers/usb/serial/keyspan_usa28msg.h +++ b/drivers/usb/serial/keyspan_usa28msg.h @@ -23,7 +23,7 @@ notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - 3. The name of InnoSys Incorprated may not be used to endorse or promote + 3. The name of InnoSys Incorporated may not be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/drivers/usb/serial/keyspan_usa49msg.h b/drivers/usb/serial/keyspan_usa49msg.h index 73444c00023d..320b2cc76874 100644 --- a/drivers/usb/serial/keyspan_usa49msg.h +++ b/drivers/usb/serial/keyspan_usa49msg.h @@ -23,7 +23,7 @@ notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - 3. The name of InnoSys Incorprated may not be used to endorse or promote + 3. The name of InnoSys Incorporated may not be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 69abd70e79cf..99c634017192 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -253,6 +253,7 @@ static int kobil_open (struct usb_serial_port *port, struct file *filp) port->write_urb = usb_alloc_urb(0, GFP_KERNEL); if (!port->write_urb) { dbg("%s - port %d usb_alloc_urb failed", __FUNCTION__, port->number); + kfree(transfer_buffer); return -1; } } @@ -260,6 +261,7 @@ static int kobil_open (struct usb_serial_port *port, struct file *filp) // allocate memory for write_urb transfer buffer port->write_urb->transfer_buffer = (unsigned char *) kmalloc(write_urb_transfer_buffer_length, GFP_KERNEL); if (! port->write_urb->transfer_buffer) { + kfree(transfer_buffer); return -1; } diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 96f5c051c8e0..b341aad16a6e 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -76,6 +76,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID) }, { USB_DEVICE(TRIPP_VENDOR_ID, TRIPP_PRODUCT_ID) }, { USB_DEVICE(RADIOSHACK_VENDOR_ID, RADIOSHACK_PRODUCT_ID) }, + { USB_DEVICE(DCU10_VENDOR_ID, DCU10_PRODUCT_ID) }, { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index 63051e66e69c..5e2abcc0db20 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -34,3 +34,6 @@ #define RADIOSHACK_VENDOR_ID 0x1453 #define RADIOSHACK_PRODUCT_ID 0x4026 + +#define DCU10_VENDOR_ID 0x0731 +#define DCU10_PRODUCT_ID 0x0528 diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 6eda0113534b..c57ce2d5f31f 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -229,7 +229,7 @@ * (01/23/2000) gkh * Fixed problem of crash when trying to open a port that didn't have a * device assigned to it. Made the minor node finding a little smarter, - * now it looks to find a continous space for the new device. + * now it looks to find a continuous space for the new device. * * (01/21/2000) gkh * Fixed bug in visor_startup with patch from Miles Lott (milos@insync.net) @@ -1293,7 +1293,7 @@ static int __init usb_serial_init(void) int i; int result = 0; - /* Initalize our global data */ + /* Initialize our global data */ for (i = 0; i < SERIAL_TTY_MINORS; ++i) { serial_table[i] = NULL; } diff --git a/drivers/usb/serial/usb-serial.h b/drivers/usb/serial/usb-serial.h index 5389343ecaf7..ad2d292ddf53 100644 --- a/drivers/usb/serial/usb-serial.h +++ b/drivers/usb/serial/usb-serial.h @@ -71,7 +71,7 @@ * usb_serial_port: structure for the specific ports of a device. * @magic: magic number for internal validity of this pointer. * @serial: pointer back to the struct usb_serial owner of this port. - * @tty: pointer to the coresponding tty for this port. + * @tty: pointer to the corresponding tty for this port. * @number: the number of the port (the minor number). * @interrupt_in_buffer: pointer to the interrupt in buffer for this port. * @interrupt_in_urb: pointer to the interrupt in struct urb for this port. diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index ee5917af0daa..22b4ef591a81 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -12,6 +12,10 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (03/09/2003) gkh + * Added support for the Sony Clie NZ90V device. Thanks to Martin Brachtl + * <brachtl@redgrep.cz> for the information. + * * (03/05/2003) gkh * Think Treo support is now working. * @@ -198,6 +202,8 @@ static struct usb_device_id id_table [] = { .driver_info = (kernel_ulong_t)&palm_os_4_probe }, { USB_DEVICE(PALM_VENDOR_ID, PALM_I705_ID), .driver_info = (kernel_ulong_t)&palm_os_4_probe }, + { USB_DEVICE(PALM_VENDOR_ID, PALM_M100_ID), + .driver_info = (kernel_ulong_t)&palm_os_4_probe }, { USB_DEVICE(PALM_VENDOR_ID, PALM_M125_ID), .driver_info = (kernel_ulong_t)&palm_os_4_probe }, { USB_DEVICE(PALM_VENDOR_ID, PALM_M130_ID), @@ -215,6 +221,8 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_1_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NX60_ID), .driver_info = (kernel_ulong_t)&palm_os_4_probe }, + { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NZ90V_ID), + .driver_info = (kernel_ulong_t)&palm_os_4_probe }, { } /* Terminating entry */ }; @@ -230,6 +238,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(PALM_VENDOR_ID, PALM_M505_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_M515_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_I705_ID) }, + { USB_DEVICE(PALM_VENDOR_ID, PALM_M100_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_M125_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_M130_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_T_ID) }, @@ -240,6 +249,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_S360_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_1_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NX60_ID) }, + { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NZ90V_ID) }, { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/visor.h b/drivers/usb/serial/visor.h index 86f8f3fb642a..089bcf98da86 100644 --- a/drivers/usb/serial/visor.h +++ b/drivers/usb/serial/visor.h @@ -31,6 +31,7 @@ #define PALM_TUNGSTEN_T_ID 0x0060 #define PALM_TUNGSTEN_Z_ID 0x0031 #define PALM_ZIRE_ID 0x0070 +#define PALM_M100_ID 0x0080 #define SONY_VENDOR_ID 0x054C #define SONY_CLIE_3_5_ID 0x0038 @@ -38,6 +39,7 @@ #define SONY_CLIE_S360_ID 0x0095 #define SONY_CLIE_4_1_ID 0x009A #define SONY_CLIE_NX60_ID 0x00DA +#define SONY_CLIE_NZ90V_ID 0x00E9 /**************************************************************************** * Handspring Visor Vendor specific request codes (bRequest values) diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c index 9a88b60c9bd5..1f56f63bb8e5 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -650,7 +650,7 @@ static void isd200_log_config( struct isd200_info* info ) /************************************************************************** * isd200_write_config * - * Write the ISD200 Configuraton data + * Write the ISD200 Configuration data * * RETURNS: * ISD status code @@ -693,7 +693,7 @@ int isd200_write_config( struct us_data *us ) /************************************************************************** * isd200_read_config * - * Reads the ISD200 Configuraton data + * Reads the ISD200 Configuration data * * RETURNS: * ISD status code @@ -977,7 +977,7 @@ int isd200_get_inquiry_data( struct us_data *us ) /* check for an ATA device */ if (info->DeviceFlags & DF_ATA_DEVICE) { /* this must be an ATA device */ - /* perform an ATA Commmand Identify */ + /* perform an ATA Command Identify */ transferStatus = isd200_action( us, ACTION_IDENTIFY, &info->drive, sizeof(struct hd_driveid) ); diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h index 799c4134751e..38c38ee90011 100644 --- a/drivers/usb/storage/transport.h +++ b/drivers/usb/storage/transport.h @@ -118,7 +118,7 @@ struct bulk_cs_wrap { */ #define USB_STOR_XFER_GOOD 0 /* good transfer */ -#define USB_STOR_XFER_SHORT 1 /* transfered less than expected */ +#define USB_STOR_XFER_SHORT 1 /* transferred less than expected */ #define USB_STOR_XFER_STALLED 2 /* endpoint stalled */ #define USB_STOR_XFER_ERROR 3 /* transfer died in the middle */ diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index b5c7e6745b1a..b89ca760a959 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -64,7 +64,7 @@ #define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com" #define DRIVER_DESC "USB Skeleton Driver" -/* Module paramaters */ +/* Module parameters */ MODULE_PARM(debug, "i"); MODULE_PARM_DESC(debug, "Debug enabled or not"); |
