summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorAndy Grover <agrover@groveronline.com>2003-03-19 00:09:51 -0800
committerAndy Grover <agrover@groveronline.com>2003-03-19 00:09:51 -0800
commitb39e199b453be169dfd1adec9686978057c1dc0f (patch)
treedd7162a8e62d003159d6250069194b48a72b76d0 /drivers
parent99d5dfb972d15c955e274d3f67e3862ce8004311 (diff)
parentd99c48c60bc52561b3125dd1f85db7a77b7031ca (diff)
Merge groveronline.com:/root/bk/linux-2.5
into groveronline.com:/root/bk/linux-acpi
Diffstat (limited to 'drivers')
-rw-r--r--drivers/base/platform.c4
-rw-r--r--drivers/block/deadline-iosched.c6
-rw-r--r--drivers/block/genhd.c10
-rw-r--r--drivers/block/ll_rw_blk.c68
-rw-r--r--drivers/char/Kconfig31
-rw-r--r--drivers/char/Makefile3
-rw-r--r--drivers/char/amd768_rng.c295
-rw-r--r--drivers/char/hw_random.c630
-rw-r--r--drivers/char/i810_rng.c404
-rw-r--r--drivers/char/tty_io.c13
-rw-r--r--drivers/char/vt_ioctl.c103
-rw-r--r--drivers/i2c/busses/Kconfig60
-rw-r--r--drivers/i2c/busses/Makefile3
-rw-r--r--drivers/i2c/busses/i2c-ali15x3.c575
-rw-r--r--drivers/i2c/busses/i2c-amd756.c3
-rw-r--r--drivers/i2c/busses/i2c-amd8111.c5
-rw-r--r--drivers/i2c/busses/i2c-i801.c715
-rw-r--r--drivers/i2c/busses/i2c-piix4.c521
-rw-r--r--drivers/i2c/i2c-core.c227
-rw-r--r--drivers/i2c/i2c-proc.c180
-rw-r--r--drivers/md/linear.c18
-rw-r--r--drivers/md/md.c963
-rw-r--r--drivers/md/multipath.c26
-rw-r--r--drivers/md/raid0.c27
-rw-r--r--drivers/md/raid1.c50
-rw-r--r--drivers/md/raid5.c56
-rw-r--r--drivers/media/radio/miropcm20-rds.c6
-rw-r--r--drivers/media/video/audiochip.h36
-rw-r--r--drivers/media/video/bttv-cards.c1
-rw-r--r--drivers/media/video/bttv-driver.c1
-rw-r--r--drivers/media/video/bttv-if.c1
-rw-r--r--drivers/media/video/bttvp.h6
-rw-r--r--drivers/media/video/id.h35
-rw-r--r--drivers/media/video/msp3400.c10
-rw-r--r--drivers/media/video/saa7134/saa7134-cards.c1
-rw-r--r--drivers/media/video/saa7134/saa7134-core.c1
-rw-r--r--drivers/media/video/saa7134/saa7134-i2c.c2
-rw-r--r--drivers/media/video/saa7134/saa7134-video.c2
-rw-r--r--drivers/media/video/saa7134/saa7134.h6
-rw-r--r--drivers/media/video/tda7432.c4
-rw-r--r--drivers/media/video/tda9875.c14
-rw-r--r--drivers/media/video/tda9887.c4
-rw-r--r--drivers/media/video/tuner-3036.c2
-rw-r--r--drivers/media/video/tuner.c4
-rw-r--r--drivers/media/video/tuner.h94
-rw-r--r--drivers/media/video/tvaudio.c13
-rw-r--r--drivers/media/video/tvmixer.c10
-rw-r--r--drivers/media/video/video-buf.c2
-rw-r--r--drivers/media/video/video-buf.h234
-rw-r--r--drivers/net/e100/e100_main.c8
-rw-r--r--drivers/oprofile/buffer_sync.c17
-rw-r--r--drivers/pci/pci.ids5
-rw-r--r--drivers/pci/probe.c49
-rw-r--r--drivers/pci/setup-bus.c43
-rw-r--r--drivers/pci/setup-res.c58
-rw-r--r--drivers/scsi/aacraid/linit.c41
-rw-r--r--drivers/scsi/hosts.h1
-rw-r--r--drivers/scsi/osst.c12
-rw-r--r--drivers/scsi/qlogicfc.c72
-rw-r--r--drivers/scsi/scsi.c248
-rw-r--r--drivers/scsi/scsi.h3
-rw-r--r--drivers/scsi/scsi_debug.c635
-rw-r--r--drivers/scsi/scsi_debug.h4
-rw-r--r--drivers/scsi/scsi_error.c22
-rw-r--r--drivers/scsi/scsi_lib.c1
-rw-r--r--drivers/scsi/scsi_scan.c139
-rw-r--r--drivers/scsi/scsi_sysfs.c20
-rw-r--r--drivers/scsi/sd.c37
-rw-r--r--drivers/scsi/sg.c150
-rw-r--r--drivers/serial/8250.c28
-rw-r--r--drivers/serial/8250.h2
-rw-r--r--drivers/serial/8250_acorn.c76
-rw-r--r--drivers/serial/8250_pci.c1773
-rw-r--r--drivers/serial/core.c946
-rw-r--r--drivers/serial/sa1100.c43
-rw-r--r--drivers/usb/class/bluetty.c4
-rw-r--r--drivers/usb/class/cdc-acm.c4
-rw-r--r--drivers/usb/class/usb-midi.c2
-rw-r--r--drivers/usb/core/hub.c4
-rw-r--r--drivers/usb/core/urb.c4
-rw-r--r--drivers/usb/host/hc_simple.c2
-rw-r--r--drivers/usb/host/hc_simple.h4
-rw-r--r--drivers/usb/host/hc_sl811.h2
-rw-r--r--drivers/usb/host/ohci-q.c6
-rw-r--r--drivers/usb/host/uhci-hcd.c2
-rw-r--r--drivers/usb/image/hpusbscsi.c4
-rw-r--r--drivers/usb/image/hpusbscsi.h2
-rw-r--r--drivers/usb/image/scanner.c76
-rw-r--r--drivers/usb/image/scanner.h4
-rw-r--r--drivers/usb/input/hid-core.c2
-rw-r--r--drivers/usb/input/hid-input.c2
-rw-r--r--drivers/usb/input/hid.h6
-rw-r--r--drivers/usb/media/dsbr100.c75
-rw-r--r--drivers/usb/media/konicawc.c2
-rw-r--r--drivers/usb/media/pwc-ctrl.c2
-rw-r--r--drivers/usb/media/pwc-if.c6
-rw-r--r--drivers/usb/media/pwc-uncompress.c2
-rw-r--r--drivers/usb/media/pwc.h2
-rw-r--r--drivers/usb/media/se401.c2
-rw-r--r--drivers/usb/media/vicam.c2
-rw-r--r--drivers/usb/misc/auerswald.c16
-rw-r--r--drivers/usb/misc/speedtouch.c70
-rw-r--r--drivers/usb/net/usbnet.c2
-rw-r--r--drivers/usb/serial/belkin_sa.c2
-rw-r--r--drivers/usb/serial/cyberjack.c2
-rw-r--r--drivers/usb/serial/io_edgeport.c2
-rw-r--r--drivers/usb/serial/io_ionsp.h2
-rw-r--r--drivers/usb/serial/io_ti.c22
-rw-r--r--drivers/usb/serial/ir-usb.c4
-rw-r--r--drivers/usb/serial/keyspan_usa26msg.h2
-rw-r--r--drivers/usb/serial/keyspan_usa28msg.h2
-rw-r--r--drivers/usb/serial/keyspan_usa49msg.h2
-rw-r--r--drivers/usb/serial/kobil_sct.c2
-rw-r--r--drivers/usb/serial/pl2303.c1
-rw-r--r--drivers/usb/serial/pl2303.h3
-rw-r--r--drivers/usb/serial/usb-serial.c4
-rw-r--r--drivers/usb/serial/usb-serial.h2
-rw-r--r--drivers/usb/serial/visor.c10
-rw-r--r--drivers/usb/serial/visor.h2
-rw-r--r--drivers/usb/storage/isd200.c6
-rw-r--r--drivers/usb/storage/transport.h2
-rw-r--r--drivers/usb/usb-skeleton.c2
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, &region, 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, &region, 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, &region, 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, &region, 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");