summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/block/Kconfig33
-rw-r--r--drivers/block/Kconfig.iosched8
-rw-r--r--drivers/block/Makefile1
-rw-r--r--drivers/block/as-iosched.c122
-rw-r--r--drivers/block/cfq-iosched.c1574
-rw-r--r--drivers/block/cpqarray.c14
-rw-r--r--drivers/block/deadline-iosched.c136
-rw-r--r--drivers/block/elevator.c318
-rw-r--r--drivers/block/ll_rw_blk.c253
-rw-r--r--drivers/block/noop-iosched.c33
-rw-r--r--drivers/block/pktcdvd.c2679
-rw-r--r--drivers/block/ub.c165
-rw-r--r--drivers/cdrom/Makefile1
-rw-r--r--drivers/cdrom/cdrom.c86
-rw-r--r--drivers/char/agp/intel-agp.c10
-rw-r--r--drivers/char/agp/sis-agp.c4
-rw-r--r--drivers/char/amiserial.c7
-rw-r--r--drivers/char/cyclades.c11
-rw-r--r--drivers/char/drm/radeon_mem.c8
-rw-r--r--drivers/char/dtlk.c19
-rw-r--r--drivers/char/epca.c3
-rw-r--r--drivers/char/esp.c56
-rw-r--r--drivers/char/ftape/lowlevel/fdc-io.c2
-rw-r--r--drivers/char/ftape/lowlevel/ftape-io.c20
-rw-r--r--drivers/char/ftape/zftape/zftape-buffers.c4
-rw-r--r--drivers/char/generic_serial.c7
-rw-r--r--drivers/char/hvc_console.c5
-rw-r--r--drivers/char/ip2main.c3
-rw-r--r--drivers/char/ipmi/ipmi_si_intf.c1
-rw-r--r--drivers/char/isicom.c3
-rw-r--r--drivers/char/istallion.c24
-rw-r--r--drivers/char/lcd.c4
-rw-r--r--drivers/char/moxa.c3
-rw-r--r--drivers/char/mxser.c4
-rw-r--r--drivers/char/pcmcia/synclink_cs.c12
-rw-r--r--drivers/char/pcxx.c3
-rw-r--r--drivers/char/rio/rio_linux.c6
-rw-r--r--drivers/char/riscom8.c7
-rw-r--r--drivers/char/rocket.c6
-rw-r--r--drivers/char/serial167.c3
-rw-r--r--drivers/char/specialix.c6
-rw-r--r--drivers/char/stallion.c25
-rw-r--r--drivers/char/sx.c2
-rw-r--r--drivers/char/synclink.c13
-rw-r--r--drivers/char/synclinkmp.c15
-rw-r--r--drivers/char/tpqic02.c3
-rw-r--r--drivers/char/tty_io.c4
-rw-r--r--drivers/char/vt.c4
-rw-r--r--drivers/ide/ide-cd.c8
-rw-r--r--drivers/ieee1394/nodemgr.c5
-rw-r--r--drivers/ieee1394/sbp2.c11
-rw-r--r--drivers/ieee1394/sbp2.h18
-rw-r--r--drivers/isdn/act2000/act2000_isa.c11
-rw-r--r--drivers/isdn/capi/kcapi.c7
-rw-r--r--drivers/isdn/hisax/config.c3
-rw-r--r--drivers/isdn/hisax/elsa.c3
-rw-r--r--drivers/isdn/hisax/hfc_pci.c3
-rw-r--r--drivers/isdn/hisax/hfc_sx.c6
-rw-r--r--drivers/isdn/hisax/hfcscard.c3
-rw-r--r--drivers/isdn/hysdn/boardergo.c7
-rw-r--r--drivers/isdn/hysdn/hysdn_sched.c7
-rw-r--r--drivers/isdn/i4l/isdn_tty.c4
-rw-r--r--drivers/isdn/icn/icn.c15
-rw-r--r--drivers/isdn/icn/icn.h3
-rw-r--r--drivers/isdn/isdnloop/isdnloop.c3
-rw-r--r--drivers/isdn/sc/card.h7
-rw-r--r--drivers/isdn/sc/hardware.h3
-rw-r--r--drivers/isdn/sc/init.c10
-rw-r--r--drivers/isdn/tpam/tpam.h11
-rw-r--r--drivers/isdn/tpam/tpam_commands.c50
-rw-r--r--drivers/isdn/tpam/tpam_nco.c34
-rw-r--r--drivers/isdn/tpam/tpam_queues.c26
-rw-r--r--drivers/md/md.c3
-rw-r--r--drivers/md/raid1.c2
-rw-r--r--drivers/md/raid10.c2
-rw-r--r--drivers/media/video/bw-qcam.c12
-rw-r--r--drivers/media/video/c-qcam.c6
-rw-r--r--drivers/media/video/cpia.c17
-rw-r--r--drivers/media/video/ovcamchip/ovcamchip_core.c7
-rw-r--r--drivers/media/video/planb.c3
-rw-r--r--drivers/media/video/saa5249.c3
-rw-r--r--drivers/media/video/tda9887.c4
-rw-r--r--drivers/media/video/videocodec.c5
-rw-r--r--drivers/media/video/zoran_driver.c6
-rw-r--r--drivers/media/video/zr36120.c6
-rw-r--r--drivers/message/fusion/mptbase.c39
-rw-r--r--drivers/message/i2o/debug.c196
-rw-r--r--drivers/message/i2o/device.c4
-rw-r--r--drivers/message/i2o/driver.c31
-rw-r--r--drivers/message/i2o/exec-osm.c13
-rw-r--r--drivers/message/i2o/i2o_block.c204
-rw-r--r--drivers/message/i2o/i2o_config.c94
-rw-r--r--drivers/message/i2o/i2o_proc.c9
-rw-r--r--drivers/message/i2o/i2o_scsi.c98
-rw-r--r--drivers/message/i2o/iop.c76
-rw-r--r--drivers/message/i2o/pci.c19
-rw-r--r--drivers/net/bsd_comp.c4
-rw-r--r--drivers/net/hamradio/dmascc.c1
-rw-r--r--drivers/net/irda/stir4200.c10
-rw-r--r--drivers/net/mac89x0.c4
-rw-r--r--drivers/net/ppp_deflate.c4
-rw-r--r--drivers/pci/quirks.c230
-rw-r--r--drivers/s390/block/dasd.c4
-rw-r--r--drivers/s390/char/tape_block.c4
-rw-r--r--drivers/scsi/sr.c7
-rw-r--r--drivers/usb/Kconfig4
-rw-r--r--drivers/usb/Makefile4
-rw-r--r--drivers/usb/atm/Kconfig30
-rw-r--r--drivers/usb/atm/Makefile7
-rw-r--r--drivers/usb/atm/speedtch.c866
-rw-r--r--drivers/usb/atm/usb_atm.c1205
-rw-r--r--drivers/usb/atm/usb_atm.h159
-rw-r--r--drivers/usb/class/Kconfig3
-rw-r--r--drivers/usb/class/audio.c37
-rw-r--r--drivers/usb/class/bluetty.c12
-rw-r--r--drivers/usb/class/cdc-acm.c33
-rw-r--r--drivers/usb/class/cdc-acm.h1
-rw-r--r--drivers/usb/class/usb-midi.c19
-rw-r--r--drivers/usb/class/usblp.c4
-rw-r--r--drivers/usb/core/devices.c18
-rw-r--r--drivers/usb/core/devio.c57
-rw-r--r--drivers/usb/core/hcd-pci.c6
-rw-r--r--drivers/usb/core/hcd.c33
-rw-r--r--drivers/usb/core/hcd.h4
-rw-r--r--drivers/usb/core/hub.c397
-rw-r--r--drivers/usb/core/inode.c121
-rw-r--r--drivers/usb/core/message.c50
-rw-r--r--drivers/usb/core/sysfs.c64
-rw-r--r--drivers/usb/core/urb.c5
-rw-r--r--drivers/usb/core/usb.c200
-rw-r--r--drivers/usb/core/usb.h10
-rw-r--r--drivers/usb/gadget/Kconfig15
-rw-r--r--drivers/usb/gadget/dummy_hcd.c39
-rw-r--r--drivers/usb/gadget/ether.c46
-rw-r--r--drivers/usb/gadget/file_storage.c43
-rw-r--r--drivers/usb/gadget/gadget_chips.h6
-rw-r--r--drivers/usb/gadget/goku_udc.c14
-rw-r--r--drivers/usb/gadget/lh7a40x_udc.c7
-rw-r--r--drivers/usb/gadget/net2280.c10
-rw-r--r--drivers/usb/gadget/omap_udc.c146
-rw-r--r--drivers/usb/gadget/omap_udc.h11
-rw-r--r--drivers/usb/gadget/pxa2xx_udc.c17
-rw-r--r--drivers/usb/gadget/rndis.c51
-rw-r--r--drivers/usb/gadget/zero.c2
-rw-r--r--drivers/usb/host/ehci-hcd.c5
-rw-r--r--drivers/usb/host/ehci-hub.c2
-rw-r--r--drivers/usb/host/hc_sl811.c8
-rw-r--r--drivers/usb/host/ohci-dbg.c4
-rw-r--r--drivers/usb/host/ohci-hcd.c254
-rw-r--r--drivers/usb/host/ohci-hub.c63
-rw-r--r--drivers/usb/host/ohci-lh7a404.c30
-rw-r--r--drivers/usb/host/ohci-omap.c30
-rw-r--r--drivers/usb/host/ohci-pci.c37
-rw-r--r--drivers/usb/host/ohci-pxa27x.c460
-rw-r--r--drivers/usb/host/ohci-sa1111.c25
-rw-r--r--drivers/usb/host/ohci.h34
-rw-r--r--drivers/usb/host/uhci-hcd.c253
-rw-r--r--drivers/usb/host/uhci-hcd.h18
-rw-r--r--drivers/usb/host/uhci-hub.c134
-rw-r--r--drivers/usb/image/Kconfig7
-rw-r--r--drivers/usb/image/hpusbscsi.c12
-rw-r--r--drivers/usb/image/mdc800.c18
-rw-r--r--drivers/usb/image/microtek.c17
-rw-r--r--drivers/usb/image/microtek.h1
-rw-r--r--drivers/usb/input/aiptek.c4
-rw-r--r--drivers/usb/input/ati_remote.c7
-rw-r--r--drivers/usb/input/hid-core.c22
-rw-r--r--drivers/usb/input/kbtab.c4
-rw-r--r--drivers/usb/input/mtouchusb.c4
-rw-r--r--drivers/usb/input/pid.c2
-rw-r--r--drivers/usb/input/powermate.c2
-rw-r--r--drivers/usb/input/touchkitusb.c4
-rw-r--r--drivers/usb/input/usbkbd.c4
-rw-r--r--drivers/usb/input/usbmouse.c4
-rw-r--r--drivers/usb/input/wacom.c4
-rw-r--r--drivers/usb/input/xpad.c4
-rw-r--r--drivers/usb/media/Kconfig6
-rw-r--r--drivers/usb/media/dabusb.c6
-rw-r--r--drivers/usb/media/konicawc.c9
-rw-r--r--drivers/usb/media/ov511.c2
-rw-r--r--drivers/usb/media/se401.c6
-rw-r--r--drivers/usb/media/sn9c102.h19
-rw-r--r--drivers/usb/media/sn9c102_core.c148
-rw-r--r--drivers/usb/media/sn9c102_pas106b.c14
-rw-r--r--drivers/usb/media/sn9c102_pas202bcb.c21
-rw-r--r--drivers/usb/media/sn9c102_sensor.h61
-rw-r--r--drivers/usb/media/sn9c102_tas5110c1b.c45
-rw-r--r--drivers/usb/media/sn9c102_tas5130d1b.c60
-rw-r--r--drivers/usb/media/stv680.c4
-rw-r--r--drivers/usb/media/usbvideo.c4
-rw-r--r--drivers/usb/misc/Kconfig12
-rw-r--r--drivers/usb/misc/Makefile1
-rw-r--r--drivers/usb/misc/auerswald.c14
-rw-r--r--drivers/usb/misc/legousbtower.c4
-rw-r--r--drivers/usb/misc/speedtch.c1373
-rw-r--r--drivers/usb/misc/tiglusb.c1
-rw-r--r--drivers/usb/misc/uss720.c4
-rw-r--r--drivers/usb/net/catc.c8
-rw-r--r--drivers/usb/net/kaweth.c22
-rw-r--r--drivers/usb/net/pegasus.c12
-rw-r--r--drivers/usb/net/rtl8150.c10
-rw-r--r--drivers/usb/net/usbnet.c21
-rw-r--r--drivers/usb/serial/Kconfig10
-rw-r--r--drivers/usb/serial/Makefile1
-rw-r--r--drivers/usb/serial/belkin_sa.c8
-rw-r--r--drivers/usb/serial/cyberjack.c6
-rw-r--r--drivers/usb/serial/digi_acceleport.c6
-rw-r--r--drivers/usb/serial/empeg.c8
-rw-r--r--drivers/usb/serial/ftdi_sio.c24
-rw-r--r--drivers/usb/serial/ftdi_sio.h15
-rw-r--r--drivers/usb/serial/generic.c4
-rw-r--r--drivers/usb/serial/io_edgeport.c6
-rw-r--r--drivers/usb/serial/io_ti.c6
-rw-r--r--drivers/usb/serial/ipaq.c23
-rw-r--r--drivers/usb/serial/ipw.c496
-rw-r--r--drivers/usb/serial/ir-usb.c2
-rw-r--r--drivers/usb/serial/keyspan_pda.c6
-rw-r--r--drivers/usb/serial/kl5kusb105.c12
-rw-r--r--drivers/usb/serial/kobil_sct.c14
-rw-r--r--drivers/usb/serial/mct_u232.c6
-rw-r--r--drivers/usb/serial/omninet.c4
-rw-r--r--drivers/usb/serial/pl2303.c430
-rw-r--r--drivers/usb/serial/usb-serial.c125
-rw-r--r--drivers/usb/serial/usb-serial.h1
-rw-r--r--drivers/usb/serial/visor.c6
-rw-r--r--drivers/usb/serial/whiteheat.c8
-rw-r--r--drivers/usb/storage/isd200.c6
-rw-r--r--drivers/usb/storage/protocol.c49
-rw-r--r--drivers/usb/storage/scsiglue.c31
-rw-r--r--drivers/usb/storage/transport.c25
-rw-r--r--drivers/usb/storage/unusual_devs.h136
-rw-r--r--drivers/usb/storage/usb.c70
-rw-r--r--drivers/usb/storage/usb.h3
-rw-r--r--drivers/video/Kconfig18
-rw-r--r--drivers/video/aty/atyfb_base.c2
-rw-r--r--drivers/video/aty/radeon_base.c307
-rw-r--r--drivers/video/aty/radeon_monitor.c57
-rw-r--r--drivers/video/aty/radeon_pm.c4
-rw-r--r--drivers/video/aty/radeonfb.h2
-rw-r--r--drivers/video/bw2.c2
-rw-r--r--drivers/video/chipsfb.c2
-rw-r--r--drivers/video/console/Makefile3
-rw-r--r--drivers/video/console/bitblit.c370
-rw-r--r--drivers/video/console/fbcon.c891
-rw-r--r--drivers/video/console/fbcon.h26
-rw-r--r--drivers/video/console/tileblit.c146
-rw-r--r--drivers/video/console/vgacon.c4
-rw-r--r--drivers/video/cyber2000fb.c4
-rw-r--r--drivers/video/fbmem.c63
-rw-r--r--drivers/video/fbsysfs.c2
-rw-r--r--drivers/video/fm2fb.c23
-rw-r--r--drivers/video/i810/i810.h4
-rw-r--r--drivers/video/i810/i810_accel.c14
-rw-r--r--drivers/video/i810/i810_gtf.c3
-rw-r--r--drivers/video/i810/i810_main.c148
-rw-r--r--drivers/video/igafb.c1
-rw-r--r--drivers/video/imsttfb.c1
-rw-r--r--drivers/video/kyro/fbdev.c1
-rw-r--r--drivers/video/matrox/matroxfb_accel.c22
-rw-r--r--drivers/video/matrox/matroxfb_base.c8
-rw-r--r--drivers/video/matrox/matroxfb_base.h140
-rw-r--r--drivers/video/matrox/matroxfb_crtc2.c2
-rw-r--r--drivers/video/pvr2fb.c1
-rw-r--r--drivers/video/radeonfb.c2
-rw-r--r--drivers/video/riva/fbdev.c22
-rw-r--r--drivers/video/sstfb.c1
-rw-r--r--drivers/video/tdfxfb.c9
-rw-r--r--drivers/video/tgafb.c1
-rw-r--r--drivers/video/tridentfb.c1
-rw-r--r--drivers/video/vesafb.c67
-rw-r--r--drivers/video/vga16fb.c29
271 files changed, 12617 insertions, 5568 deletions
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
index a1d50242b8cd..6a43c807497d 100644
--- a/drivers/block/Kconfig
+++ b/drivers/block/Kconfig
@@ -356,6 +356,39 @@ config LBD
your machine, or if you want to have a raid or loopback device
bigger than 2TB. Otherwise say N.
+config CDROM_PKTCDVD
+ tristate "Packet writing on CD/DVD media"
+ help
+ If you have a CDROM drive that supports packet writing, say Y to
+ include preliminary support. It should work with any MMC/Mt Fuji
+ compliant ATAPI or SCSI drive, which is just about any newer CD
+ writer.
+
+ Currently only writing to CD-RW, DVD-RW and DVD+RW discs is possible.
+ DVD-RW disks must be in restricted overwrite mode.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pktcdvd.
+
+config CDROM_PKTCDVD_BUFFERS
+ int "Free buffers for data gathering"
+ depends on CDROM_PKTCDVD
+ default "8"
+ help
+ This controls the maximum number of active concurrent packets. More
+ concurrent packets can increase write performance, but also require
+ more memory. Each concurrent packet will require approximately 64Kb
+ of non-swappable kernel memory, memory which will be allocated at
+ pktsetup time.
+
+config CDROM_PKTCDVD_WCACHE
+ bool "Enable write caching"
+ depends on CDROM_PKTCDVD
+ help
+ If enabled, write caching will be set for the CD-R/W device. For now
+ this option is dangerous unless the CD-RW media is known good, as we
+ don't do deferred write error handling yet.
+
source "drivers/s390/block/Kconfig"
endmenu
diff --git a/drivers/block/Kconfig.iosched b/drivers/block/Kconfig.iosched
index d938c5fd130b..e0ba6c93717e 100644
--- a/drivers/block/Kconfig.iosched
+++ b/drivers/block/Kconfig.iosched
@@ -1,5 +1,5 @@
config IOSCHED_NOOP
- bool "No-op I/O scheduler" if EMBEDDED
+ bool
default y
---help---
The no-op I/O scheduler is a minimal scheduler that does basic merging
@@ -9,7 +9,7 @@ config IOSCHED_NOOP
the kernel.
config IOSCHED_AS
- bool "Anticipatory I/O scheduler" if EMBEDDED
+ tristate "Anticipatory I/O scheduler"
default y
---help---
The anticipatory I/O scheduler is the default disk scheduler. It is
@@ -18,7 +18,7 @@ config IOSCHED_AS
slower in some cases especially some database loads.
config IOSCHED_DEADLINE
- bool "Deadline I/O scheduler" if EMBEDDED
+ tristate "Deadline I/O scheduler"
default y
---help---
The deadline I/O scheduler is simple and compact, and is often as
@@ -28,7 +28,7 @@ config IOSCHED_DEADLINE
anticipatory I/O scheduler and so is a good choice.
config IOSCHED_CFQ
- bool "CFQ I/O scheduler" if EMBEDDED
+ tristate "CFQ I/O scheduler"
default y
---help---
The CFQ I/O scheduler tries to distribute bandwidth equally
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index c8fbbf14ce94..1cf09a1c065b 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_BLK_DEV_XD) += xd.o
obj-$(CONFIG_BLK_CPQ_DA) += cpqarray.o
obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o
obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o
+obj-$(CONFIG_CDROM_PKTCDVD) += pktcdvd.o
obj-$(CONFIG_BLK_DEV_UMEM) += umem.o
obj-$(CONFIG_BLK_DEV_NBD) += nbd.o
diff --git a/drivers/block/as-iosched.c b/drivers/block/as-iosched.c
index 0ef6a665d93e..0aa3ee8c309b 100644
--- a/drivers/block/as-iosched.c
+++ b/drivers/block/as-iosched.c
@@ -614,7 +614,7 @@ static void as_antic_stop(struct as_data *ad)
static void as_antic_timeout(unsigned long data)
{
struct request_queue *q = (struct request_queue *)data;
- struct as_data *ad = q->elevator.elevator_data;
+ struct as_data *ad = q->elevator->elevator_data;
unsigned long flags;
spin_lock_irqsave(q->queue_lock, flags);
@@ -945,7 +945,7 @@ static void update_write_batch(struct as_data *ad)
*/
static void as_completed_request(request_queue_t *q, struct request *rq)
{
- struct as_data *ad = q->elevator.elevator_data;
+ struct as_data *ad = q->elevator->elevator_data;
struct as_rq *arq = RQ_DATA(rq);
WARN_ON(!list_empty(&rq->queuelist));
@@ -1030,7 +1030,7 @@ static void as_remove_queued_request(request_queue_t *q, struct request *rq)
{
struct as_rq *arq = RQ_DATA(rq);
const int data_dir = arq->is_sync;
- struct as_data *ad = q->elevator.elevator_data;
+ struct as_data *ad = q->elevator->elevator_data;
WARN_ON(arq->state != AS_RQ_QUEUED);
@@ -1361,7 +1361,7 @@ fifo_expired:
static struct request *as_next_request(request_queue_t *q)
{
- struct as_data *ad = q->elevator.elevator_data;
+ struct as_data *ad = q->elevator->elevator_data;
struct request *rq = NULL;
/*
@@ -1469,7 +1469,7 @@ static void as_add_request(struct as_data *ad, struct as_rq *arq)
*/
static void as_requeue_request(request_queue_t *q, struct request *rq)
{
- struct as_data *ad = q->elevator.elevator_data;
+ struct as_data *ad = q->elevator->elevator_data;
struct as_rq *arq = RQ_DATA(rq);
if (arq) {
@@ -1509,7 +1509,7 @@ static void as_account_queued_request(struct as_data *ad, struct request *rq)
static void
as_insert_request(request_queue_t *q, struct request *rq, int where)
{
- struct as_data *ad = q->elevator.elevator_data;
+ struct as_data *ad = q->elevator->elevator_data;
struct as_rq *arq = RQ_DATA(rq);
if (arq) {
@@ -1562,7 +1562,7 @@ as_insert_request(request_queue_t *q, struct request *rq, int where)
*/
static int as_queue_empty(request_queue_t *q)
{
- struct as_data *ad = q->elevator.elevator_data;
+ struct as_data *ad = q->elevator->elevator_data;
if (!list_empty(&ad->fifo_list[REQ_ASYNC])
|| !list_empty(&ad->fifo_list[REQ_SYNC])
@@ -1601,7 +1601,7 @@ as_latter_request(request_queue_t *q, struct request *rq)
static int
as_merge(request_queue_t *q, struct request **req, struct bio *bio)
{
- struct as_data *ad = q->elevator.elevator_data;
+ struct as_data *ad = q->elevator->elevator_data;
sector_t rb_key = bio->bi_sector + bio_sectors(bio);
struct request *__rq;
int ret;
@@ -1656,7 +1656,7 @@ out_insert:
static void as_merged_request(request_queue_t *q, struct request *req)
{
- struct as_data *ad = q->elevator.elevator_data;
+ struct as_data *ad = q->elevator->elevator_data;
struct as_rq *arq = RQ_DATA(req);
/*
@@ -1701,7 +1701,7 @@ static void
as_merged_requests(request_queue_t *q, struct request *req,
struct request *next)
{
- struct as_data *ad = q->elevator.elevator_data;
+ struct as_data *ad = q->elevator->elevator_data;
struct as_rq *arq = RQ_DATA(req);
struct as_rq *anext = RQ_DATA(next);
@@ -1788,7 +1788,7 @@ static void as_work_handler(void *data)
static void as_put_request(request_queue_t *q, struct request *rq)
{
- struct as_data *ad = q->elevator.elevator_data;
+ struct as_data *ad = q->elevator->elevator_data;
struct as_rq *arq = RQ_DATA(rq);
if (!arq) {
@@ -1807,7 +1807,7 @@ static void as_put_request(request_queue_t *q, struct request *rq)
static int as_set_request(request_queue_t *q, struct request *rq, int gfp_mask)
{
- struct as_data *ad = q->elevator.elevator_data;
+ struct as_data *ad = q->elevator->elevator_data;
struct as_rq *arq = mempool_alloc(ad->arq_pool, gfp_mask);
if (arq) {
@@ -1828,21 +1828,21 @@ static int as_set_request(request_queue_t *q, struct request *rq, int gfp_mask)
static int as_may_queue(request_queue_t *q, int rw)
{
- int ret = 0;
- struct as_data *ad = q->elevator.elevator_data;
+ int ret = ELV_MQUEUE_MAY;
+ struct as_data *ad = q->elevator->elevator_data;
struct io_context *ioc;
if (ad->antic_status == ANTIC_WAIT_REQ ||
ad->antic_status == ANTIC_WAIT_NEXT) {
ioc = as_get_io_context();
if (ad->io_context == ioc)
- ret = 1;
+ ret = ELV_MQUEUE_MUST;
put_io_context(ioc);
}
return ret;
}
-static void as_exit(request_queue_t *q, elevator_t *e)
+static void as_exit_queue(elevator_t *e)
{
struct as_data *ad = e->elevator_data;
@@ -1862,7 +1862,7 @@ static void as_exit(request_queue_t *q, elevator_t *e)
* initialize elevator private data (as_data), and alloc a arq for
* each request on the free lists
*/
-static int as_init(request_queue_t *q, elevator_t *e)
+static int as_init_queue(request_queue_t *q, elevator_t *e)
{
struct as_data *ad;
int i;
@@ -1962,10 +1962,10 @@ static ssize_t as_est_show(struct as_data *ad, char *page)
return pos;
}
-#define SHOW_FUNCTION(__FUNC, __VAR) \
+#define SHOW_FUNCTION(__FUNC, __VAR) \
static ssize_t __FUNC(struct as_data *ad, char *page) \
-{ \
- return as_var_show(__VAR, (page)); \
+{ \
+ return as_var_show(jiffies_to_msecs((__VAR)), (page)); \
}
SHOW_FUNCTION(as_readexpire_show, ad->fifo_expire[REQ_SYNC]);
SHOW_FUNCTION(as_writeexpire_show, ad->fifo_expire[REQ_ASYNC]);
@@ -1982,6 +1982,7 @@ static ssize_t __FUNC(struct as_data *ad, const char *page, size_t count) \
*(__PTR) = (MIN); \
else if (*(__PTR) > (MAX)) \
*(__PTR) = (MAX); \
+ *(__PTR) = msecs_to_jiffies(*(__PTR)); \
return ret; \
}
STORE_FUNCTION(as_readexpire_store, &ad->fifo_expire[REQ_SYNC], 0, INT_MAX);
@@ -2070,39 +2071,64 @@ static struct kobj_type as_ktype = {
.default_attrs = default_attrs,
};
-static int __init as_slab_setup(void)
+static struct elevator_type iosched_as = {
+ .ops = {
+ .elevator_merge_fn = as_merge,
+ .elevator_merged_fn = as_merged_request,
+ .elevator_merge_req_fn = as_merged_requests,
+ .elevator_next_req_fn = as_next_request,
+ .elevator_add_req_fn = as_insert_request,
+ .elevator_remove_req_fn = as_remove_request,
+ .elevator_requeue_req_fn = as_requeue_request,
+ .elevator_queue_empty_fn = as_queue_empty,
+ .elevator_completed_req_fn = as_completed_request,
+ .elevator_former_req_fn = as_former_request,
+ .elevator_latter_req_fn = as_latter_request,
+ .elevator_set_req_fn = as_set_request,
+ .elevator_put_req_fn = as_put_request,
+ .elevator_may_queue_fn = as_may_queue,
+ .elevator_init_fn = as_init_queue,
+ .elevator_exit_fn = as_exit_queue,
+ },
+
+ .elevator_ktype = &as_ktype,
+ .elevator_name = "anticipatory",
+ .elevator_owner = THIS_MODULE,
+};
+
+int as_init(void)
{
+ int ret;
+
arq_pool = kmem_cache_create("as_arq", sizeof(struct as_rq),
0, 0, NULL, NULL);
-
if (!arq_pool)
- panic("as: can't init slab pool\n");
+ return -ENOMEM;
- return 0;
+ ret = elv_register(&iosched_as);
+ if (!ret) {
+ /*
+ * don't allow AS to get unregistered, since we would have
+ * to browse all tasks in the system and release their
+ * as_io_context first
+ */
+ __module_get(THIS_MODULE);
+ return 0;
+ }
+
+ kmem_cache_destroy(arq_pool);
+ return ret;
}
-subsys_initcall(as_slab_setup);
-
-elevator_t iosched_as = {
- .elevator_merge_fn = as_merge,
- .elevator_merged_fn = as_merged_request,
- .elevator_merge_req_fn = as_merged_requests,
- .elevator_next_req_fn = as_next_request,
- .elevator_add_req_fn = as_insert_request,
- .elevator_remove_req_fn = as_remove_request,
- .elevator_requeue_req_fn = as_requeue_request,
- .elevator_queue_empty_fn = as_queue_empty,
- .elevator_completed_req_fn = as_completed_request,
- .elevator_former_req_fn = as_former_request,
- .elevator_latter_req_fn = as_latter_request,
- .elevator_set_req_fn = as_set_request,
- .elevator_put_req_fn = as_put_request,
- .elevator_may_queue_fn = as_may_queue,
- .elevator_init_fn = as_init,
- .elevator_exit_fn = as_exit,
-
- .elevator_ktype = &as_ktype,
- .elevator_name = "anticipatory",
-};
+void as_exit(void)
+{
+ kmem_cache_destroy(arq_pool);
+ elv_unregister(&iosched_as);
+}
+
+module_init(as_init);
+module_exit(as_exit);
-EXPORT_SYMBOL(iosched_as);
+MODULE_AUTHOR("Nick Piggin");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("anticipatory IO scheduler");
diff --git a/drivers/block/cfq-iosched.c b/drivers/block/cfq-iosched.c
index 068f4eae0b5c..cf7fc7609e67 100644
--- a/drivers/block/cfq-iosched.c
+++ b/drivers/block/cfq-iosched.c
@@ -22,96 +22,216 @@
#include <linux/rbtree.h>
#include <linux/mempool.h>
+static unsigned long max_elapsed_crq;
+static unsigned long max_elapsed_dispatch;
+
/*
* tunables
*/
-static int cfq_quantum = 4;
-static int cfq_queued = 8;
+static int cfq_quantum = 4; /* max queue in one round of service */
+static int cfq_queued = 8; /* minimum rq allocate limit per-queue*/
+static int cfq_service = HZ; /* period over which service is avg */
+static int cfq_fifo_expire_r = HZ / 2; /* fifo timeout for sync requests */
+static int cfq_fifo_expire_w = 5 * HZ; /* fifo timeout for async requests */
+static int cfq_fifo_rate = HZ / 8; /* fifo expiry rate */
+static int cfq_back_max = 16 * 1024; /* maximum backwards seek, in KiB */
+static int cfq_back_penalty = 2; /* penalty of a backwards seek */
+/*
+ * for the hash of cfqq inside the cfqd
+ */
#define CFQ_QHASH_SHIFT 6
#define CFQ_QHASH_ENTRIES (1 << CFQ_QHASH_SHIFT)
-#define list_entry_qhash(entry) list_entry((entry), struct cfq_queue, cfq_hash)
+#define list_entry_qhash(entry) hlist_entry((entry), struct cfq_queue, cfq_hash)
-#define CFQ_MHASH_SHIFT 8
+/*
+ * for the hash of crq inside the cfqq
+ */
+#define CFQ_MHASH_SHIFT 6
#define CFQ_MHASH_BLOCK(sec) ((sec) >> 3)
#define CFQ_MHASH_ENTRIES (1 << CFQ_MHASH_SHIFT)
-#define CFQ_MHASH_FN(sec) (hash_long(CFQ_MHASH_BLOCK((sec)),CFQ_MHASH_SHIFT))
-#define ON_MHASH(crq) !list_empty(&(crq)->hash)
+#define CFQ_MHASH_FN(sec) hash_long(CFQ_MHASH_BLOCK(sec), CFQ_MHASH_SHIFT)
#define rq_hash_key(rq) ((rq)->sector + (rq)->nr_sectors)
-#define list_entry_hash(ptr) list_entry((ptr), struct cfq_rq, hash)
+#define list_entry_hash(ptr) hlist_entry((ptr), struct cfq_rq, hash)
#define list_entry_cfqq(ptr) list_entry((ptr), struct cfq_queue, cfq_list)
-#define RQ_DATA(rq) ((struct cfq_rq *) (rq)->elevator_private)
+#define RQ_DATA(rq) (rq)->elevator_private
+
+/*
+ * rb-tree defines
+ */
+#define RB_NONE (2)
+#define RB_EMPTY(node) ((node)->rb_node == NULL)
+#define RB_CLEAR_COLOR(node) (node)->rb_color = RB_NONE
+#define RB_CLEAR(node) do { \
+ (node)->rb_parent = NULL; \
+ RB_CLEAR_COLOR((node)); \
+ (node)->rb_right = NULL; \
+ (node)->rb_left = NULL; \
+} while (0)
+#define RB_CLEAR_ROOT(root) ((root)->rb_node = NULL)
+#define ON_RB(node) ((node)->rb_color != RB_NONE)
+#define rb_entry_crq(node) rb_entry((node), struct cfq_rq, rb_node)
+#define rq_rb_key(rq) (rq)->sector
+
+/*
+ * threshold for switching off non-tag accounting
+ */
+#define CFQ_MAX_TAG (4)
+
+/*
+ * sort key types and names
+ */
+enum {
+ CFQ_KEY_PGID,
+ CFQ_KEY_TGID,
+ CFQ_KEY_UID,
+ CFQ_KEY_GID,
+ CFQ_KEY_LAST,
+};
+
+static char *cfq_key_types[] = { "pgid", "tgid", "uid", "gid", NULL };
+
+/*
+ * spare queue
+ */
+#define CFQ_KEY_SPARE (~0UL)
static kmem_cache_t *crq_pool;
static kmem_cache_t *cfq_pool;
-static mempool_t *cfq_mpool;
+static kmem_cache_t *cfq_ioc_pool;
struct cfq_data {
struct list_head rr_list;
- struct list_head *dispatch;
- struct list_head *cfq_hash;
+ struct list_head empty_list;
- struct list_head *crq_hash;
+ struct hlist_head *cfq_hash;
+ struct hlist_head *crq_hash;
+ /* queues on rr_list (ie they have pending requests */
unsigned int busy_queues;
+
unsigned int max_queued;
+ atomic_t ref;
+
+ int key_type;
+
mempool_t *crq_pool;
request_queue_t *queue;
+ sector_t last_sector;
+
+ int rq_in_driver;
+
/*
- * tunables
+ * tunables, see top of file
*/
unsigned int cfq_quantum;
unsigned int cfq_queued;
+ unsigned int cfq_fifo_expire_r;
+ unsigned int cfq_fifo_expire_w;
+ unsigned int cfq_fifo_batch_expire;
+ unsigned int cfq_back_penalty;
+ unsigned int cfq_back_max;
+ unsigned int find_best_crq;
+
+ unsigned int cfq_tagged;
};
struct cfq_queue {
- struct list_head cfq_hash;
+ /* reference count */
+ atomic_t ref;
+ /* parent cfq_data */
+ struct cfq_data *cfqd;
+ /* hash of mergeable requests */
+ struct hlist_node cfq_hash;
+ /* hash key */
+ unsigned long key;
+ /* whether queue is on rr (or empty) list */
+ int on_rr;
+ /* on either rr or empty list of cfqd */
struct list_head cfq_list;
+ /* sorted list of pending requests */
struct rb_root sort_list;
- int pid;
+ /* if fifo isn't expired, next request to serve */
+ struct cfq_rq *next_crq;
+ /* requests queued in sort_list */
int queued[2];
-#if 0
- /*
- * with a simple addition like this, we can do io priorities. almost.
- * does need a split request free list, too.
- */
- int io_prio
-#endif
+ /* currently allocated requests */
+ int allocated[2];
+ /* fifo list of requests in sort_list */
+ struct list_head fifo[2];
+ /* last time fifo expired */
+ unsigned long last_fifo_expire;
+
+ int key_type;
+
+ unsigned long service_start;
+ unsigned long service_used;
+
+ unsigned int max_rate;
+
+ /* number of requests that have been handed to the driver */
+ int in_flight;
+ /* number of currently allocated requests */
+ int alloc_limit[2];
};
struct cfq_rq {
struct rb_node rb_node;
sector_t rb_key;
-
struct request *request;
+ struct hlist_node hash;
struct cfq_queue *cfq_queue;
+ struct cfq_io_context *io_context;
+
+ unsigned long service_start;
+ unsigned long queue_start;
- struct list_head hash;
+ unsigned int in_flight : 1;
+ unsigned int accounted : 1;
+ unsigned int is_sync : 1;
+ unsigned int is_write : 1;
};
-static void cfq_put_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq);
-static struct cfq_queue *cfq_find_cfq_hash(struct cfq_data *cfqd, int pid);
-static void cfq_dispatch_sort(struct cfq_data *cfqd, struct cfq_queue *cfqq,
- struct cfq_rq *crq);
+static struct cfq_queue *cfq_find_cfq_hash(struct cfq_data *, unsigned long);
+static void cfq_dispatch_sort(request_queue_t *, struct cfq_rq *);
+static void cfq_update_next_crq(struct cfq_rq *);
+static void cfq_put_cfqd(struct cfq_data *cfqd);
/*
- * lots of deadline iosched dupes, can be abstracted later...
+ * what the fairness is based on (ie how processes are grouped and
+ * differentiated)
*/
-static inline void __cfq_del_crq_hash(struct cfq_rq *crq)
+static inline unsigned long
+cfq_hash_key(struct cfq_data *cfqd, struct task_struct *tsk)
{
- list_del_init(&crq->hash);
+ /*
+ * optimize this so that ->key_type is the offset into the struct
+ */
+ switch (cfqd->key_type) {
+ case CFQ_KEY_PGID:
+ return process_group(tsk);
+ default:
+ case CFQ_KEY_TGID:
+ return tsk->tgid;
+ case CFQ_KEY_UID:
+ return tsk->uid;
+ case CFQ_KEY_GID:
+ return tsk->gid;
+ }
}
+/*
+ * lots of deadline iosched dupes, can be abstracted later...
+ */
static inline void cfq_del_crq_hash(struct cfq_rq *crq)
{
- if (ON_MHASH(crq))
- __cfq_del_crq_hash(crq);
+ hlist_del_init(&crq->hash);
}
static void cfq_remove_merge_hints(request_queue_t *q, struct cfq_rq *crq)
@@ -120,32 +240,32 @@ static void cfq_remove_merge_hints(request_queue_t *q, struct cfq_rq *crq)
if (q->last_merge == crq->request)
q->last_merge = NULL;
+
+ cfq_update_next_crq(crq);
}
static inline void cfq_add_crq_hash(struct cfq_data *cfqd, struct cfq_rq *crq)
{
- struct request *rq = crq->request;
+ const int hash_idx = CFQ_MHASH_FN(rq_hash_key(crq->request));
- BUG_ON(ON_MHASH(crq));
+ BUG_ON(!hlist_unhashed(&crq->hash));
- list_add(&crq->hash, &cfqd->crq_hash[CFQ_MHASH_FN(rq_hash_key(rq))]);
+ hlist_add_head(&crq->hash, &cfqd->crq_hash[hash_idx]);
}
static struct request *cfq_find_rq_hash(struct cfq_data *cfqd, sector_t offset)
{
- struct list_head *hash_list = &cfqd->crq_hash[CFQ_MHASH_FN(offset)];
- struct list_head *entry, *next = hash_list->next;
+ struct hlist_head *hash_list = &cfqd->crq_hash[CFQ_MHASH_FN(offset)];
+ struct hlist_node *entry, *next;
- while ((entry = next) != hash_list) {
+ hlist_for_each_safe(entry, next, hash_list) {
struct cfq_rq *crq = list_entry_hash(entry);
struct request *__rq = crq->request;
- next = entry->next;
-
- BUG_ON(!ON_MHASH(crq));
+ BUG_ON(hlist_unhashed(&crq->hash));
if (!rq_mergeable(__rq)) {
- __cfq_del_crq_hash(crq);
+ cfq_del_crq_hash(crq);
continue;
}
@@ -157,29 +277,257 @@ static struct request *cfq_find_rq_hash(struct cfq_data *cfqd, sector_t offset)
}
/*
- * rb tree support functions
+ * Lifted from AS - choose which of crq1 and crq2 that is best served now.
+ * We choose the request that is closest to the head right now. Distance
+ * behind the head are penalized and only allowed to a certain extent.
*/
-#define RB_NONE (2)
-#define RB_EMPTY(node) ((node)->rb_node == NULL)
-#define RB_CLEAR(node) ((node)->rb_color = RB_NONE)
-#define RB_CLEAR_ROOT(root) ((root)->rb_node = NULL)
-#define ON_RB(node) ((node)->rb_color != RB_NONE)
-#define rb_entry_crq(node) rb_entry((node), struct cfq_rq, rb_node)
-#define rq_rb_key(rq) (rq)->sector
+static struct cfq_rq *
+cfq_choose_req(struct cfq_data *cfqd, struct cfq_rq *crq1, struct cfq_rq *crq2)
+{
+ sector_t last, s1, s2, d1 = 0, d2 = 0;
+ int r1_wrap = 0, r2_wrap = 0; /* requests are behind the disk head */
+ unsigned long back_max;
+
+ if (crq1 == NULL || crq1 == crq2)
+ return crq2;
+ if (crq2 == NULL)
+ return crq1;
-static inline void cfq_del_crq_rb(struct cfq_queue *cfqq, struct cfq_rq *crq)
+ s1 = crq1->request->sector;
+ s2 = crq2->request->sector;
+
+ last = cfqd->last_sector;
+
+#if 0
+ if (!list_empty(&cfqd->queue->queue_head)) {
+ struct list_head *entry = &cfqd->queue->queue_head;
+ unsigned long distance = ~0UL;
+ struct request *rq;
+
+ while ((entry = entry->prev) != &cfqd->queue->queue_head) {
+ rq = list_entry_rq(entry);
+
+ if (blk_barrier_rq(rq))
+ break;
+
+ if (distance < abs(s1 - rq->sector + rq->nr_sectors)) {
+ distance = abs(s1 - rq->sector +rq->nr_sectors);
+ last = rq->sector + rq->nr_sectors;
+ }
+ if (distance < abs(s2 - rq->sector + rq->nr_sectors)) {
+ distance = abs(s2 - rq->sector +rq->nr_sectors);
+ last = rq->sector + rq->nr_sectors;
+ }
+ }
+ }
+#endif
+
+ /*
+ * by definition, 1KiB is 2 sectors
+ */
+ back_max = cfqd->cfq_back_max * 2;
+
+ /*
+ * Strict one way elevator _except_ in the case where we allow
+ * short backward seeks which are biased as twice the cost of a
+ * similar forward seek.
+ */
+ if (s1 >= last)
+ d1 = s1 - last;
+ else if (s1 + back_max >= last)
+ d1 = (last - s1) * cfqd->cfq_back_penalty;
+ else
+ r1_wrap = 1;
+
+ if (s2 >= last)
+ d2 = s2 - last;
+ else if (s2 + back_max >= last)
+ d2 = (last - s2) * cfqd->cfq_back_penalty;
+ else
+ r2_wrap = 1;
+
+ /* Found required data */
+ if (!r1_wrap && r2_wrap)
+ return crq1;
+ else if (!r2_wrap && r1_wrap)
+ return crq2;
+ else if (r1_wrap && r2_wrap) {
+ /* both behind the head */
+ if (s1 <= s2)
+ return crq1;
+ else
+ return crq2;
+ }
+
+ /* Both requests in front of the head */
+ if (d1 < d2)
+ return crq1;
+ else if (d2 < d1)
+ return crq2;
+ else {
+ if (s1 >= s2)
+ return crq1;
+ else
+ return crq2;
+ }
+}
+
+/*
+ * would be nice to take fifo expire time into account as well
+ */
+static struct cfq_rq *
+cfq_find_next_crq(struct cfq_data *cfqd, struct cfq_queue *cfqq,
+ struct cfq_rq *last)
+{
+ struct cfq_rq *crq_next = NULL, *crq_prev = NULL;
+ struct rb_node *rbnext, *rbprev;
+
+ if (!ON_RB(&last->rb_node))
+ return NULL;
+
+ if ((rbnext = rb_next(&last->rb_node)) == NULL)
+ rbnext = rb_first(&cfqq->sort_list);
+
+ rbprev = rb_prev(&last->rb_node);
+
+ if (rbprev)
+ crq_prev = rb_entry_crq(rbprev);
+ if (rbnext)
+ crq_next = rb_entry_crq(rbnext);
+
+ return cfq_choose_req(cfqd, crq_next, crq_prev);
+}
+
+static void cfq_update_next_crq(struct cfq_rq *crq)
{
+ struct cfq_queue *cfqq = crq->cfq_queue;
+
+ if (cfqq->next_crq == crq)
+ cfqq->next_crq = cfq_find_next_crq(cfqq->cfqd, cfqq, crq);
+}
+
+static int cfq_check_sort_rr_list(struct cfq_queue *cfqq)
+{
+ struct list_head *head = &cfqq->cfqd->rr_list;
+ struct list_head *next, *prev;
+
+ /*
+ * list might still be ordered
+ */
+ next = cfqq->cfq_list.next;
+ if (next != head) {
+ struct cfq_queue *cnext = list_entry_cfqq(next);
+
+ if (cfqq->service_used > cnext->service_used)
+ return 1;
+ }
+
+ prev = cfqq->cfq_list.prev;
+ if (prev != head) {
+ struct cfq_queue *cprev = list_entry_cfqq(prev);
+
+ if (cfqq->service_used < cprev->service_used)
+ return 1;
+ }
+
+ return 0;
+}
+
+static void cfq_sort_rr_list(struct cfq_queue *cfqq, int new_queue)
+{
+ struct list_head *entry = &cfqq->cfqd->rr_list;
+
+ if (!cfqq->on_rr)
+ return;
+ if (!new_queue && !cfq_check_sort_rr_list(cfqq))
+ return;
+
+ list_del(&cfqq->cfq_list);
+
+ /*
+ * sort by our mean service_used, sub-sort by in-flight requests
+ */
+ while ((entry = entry->prev) != &cfqq->cfqd->rr_list) {
+ struct cfq_queue *__cfqq = list_entry_cfqq(entry);
+
+ if (cfqq->service_used > __cfqq->service_used)
+ break;
+ else if (cfqq->service_used == __cfqq->service_used) {
+ struct list_head *prv;
+
+ while ((prv = entry->prev) != &cfqq->cfqd->rr_list) {
+ __cfqq = list_entry_cfqq(prv);
+
+ WARN_ON(__cfqq->service_used > cfqq->service_used);
+ if (cfqq->service_used != __cfqq->service_used)
+ break;
+ if (cfqq->in_flight > __cfqq->in_flight)
+ break;
+
+ entry = prv;
+ }
+ }
+ }
+
+ list_add(&cfqq->cfq_list, entry);
+}
+
+/*
+ * add to busy list of queues for service, trying to be fair in ordering
+ * the pending list according to requests serviced
+ */
+static inline void
+cfq_add_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq)
+{
+ /*
+ * it's currently on the empty list
+ */
+ cfqq->on_rr = 1;
+ cfqd->busy_queues++;
+
+ if (time_after(jiffies, cfqq->service_start + cfq_service))
+ cfqq->service_used >>= 3;
+
+ cfq_sort_rr_list(cfqq, 1);
+}
+
+static inline void
+cfq_del_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq)
+{
+ list_move(&cfqq->cfq_list, &cfqd->empty_list);
+ cfqq->on_rr = 0;
+
+ BUG_ON(!cfqd->busy_queues);
+ cfqd->busy_queues--;
+}
+
+/*
+ * rb tree support functions
+ */
+static inline void cfq_del_crq_rb(struct cfq_rq *crq)
+{
+ struct cfq_queue *cfqq = crq->cfq_queue;
+
if (ON_RB(&crq->rb_node)) {
- cfqq->queued[rq_data_dir(crq->request)]--;
+ struct cfq_data *cfqd = cfqq->cfqd;
+
+ BUG_ON(!cfqq->queued[crq->is_sync]);
+
+ cfq_update_next_crq(crq);
+
+ cfqq->queued[crq->is_sync]--;
rb_erase(&crq->rb_node, &cfqq->sort_list);
- crq->cfq_queue = NULL;
+ RB_CLEAR_COLOR(&crq->rb_node);
+
+ if (RB_EMPTY(&cfqq->sort_list) && cfqq->on_rr)
+ cfq_del_cfqq_rr(cfqd, cfqq);
}
}
static struct cfq_rq *
-__cfq_add_crq_rb(struct cfq_queue *cfqq, struct cfq_rq *crq)
+__cfq_add_crq_rb(struct cfq_rq *crq)
{
- struct rb_node **p = &cfqq->sort_list.rb_node;
+ struct rb_node **p = &crq->cfq_queue->sort_list.rb_node;
struct rb_node *parent = NULL;
struct cfq_rq *__crq;
@@ -199,30 +547,50 @@ __cfq_add_crq_rb(struct cfq_queue *cfqq, struct cfq_rq *crq)
return NULL;
}
-static void
-cfq_add_crq_rb(struct cfq_data *cfqd, struct cfq_queue *cfqq,struct cfq_rq *crq)
+static void cfq_add_crq_rb(struct cfq_rq *crq)
{
+ struct cfq_queue *cfqq = crq->cfq_queue;
+ struct cfq_data *cfqd = cfqq->cfqd;
struct request *rq = crq->request;
struct cfq_rq *__alias;
crq->rb_key = rq_rb_key(rq);
- cfqq->queued[rq_data_dir(rq)]++;
-retry:
- __alias = __cfq_add_crq_rb(cfqq, crq);
- if (!__alias) {
- rb_insert_color(&crq->rb_node, &cfqq->sort_list);
- crq->cfq_queue = cfqq;
- return;
+ cfqq->queued[crq->is_sync]++;
+
+ /*
+ * looks a little odd, but the first insert might return an alias.
+ * if that happens, put the alias on the dispatch list
+ */
+ while ((__alias = __cfq_add_crq_rb(crq)) != NULL)
+ cfq_dispatch_sort(cfqd->queue, __alias);
+
+ rb_insert_color(&crq->rb_node, &cfqq->sort_list);
+
+ if (!cfqq->on_rr)
+ cfq_add_cfqq_rr(cfqd, cfqq);
+
+ /*
+ * check if this request is a better next-serve candidate
+ */
+ cfqq->next_crq = cfq_choose_req(cfqd, cfqq->next_crq, crq);
+}
+
+static inline void
+cfq_reposition_crq_rb(struct cfq_queue *cfqq, struct cfq_rq *crq)
+{
+ if (ON_RB(&crq->rb_node)) {
+ rb_erase(&crq->rb_node, &cfqq->sort_list);
+ cfqq->queued[crq->is_sync]--;
}
- cfq_dispatch_sort(cfqd, cfqq, __alias);
- goto retry;
+ cfq_add_crq_rb(crq);
}
static struct request *
cfq_find_rq_rb(struct cfq_data *cfqd, sector_t sector)
{
- struct cfq_queue *cfqq = cfq_find_cfq_hash(cfqd, current->tgid);
+ const unsigned long key = cfq_hash_key(cfqd, current);
+ struct cfq_queue *cfqq = cfq_find_cfq_hash(cfqd, key);
struct rb_node *n;
if (!cfqq)
@@ -244,30 +612,44 @@ out:
return NULL;
}
-static void cfq_remove_request(request_queue_t *q, struct request *rq)
+/*
+ * make sure the service time gets corrected on reissue of this request
+ */
+static void cfq_requeue_request(request_queue_t *q, struct request *rq)
{
- struct cfq_data *cfqd = q->elevator.elevator_data;
struct cfq_rq *crq = RQ_DATA(rq);
if (crq) {
struct cfq_queue *cfqq = crq->cfq_queue;
+ if (cfqq->cfqd->cfq_tagged) {
+ cfqq->service_used--;
+ cfq_sort_rr_list(cfqq, 0);
+ }
+
+ crq->accounted = 0;
+ cfqq->cfqd->rq_in_driver--;
+ }
+ list_add(&rq->queuelist, &q->queue_head);
+}
+
+static void cfq_remove_request(request_queue_t *q, struct request *rq)
+{
+ struct cfq_rq *crq = RQ_DATA(rq);
+
+ if (crq) {
cfq_remove_merge_hints(q, crq);
list_del_init(&rq->queuelist);
- if (cfqq) {
- cfq_del_crq_rb(cfqq, crq);
-
- if (RB_EMPTY(&cfqq->sort_list))
- cfq_put_queue(cfqd, cfqq);
- }
+ if (crq->cfq_queue)
+ cfq_del_crq_rb(crq);
}
}
static int
cfq_merge(request_queue_t *q, struct request **req, struct bio *bio)
{
- struct cfq_data *cfqd = q->elevator.elevator_data;
+ struct cfq_data *cfqd = q->elevator->elevator_data;
struct request *__rq;
int ret;
@@ -305,7 +687,7 @@ out_insert:
static void cfq_merged_request(request_queue_t *q, struct request *req)
{
- struct cfq_data *cfqd = q->elevator.elevator_data;
+ struct cfq_data *cfqd = q->elevator->elevator_data;
struct cfq_rq *crq = RQ_DATA(req);
cfq_del_crq_hash(crq);
@@ -314,193 +696,546 @@ static void cfq_merged_request(request_queue_t *q, struct request *req)
if (ON_RB(&crq->rb_node) && (rq_rb_key(req) != crq->rb_key)) {
struct cfq_queue *cfqq = crq->cfq_queue;
- cfq_del_crq_rb(cfqq, crq);
- cfq_add_crq_rb(cfqd, cfqq, crq);
+ cfq_update_next_crq(crq);
+ cfq_reposition_crq_rb(cfqq, crq);
}
q->last_merge = req;
}
static void
-cfq_merged_requests(request_queue_t *q, struct request *req,
+cfq_merged_requests(request_queue_t *q, struct request *rq,
struct request *next)
{
- cfq_merged_request(q, req);
+ struct cfq_rq *crq = RQ_DATA(rq);
+ struct cfq_rq *cnext = RQ_DATA(next);
+
+ cfq_merged_request(q, rq);
+
+ if (!list_empty(&rq->queuelist) && !list_empty(&next->queuelist)) {
+ if (time_before(cnext->queue_start, crq->queue_start)) {
+ list_move(&rq->queuelist, &next->queuelist);
+ crq->queue_start = cnext->queue_start;
+ }
+ }
+
+ cfq_update_next_crq(cnext);
cfq_remove_request(q, next);
}
-static void
-cfq_dispatch_sort(struct cfq_data *cfqd, struct cfq_queue *cfqq,
- struct cfq_rq *crq)
+/*
+ * we dispatch cfqd->cfq_quantum requests in total from the rr_list queues,
+ * this function sector sorts the selected request to minimize seeks. we start
+ * at cfqd->last_sector, not 0.
+ */
+static void cfq_dispatch_sort(request_queue_t *q, struct cfq_rq *crq)
{
- struct list_head *head = cfqd->dispatch, *entry = head;
+ struct cfq_data *cfqd = q->elevator->elevator_data;
+ struct cfq_queue *cfqq = crq->cfq_queue;
+ struct list_head *head = &q->queue_head, *entry = head;
struct request *__rq;
+ sector_t last;
- cfq_del_crq_rb(cfqq, crq);
- cfq_remove_merge_hints(cfqd->queue, crq);
+ cfq_del_crq_rb(crq);
+ cfq_remove_merge_hints(q, crq);
+ list_del(&crq->request->queuelist);
- if (!list_empty(head)) {
- __rq = list_entry_rq(head->next);
+ last = cfqd->last_sector;
+ while ((entry = entry->prev) != head) {
+ __rq = list_entry_rq(entry);
- if (crq->request->sector < __rq->sector) {
- entry = head->prev;
- goto link;
+ if (blk_barrier_rq(crq->request))
+ break;
+ if (!blk_fs_request(crq->request))
+ break;
+
+ if (crq->request->sector > __rq->sector)
+ break;
+ if (__rq->sector > last && crq->request->sector < last) {
+ last = crq->request->sector;
+ break;
}
}
- while ((entry = entry->prev) != head) {
- __rq = list_entry_rq(entry);
+ cfqd->last_sector = last;
+ crq->in_flight = 1;
+ cfqq->in_flight++;
+ list_add(&crq->request->queuelist, entry);
+}
- if (crq->request->sector <= __rq->sector)
- break;
+/*
+ * return expired entry, or NULL to just start from scratch in rbtree
+ */
+static inline struct cfq_rq *cfq_check_fifo(struct cfq_queue *cfqq)
+{
+ struct cfq_data *cfqd = cfqq->cfqd;
+ const int reads = !list_empty(&cfqq->fifo[0]);
+ const int writes = !list_empty(&cfqq->fifo[1]);
+ unsigned long now = jiffies;
+ struct cfq_rq *crq;
+
+ if (time_before(now, cfqq->last_fifo_expire + cfqd->cfq_fifo_batch_expire))
+ return NULL;
+
+ crq = RQ_DATA(list_entry(cfqq->fifo[0].next, struct request, queuelist));
+ if (reads && time_after(now, crq->queue_start + cfqd->cfq_fifo_expire_r)) {
+ cfqq->last_fifo_expire = now;
+ return crq;
+ }
+
+ crq = RQ_DATA(list_entry(cfqq->fifo[1].next, struct request, queuelist));
+ if (writes && time_after(now, crq->queue_start + cfqd->cfq_fifo_expire_w)) {
+ cfqq->last_fifo_expire = now;
+ return crq;
}
-link:
- list_add_tail(&crq->request->queuelist, entry);
+ return NULL;
}
+/*
+ * dispatch a single request from given queue
+ */
static inline void
-__cfq_dispatch_requests(request_queue_t *q, struct cfq_data *cfqd,
- struct cfq_queue *cfqq)
+cfq_dispatch_request(request_queue_t *q, struct cfq_data *cfqd,
+ struct cfq_queue *cfqq)
{
- struct cfq_rq *crq = rb_entry_crq(rb_first(&cfqq->sort_list));
+ struct cfq_rq *crq;
+
+ /*
+ * follow expired path, else get first next available
+ */
+ if ((crq = cfq_check_fifo(cfqq)) == NULL) {
+ if (cfqd->find_best_crq)
+ crq = cfqq->next_crq;
+ else
+ crq = rb_entry_crq(rb_first(&cfqq->sort_list));
+ }
+
+ cfqd->last_sector = crq->request->sector + crq->request->nr_sectors;
- cfq_dispatch_sort(cfqd, cfqq, crq);
+ /*
+ * finally, insert request into driver list
+ */
+ cfq_dispatch_sort(q, crq);
}
-static int cfq_dispatch_requests(request_queue_t *q, struct cfq_data *cfqd)
+static int cfq_dispatch_requests(request_queue_t *q, int max_dispatch)
{
+ struct cfq_data *cfqd = q->elevator->elevator_data;
struct cfq_queue *cfqq;
struct list_head *entry, *tmp;
- int ret, queued, good_queues;
+ int queued, busy_queues, first_round;
if (list_empty(&cfqd->rr_list))
return 0;
- queued = ret = 0;
+ queued = 0;
+ first_round = 1;
restart:
- good_queues = 0;
+ busy_queues = 0;
list_for_each_safe(entry, tmp, &cfqd->rr_list) {
- cfqq = list_entry_cfqq(cfqd->rr_list.next);
+ cfqq = list_entry_cfqq(entry);
BUG_ON(RB_EMPTY(&cfqq->sort_list));
- __cfq_dispatch_requests(q, cfqd, cfqq);
+ /*
+ * first round of queueing, only select from queues that
+ * don't already have io in-flight
+ */
+ if (first_round && cfqq->in_flight)
+ continue;
- if (RB_EMPTY(&cfqq->sort_list))
- cfq_put_queue(cfqd, cfqq);
- else
- good_queues++;
+ cfq_dispatch_request(q, cfqd, cfqq);
+
+ if (!RB_EMPTY(&cfqq->sort_list))
+ busy_queues++;
queued++;
- ret = 1;
}
- if ((queued < cfqd->cfq_quantum) && good_queues)
+ if ((queued < max_dispatch) && (busy_queues || first_round)) {
+ first_round = 0;
goto restart;
+ }
- return ret;
+ return queued;
+}
+
+static inline void cfq_account_dispatch(struct cfq_rq *crq)
+{
+ struct cfq_queue *cfqq = crq->cfq_queue;
+ struct cfq_data *cfqd = cfqq->cfqd;
+ unsigned long now, elapsed;
+
+ /*
+ * accounted bit is necessary since some drivers will call
+ * elv_next_request() many times for the same request (eg ide)
+ */
+ if (crq->accounted)
+ return;
+
+ now = jiffies;
+ if (cfqq->service_start == ~0UL)
+ cfqq->service_start = now;
+
+ /*
+ * on drives with tagged command queueing, command turn-around time
+ * doesn't necessarily reflect the time spent processing this very
+ * command inside the drive. so do the accounting differently there,
+ * by just sorting on the number of requests
+ */
+ if (cfqd->cfq_tagged) {
+ if (time_after(now, cfqq->service_start + cfq_service)) {
+ cfqq->service_start = now;
+ cfqq->service_used /= 10;
+ }
+
+ cfqq->service_used++;
+ cfq_sort_rr_list(cfqq, 0);
+ }
+
+ elapsed = now - crq->queue_start;
+ if (elapsed > max_elapsed_dispatch)
+ max_elapsed_dispatch = elapsed;
+
+ crq->accounted = 1;
+ crq->service_start = now;
+
+ if (++cfqd->rq_in_driver >= CFQ_MAX_TAG && !cfqd->cfq_tagged) {
+ cfqq->cfqd->cfq_tagged = 1;
+ printk("cfq: depth %d reached, tagging now on\n", CFQ_MAX_TAG);
+ }
+}
+
+static inline void
+cfq_account_completion(struct cfq_queue *cfqq, struct cfq_rq *crq)
+{
+ struct cfq_data *cfqd = cfqq->cfqd;
+
+ WARN_ON(!cfqd->rq_in_driver);
+ cfqd->rq_in_driver--;
+
+ if (!cfqd->cfq_tagged) {
+ unsigned long now = jiffies;
+ unsigned long duration = now - crq->service_start;
+
+ if (time_after(now, cfqq->service_start + cfq_service)) {
+ cfqq->service_start = now;
+ cfqq->service_used >>= 3;
+ }
+
+ cfqq->service_used += duration;
+ cfq_sort_rr_list(cfqq, 0);
+
+ if (duration > max_elapsed_crq)
+ max_elapsed_crq = duration;
+ }
}
static struct request *cfq_next_request(request_queue_t *q)
{
- struct cfq_data *cfqd = q->elevator.elevator_data;
+ struct cfq_data *cfqd = q->elevator->elevator_data;
struct request *rq;
- if (!list_empty(cfqd->dispatch)) {
+ if (!list_empty(&q->queue_head)) {
struct cfq_rq *crq;
dispatch:
- rq = list_entry_rq(cfqd->dispatch->next);
+ rq = list_entry_rq(q->queue_head.next);
- crq = RQ_DATA(rq);
- if (crq)
+ if ((crq = RQ_DATA(rq)) != NULL) {
cfq_remove_merge_hints(q, crq);
+ cfq_account_dispatch(crq);
+ }
return rq;
}
- if (cfq_dispatch_requests(q, cfqd))
+ if (cfq_dispatch_requests(q, cfqd->cfq_quantum))
goto dispatch;
return NULL;
}
+/*
+ * task holds one reference to the queue, dropped when task exits. each crq
+ * in-flight on this queue also holds a reference, dropped when crq is freed.
+ *
+ * queue lock must be held here.
+ */
+static void cfq_put_queue(struct cfq_queue *cfqq)
+{
+ BUG_ON(!atomic_read(&cfqq->ref));
+
+ if (!atomic_dec_and_test(&cfqq->ref))
+ return;
+
+ BUG_ON(rb_first(&cfqq->sort_list));
+ BUG_ON(cfqq->on_rr);
+
+ cfq_put_cfqd(cfqq->cfqd);
+
+ /*
+ * it's on the empty list and still hashed
+ */
+ list_del(&cfqq->cfq_list);
+ hlist_del(&cfqq->cfq_hash);
+ kmem_cache_free(cfq_pool, cfqq);
+}
+
static inline struct cfq_queue *
-__cfq_find_cfq_hash(struct cfq_data *cfqd, int pid, const int hashval)
+__cfq_find_cfq_hash(struct cfq_data *cfqd, unsigned long key, const int hashval)
{
- struct list_head *hash_list = &cfqd->cfq_hash[hashval];
- struct list_head *entry;
+ struct hlist_head *hash_list = &cfqd->cfq_hash[hashval];
+ struct hlist_node *entry, *next;
- list_for_each(entry, hash_list) {
+ hlist_for_each_safe(entry, next, hash_list) {
struct cfq_queue *__cfqq = list_entry_qhash(entry);
- if (__cfqq->pid == pid)
+ if (__cfqq->key == key)
return __cfqq;
}
return NULL;
}
-static struct cfq_queue *cfq_find_cfq_hash(struct cfq_data *cfqd, int pid)
+static struct cfq_queue *
+cfq_find_cfq_hash(struct cfq_data *cfqd, unsigned long key)
{
- const int hashval = hash_long(current->tgid, CFQ_QHASH_SHIFT);
+ return __cfq_find_cfq_hash(cfqd, key, hash_long(key, CFQ_QHASH_SHIFT));
+}
+
+static inline void
+cfq_rehash_cfqq(struct cfq_data *cfqd, struct cfq_queue **cfqq,
+ struct cfq_io_context *cic)
+{
+ unsigned long hashkey = cfq_hash_key(cfqd, current);
+ unsigned long hashval = hash_long(hashkey, CFQ_QHASH_SHIFT);
+ struct cfq_queue *__cfqq;
+ unsigned long flags;
+
+ spin_lock_irqsave(cfqd->queue->queue_lock, flags);
- return __cfq_find_cfq_hash(cfqd, pid, hashval);
+ hlist_del(&(*cfqq)->cfq_hash);
+
+ __cfqq = __cfq_find_cfq_hash(cfqd, hashkey, hashval);
+ if (!__cfqq || __cfqq == *cfqq) {
+ __cfqq = *cfqq;
+ hlist_add_head(&__cfqq->cfq_hash, &cfqd->cfq_hash[hashval]);
+ __cfqq->key_type = cfqd->key_type;
+ } else {
+ atomic_inc(&__cfqq->ref);
+ cic->cfqq = __cfqq;
+ cfq_put_queue(*cfqq);
+ *cfqq = __cfqq;
+ }
+
+ cic->cfqq = __cfqq;
+ spin_unlock_irqrestore(cfqd->queue->queue_lock, flags);
}
-static void cfq_put_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq)
+static void cfq_free_io_context(struct cfq_io_context *cic)
{
- cfqd->busy_queues--;
- list_del(&cfqq->cfq_list);
- list_del(&cfqq->cfq_hash);
- mempool_free(cfqq, cfq_mpool);
+ kmem_cache_free(cfq_ioc_pool, cic);
}
-static struct cfq_queue *__cfq_get_queue(struct cfq_data *cfqd, int pid,
- int gfp_mask)
+/*
+ * locking hierarchy is: io_context lock -> queue locks
+ */
+static void cfq_exit_io_context(struct cfq_io_context *cic)
{
- const int hashval = hash_long(current->tgid, CFQ_QHASH_SHIFT);
+ struct cfq_queue *cfqq = cic->cfqq;
+ struct list_head *entry = &cic->list;
+ request_queue_t *q;
+ unsigned long flags;
+
+ /*
+ * put the reference this task is holding to the various queues
+ */
+ spin_lock_irqsave(&cic->ioc->lock, flags);
+ while ((entry = cic->list.next) != &cic->list) {
+ struct cfq_io_context *__cic;
+
+ __cic = list_entry(entry, struct cfq_io_context, list);
+ list_del(entry);
+
+ q = __cic->cfqq->cfqd->queue;
+ spin_lock(q->queue_lock);
+ cfq_put_queue(__cic->cfqq);
+ spin_unlock(q->queue_lock);
+ }
+
+ q = cfqq->cfqd->queue;
+ spin_lock(q->queue_lock);
+ cfq_put_queue(cfqq);
+ spin_unlock(q->queue_lock);
+
+ cic->cfqq = NULL;
+ spin_unlock_irqrestore(&cic->ioc->lock, flags);
+}
+
+static struct cfq_io_context *cfq_alloc_io_context(int gfp_flags)
+{
+ struct cfq_io_context *cic = kmem_cache_alloc(cfq_ioc_pool, gfp_flags);
+
+ if (cic) {
+ cic->dtor = cfq_free_io_context;
+ cic->exit = cfq_exit_io_context;
+ INIT_LIST_HEAD(&cic->list);
+ cic->cfqq = NULL;
+ }
+
+ return cic;
+}
+
+/*
+ * Setup general io context and cfq io context. There can be several cfq
+ * io contexts per general io context, if this process is doing io to more
+ * than one device managed by cfq. Note that caller is holding a reference to
+ * cfqq, so we don't need to worry about it disappearing
+ */
+static struct cfq_io_context *
+cfq_get_io_context(struct cfq_queue **cfqq, int gfp_flags)
+{
+ struct cfq_data *cfqd = (*cfqq)->cfqd;
+ struct cfq_queue *__cfqq = *cfqq;
+ struct cfq_io_context *cic;
+ struct io_context *ioc;
+
+ might_sleep_if(gfp_flags & __GFP_WAIT);
+
+ ioc = get_io_context(gfp_flags);
+ if (!ioc)
+ return NULL;
+
+ if ((cic = ioc->cic) == NULL) {
+ cic = cfq_alloc_io_context(gfp_flags);
+
+ if (cic == NULL)
+ goto err;
+
+ ioc->cic = cic;
+ cic->ioc = ioc;
+ cic->cfqq = __cfqq;
+ atomic_inc(&__cfqq->ref);
+ } else {
+ struct cfq_io_context *__cic;
+ unsigned long flags;
+
+ /*
+ * since the first cic on the list is actually the head
+ * itself, need to check this here or we'll duplicate an
+ * cic per ioc for no reason
+ */
+ if (cic->cfqq == __cfqq)
+ goto out;
+
+ /*
+ * cic exists, check if we already are there. linear search
+ * should be ok here, the list will usually not be more than
+ * 1 or a few entries long
+ */
+ spin_lock_irqsave(&ioc->lock, flags);
+ list_for_each_entry(__cic, &cic->list, list) {
+ /*
+ * this process is already holding a reference to
+ * this queue, so no need to get one more
+ */
+ if (__cic->cfqq == __cfqq) {
+ cic = __cic;
+ spin_unlock_irqrestore(&ioc->lock, flags);
+ goto out;
+ }
+ }
+ spin_unlock_irqrestore(&ioc->lock, flags);
+
+ /*
+ * nope, process doesn't have a cic assoicated with this
+ * cfqq yet. get a new one and add to list
+ */
+ __cic = cfq_alloc_io_context(gfp_flags);
+ if (__cic == NULL)
+ goto err;
+
+ __cic->ioc = ioc;
+ __cic->cfqq = __cfqq;
+ atomic_inc(&__cfqq->ref);
+ spin_lock_irqsave(&ioc->lock, flags);
+ list_add(&__cic->list, &cic->list);
+ spin_unlock_irqrestore(&ioc->lock, flags);
+
+ cic = __cic;
+ *cfqq = __cfqq;
+ }
+
+out:
+ /*
+ * if key_type has been changed on the fly, we lazily rehash
+ * each queue at lookup time
+ */
+ if ((*cfqq)->key_type != cfqd->key_type)
+ cfq_rehash_cfqq(cfqd, cfqq, cic);
+
+ return cic;
+err:
+ put_io_context(ioc);
+ return NULL;
+}
+
+static struct cfq_queue *
+__cfq_get_queue(struct cfq_data *cfqd, unsigned long key, int gfp_mask)
+{
+ const int hashval = hash_long(key, CFQ_QHASH_SHIFT);
struct cfq_queue *cfqq, *new_cfqq = NULL;
- request_queue_t *q = cfqd->queue;
retry:
- cfqq = __cfq_find_cfq_hash(cfqd, pid, hashval);
+ cfqq = __cfq_find_cfq_hash(cfqd, key, hashval);
if (!cfqq) {
if (new_cfqq) {
cfqq = new_cfqq;
new_cfqq = NULL;
} else if (gfp_mask & __GFP_WAIT) {
- spin_unlock_irq(q->queue_lock);
- new_cfqq = mempool_alloc(cfq_mpool, gfp_mask);
- spin_lock_irq(q->queue_lock);
+ spin_unlock_irq(cfqd->queue->queue_lock);
+ new_cfqq = kmem_cache_alloc(cfq_pool, gfp_mask);
+ spin_lock_irq(cfqd->queue->queue_lock);
goto retry;
} else
- return NULL;
+ goto out;
+
+ memset(cfqq, 0, sizeof(*cfqq));
- INIT_LIST_HEAD(&cfqq->cfq_hash);
+ INIT_HLIST_NODE(&cfqq->cfq_hash);
INIT_LIST_HEAD(&cfqq->cfq_list);
RB_CLEAR_ROOT(&cfqq->sort_list);
-
- cfqq->pid = pid;
- cfqq->queued[0] = cfqq->queued[1] = 0;
- list_add(&cfqq->cfq_hash, &cfqd->cfq_hash[hashval]);
+ INIT_LIST_HEAD(&cfqq->fifo[0]);
+ INIT_LIST_HEAD(&cfqq->fifo[1]);
+
+ cfqq->key = key;
+ hlist_add_head(&cfqq->cfq_hash, &cfqd->cfq_hash[hashval]);
+ atomic_set(&cfqq->ref, 0);
+ cfqq->cfqd = cfqd;
+ atomic_inc(&cfqd->ref);
+ cfqq->key_type = cfqd->key_type;
+ cfqq->service_start = ~0UL;
}
if (new_cfqq)
- mempool_free(new_cfqq, cfq_mpool);
+ kmem_cache_free(cfq_pool, new_cfqq);
+ atomic_inc(&cfqq->ref);
+out:
+ WARN_ON((gfp_mask & __GFP_WAIT) && !cfqq);
return cfqq;
}
-static struct cfq_queue *cfq_get_queue(struct cfq_data *cfqd, int pid,
- int gfp_mask)
+static struct cfq_queue *
+cfq_get_queue(struct cfq_data *cfqd, unsigned long key, int gfp_mask)
{
request_queue_t *q = cfqd->queue;
struct cfq_queue *cfqq;
spin_lock_irq(q->queue_lock);
- cfqq = __cfq_get_queue(cfqd, pid, gfp_mask);
+ cfqq = __cfq_get_queue(cfqd, key, gfp_mask);
spin_unlock_irq(q->queue_lock);
return cfqq;
@@ -508,40 +1243,30 @@ static struct cfq_queue *cfq_get_queue(struct cfq_data *cfqd, int pid,
static void cfq_enqueue(struct cfq_data *cfqd, struct cfq_rq *crq)
{
- struct cfq_queue *cfqq;
+ crq->is_sync = 0;
+ if (rq_data_dir(crq->request) == READ || current->flags & PF_SYNCWRITE)
+ crq->is_sync = 1;
- cfqq = __cfq_get_queue(cfqd, current->tgid, GFP_ATOMIC);
- if (cfqq) {
- cfq_add_crq_rb(cfqd, cfqq, crq);
+ cfq_add_crq_rb(crq);
+ crq->queue_start = jiffies;
- if (list_empty(&cfqq->cfq_list)) {
- list_add(&cfqq->cfq_list, &cfqd->rr_list);
- cfqd->busy_queues++;
- }
- } else {
- /*
- * should can only happen if the request wasn't allocated
- * through blk_alloc_request(), eg stack requests from ide-cd
- * (those should be removed) _and_ we are in OOM.
- */
- list_add_tail(&crq->request->queuelist, cfqd->dispatch);
- }
+ list_add_tail(&crq->request->queuelist, &crq->cfq_queue->fifo[crq->is_sync]);
}
static void
cfq_insert_request(request_queue_t *q, struct request *rq, int where)
{
- struct cfq_data *cfqd = q->elevator.elevator_data;
+ struct cfq_data *cfqd = q->elevator->elevator_data;
struct cfq_rq *crq = RQ_DATA(rq);
switch (where) {
case ELEVATOR_INSERT_BACK:
- while (cfq_dispatch_requests(q, cfqd))
+ while (cfq_dispatch_requests(q, cfqd->cfq_quantum))
;
- list_add_tail(&rq->queuelist, cfqd->dispatch);
+ list_add_tail(&rq->queuelist, &q->queue_head);
break;
case ELEVATOR_INSERT_FRONT:
- list_add(&rq->queuelist, cfqd->dispatch);
+ list_add(&rq->queuelist, &q->queue_head);
break;
case ELEVATOR_INSERT_SORT:
BUG_ON(!blk_fs_request(rq));
@@ -562,12 +1287,27 @@ cfq_insert_request(request_queue_t *q, struct request *rq, int where)
static int cfq_queue_empty(request_queue_t *q)
{
- struct cfq_data *cfqd = q->elevator.elevator_data;
+ struct cfq_data *cfqd = q->elevator->elevator_data;
- if (list_empty(cfqd->dispatch) && list_empty(&cfqd->rr_list))
- return 1;
+ return list_empty(&q->queue_head) && list_empty(&cfqd->rr_list);
+}
+
+static void cfq_completed_request(request_queue_t *q, struct request *rq)
+{
+ struct cfq_rq *crq = RQ_DATA(rq);
+
+ if (unlikely(!blk_fs_request(rq)))
+ return;
+
+ if (crq->in_flight) {
+ struct cfq_queue *cfqq = crq->cfq_queue;
+
+ WARN_ON(!cfqq->in_flight);
+ cfqq->in_flight--;
+
+ cfq_account_completion(cfqq, crq);
+ }
- return 0;
}
static struct request *
@@ -596,92 +1336,169 @@ cfq_latter_request(request_queue_t *q, struct request *rq)
static int cfq_may_queue(request_queue_t *q, int rw)
{
- struct cfq_data *cfqd = q->elevator.elevator_data;
+ struct cfq_data *cfqd = q->elevator->elevator_data;
struct cfq_queue *cfqq;
- int ret = 1;
+ int ret = ELV_MQUEUE_MAY;
- if (!cfqd->busy_queues)
- goto out;
+ if (current->flags & PF_MEMALLOC)
+ return ELV_MQUEUE_MAY;
- cfqq = cfq_find_cfq_hash(cfqd, current->tgid);
+ cfqq = cfq_find_cfq_hash(cfqd, cfq_hash_key(cfqd, current));
if (cfqq) {
- int limit = (q->nr_requests - cfqd->cfq_queued) / cfqd->busy_queues;
+ int limit = cfqd->max_queued;
+
+ if (cfqq->allocated[rw] < cfqd->cfq_queued)
+ return ELV_MQUEUE_MUST;
- if (limit < 3)
- limit = 3;
+ if (cfqd->busy_queues)
+ limit = q->nr_requests / cfqd->busy_queues;
+
+ if (limit < cfqd->cfq_queued)
+ limit = cfqd->cfq_queued;
else if (limit > cfqd->max_queued)
limit = cfqd->max_queued;
- if (cfqq->queued[rw] > limit)
- ret = 0;
+ if (cfqq->allocated[rw] >= limit) {
+ if (limit > cfqq->alloc_limit[rw])
+ cfqq->alloc_limit[rw] = limit;
+
+ ret = ELV_MQUEUE_NO;
+ }
}
-out:
+
return ret;
}
+static void cfq_check_waiters(request_queue_t *q, struct cfq_queue *cfqq)
+{
+ struct request_list *rl = &q->rq;
+ const int write = waitqueue_active(&rl->wait[WRITE]);
+ const int read = waitqueue_active(&rl->wait[READ]);
+
+ if (read && cfqq->allocated[READ] < cfqq->alloc_limit[READ])
+ wake_up(&rl->wait[READ]);
+ if (write && cfqq->allocated[WRITE] < cfqq->alloc_limit[WRITE])
+ wake_up(&rl->wait[WRITE]);
+}
+
+/*
+ * queue lock held here
+ */
static void cfq_put_request(request_queue_t *q, struct request *rq)
{
- struct cfq_data *cfqd = q->elevator.elevator_data;
+ struct cfq_data *cfqd = q->elevator->elevator_data;
struct cfq_rq *crq = RQ_DATA(rq);
- struct request_list *rl;
- int other_rw;
if (crq) {
+ struct cfq_queue *cfqq = crq->cfq_queue;
+
BUG_ON(q->last_merge == rq);
- BUG_ON(ON_MHASH(crq));
+ BUG_ON(!hlist_unhashed(&crq->hash));
+
+ if (crq->io_context)
+ put_io_context(crq->io_context->ioc);
+
+ if (!cfqq->allocated[crq->is_write]) {
+ WARN_ON(1);
+ cfqq->allocated[crq->is_write] = 1;
+ }
+ cfqq->allocated[crq->is_write]--;
mempool_free(crq, cfqd->crq_pool);
rq->elevator_private = NULL;
- }
- /*
- * work-around for may_queue "bug": if a read gets issued and refused
- * to queue because writes ate all the allowed slots and no other
- * reads are pending for this queue, it could get stuck infinitely
- * since freed_request() only checks the waitqueue for writes when
- * freeing them. or vice versa for a single write vs many reads.
- * so check here whether "the other" data direction might be able
- * to queue and wake them
- */
- rl = &q->rq;
- other_rw = rq_data_dir(rq) ^ 1;
- if (rl->count[other_rw] <= q->nr_requests) {
smp_mb();
- if (waitqueue_active(&rl->wait[other_rw]))
- wake_up(&rl->wait[other_rw]);
+ cfq_check_waiters(q, cfqq);
+ cfq_put_queue(cfqq);
}
}
+/*
+ * Allocate cfq data structures associated with this request. A queue and
+ */
static int cfq_set_request(request_queue_t *q, struct request *rq, int gfp_mask)
{
- struct cfq_data *cfqd = q->elevator.elevator_data;
+ struct cfq_data *cfqd = q->elevator->elevator_data;
+ struct cfq_io_context *cic;
+ const int rw = rq_data_dir(rq);
struct cfq_queue *cfqq;
struct cfq_rq *crq;
+ unsigned long flags;
+
+ might_sleep_if(gfp_mask & __GFP_WAIT);
+
+ spin_lock_irqsave(q->queue_lock, flags);
+
+ cfqq = __cfq_get_queue(cfqd, cfq_hash_key(cfqd, current), gfp_mask);
+ if (!cfqq) {
+#if 0
+ cfqq = cfq_get_queue(cfqd, CFQ_KEY_SPARE, gfp_mask);
+ printk("%s: got spare queue\n", current->comm);
+#else
+ goto out_lock;
+#endif
+ }
+
+ if (cfqq->allocated[rw] >= cfqd->max_queued)
+ goto out_lock;
+
+ spin_unlock_irqrestore(q->queue_lock, flags);
/*
- * prepare a queue up front, so cfq_enqueue() doesn't have to
+ * if hashing type has changed, the cfq_queue might change here. we
+ * don't bother rechecking ->allocated since it should be a rare
+ * event
*/
- cfqq = cfq_get_queue(cfqd, current->tgid, gfp_mask);
- if (!cfqq)
- return 1;
+ cic = cfq_get_io_context(&cfqq, gfp_mask);
+ if (!cic)
+ goto err;
crq = mempool_alloc(cfqd->crq_pool, gfp_mask);
if (crq) {
- memset(crq, 0, sizeof(*crq));
RB_CLEAR(&crq->rb_node);
+ crq->rb_key = 0;
crq->request = rq;
- crq->cfq_queue = NULL;
- INIT_LIST_HEAD(&crq->hash);
+ INIT_HLIST_NODE(&crq->hash);
+ crq->cfq_queue = cfqq;
+ crq->io_context = cic;
+ crq->service_start = crq->queue_start = 0;
+ crq->in_flight = crq->accounted = crq->is_sync = 0;
+ crq->is_write = rw;
rq->elevator_private = crq;
+ cfqq->allocated[rw]++;
+ cfqq->alloc_limit[rw] = 0;
return 0;
}
+ put_io_context(cic->ioc);
+err:
+ spin_lock_irqsave(q->queue_lock, flags);
+ cfq_put_queue(cfqq);
+out_lock:
+ spin_unlock_irqrestore(q->queue_lock, flags);
return 1;
}
-static void cfq_exit(request_queue_t *q, elevator_t *e)
+static void cfq_put_cfqd(struct cfq_data *cfqd)
{
- struct cfq_data *cfqd = e->elevator_data;
+ request_queue_t *q = cfqd->queue;
+ elevator_t *e = q->elevator;
+ struct cfq_queue *cfqq;
+
+ if (!atomic_dec_and_test(&cfqd->ref))
+ return;
+
+ /*
+ * kill spare queue, getting it means we have two refences to it.
+ * drop both
+ */
+ spin_lock_irq(q->queue_lock);
+ cfqq = __cfq_get_queue(cfqd, CFQ_KEY_SPARE, GFP_ATOMIC);
+ cfq_put_queue(cfqq);
+ cfq_put_queue(cfqq);
+ spin_unlock_irq(q->queue_lock);
+
+ blk_put_queue(q);
e->elevator_data = NULL;
mempool_destroy(cfqd->crq_pool);
@@ -690,9 +1507,15 @@ static void cfq_exit(request_queue_t *q, elevator_t *e)
kfree(cfqd);
}
-static int cfq_init(request_queue_t *q, elevator_t *e)
+static void cfq_exit_queue(elevator_t *e)
+{
+ cfq_put_cfqd(e->elevator_data);
+}
+
+static int cfq_init_queue(request_queue_t *q, elevator_t *e)
{
struct cfq_data *cfqd;
+ struct cfq_queue *cfqq;
int i;
cfqd = kmalloc(sizeof(*cfqd), GFP_KERNEL);
@@ -701,12 +1524,13 @@ static int cfq_init(request_queue_t *q, elevator_t *e)
memset(cfqd, 0, sizeof(*cfqd));
INIT_LIST_HEAD(&cfqd->rr_list);
+ INIT_LIST_HEAD(&cfqd->empty_list);
- cfqd->crq_hash = kmalloc(sizeof(struct list_head) * CFQ_MHASH_ENTRIES, GFP_KERNEL);
+ cfqd->crq_hash = kmalloc(sizeof(struct hlist_head) * CFQ_MHASH_ENTRIES, GFP_KERNEL);
if (!cfqd->crq_hash)
goto out_crqhash;
- cfqd->cfq_hash = kmalloc(sizeof(struct list_head) * CFQ_QHASH_ENTRIES, GFP_KERNEL);
+ cfqd->cfq_hash = kmalloc(sizeof(struct hlist_head) * CFQ_QHASH_ENTRIES, GFP_KERNEL);
if (!cfqd->cfq_hash)
goto out_cfqhash;
@@ -715,25 +1539,44 @@ static int cfq_init(request_queue_t *q, elevator_t *e)
goto out_crqpool;
for (i = 0; i < CFQ_MHASH_ENTRIES; i++)
- INIT_LIST_HEAD(&cfqd->crq_hash[i]);
+ INIT_HLIST_HEAD(&cfqd->crq_hash[i]);
for (i = 0; i < CFQ_QHASH_ENTRIES; i++)
- INIT_LIST_HEAD(&cfqd->cfq_hash[i]);
+ INIT_HLIST_HEAD(&cfqd->cfq_hash[i]);
- cfqd->dispatch = &q->queue_head;
e->elevator_data = cfqd;
+
cfqd->queue = q;
+ atomic_inc(&q->refcnt);
+
+ /*
+ * setup spare failure queue
+ */
+ cfqq = cfq_get_queue(cfqd, CFQ_KEY_SPARE, GFP_KERNEL);
+ if (!cfqq)
+ goto out_spare;
/*
* just set it to some high value, we want anyone to be able to queue
* some requests. fairness is handled differently
*/
- cfqd->max_queued = q->nr_requests;
- q->nr_requests = 8192;
+ q->nr_requests = 1024;
+ cfqd->max_queued = q->nr_requests / 16;
+ q->nr_batching = cfq_queued;
+ cfqd->key_type = CFQ_KEY_TGID;
+ cfqd->find_best_crq = 1;
+ atomic_set(&cfqd->ref, 1);
cfqd->cfq_queued = cfq_queued;
cfqd->cfq_quantum = cfq_quantum;
+ cfqd->cfq_fifo_expire_r = cfq_fifo_expire_r;
+ cfqd->cfq_fifo_expire_w = cfq_fifo_expire_w;
+ cfqd->cfq_fifo_batch_expire = cfq_fifo_rate;
+ cfqd->cfq_back_max = cfq_back_max;
+ cfqd->cfq_back_penalty = cfq_back_penalty;
return 0;
+out_spare:
+ mempool_destroy(cfqd->crq_pool);
out_crqpool:
kfree(cfqd->cfq_hash);
out_cfqhash:
@@ -743,29 +1586,39 @@ out_crqhash:
return -ENOMEM;
}
+static void cfq_slab_kill(void)
+{
+ if (crq_pool)
+ kmem_cache_destroy(crq_pool);
+ if (cfq_pool)
+ kmem_cache_destroy(cfq_pool);
+ if (cfq_ioc_pool)
+ kmem_cache_destroy(cfq_ioc_pool);
+}
+
static int __init cfq_slab_setup(void)
{
crq_pool = kmem_cache_create("crq_pool", sizeof(struct cfq_rq), 0, 0,
NULL, NULL);
-
if (!crq_pool)
- panic("cfq_iosched: can't init crq pool\n");
+ goto fail;
cfq_pool = kmem_cache_create("cfq_pool", sizeof(struct cfq_queue), 0, 0,
NULL, NULL);
-
if (!cfq_pool)
- panic("cfq_iosched: can't init cfq pool\n");
+ goto fail;
- cfq_mpool = mempool_create(64, mempool_alloc_slab, mempool_free_slab, cfq_pool);
-
- if (!cfq_mpool)
- panic("cfq_iosched: can't init cfq mpool\n");
+ cfq_ioc_pool = kmem_cache_create("cfq_ioc_pool",
+ sizeof(struct cfq_io_context), 0, 0, NULL, NULL);
+ if (!cfq_ioc_pool)
+ goto fail;
return 0;
+fail:
+ cfq_slab_kill();
+ return -ENOMEM;
}
-subsys_initcall(cfq_slab_setup);
/*
* sysfs parts below -->
@@ -791,27 +1644,135 @@ cfq_var_store(unsigned int *var, const char *page, size_t count)
return count;
}
-#define SHOW_FUNCTION(__FUNC, __VAR) \
+static ssize_t
+cfq_clear_elapsed(struct cfq_data *cfqd, const char *page, size_t count)
+{
+ max_elapsed_dispatch = max_elapsed_crq = 0;
+ return count;
+}
+
+static ssize_t
+cfq_set_key_type(struct cfq_data *cfqd, const char *page, size_t count)
+{
+ spin_lock_irq(cfqd->queue->queue_lock);
+ if (!strncmp(page, "pgid", 4))
+ cfqd->key_type = CFQ_KEY_PGID;
+ else if (!strncmp(page, "tgid", 4))
+ cfqd->key_type = CFQ_KEY_TGID;
+ else if (!strncmp(page, "uid", 3))
+ cfqd->key_type = CFQ_KEY_UID;
+ else if (!strncmp(page, "gid", 3))
+ cfqd->key_type = CFQ_KEY_GID;
+ spin_unlock_irq(cfqd->queue->queue_lock);
+ return count;
+}
+
+static ssize_t
+cfq_read_key_type(struct cfq_data *cfqd, char *page)
+{
+ ssize_t len = 0;
+ int i;
+
+ for (i = CFQ_KEY_PGID; i < CFQ_KEY_LAST; i++) {
+ if (cfqd->key_type == i)
+ len += sprintf(page+len, "[%s] ", cfq_key_types[i]);
+ else
+ len += sprintf(page+len, "%s ", cfq_key_types[i]);
+ }
+ len += sprintf(page+len, "\n");
+ return len;
+}
+
+static ssize_t
+cfq_status_show(struct cfq_data *cfqd, char *page)
+{
+ struct list_head *entry;
+ struct cfq_queue *cfqq;
+ ssize_t len;
+ int i = 0, queues;
+
+ len = sprintf(page, "Busy queues: %u\n", cfqd->busy_queues);
+ len += sprintf(page+len, "key type: %s\n",
+ cfq_key_types[cfqd->key_type]);
+ len += sprintf(page+len, "last sector: %Lu\n",
+ (unsigned long long)cfqd->last_sector);
+ len += sprintf(page+len, "max time in iosched: %lu\n",
+ max_elapsed_dispatch);
+ len += sprintf(page+len, "max completion time: %lu\n", max_elapsed_crq);
+
+ len += sprintf(page+len, "Busy queue list:\n");
+ spin_lock_irq(cfqd->queue->queue_lock);
+ list_for_each(entry, &cfqd->rr_list) {
+ i++;
+ cfqq = list_entry_cfqq(entry);
+ len += sprintf(page+len, " cfqq: key=%lu alloc=%d/%d, "
+ "queued=%d/%d, last_fifo=%lu, service_used=%lu\n",
+ cfqq->key, cfqq->allocated[0], cfqq->allocated[1],
+ cfqq->queued[0], cfqq->queued[1],
+ cfqq->last_fifo_expire, cfqq->service_used);
+ }
+ len += sprintf(page+len, " busy queues total: %d\n", i);
+ queues = i;
+
+ len += sprintf(page+len, "Empty queue list:\n");
+ i = 0;
+ list_for_each(entry, &cfqd->empty_list) {
+ i++;
+ cfqq = list_entry_cfqq(entry);
+ len += sprintf(page+len, " cfqq: key=%lu alloc=%d/%d, "
+ "queued=%d/%d, last_fifo=%lu, service_used=%lu\n",
+ cfqq->key, cfqq->allocated[0], cfqq->allocated[1],
+ cfqq->queued[0], cfqq->queued[1],
+ cfqq->last_fifo_expire, cfqq->service_used);
+ }
+ len += sprintf(page+len, " empty queues total: %d\n", i);
+ queues += i;
+ len += sprintf(page+len, "Total queues: %d\n", queues);
+ spin_unlock_irq(cfqd->queue->queue_lock);
+ return len;
+}
+
+#define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \
static ssize_t __FUNC(struct cfq_data *cfqd, char *page) \
{ \
- return cfq_var_show(__VAR, (page)); \
+ unsigned int __data = __VAR; \
+ if (__CONV) \
+ __data = jiffies_to_msecs(__data); \
+ return cfq_var_show(__data, (page)); \
}
-SHOW_FUNCTION(cfq_quantum_show, cfqd->cfq_quantum);
-SHOW_FUNCTION(cfq_queued_show, cfqd->cfq_queued);
+SHOW_FUNCTION(cfq_quantum_show, cfqd->cfq_quantum, 0);
+SHOW_FUNCTION(cfq_queued_show, cfqd->cfq_queued, 0);
+SHOW_FUNCTION(cfq_fifo_expire_r_show, cfqd->cfq_fifo_expire_r, 1);
+SHOW_FUNCTION(cfq_fifo_expire_w_show, cfqd->cfq_fifo_expire_w, 1);
+SHOW_FUNCTION(cfq_fifo_batch_expire_show, cfqd->cfq_fifo_batch_expire, 1);
+SHOW_FUNCTION(cfq_find_best_show, cfqd->find_best_crq, 0);
+SHOW_FUNCTION(cfq_back_max_show, cfqd->cfq_back_max, 0);
+SHOW_FUNCTION(cfq_back_penalty_show, cfqd->cfq_back_penalty, 0);
#undef SHOW_FUNCTION
-#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX) \
+#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \
static ssize_t __FUNC(struct cfq_data *cfqd, const char *page, size_t count) \
{ \
- int ret = cfq_var_store(__PTR, (page), count); \
- if (*(__PTR) < (MIN)) \
- *(__PTR) = (MIN); \
- else if (*(__PTR) > (MAX)) \
- *(__PTR) = (MAX); \
+ unsigned int __data; \
+ int ret = cfq_var_store(&__data, (page), count); \
+ if (__data < (MIN)) \
+ __data = (MIN); \
+ else if (__data > (MAX)) \
+ __data = (MAX); \
+ if (__CONV) \
+ *(__PTR) = msecs_to_jiffies(__data); \
+ else \
+ *(__PTR) = __data; \
return ret; \
}
-STORE_FUNCTION(cfq_quantum_store, &cfqd->cfq_quantum, 1, INT_MAX);
-STORE_FUNCTION(cfq_queued_store, &cfqd->cfq_queued, 1, INT_MAX);
+STORE_FUNCTION(cfq_quantum_store, &cfqd->cfq_quantum, 1, UINT_MAX, 0);
+STORE_FUNCTION(cfq_queued_store, &cfqd->cfq_queued, 1, UINT_MAX, 0);
+STORE_FUNCTION(cfq_fifo_expire_r_store, &cfqd->cfq_fifo_expire_r, 1, UINT_MAX, 1);
+STORE_FUNCTION(cfq_fifo_expire_w_store, &cfqd->cfq_fifo_expire_w, 1, UINT_MAX, 1);
+STORE_FUNCTION(cfq_fifo_batch_expire_store, &cfqd->cfq_fifo_batch_expire, 0, UINT_MAX, 1);
+STORE_FUNCTION(cfq_find_best_store, &cfqd->find_best_crq, 0, 1, 0);
+STORE_FUNCTION(cfq_back_max_store, &cfqd->cfq_back_max, 0, UINT_MAX, 0);
+STORE_FUNCTION(cfq_back_penalty_store, &cfqd->cfq_back_penalty, 1, UINT_MAX, 0);
#undef STORE_FUNCTION
static struct cfq_fs_entry cfq_quantum_entry = {
@@ -824,10 +1785,62 @@ static struct cfq_fs_entry cfq_queued_entry = {
.show = cfq_queued_show,
.store = cfq_queued_store,
};
+static struct cfq_fs_entry cfq_fifo_expire_r_entry = {
+ .attr = {.name = "fifo_expire_sync", .mode = S_IRUGO | S_IWUSR },
+ .show = cfq_fifo_expire_r_show,
+ .store = cfq_fifo_expire_r_store,
+};
+static struct cfq_fs_entry cfq_fifo_expire_w_entry = {
+ .attr = {.name = "fifo_expire_async", .mode = S_IRUGO | S_IWUSR },
+ .show = cfq_fifo_expire_w_show,
+ .store = cfq_fifo_expire_w_store,
+};
+static struct cfq_fs_entry cfq_fifo_batch_expire_entry = {
+ .attr = {.name = "fifo_batch_expire", .mode = S_IRUGO | S_IWUSR },
+ .show = cfq_fifo_batch_expire_show,
+ .store = cfq_fifo_batch_expire_store,
+};
+static struct cfq_fs_entry cfq_find_best_entry = {
+ .attr = {.name = "find_best_crq", .mode = S_IRUGO | S_IWUSR },
+ .show = cfq_find_best_show,
+ .store = cfq_find_best_store,
+};
+static struct cfq_fs_entry cfq_back_max_entry = {
+ .attr = {.name = "back_seek_max", .mode = S_IRUGO | S_IWUSR },
+ .show = cfq_back_max_show,
+ .store = cfq_back_max_store,
+};
+static struct cfq_fs_entry cfq_back_penalty_entry = {
+ .attr = {.name = "back_seek_penalty", .mode = S_IRUGO | S_IWUSR },
+ .show = cfq_back_penalty_show,
+ .store = cfq_back_penalty_store,
+};
+static struct cfq_fs_entry cfq_clear_elapsed_entry = {
+ .attr = {.name = "clear_elapsed", .mode = S_IWUSR },
+ .store = cfq_clear_elapsed,
+};
+static struct cfq_fs_entry cfq_misc_entry = {
+ .attr = {.name = "show_status", .mode = S_IRUGO },
+ .show = cfq_status_show,
+};
+static struct cfq_fs_entry cfq_key_type_entry = {
+ .attr = {.name = "key_type", .mode = S_IRUGO | S_IWUSR },
+ .show = cfq_read_key_type,
+ .store = cfq_set_key_type,
+};
static struct attribute *default_attrs[] = {
&cfq_quantum_entry.attr,
&cfq_queued_entry.attr,
+ &cfq_fifo_expire_r_entry.attr,
+ &cfq_fifo_expire_w_entry.attr,
+ &cfq_fifo_batch_expire_entry.attr,
+ &cfq_key_type_entry.attr,
+ &cfq_find_best_entry.attr,
+ &cfq_back_max_entry.attr,
+ &cfq_back_penalty_entry.attr,
+ &cfq_clear_elapsed_entry.attr,
+ &cfq_misc_entry.attr,
NULL,
};
@@ -868,23 +1881,56 @@ struct kobj_type cfq_ktype = {
.default_attrs = default_attrs,
};
-elevator_t iosched_cfq = {
- .elevator_name = "cfq",
- .elevator_ktype = &cfq_ktype,
- .elevator_merge_fn = cfq_merge,
- .elevator_merged_fn = cfq_merged_request,
- .elevator_merge_req_fn = cfq_merged_requests,
- .elevator_next_req_fn = cfq_next_request,
- .elevator_add_req_fn = cfq_insert_request,
- .elevator_remove_req_fn = cfq_remove_request,
- .elevator_queue_empty_fn = cfq_queue_empty,
- .elevator_former_req_fn = cfq_former_request,
- .elevator_latter_req_fn = cfq_latter_request,
- .elevator_set_req_fn = cfq_set_request,
- .elevator_put_req_fn = cfq_put_request,
- .elevator_may_queue_fn = cfq_may_queue,
- .elevator_init_fn = cfq_init,
- .elevator_exit_fn = cfq_exit,
+static struct elevator_type iosched_cfq = {
+ .ops = {
+ .elevator_merge_fn = cfq_merge,
+ .elevator_merged_fn = cfq_merged_request,
+ .elevator_merge_req_fn = cfq_merged_requests,
+ .elevator_next_req_fn = cfq_next_request,
+ .elevator_add_req_fn = cfq_insert_request,
+ .elevator_remove_req_fn = cfq_remove_request,
+ .elevator_requeue_req_fn = cfq_requeue_request,
+ .elevator_queue_empty_fn = cfq_queue_empty,
+ .elevator_completed_req_fn = cfq_completed_request,
+ .elevator_former_req_fn = cfq_former_request,
+ .elevator_latter_req_fn = cfq_latter_request,
+ .elevator_set_req_fn = cfq_set_request,
+ .elevator_put_req_fn = cfq_put_request,
+ .elevator_may_queue_fn = cfq_may_queue,
+ .elevator_init_fn = cfq_init_queue,
+ .elevator_exit_fn = cfq_exit_queue,
+ },
+ .elevator_ktype = &cfq_ktype,
+ .elevator_name = "cfq",
+ .elevator_owner = THIS_MODULE,
};
-EXPORT_SYMBOL(iosched_cfq);
+int cfq_init(void)
+{
+ int ret;
+
+ if (cfq_slab_setup())
+ return -ENOMEM;
+
+ ret = elv_register(&iosched_cfq);
+ if (!ret) {
+ __module_get(THIS_MODULE);
+ return 0;
+ }
+
+ cfq_slab_kill();
+ return ret;
+}
+
+void cfq_exit(void)
+{
+ cfq_slab_kill();
+ elv_unregister(&iosched_cfq);
+}
+
+module_init(cfq_init);
+module_exit(cfq_exit);
+
+MODULE_AUTHOR("Jens Axboe");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Completely Fair Queueing IO scheduler");
diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c
index 204b3182900d..dc896a12283b 100644
--- a/drivers/block/cpqarray.c
+++ b/drivers/block/cpqarray.c
@@ -21,7 +21,6 @@
*/
#include <linux/config.h> /* CONFIG_PROC_FS */
#include <linux/module.h>
-#include <linux/version.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/bio.h>
@@ -732,7 +731,6 @@ static void __iomem *remap_pci_mem(ulong base, ulong size)
}
#ifndef MODULE
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,13)
/*
* Config string is a comma separated set of i/o addresses of EISA cards.
*/
@@ -749,18 +747,6 @@ static int cpqarray_setup(char *str)
__setup("smart2=", cpqarray_setup);
-#else
-
-/*
- * Copy the contents of the ints[] array passed to us by init.
- */
-void cpqarray_setup(char *str, int *ints)
-{
- int i;
- for(i=0; i<ints[0] && i<8; i++)
- eisa[i] = ints[i+1];
-}
-#endif
#endif
/*
diff --git a/drivers/block/deadline-iosched.c b/drivers/block/deadline-iosched.c
index fb7ab733c709..f482e8bdb4d6 100644
--- a/drivers/block/deadline-iosched.c
+++ b/drivers/block/deadline-iosched.c
@@ -289,7 +289,7 @@ deadline_find_first_drq(struct deadline_data *dd, int data_dir)
static inline void
deadline_add_request(struct request_queue *q, struct request *rq)
{
- struct deadline_data *dd = q->elevator.elevator_data;
+ struct deadline_data *dd = q->elevator->elevator_data;
struct deadline_rq *drq = RQ_DATA(rq);
const int data_dir = rq_data_dir(drq->request);
@@ -317,7 +317,7 @@ static void deadline_remove_request(request_queue_t *q, struct request *rq)
struct deadline_rq *drq = RQ_DATA(rq);
if (drq) {
- struct deadline_data *dd = q->elevator.elevator_data;
+ struct deadline_data *dd = q->elevator->elevator_data;
list_del_init(&drq->fifo);
deadline_remove_merge_hints(q, drq);
@@ -328,7 +328,7 @@ static void deadline_remove_request(request_queue_t *q, struct request *rq)
static int
deadline_merge(request_queue_t *q, struct request **req, struct bio *bio)
{
- struct deadline_data *dd = q->elevator.elevator_data;
+ struct deadline_data *dd = q->elevator->elevator_data;
struct request *__rq;
int ret;
@@ -383,7 +383,7 @@ out_insert:
static void deadline_merged_request(request_queue_t *q, struct request *req)
{
- struct deadline_data *dd = q->elevator.elevator_data;
+ struct deadline_data *dd = q->elevator->elevator_data;
struct deadline_rq *drq = RQ_DATA(req);
/*
@@ -407,7 +407,7 @@ static void
deadline_merged_requests(request_queue_t *q, struct request *req,
struct request *next)
{
- struct deadline_data *dd = q->elevator.elevator_data;
+ struct deadline_data *dd = q->elevator->elevator_data;
struct deadline_rq *drq = RQ_DATA(req);
struct deadline_rq *dnext = RQ_DATA(next);
@@ -604,7 +604,7 @@ dispatch_request:
static struct request *deadline_next_request(request_queue_t *q)
{
- struct deadline_data *dd = q->elevator.elevator_data;
+ struct deadline_data *dd = q->elevator->elevator_data;
struct request *rq;
/*
@@ -625,7 +625,7 @@ dispatch:
static void
deadline_insert_request(request_queue_t *q, struct request *rq, int where)
{
- struct deadline_data *dd = q->elevator.elevator_data;
+ struct deadline_data *dd = q->elevator->elevator_data;
/* barriers must flush the reorder queue */
if (unlikely(rq->flags & (REQ_SOFTBARRIER | REQ_HARDBARRIER)
@@ -653,7 +653,7 @@ deadline_insert_request(request_queue_t *q, struct request *rq, int where)
static int deadline_queue_empty(request_queue_t *q)
{
- struct deadline_data *dd = q->elevator.elevator_data;
+ struct deadline_data *dd = q->elevator->elevator_data;
if (!list_empty(&dd->fifo_list[WRITE])
|| !list_empty(&dd->fifo_list[READ])
@@ -687,7 +687,7 @@ deadline_latter_request(request_queue_t *q, struct request *rq)
return NULL;
}
-static void deadline_exit(request_queue_t *q, elevator_t *e)
+static void deadline_exit_queue(elevator_t *e)
{
struct deadline_data *dd = e->elevator_data;
@@ -703,7 +703,7 @@ static void deadline_exit(request_queue_t *q, elevator_t *e)
* initialize elevator private data (deadline_data), and alloc a drq for
* each request on the free lists
*/
-static int deadline_init(request_queue_t *q, elevator_t *e)
+static int deadline_init_queue(request_queue_t *q, elevator_t *e)
{
struct deadline_data *dd;
int i;
@@ -748,7 +748,7 @@ static int deadline_init(request_queue_t *q, elevator_t *e)
static void deadline_put_request(request_queue_t *q, struct request *rq)
{
- struct deadline_data *dd = q->elevator.elevator_data;
+ struct deadline_data *dd = q->elevator->elevator_data;
struct deadline_rq *drq = RQ_DATA(rq);
if (drq) {
@@ -760,7 +760,7 @@ static void deadline_put_request(request_queue_t *q, struct request *rq)
static int
deadline_set_request(request_queue_t *q, struct request *rq, int gfp_mask)
{
- struct deadline_data *dd = q->elevator.elevator_data;
+ struct deadline_data *dd = q->elevator->elevator_data;
struct deadline_rq *drq;
drq = mempool_alloc(dd->drq_pool, gfp_mask);
@@ -805,33 +805,41 @@ deadline_var_store(unsigned int *var, const char *page, size_t count)
return count;
}
-#define SHOW_FUNCTION(__FUNC, __VAR) \
+#define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \
static ssize_t __FUNC(struct deadline_data *dd, char *page) \
{ \
- return deadline_var_show(__VAR, (page)); \
-}
-SHOW_FUNCTION(deadline_readexpire_show, dd->fifo_expire[READ]);
-SHOW_FUNCTION(deadline_writeexpire_show, dd->fifo_expire[WRITE]);
-SHOW_FUNCTION(deadline_writesstarved_show, dd->writes_starved);
-SHOW_FUNCTION(deadline_frontmerges_show, dd->front_merges);
-SHOW_FUNCTION(deadline_fifobatch_show, dd->fifo_batch);
+ unsigned int __data = __VAR; \
+ if (__CONV) \
+ __data = jiffies_to_msecs(__data); \
+ return deadline_var_show(__data, (page)); \
+}
+SHOW_FUNCTION(deadline_readexpire_show, dd->fifo_expire[READ], 1);
+SHOW_FUNCTION(deadline_writeexpire_show, dd->fifo_expire[WRITE], 1);
+SHOW_FUNCTION(deadline_writesstarved_show, dd->writes_starved, 0);
+SHOW_FUNCTION(deadline_frontmerges_show, dd->front_merges, 0);
+SHOW_FUNCTION(deadline_fifobatch_show, dd->fifo_batch, 0);
#undef SHOW_FUNCTION
-#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX) \
+#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \
static ssize_t __FUNC(struct deadline_data *dd, const char *page, size_t count) \
{ \
- int ret = deadline_var_store(__PTR, (page), count); \
- if (*(__PTR) < (MIN)) \
- *(__PTR) = (MIN); \
- else if (*(__PTR) > (MAX)) \
- *(__PTR) = (MAX); \
+ unsigned int __data; \
+ int ret = deadline_var_store(&__data, (page), count); \
+ if (__data < (MIN)) \
+ __data = (MIN); \
+ else if (__data > (MAX)) \
+ __data = (MAX); \
+ if (__CONV) \
+ *(__PTR) = msecs_to_jiffies(__data); \
+ else \
+ *(__PTR) = __data; \
return ret; \
}
-STORE_FUNCTION(deadline_readexpire_store, &dd->fifo_expire[READ], 0, INT_MAX);
-STORE_FUNCTION(deadline_writeexpire_store, &dd->fifo_expire[WRITE], 0, INT_MAX);
-STORE_FUNCTION(deadline_writesstarved_store, &dd->writes_starved, INT_MIN, INT_MAX);
-STORE_FUNCTION(deadline_frontmerges_store, &dd->front_merges, 0, 1);
-STORE_FUNCTION(deadline_fifobatch_store, &dd->fifo_batch, 0, INT_MAX);
+STORE_FUNCTION(deadline_readexpire_store, &dd->fifo_expire[READ], 0, INT_MAX, 1);
+STORE_FUNCTION(deadline_writeexpire_store, &dd->fifo_expire[WRITE], 0, INT_MAX, 1);
+STORE_FUNCTION(deadline_writesstarved_store, &dd->writes_starved, INT_MIN, INT_MAX, 0);
+STORE_FUNCTION(deadline_frontmerges_store, &dd->front_merges, 0, 1, 0);
+STORE_FUNCTION(deadline_fifobatch_store, &dd->fifo_batch, 0, INT_MAX, 0);
#undef STORE_FUNCTION
static struct deadline_fs_entry deadline_readexpire_entry = {
@@ -906,36 +914,54 @@ struct kobj_type deadline_ktype = {
.default_attrs = default_attrs,
};
-static int __init deadline_slab_setup(void)
+static struct elevator_type iosched_deadline = {
+ .ops = {
+ .elevator_merge_fn = deadline_merge,
+ .elevator_merged_fn = deadline_merged_request,
+ .elevator_merge_req_fn = deadline_merged_requests,
+ .elevator_next_req_fn = deadline_next_request,
+ .elevator_add_req_fn = deadline_insert_request,
+ .elevator_remove_req_fn = deadline_remove_request,
+ .elevator_queue_empty_fn = deadline_queue_empty,
+ .elevator_former_req_fn = deadline_former_request,
+ .elevator_latter_req_fn = deadline_latter_request,
+ .elevator_set_req_fn = deadline_set_request,
+ .elevator_put_req_fn = deadline_put_request,
+ .elevator_init_fn = deadline_init_queue,
+ .elevator_exit_fn = deadline_exit_queue,
+ },
+
+ .elevator_ktype = &deadline_ktype,
+ .elevator_name = "deadline",
+ .elevator_owner = THIS_MODULE,
+};
+
+int deadline_init(void)
{
+ int ret;
+
drq_pool = kmem_cache_create("deadline_drq", sizeof(struct deadline_rq),
0, 0, NULL, NULL);
if (!drq_pool)
- panic("deadline: can't init slab pool\n");
+ return -ENOMEM;
- return 0;
+ ret = elv_register(&iosched_deadline);
+ if (ret)
+ kmem_cache_destroy(drq_pool);
+
+ return ret;
}
-subsys_initcall(deadline_slab_setup);
-
-elevator_t iosched_deadline = {
- .elevator_merge_fn = deadline_merge,
- .elevator_merged_fn = deadline_merged_request,
- .elevator_merge_req_fn = deadline_merged_requests,
- .elevator_next_req_fn = deadline_next_request,
- .elevator_add_req_fn = deadline_insert_request,
- .elevator_remove_req_fn = deadline_remove_request,
- .elevator_queue_empty_fn = deadline_queue_empty,
- .elevator_former_req_fn = deadline_former_request,
- .elevator_latter_req_fn = deadline_latter_request,
- .elevator_set_req_fn = deadline_set_request,
- .elevator_put_req_fn = deadline_put_request,
- .elevator_init_fn = deadline_init,
- .elevator_exit_fn = deadline_exit,
-
- .elevator_ktype = &deadline_ktype,
- .elevator_name = "deadline",
-};
+void deadline_exit(void)
+{
+ kmem_cache_destroy(drq_pool);
+ elv_unregister(&iosched_deadline);
+}
+
+module_init(deadline_init);
+module_exit(deadline_exit);
-EXPORT_SYMBOL(iosched_deadline);
+MODULE_AUTHOR("Jens Axboe");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("deadline IO scheduler");
diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c
index 35c9385ac133..1b4f6a70c0ca 100644
--- a/drivers/block/elevator.c
+++ b/drivers/block/elevator.c
@@ -37,6 +37,9 @@
#include <asm/uaccess.h>
+static spinlock_t elv_list_lock = SPIN_LOCK_UNLOCKED;
+static LIST_HEAD(elv_list);
+
/*
* can we safely merge with this request?
*/
@@ -60,6 +63,7 @@ inline int elv_rq_merge_ok(struct request *rq, struct bio *bio)
return 0;
}
+EXPORT_SYMBOL(elv_rq_merge_ok);
inline int elv_try_merge(struct request *__rq, struct bio *bio)
{
@@ -77,6 +81,7 @@ inline int elv_try_merge(struct request *__rq, struct bio *bio)
return ret;
}
+EXPORT_SYMBOL(elv_try_merge);
inline int elv_try_last_merge(request_queue_t *q, struct bio *bio)
{
@@ -85,31 +90,117 @@ inline int elv_try_last_merge(request_queue_t *q, struct bio *bio)
return ELEVATOR_NO_MERGE;
}
+EXPORT_SYMBOL(elv_try_last_merge);
-/*
- * general block -> elevator interface starts here
- */
-int elevator_init(request_queue_t *q, elevator_t *type)
+struct elevator_type *elevator_find(const char *name)
+{
+ struct elevator_type *e = NULL;
+ struct list_head *entry;
+
+ spin_lock_irq(&elv_list_lock);
+ list_for_each(entry, &elv_list) {
+ struct elevator_type *__e;
+
+ __e = list_entry(entry, struct elevator_type, list);
+
+ if (!strcmp(__e->elevator_name, name)) {
+ e = __e;
+ break;
+ }
+ }
+ spin_unlock_irq(&elv_list_lock);
+
+ return e;
+}
+
+static int elevator_attach(request_queue_t *q, struct elevator_type *e,
+ struct elevator_queue *eq)
{
- elevator_t *e = &q->elevator;
+ int ret = 0;
- memcpy(e, type, sizeof(*e));
+ if (!try_module_get(e->elevator_owner))
+ return -EINVAL;
+
+ memset(eq, 0, sizeof(*eq));
+ eq->ops = &e->ops;
+ eq->elevator_type = e;
INIT_LIST_HEAD(&q->queue_head);
q->last_merge = NULL;
+ q->elevator = eq;
+
+ if (eq->ops->elevator_init_fn)
+ ret = eq->ops->elevator_init_fn(q, eq);
- if (e->elevator_init_fn)
- return e->elevator_init_fn(q, e);
+ return ret;
+}
+
+static char chosen_elevator[16];
+
+static void elevator_setup_default(void)
+{
+ /*
+ * check if default is set and exists
+ */
+ if (chosen_elevator[0] && elevator_find(chosen_elevator))
+ return;
+
+#if defined(CONFIG_IOSCHED_AS)
+ strcpy(chosen_elevator, "anticipatory");
+#elif defined(CONFIG_IOSCHED_DEADLINE)
+ strcpy(chosen_elevator, "deadline");
+#elif defined(CONFIG_IOSCHED_CFQ)
+ strcpy(chosen_elevator, "cfq");
+#elif defined(CONFIG_IOSCHED_NOOP)
+ strcpy(chosen_elevator, "noop");
+#else
+#error "You must build at least 1 IO scheduler into the kernel"
+#endif
+ printk("elevator: using %s as default io scheduler\n", chosen_elevator);
+}
+static int __init elevator_setup(char *str)
+{
+ strncpy(chosen_elevator, str, sizeof(chosen_elevator) - 1);
return 0;
}
-void elevator_exit(request_queue_t *q)
+__setup("elevator=", elevator_setup);
+
+int elevator_init(request_queue_t *q, char *name)
+{
+ struct elevator_type *e = NULL;
+ struct elevator_queue *eq;
+ int ret = 0;
+
+ elevator_setup_default();
+
+ if (!name)
+ name = chosen_elevator;
+
+ e = elevator_find(name);
+ if (!e)
+ return -EINVAL;
+
+ eq = kmalloc(sizeof(struct elevator_queue), GFP_KERNEL);
+ if (!eq)
+ return -ENOMEM;
+
+ ret = elevator_attach(q, e, eq);
+ if (ret)
+ kfree(eq);
+
+ return ret;
+}
+
+void elevator_exit(elevator_t *e)
{
- elevator_t *e = &q->elevator;
+ if (e->ops->elevator_exit_fn)
+ e->ops->elevator_exit_fn(e);
- if (e->elevator_exit_fn)
- e->elevator_exit_fn(q, e);
+ module_put(e->elevator_type->elevator_owner);
+ e->elevator_type = NULL;
+ kfree(e);
}
int elevator_global_init(void)
@@ -119,32 +210,32 @@ int elevator_global_init(void)
int elv_merge(request_queue_t *q, struct request **req, struct bio *bio)
{
- elevator_t *e = &q->elevator;
+ elevator_t *e = q->elevator;
- if (e->elevator_merge_fn)
- return e->elevator_merge_fn(q, req, bio);
+ if (e->ops->elevator_merge_fn)
+ return e->ops->elevator_merge_fn(q, req, bio);
return ELEVATOR_NO_MERGE;
}
void elv_merged_request(request_queue_t *q, struct request *rq)
{
- elevator_t *e = &q->elevator;
+ elevator_t *e = q->elevator;
- if (e->elevator_merged_fn)
- e->elevator_merged_fn(q, rq);
+ if (e->ops->elevator_merged_fn)
+ e->ops->elevator_merged_fn(q, rq);
}
void elv_merge_requests(request_queue_t *q, struct request *rq,
struct request *next)
{
- elevator_t *e = &q->elevator;
+ elevator_t *e = q->elevator;
if (q->last_merge == next)
q->last_merge = NULL;
- if (e->elevator_merge_req_fn)
- e->elevator_merge_req_fn(q, rq, next);
+ if (e->ops->elevator_merge_req_fn)
+ e->ops->elevator_merge_req_fn(q, rq, next);
}
void elv_requeue_request(request_queue_t *q, struct request *rq)
@@ -160,8 +251,8 @@ void elv_requeue_request(request_queue_t *q, struct request *rq)
* if iosched has an explicit requeue hook, then use that. otherwise
* just put the request at the front of the queue
*/
- if (q->elevator.elevator_requeue_req_fn)
- q->elevator.elevator_requeue_req_fn(q, rq);
+ if (q->elevator->ops->elevator_requeue_req_fn)
+ q->elevator->ops->elevator_requeue_req_fn(q, rq);
else
__elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 0);
}
@@ -180,7 +271,7 @@ void __elv_add_request(request_queue_t *q, struct request *rq, int where,
blk_plug_device(q);
rq->q = q;
- q->elevator.elevator_add_req_fn(q, rq, where);
+ q->elevator->ops->elevator_add_req_fn(q, rq, where);
if (blk_queue_plugged(q)) {
int nrq = q->rq.count[READ] + q->rq.count[WRITE] - q->in_flight;
@@ -203,7 +294,7 @@ void elv_add_request(request_queue_t *q, struct request *rq, int where,
static inline struct request *__elv_next_request(request_queue_t *q)
{
- return q->elevator.elevator_next_req_fn(q);
+ return q->elevator->ops->elevator_next_req_fn(q);
}
struct request *elv_next_request(request_queue_t *q)
@@ -252,7 +343,7 @@ struct request *elv_next_request(request_queue_t *q)
void elv_remove_request(request_queue_t *q, struct request *rq)
{
- elevator_t *e = &q->elevator;
+ elevator_t *e = q->elevator;
/*
* the time frame between a request being removed from the lists
@@ -274,16 +365,16 @@ void elv_remove_request(request_queue_t *q, struct request *rq)
if (rq == q->last_merge)
q->last_merge = NULL;
- if (e->elevator_remove_req_fn)
- e->elevator_remove_req_fn(q, rq);
+ if (e->ops->elevator_remove_req_fn)
+ e->ops->elevator_remove_req_fn(q, rq);
}
int elv_queue_empty(request_queue_t *q)
{
- elevator_t *e = &q->elevator;
+ elevator_t *e = q->elevator;
- if (e->elevator_queue_empty_fn)
- return e->elevator_queue_empty_fn(q);
+ if (e->ops->elevator_queue_empty_fn)
+ return e->ops->elevator_queue_empty_fn(q);
return list_empty(&q->queue_head);
}
@@ -292,10 +383,10 @@ struct request *elv_latter_request(request_queue_t *q, struct request *rq)
{
struct list_head *next;
- elevator_t *e = &q->elevator;
+ elevator_t *e = q->elevator;
- if (e->elevator_latter_req_fn)
- return e->elevator_latter_req_fn(q, rq);
+ if (e->ops->elevator_latter_req_fn)
+ return e->ops->elevator_latter_req_fn(q, rq);
next = rq->queuelist.next;
if (next != &q->queue_head && next != &rq->queuelist)
@@ -308,10 +399,10 @@ struct request *elv_former_request(request_queue_t *q, struct request *rq)
{
struct list_head *prev;
- elevator_t *e = &q->elevator;
+ elevator_t *e = q->elevator;
- if (e->elevator_former_req_fn)
- return e->elevator_former_req_fn(q, rq);
+ if (e->ops->elevator_former_req_fn)
+ return e->ops->elevator_former_req_fn(q, rq);
prev = rq->queuelist.prev;
if (prev != &q->queue_head && prev != &rq->queuelist)
@@ -322,10 +413,10 @@ struct request *elv_former_request(request_queue_t *q, struct request *rq)
int elv_set_request(request_queue_t *q, struct request *rq, int gfp_mask)
{
- elevator_t *e = &q->elevator;
+ elevator_t *e = q->elevator;
- if (e->elevator_set_req_fn)
- return e->elevator_set_req_fn(q, rq, gfp_mask);
+ if (e->ops->elevator_set_req_fn)
+ return e->ops->elevator_set_req_fn(q, rq, gfp_mask);
rq->elevator_private = NULL;
return 0;
@@ -333,25 +424,25 @@ int elv_set_request(request_queue_t *q, struct request *rq, int gfp_mask)
void elv_put_request(request_queue_t *q, struct request *rq)
{
- elevator_t *e = &q->elevator;
+ elevator_t *e = q->elevator;
- if (e->elevator_put_req_fn)
- e->elevator_put_req_fn(q, rq);
+ if (e->ops->elevator_put_req_fn)
+ e->ops->elevator_put_req_fn(q, rq);
}
int elv_may_queue(request_queue_t *q, int rw)
{
- elevator_t *e = &q->elevator;
+ elevator_t *e = q->elevator;
- if (e->elevator_may_queue_fn)
- return e->elevator_may_queue_fn(q, rw);
+ if (e->ops->elevator_may_queue_fn)
+ return e->ops->elevator_may_queue_fn(q, rw);
- return 0;
+ return ELV_MQUEUE_MAY;
}
void elv_completed_request(request_queue_t *q, struct request *rq)
{
- elevator_t *e = &q->elevator;
+ elevator_t *e = q->elevator;
/*
* request is released from the driver, io must be done
@@ -359,22 +450,20 @@ void elv_completed_request(request_queue_t *q, struct request *rq)
if (blk_account_rq(rq))
q->in_flight--;
- if (e->elevator_completed_req_fn)
- e->elevator_completed_req_fn(q, rq);
+ if (e->ops->elevator_completed_req_fn)
+ e->ops->elevator_completed_req_fn(q, rq);
}
int elv_register_queue(struct request_queue *q)
{
- elevator_t *e;
-
- e = &q->elevator;
+ elevator_t *e = q->elevator;
e->kobj.parent = kobject_get(&q->kobj);
if (!e->kobj.parent)
return -EBUSY;
snprintf(e->kobj.name, KOBJ_NAME_LEN, "%s", "iosched");
- e->kobj.ktype = e->elevator_ktype;
+ e->kobj.ktype = e->elevator_type->elevator_ktype;
return kobject_register(&e->kobj);
}
@@ -382,12 +471,131 @@ int elv_register_queue(struct request_queue *q)
void elv_unregister_queue(struct request_queue *q)
{
if (q) {
- elevator_t * e = &q->elevator;
+ elevator_t *e = q->elevator;
kobject_unregister(&e->kobj);
kobject_put(&q->kobj);
}
}
+int elv_register(struct elevator_type *e)
+{
+ if (elevator_find(e->elevator_name))
+ BUG();
+
+ spin_lock_irq(&elv_list_lock);
+ list_add_tail(&e->list, &elv_list);
+ spin_unlock_irq(&elv_list_lock);
+
+ printk("io scheduler %s registered\n", e->elevator_name);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(elv_register);
+
+void elv_unregister(struct elevator_type *e)
+{
+ spin_lock_irq(&elv_list_lock);
+ list_del_init(&e->list);
+ spin_unlock_irq(&elv_list_lock);
+}
+EXPORT_SYMBOL_GPL(elv_unregister);
+
+/*
+ * switch to new_e io scheduler. be careful not to introduce deadlocks -
+ * we don't free the old io scheduler, before we have allocated what we
+ * need for the new one. this way we have a chance of going back to the old
+ * one, if the new one fails init for some reason
+ */
+static void elevator_switch(request_queue_t *q, struct elevator_type *new_e)
+{
+ elevator_t *e = kmalloc(sizeof(elevator_t), GFP_KERNEL);
+ elevator_t *old_elevator;
+
+ if (!e) {
+ printk("elevator: out of memory\n");
+ return;
+ }
+
+ blk_wait_queue_drained(q);
+
+ /*
+ * unregister old elevator data
+ */
+ elv_unregister_queue(q);
+ old_elevator = q->elevator;
+
+ /*
+ * attach and start new elevator
+ */
+ if (elevator_attach(q, new_e, e))
+ goto fail;
+
+ if (elv_register_queue(q))
+ goto fail_register;
+
+ /*
+ * finally exit old elevator and start queue again
+ */
+ elevator_exit(old_elevator);
+ blk_finish_queue_drain(q);
+ return;
+
+fail_register:
+ /*
+ * switch failed, exit the new io scheduler and reattach the old
+ * one again (along with re-adding the sysfs dir)
+ */
+ elevator_exit(e);
+fail:
+ q->elevator = old_elevator;
+ elv_register_queue(q);
+ blk_finish_queue_drain(q);
+ printk("elevator: switch to %s failed\n", new_e->elevator_name);
+}
+
+ssize_t elv_iosched_store(request_queue_t *q, const char *name, size_t count)
+{
+ char elevator_name[ELV_NAME_MAX];
+ struct elevator_type *e;
+
+ memset(elevator_name, 0, sizeof(elevator_name));
+ strncpy(elevator_name, name, sizeof(elevator_name));
+
+ if (elevator_name[strlen(elevator_name) - 1] == '\n')
+ elevator_name[strlen(elevator_name) - 1] = '\0';
+
+ e = elevator_find(elevator_name);
+ if (!e) {
+ printk("elevator: type %s not found\n", elevator_name);
+ return -EINVAL;
+ }
+
+ elevator_switch(q, e);
+ return count;
+}
+
+ssize_t elv_iosched_show(request_queue_t *q, char *name)
+{
+ elevator_t *e = q->elevator;
+ struct elevator_type *elv = e->elevator_type;
+ struct list_head *entry;
+ int len = 0;
+
+ spin_lock_irq(q->queue_lock);
+ list_for_each(entry, &elv_list) {
+ struct elevator_type *__e;
+
+ __e = list_entry(entry, struct elevator_type, list);
+ if (!strcmp(elv->elevator_name, __e->elevator_name))
+ len += sprintf(name+len, "[%s] ", elv->elevator_name);
+ else
+ len += sprintf(name+len, "%s ", __e->elevator_name);
+ }
+ spin_unlock_irq(q->queue_lock);
+
+ len += sprintf(len+name, "\n");
+ return len;
+}
+
module_init(elevator_global_init);
EXPORT_SYMBOL(elv_add_request);
diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c
index 26fdf6be6bd0..3ba6430899df 100644
--- a/drivers/block/ll_rw_blk.c
+++ b/drivers/block/ll_rw_blk.c
@@ -243,6 +243,7 @@ void blk_queue_make_request(request_queue_t * q, make_request_fn * mfn)
blk_queue_hardsect_size(q, 512);
blk_queue_dma_alignment(q, 511);
blk_queue_congestion_threshold(q);
+ q->nr_batching = BLK_BATCH_REQ;
q->unplug_thresh = 4; /* hmm */
q->unplug_delay = (3 * HZ) / 1000; /* 3 milliseconds */
@@ -1395,7 +1396,8 @@ void blk_cleanup_queue(request_queue_t * q)
if (!atomic_dec_and_test(&q->refcnt))
return;
- elevator_exit(q);
+ if (q->elevator)
+ elevator_exit(q->elevator);
del_timer_sync(&q->unplug_timer);
kblockd_flush();
@@ -1418,6 +1420,7 @@ static int blk_init_free_list(request_queue_t *q)
rl->count[READ] = rl->count[WRITE] = 0;
init_waitqueue_head(&rl->wait[READ]);
init_waitqueue_head(&rl->wait[WRITE]);
+ init_waitqueue_head(&rl->drain);
rl->rq_pool = mempool_create(BLKDEV_MIN_RQ, mempool_alloc_slab, mempool_free_slab, request_cachep);
@@ -1429,45 +1432,6 @@ static int blk_init_free_list(request_queue_t *q)
static int __make_request(request_queue_t *, struct bio *);
-static elevator_t *chosen_elevator =
-#if defined(CONFIG_IOSCHED_AS)
- &iosched_as;
-#elif defined(CONFIG_IOSCHED_DEADLINE)
- &iosched_deadline;
-#elif defined(CONFIG_IOSCHED_CFQ)
- &iosched_cfq;
-#elif defined(CONFIG_IOSCHED_NOOP)
- &elevator_noop;
-#else
- NULL;
-#error "You must have at least 1 I/O scheduler selected"
-#endif
-
-#if defined(CONFIG_IOSCHED_AS) || defined(CONFIG_IOSCHED_DEADLINE) || defined (CONFIG_IOSCHED_NOOP)
-static int __init elevator_setup(char *str)
-{
-#ifdef CONFIG_IOSCHED_DEADLINE
- if (!strcmp(str, "deadline"))
- chosen_elevator = &iosched_deadline;
-#endif
-#ifdef CONFIG_IOSCHED_AS
- if (!strcmp(str, "as"))
- chosen_elevator = &iosched_as;
-#endif
-#ifdef CONFIG_IOSCHED_CFQ
- if (!strcmp(str, "cfq"))
- chosen_elevator = &iosched_cfq;
-#endif
-#ifdef CONFIG_IOSCHED_NOOP
- if (!strcmp(str, "noop"))
- chosen_elevator = &elevator_noop;
-#endif
- return 1;
-}
-
-__setup("elevator=", elevator_setup);
-#endif /* CONFIG_IOSCHED_AS || CONFIG_IOSCHED_DEADLINE || CONFIG_IOSCHED_NOOP */
-
request_queue_t *blk_alloc_queue(int gfp_mask)
{
request_queue_t *q = kmem_cache_alloc(requestq_cachep, gfp_mask);
@@ -1520,21 +1484,14 @@ EXPORT_SYMBOL(blk_alloc_queue);
**/
request_queue_t *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)
{
- request_queue_t *q;
- static int printed;
+ request_queue_t *q = blk_alloc_queue(GFP_KERNEL);
- q = blk_alloc_queue(GFP_KERNEL);
if (!q)
return NULL;
if (blk_init_free_list(q))
goto out_init;
- if (!printed) {
- printed = 1;
- printk("Using %s io scheduler\n", chosen_elevator->elevator_name);
- }
-
q->request_fn = rfn;
q->back_merge_fn = ll_back_merge_fn;
q->front_merge_fn = ll_front_merge_fn;
@@ -1555,8 +1512,10 @@ request_queue_t *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)
/*
* all done
*/
- if (!elevator_init(q, chosen_elevator))
+ if (!elevator_init(q, NULL)) {
+ blk_queue_congestion_threshold(q);
return q;
+ }
blk_cleanup_queue(q);
out_init:
@@ -1584,13 +1543,20 @@ static inline void blk_free_request(request_queue_t *q, struct request *rq)
mempool_free(rq, q->rq.rq_pool);
}
-static inline struct request *blk_alloc_request(request_queue_t *q,int gfp_mask)
+static inline struct request *blk_alloc_request(request_queue_t *q, int rw,
+ int gfp_mask)
{
struct request *rq = mempool_alloc(q->rq.rq_pool, gfp_mask);
if (!rq)
return NULL;
+ /*
+ * first three bits are identical in rq->flags and bio->bi_rw,
+ * see bio.h and blkdev.h
+ */
+ rq->flags = rw;
+
if (!elv_set_request(q, rq, gfp_mask))
return rq;
@@ -1602,7 +1568,7 @@ static inline struct request *blk_alloc_request(request_queue_t *q,int gfp_mask)
* ioc_batching returns true if the ioc is a valid batching request and
* should be given priority access to a request.
*/
-static inline int ioc_batching(struct io_context *ioc)
+static inline int ioc_batching(request_queue_t *q, struct io_context *ioc)
{
if (!ioc)
return 0;
@@ -1612,7 +1578,7 @@ static inline int ioc_batching(struct io_context *ioc)
* even if the batch times out, otherwise we could theoretically
* lose wakeups.
*/
- return ioc->nr_batch_requests == BLK_BATCH_REQ ||
+ return ioc->nr_batch_requests == q->nr_batching ||
(ioc->nr_batch_requests > 0
&& time_before(jiffies, ioc->last_waited + BLK_BATCH_TIME));
}
@@ -1623,12 +1589,12 @@ static inline int ioc_batching(struct io_context *ioc)
* is the behaviour we want though - once it gets a wakeup it should be given
* a nice run.
*/
-void ioc_set_batching(struct io_context *ioc)
+void ioc_set_batching(request_queue_t *q, struct io_context *ioc)
{
- if (!ioc || ioc_batching(ioc))
+ if (!ioc || ioc_batching(q, ioc))
return;
- ioc->nr_batch_requests = BLK_BATCH_REQ;
+ ioc->nr_batch_requests = q->nr_batching;
ioc->last_waited = jiffies;
}
@@ -1644,11 +1610,14 @@ static void freed_request(request_queue_t *q, int rw)
if (rl->count[rw] < queue_congestion_off_threshold(q))
clear_queue_congested(q, rw);
if (rl->count[rw]+1 <= q->nr_requests) {
+ smp_mb();
if (waitqueue_active(&rl->wait[rw]))
wake_up(&rl->wait[rw]);
- if (!waitqueue_active(&rl->wait[rw]))
- blk_clear_queue_full(q, rw);
+ blk_clear_queue_full(q, rw);
}
+ if (unlikely(waitqueue_active(&rl->drain)) &&
+ !rl->count[READ] && !rl->count[WRITE])
+ wake_up(&rl->drain);
}
#define blkdev_free_rq(list) list_entry((list)->next, struct request, queuelist)
@@ -1661,6 +1630,9 @@ static struct request *get_request(request_queue_t *q, int rw, int gfp_mask)
struct request_list *rl = &q->rq;
struct io_context *ioc = get_io_context(gfp_mask);
+ if (unlikely(test_bit(QUEUE_FLAG_DRAIN, &q->queue_flags)))
+ return NULL;
+
spin_lock_irq(q->queue_lock);
if (rl->count[rw]+1 >= q->nr_requests) {
/*
@@ -1670,13 +1642,22 @@ static struct request *get_request(request_queue_t *q, int rw, int gfp_mask)
* will be blocked.
*/
if (!blk_queue_full(q, rw)) {
- ioc_set_batching(ioc);
+ ioc_set_batching(q, ioc);
blk_set_queue_full(q, rw);
}
}
- if (blk_queue_full(q, rw)
- && !ioc_batching(ioc) && !elv_may_queue(q, rw)) {
+ switch (elv_may_queue(q, rw)) {
+ case ELV_MQUEUE_NO:
+ spin_unlock_irq(q->queue_lock);
+ goto out;
+ case ELV_MQUEUE_MAY:
+ break;
+ case ELV_MQUEUE_MUST:
+ goto get_rq;
+ }
+
+ if (blk_queue_full(q, rw) && !ioc_batching(q, ioc)) {
/*
* The queue is full and the allocating process is not a
* "batcher", and not exempted by the IO scheduler
@@ -1685,12 +1666,13 @@ static struct request *get_request(request_queue_t *q, int rw, int gfp_mask)
goto out;
}
+get_rq:
rl->count[rw]++;
if (rl->count[rw] >= queue_congestion_on_threshold(q))
set_queue_congested(q, rw);
spin_unlock_irq(q->queue_lock);
- rq = blk_alloc_request(q, gfp_mask);
+ rq = blk_alloc_request(q, rw, gfp_mask);
if (!rq) {
/*
* Allocation failed presumably due to memory. Undo anything
@@ -1705,17 +1687,11 @@ static struct request *get_request(request_queue_t *q, int rw, int gfp_mask)
goto out;
}
- if (ioc_batching(ioc))
+ if (ioc_batching(q, ioc))
ioc->nr_batch_requests--;
INIT_LIST_HEAD(&rq->queuelist);
- /*
- * first three bits are identical in rq->flags and bio->bi_rw,
- * see bio.h and blkdev.h
- */
- rq->flags = rw;
-
rq->errors = 0;
rq->rq_status = RQ_ACTIVE;
rq->bio = rq->biotail = NULL;
@@ -1764,7 +1740,7 @@ static struct request *get_request_wait(request_queue_t *q, int rw)
* See ioc_batching, ioc_set_batching
*/
ioc = get_io_context(GFP_NOIO);
- ioc_set_batching(ioc);
+ ioc_set_batching(q, ioc);
put_io_context(ioc);
}
finish_wait(&rl->wait[rw], &wait);
@@ -2506,6 +2482,70 @@ static inline void blk_partition_remap(struct bio *bio)
}
}
+void blk_finish_queue_drain(request_queue_t *q)
+{
+ struct request_list *rl = &q->rq;
+
+ clear_bit(QUEUE_FLAG_DRAIN, &q->queue_flags);
+ wake_up(&rl->wait[0]);
+ wake_up(&rl->wait[1]);
+ wake_up(&rl->drain);
+}
+
+/*
+ * We rely on the fact that only requests allocated through blk_alloc_request()
+ * have io scheduler private data structures associated with them. Any other
+ * type of request (allocated on stack or through kmalloc()) should not go
+ * to the io scheduler core, but be attached to the queue head instead.
+ */
+void blk_wait_queue_drained(request_queue_t *q)
+{
+ struct request_list *rl = &q->rq;
+ DEFINE_WAIT(wait);
+
+ spin_lock_irq(q->queue_lock);
+ set_bit(QUEUE_FLAG_DRAIN, &q->queue_flags);
+
+ while (rl->count[READ] || rl->count[WRITE]) {
+ prepare_to_wait(&rl->drain, &wait, TASK_UNINTERRUPTIBLE);
+
+ if (rl->count[READ] || rl->count[WRITE]) {
+ __generic_unplug_device(q);
+ spin_unlock_irq(q->queue_lock);
+ io_schedule();
+ spin_lock_irq(q->queue_lock);
+ }
+
+ finish_wait(&rl->drain, &wait);
+ }
+
+ spin_unlock_irq(q->queue_lock);
+}
+
+/*
+ * block waiting for the io scheduler being started again.
+ */
+static inline void block_wait_queue_running(request_queue_t *q)
+{
+ DEFINE_WAIT(wait);
+
+ while (test_bit(QUEUE_FLAG_DRAIN, &q->queue_flags)) {
+ struct request_list *rl = &q->rq;
+
+ prepare_to_wait_exclusive(&rl->drain, &wait,
+ TASK_UNINTERRUPTIBLE);
+
+ /*
+ * re-check the condition. avoids using prepare_to_wait()
+ * in the fast path (queue is running)
+ */
+ if (test_bit(QUEUE_FLAG_DRAIN, &q->queue_flags))
+ io_schedule();
+
+ finish_wait(&rl->drain, &wait);
+ }
+}
+
/**
* generic_make_request: hand a buffer to its device driver for I/O
* @bio: The bio describing the location in memory and on the device.
@@ -2595,6 +2635,8 @@ end_io:
if (test_bit(QUEUE_FLAG_DEAD, &q->queue_flags))
goto end_io;
+ block_wait_queue_running(q);
+
/*
* If this device has partitions, remap block n
* of partition p to block n+start(p) of the disk.
@@ -3018,6 +3060,7 @@ void kblockd_flush(void)
{
flush_workqueue(kblockd_workqueue);
}
+EXPORT_SYMBOL(kblockd_flush);
int __init blk_dev_init(void)
{
@@ -3036,6 +3079,7 @@ int __init blk_dev_init(void)
blk_max_low_pfn = max_low_pfn;
blk_max_pfn = max_pfn;
+
return 0;
}
@@ -3052,9 +3096,13 @@ void put_io_context(struct io_context *ioc)
if (atomic_dec_and_test(&ioc->refcount)) {
if (ioc->aic && ioc->aic->dtor)
ioc->aic->dtor(ioc->aic);
+ if (ioc->cic && ioc->cic->dtor)
+ ioc->cic->dtor(ioc->cic);
+
kmem_cache_free(iocontext_cachep, ioc);
}
}
+EXPORT_SYMBOL(put_io_context);
/* Called by the exitting task */
void exit_io_context(void)
@@ -3064,14 +3112,15 @@ void exit_io_context(void)
local_irq_save(flags);
ioc = current->io_context;
- if (ioc) {
- if (ioc->aic && ioc->aic->exit)
- ioc->aic->exit(ioc->aic);
- put_io_context(ioc);
- current->io_context = NULL;
- } else
- WARN_ON(1);
+ current->io_context = NULL;
local_irq_restore(flags);
+
+ if (ioc->aic && ioc->aic->exit)
+ ioc->aic->exit(ioc->aic);
+ if (ioc->cic && ioc->cic->exit)
+ ioc->cic->exit(ioc->cic);
+
+ put_io_context(ioc);
}
/*
@@ -3090,22 +3139,42 @@ struct io_context *get_io_context(int gfp_flags)
local_irq_save(flags);
ret = tsk->io_context;
- if (ret == NULL) {
- ret = kmem_cache_alloc(iocontext_cachep, GFP_ATOMIC);
- if (ret) {
- atomic_set(&ret->refcount, 1);
- ret->pid = tsk->pid;
- ret->last_waited = jiffies; /* doesn't matter... */
- ret->nr_batch_requests = 0; /* because this is 0 */
- ret->aic = NULL;
+ if (ret)
+ goto out;
+
+ local_irq_restore(flags);
+
+ ret = kmem_cache_alloc(iocontext_cachep, gfp_flags);
+ if (ret) {
+ atomic_set(&ret->refcount, 1);
+ ret->pid = tsk->pid;
+ ret->last_waited = jiffies; /* doesn't matter... */
+ ret->nr_batch_requests = 0; /* because this is 0 */
+ ret->aic = NULL;
+ ret->cic = NULL;
+ spin_lock_init(&ret->lock);
+
+ local_irq_save(flags);
+
+ /*
+ * very unlikely, someone raced with us in setting up the task
+ * io context. free new context and just grab a reference.
+ */
+ if (!tsk->io_context)
tsk->io_context = ret;
+ else {
+ kmem_cache_free(iocontext_cachep, ret);
+ ret = tsk->io_context;
}
- }
- if (ret)
+
+out:
atomic_inc(&ret->refcount);
- local_irq_restore(flags);
+ local_irq_restore(flags);
+ }
+
return ret;
}
+EXPORT_SYMBOL(get_io_context);
void copy_io_context(struct io_context **pdst, struct io_context **psrc)
{
@@ -3119,6 +3188,7 @@ void copy_io_context(struct io_context **pdst, struct io_context **psrc)
*pdst = src;
}
}
+EXPORT_SYMBOL(copy_io_context);
void swap_io_context(struct io_context **ioc1, struct io_context **ioc2)
{
@@ -3127,7 +3197,7 @@ void swap_io_context(struct io_context **ioc1, struct io_context **ioc2)
*ioc1 = *ioc2;
*ioc2 = temp;
}
-
+EXPORT_SYMBOL(swap_io_context);
/*
* sysfs parts below
@@ -3285,11 +3355,18 @@ static struct queue_sysfs_entry queue_max_hw_sectors_entry = {
.show = queue_max_hw_sectors_show,
};
+static struct queue_sysfs_entry queue_iosched_entry = {
+ .attr = {.name = "scheduler", .mode = S_IRUGO | S_IWUSR },
+ .show = elv_iosched_show,
+ .store = elv_iosched_store,
+};
+
static struct attribute *default_attrs[] = {
&queue_requests_entry.attr,
&queue_ra_entry.attr,
&queue_max_hw_sectors_entry.attr,
&queue_max_sectors_entry.attr,
+ &queue_iosched_entry.attr,
NULL,
};
diff --git a/drivers/block/noop-iosched.c b/drivers/block/noop-iosched.c
index ffef40be1f92..707dddd7d881 100644
--- a/drivers/block/noop-iosched.c
+++ b/drivers/block/noop-iosched.c
@@ -83,12 +83,31 @@ struct request *elevator_noop_next_request(request_queue_t *q)
return NULL;
}
-elevator_t elevator_noop = {
- .elevator_merge_fn = elevator_noop_merge,
- .elevator_merge_req_fn = elevator_noop_merge_requests,
- .elevator_next_req_fn = elevator_noop_next_request,
- .elevator_add_req_fn = elevator_noop_add_request,
- .elevator_name = "noop",
+static struct elevator_type elevator_noop = {
+ .ops = {
+ .elevator_merge_fn = elevator_noop_merge,
+ .elevator_merge_req_fn = elevator_noop_merge_requests,
+ .elevator_next_req_fn = elevator_noop_next_request,
+ .elevator_add_req_fn = elevator_noop_add_request,
+ },
+ .elevator_name = "noop",
+ .elevator_owner = THIS_MODULE,
};
-EXPORT_SYMBOL(elevator_noop);
+int noop_init(void)
+{
+ return elv_register(&elevator_noop);
+}
+
+void noop_exit(void)
+{
+ elv_unregister(&elevator_noop);
+}
+
+module_init(noop_init);
+module_exit(noop_exit);
+
+
+MODULE_AUTHOR("Jens Axboe");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("No-op IO scheduler");
diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c
new file mode 100644
index 000000000000..fb80b6a91f84
--- /dev/null
+++ b/drivers/block/pktcdvd.c
@@ -0,0 +1,2679 @@
+/*
+ * Copyright (C) 2000 Jens Axboe <axboe@suse.de>
+ * Copyright (C) 2001-2004 Peter Osterlund <petero2@telia.com>
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * Packet writing layer for ATAPI and SCSI CD-R, CD-RW, DVD-R, and
+ * DVD-RW devices (aka an exercise in block layer masturbation)
+ *
+ *
+ * TODO: (circa order of when I will fix it)
+ * - Only able to write on CD-RW media right now.
+ * - check host application code on media and set it in write page
+ * - interface for UDF <-> packet to negotiate a new location when a write
+ * fails.
+ * - handle OPC, especially for -RW media
+ *
+ * Theory of operation:
+ *
+ * We use a custom make_request_fn function that forwards reads directly to
+ * the underlying CD device. Write requests are either attached directly to
+ * a live packet_data object, or simply stored sequentially in a list for
+ * later processing by the kcdrwd kernel thread. This driver doesn't use
+ * any elevator functionally as defined by the elevator_s struct, but the
+ * underlying CD device uses a standard elevator.
+ *
+ * This strategy makes it possible to do very late merging of IO requests.
+ * A new bio sent to pkt_make_request can be merged with a live packet_data
+ * object even if the object is in the data gathering state.
+ *
+ *************************************************************************/
+
+#define VERSION_CODE "v0.2.0a 2004-07-14 Jens Axboe (axboe@suse.de) and petero2@telia.com"
+
+#include <linux/pktcdvd.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/file.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/miscdevice.h>
+#include <linux/suspend.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_ioctl.h>
+
+#include <asm/uaccess.h>
+
+#if PACKET_DEBUG
+#define DPRINTK(fmt, args...) printk(KERN_NOTICE fmt, ##args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+#if PACKET_DEBUG > 1
+#define VPRINTK(fmt, args...) printk(KERN_NOTICE fmt, ##args)
+#else
+#define VPRINTK(fmt, args...)
+#endif
+
+#define MAX_SPEED 0xffff
+
+#define ZONE(sector, pd) (((sector) + (pd)->offset) & ~((pd)->settings.size - 1))
+
+static struct pktcdvd_device *pkt_devs[MAX_WRITERS];
+static struct proc_dir_entry *pkt_proc;
+static int pkt_major;
+static struct semaphore ctl_mutex; /* Serialize open/close/setup/teardown */
+static mempool_t *psd_pool;
+
+
+static void pkt_bio_finished(struct pktcdvd_device *pd)
+{
+ BUG_ON(atomic_read(&pd->cdrw.pending_bios) <= 0);
+ if (atomic_dec_and_test(&pd->cdrw.pending_bios)) {
+ VPRINTK("pktcdvd: queue empty\n");
+ atomic_set(&pd->iosched.attention, 1);
+ wake_up(&pd->wqueue);
+ }
+}
+
+static void pkt_bio_destructor(struct bio *bio)
+{
+ kfree(bio->bi_io_vec);
+ kfree(bio);
+}
+
+static struct bio *pkt_bio_alloc(int nr_iovecs)
+{
+ struct bio_vec *bvl = NULL;
+ struct bio *bio;
+
+ bio = kmalloc(sizeof(struct bio), GFP_KERNEL);
+ if (!bio)
+ goto no_bio;
+ bio_init(bio);
+
+ bvl = kmalloc(nr_iovecs * sizeof(struct bio_vec), GFP_KERNEL);
+ if (!bvl)
+ goto no_bvl;
+ memset(bvl, 0, nr_iovecs * sizeof(struct bio_vec));
+
+ bio->bi_max_vecs = nr_iovecs;
+ bio->bi_io_vec = bvl;
+ bio->bi_destructor = pkt_bio_destructor;
+
+ return bio;
+
+ no_bvl:
+ kfree(bio);
+ no_bio:
+ return NULL;
+}
+
+/*
+ * Allocate a packet_data struct
+ */
+static struct packet_data *pkt_alloc_packet_data(void)
+{
+ int i;
+ struct packet_data *pkt;
+
+ pkt = kmalloc(sizeof(struct packet_data), GFP_KERNEL);
+ if (!pkt)
+ goto no_pkt;
+ memset(pkt, 0, sizeof(struct packet_data));
+
+ pkt->w_bio = pkt_bio_alloc(PACKET_MAX_SIZE);
+ if (!pkt->w_bio)
+ goto no_bio;
+
+ for (i = 0; i < PAGES_PER_PACKET; i++) {
+ pkt->pages[i] = alloc_page(GFP_KERNEL);
+ if (!pkt->pages[i])
+ goto no_page;
+ }
+ for (i = 0; i < PAGES_PER_PACKET; i++)
+ clear_page(page_address(pkt->pages[i]));
+
+ spin_lock_init(&pkt->lock);
+
+ for (i = 0; i < PACKET_MAX_SIZE; i++) {
+ struct bio *bio = pkt_bio_alloc(1);
+ if (!bio)
+ goto no_rd_bio;
+ pkt->r_bios[i] = bio;
+ }
+
+ return pkt;
+
+no_rd_bio:
+ for (i = 0; i < PACKET_MAX_SIZE; i++) {
+ struct bio *bio = pkt->r_bios[i];
+ if (bio)
+ bio_put(bio);
+ }
+
+no_page:
+ for (i = 0; i < PAGES_PER_PACKET; i++)
+ if (pkt->pages[i])
+ __free_page(pkt->pages[i]);
+ bio_put(pkt->w_bio);
+no_bio:
+ kfree(pkt);
+no_pkt:
+ return NULL;
+}
+
+/*
+ * Free a packet_data struct
+ */
+static void pkt_free_packet_data(struct packet_data *pkt)
+{
+ int i;
+
+ for (i = 0; i < PACKET_MAX_SIZE; i++) {
+ struct bio *bio = pkt->r_bios[i];
+ if (bio)
+ bio_put(bio);
+ }
+ for (i = 0; i < PAGES_PER_PACKET; i++)
+ __free_page(pkt->pages[i]);
+ bio_put(pkt->w_bio);
+ kfree(pkt);
+}
+
+static void pkt_shrink_pktlist(struct pktcdvd_device *pd)
+{
+ struct packet_data *pkt, *next;
+
+ BUG_ON(!list_empty(&pd->cdrw.pkt_active_list));
+
+ list_for_each_entry_safe(pkt, next, &pd->cdrw.pkt_free_list, list) {
+ pkt_free_packet_data(pkt);
+ }
+}
+
+static int pkt_grow_pktlist(struct pktcdvd_device *pd, int nr_packets)
+{
+ struct packet_data *pkt;
+
+ INIT_LIST_HEAD(&pd->cdrw.pkt_free_list);
+ INIT_LIST_HEAD(&pd->cdrw.pkt_active_list);
+ spin_lock_init(&pd->cdrw.active_list_lock);
+ while (nr_packets > 0) {
+ pkt = pkt_alloc_packet_data();
+ if (!pkt) {
+ pkt_shrink_pktlist(pd);
+ return 0;
+ }
+ pkt->id = nr_packets;
+ pkt->pd = pd;
+ list_add(&pkt->list, &pd->cdrw.pkt_free_list);
+ nr_packets--;
+ }
+ return 1;
+}
+
+static void *pkt_rb_alloc(int gfp_mask, void *data)
+{
+ return kmalloc(sizeof(struct pkt_rb_node), gfp_mask);
+}
+
+static void pkt_rb_free(void *ptr, void *data)
+{
+ kfree(ptr);
+}
+
+static inline struct pkt_rb_node *pkt_rbtree_next(struct pkt_rb_node *node)
+{
+ struct rb_node *n = rb_next(&node->rb_node);
+ if (!n)
+ return NULL;
+ return rb_entry(n, struct pkt_rb_node, rb_node);
+}
+
+static inline void pkt_rbtree_erase(struct pktcdvd_device *pd, struct pkt_rb_node *node)
+{
+ rb_erase(&node->rb_node, &pd->bio_queue);
+ mempool_free(node, pd->rb_pool);
+ pd->bio_queue_size--;
+ BUG_ON(pd->bio_queue_size < 0);
+}
+
+/*
+ * Find the first node in the pd->bio_queue rb tree with a starting sector >= s.
+ */
+static struct pkt_rb_node *pkt_rbtree_find(struct pktcdvd_device *pd, sector_t s)
+{
+ struct rb_node *n = pd->bio_queue.rb_node;
+ struct rb_node *next;
+ struct pkt_rb_node *tmp;
+
+ if (!n) {
+ BUG_ON(pd->bio_queue_size > 0);
+ return NULL;
+ }
+
+ for (;;) {
+ tmp = rb_entry(n, struct pkt_rb_node, rb_node);
+ if (s <= tmp->bio->bi_sector)
+ next = n->rb_left;
+ else
+ next = n->rb_right;
+ if (!next)
+ break;
+ n = next;
+ }
+
+ if (s > tmp->bio->bi_sector) {
+ tmp = pkt_rbtree_next(tmp);
+ if (!tmp)
+ return NULL;
+ }
+ BUG_ON(s > tmp->bio->bi_sector);
+ return tmp;
+}
+
+/*
+ * Insert a node into the pd->bio_queue rb tree.
+ */
+static void pkt_rbtree_insert(struct pktcdvd_device *pd, struct pkt_rb_node *node)
+{
+ struct rb_node **p = &pd->bio_queue.rb_node;
+ struct rb_node *parent = NULL;
+ sector_t s = node->bio->bi_sector;
+ struct pkt_rb_node *tmp;
+
+ while (*p) {
+ parent = *p;
+ tmp = rb_entry(parent, struct pkt_rb_node, rb_node);
+ if (s < tmp->bio->bi_sector)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+ rb_link_node(&node->rb_node, parent, p);
+ rb_insert_color(&node->rb_node, &pd->bio_queue);
+ pd->bio_queue_size++;
+}
+
+/*
+ * Add a bio to a single linked list defined by its head and tail pointers.
+ */
+static inline void pkt_add_list_last(struct bio *bio, struct bio **list_head, struct bio **list_tail)
+{
+ bio->bi_next = NULL;
+ if (*list_tail) {
+ BUG_ON((*list_head) == NULL);
+ (*list_tail)->bi_next = bio;
+ (*list_tail) = bio;
+ } else {
+ BUG_ON((*list_head) != NULL);
+ (*list_head) = bio;
+ (*list_tail) = bio;
+ }
+}
+
+/*
+ * Remove and return the first bio from a single linked list defined by its
+ * head and tail pointers.
+ */
+static inline struct bio *pkt_get_list_first(struct bio **list_head, struct bio **list_tail)
+{
+ struct bio *bio;
+
+ if (*list_head == NULL)
+ return NULL;
+
+ bio = *list_head;
+ *list_head = bio->bi_next;
+ if (*list_head == NULL)
+ *list_tail = NULL;
+
+ bio->bi_next = NULL;
+ return bio;
+}
+
+/*
+ * Send a packet_command to the underlying block device and
+ * wait for completion.
+ */
+static int pkt_generic_packet(struct pktcdvd_device *pd, struct packet_command *cgc)
+{
+ char sense[SCSI_SENSE_BUFFERSIZE];
+ request_queue_t *q;
+ struct request *rq;
+ DECLARE_COMPLETION(wait);
+ int err = 0;
+
+ q = bdev_get_queue(pd->bdev);
+
+ rq = blk_get_request(q, (cgc->data_direction == CGC_DATA_WRITE) ? WRITE : READ,
+ __GFP_WAIT);
+ rq->errors = 0;
+ rq->rq_disk = pd->bdev->bd_disk;
+ rq->bio = NULL;
+ rq->buffer = NULL;
+ rq->timeout = 60*HZ;
+ rq->data = cgc->buffer;
+ rq->data_len = cgc->buflen;
+ rq->sense = sense;
+ memset(sense, 0, sizeof(sense));
+ rq->sense_len = 0;
+ rq->flags |= REQ_BLOCK_PC | REQ_HARDBARRIER;
+ if (cgc->quiet)
+ rq->flags |= REQ_QUIET;
+ memcpy(rq->cmd, cgc->cmd, CDROM_PACKET_SIZE);
+ if (sizeof(rq->cmd) > CDROM_PACKET_SIZE)
+ memset(rq->cmd + CDROM_PACKET_SIZE, 0, sizeof(rq->cmd) - CDROM_PACKET_SIZE);
+
+ rq->ref_count++;
+ rq->flags |= REQ_NOMERGE;
+ rq->waiting = &wait;
+ elv_add_request(q, rq, ELEVATOR_INSERT_BACK, 1);
+ generic_unplug_device(q);
+ wait_for_completion(&wait);
+
+ if (rq->errors)
+ err = -EIO;
+
+ blk_put_request(rq);
+ return err;
+}
+
+/*
+ * A generic sense dump / resolve mechanism should be implemented across
+ * all ATAPI + SCSI devices.
+ */
+static void pkt_dump_sense(struct packet_command *cgc)
+{
+ static char *info[9] = { "No sense", "Recovered error", "Not ready",
+ "Medium error", "Hardware error", "Illegal request",
+ "Unit attention", "Data protect", "Blank check" };
+ int i;
+ struct request_sense *sense = cgc->sense;
+
+ printk("pktcdvd:");
+ for (i = 0; i < CDROM_PACKET_SIZE; i++)
+ printk(" %02x", cgc->cmd[i]);
+ printk(" - ");
+
+ if (sense == NULL) {
+ printk("no sense\n");
+ return;
+ }
+
+ printk("sense %02x.%02x.%02x", sense->sense_key, sense->asc, sense->ascq);
+
+ if (sense->sense_key > 8) {
+ printk(" (INVALID)\n");
+ return;
+ }
+
+ printk(" (%s)\n", info[sense->sense_key]);
+}
+
+/*
+ * flush the drive cache to media
+ */
+static int pkt_flush_cache(struct pktcdvd_device *pd)
+{
+ struct packet_command cgc;
+
+ init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+ cgc.cmd[0] = GPCMD_FLUSH_CACHE;
+ cgc.quiet = 1;
+
+ /*
+ * the IMMED bit -- we default to not setting it, although that
+ * would allow a much faster close, this is safer
+ */
+#if 0
+ cgc.cmd[1] = 1 << 1;
+#endif
+ return pkt_generic_packet(pd, &cgc);
+}
+
+/*
+ * speed is given as the normal factor, e.g. 4 for 4x
+ */
+static int pkt_set_speed(struct pktcdvd_device *pd, unsigned write_speed, unsigned read_speed)
+{
+ struct packet_command cgc;
+ struct request_sense sense;
+ int ret;
+
+ init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+ cgc.sense = &sense;
+ cgc.cmd[0] = GPCMD_SET_SPEED;
+ cgc.cmd[2] = (read_speed >> 8) & 0xff;
+ cgc.cmd[3] = read_speed & 0xff;
+ cgc.cmd[4] = (write_speed >> 8) & 0xff;
+ cgc.cmd[5] = write_speed & 0xff;
+
+ if ((ret = pkt_generic_packet(pd, &cgc)))
+ pkt_dump_sense(&cgc);
+
+ return ret;
+}
+
+/*
+ * Queue a bio for processing by the low-level CD device. Must be called
+ * from process context.
+ */
+static void pkt_queue_bio(struct pktcdvd_device *pd, struct bio *bio, int high_prio_read)
+{
+ spin_lock(&pd->iosched.lock);
+ if (bio_data_dir(bio) == READ) {
+ pkt_add_list_last(bio, &pd->iosched.read_queue,
+ &pd->iosched.read_queue_tail);
+ if (high_prio_read)
+ pd->iosched.high_prio_read = 1;
+ } else {
+ pkt_add_list_last(bio, &pd->iosched.write_queue,
+ &pd->iosched.write_queue_tail);
+ }
+ spin_unlock(&pd->iosched.lock);
+
+ atomic_set(&pd->iosched.attention, 1);
+ wake_up(&pd->wqueue);
+}
+
+/*
+ * Process the queued read/write requests. This function handles special
+ * requirements for CDRW drives:
+ * - A cache flush command must be inserted before a read request if the
+ * previous request was a write.
+ * - Switching between reading and writing is slow, so don't it more often
+ * than necessary.
+ * - Set the read speed according to current usage pattern. When only reading
+ * from the device, it's best to use the highest possible read speed, but
+ * when switching often between reading and writing, it's better to have the
+ * same read and write speeds.
+ * - Reads originating from user space should have higher priority than reads
+ * originating from pkt_gather_data, because some process is usually waiting
+ * on reads of the first kind.
+ */
+static void pkt_iosched_process_queue(struct pktcdvd_device *pd)
+{
+ request_queue_t *q;
+
+ if (atomic_read(&pd->iosched.attention) == 0)
+ return;
+ atomic_set(&pd->iosched.attention, 0);
+
+ q = bdev_get_queue(pd->bdev);
+
+ for (;;) {
+ struct bio *bio;
+ int reads_queued, writes_queued, high_prio_read;
+
+ spin_lock(&pd->iosched.lock);
+ reads_queued = (pd->iosched.read_queue != NULL);
+ writes_queued = (pd->iosched.write_queue != NULL);
+ if (!reads_queued)
+ pd->iosched.high_prio_read = 0;
+ high_prio_read = pd->iosched.high_prio_read;
+ spin_unlock(&pd->iosched.lock);
+
+ if (!reads_queued && !writes_queued)
+ break;
+
+ if (pd->iosched.writing) {
+ if (high_prio_read || (!writes_queued && reads_queued)) {
+ if (atomic_read(&pd->cdrw.pending_bios) > 0) {
+ VPRINTK("pktcdvd: write, waiting\n");
+ break;
+ }
+ pkt_flush_cache(pd);
+ pd->iosched.writing = 0;
+ }
+ } else {
+ if (!reads_queued && writes_queued) {
+ if (atomic_read(&pd->cdrw.pending_bios) > 0) {
+ VPRINTK("pktcdvd: read, waiting\n");
+ break;
+ }
+ pd->iosched.writing = 1;
+ }
+ }
+
+ spin_lock(&pd->iosched.lock);
+ if (pd->iosched.writing) {
+ bio = pkt_get_list_first(&pd->iosched.write_queue,
+ &pd->iosched.write_queue_tail);
+ } else {
+ bio = pkt_get_list_first(&pd->iosched.read_queue,
+ &pd->iosched.read_queue_tail);
+ }
+ spin_unlock(&pd->iosched.lock);
+
+ if (!bio)
+ continue;
+
+ if (bio_data_dir(bio) == READ)
+ pd->iosched.successive_reads += bio->bi_size >> 10;
+ else
+ pd->iosched.successive_reads = 0;
+ if (pd->iosched.successive_reads >= HI_SPEED_SWITCH) {
+ if (pd->read_speed == pd->write_speed) {
+ pd->read_speed = MAX_SPEED;
+ pkt_set_speed(pd, pd->write_speed, pd->read_speed);
+ }
+ } else {
+ if (pd->read_speed != pd->write_speed) {
+ pd->read_speed = pd->write_speed;
+ pkt_set_speed(pd, pd->write_speed, pd->read_speed);
+ }
+ }
+
+ atomic_inc(&pd->cdrw.pending_bios);
+ generic_make_request(bio);
+ }
+}
+
+/*
+ * Special care is needed if the underlying block device has a small
+ * max_phys_segments value.
+ */
+static int pkt_set_segment_merging(struct pktcdvd_device *pd, request_queue_t *q)
+{
+ if ((pd->settings.size << 9) / CD_FRAMESIZE <= q->max_phys_segments) {
+ /*
+ * The cdrom device can handle one segment/frame
+ */
+ clear_bit(PACKET_MERGE_SEGS, &pd->flags);
+ return 0;
+ } else if ((pd->settings.size << 9) / PAGE_SIZE <= q->max_phys_segments) {
+ /*
+ * We can handle this case at the expense of some extra memory
+ * copies during write operations
+ */
+ set_bit(PACKET_MERGE_SEGS, &pd->flags);
+ return 0;
+ } else {
+ printk("pktcdvd: cdrom max_phys_segments too small\n");
+ return -EIO;
+ }
+}
+
+/*
+ * Copy CD_FRAMESIZE bytes from src_bio into a destination page
+ */
+static void pkt_copy_bio_data(struct bio *src_bio, int seg, int offs,
+ struct page *dst_page, int dst_offs)
+{
+ unsigned int copy_size = CD_FRAMESIZE;
+
+ while (copy_size > 0) {
+ struct bio_vec *src_bvl = bio_iovec_idx(src_bio, seg);
+ void *vfrom = kmap_atomic(src_bvl->bv_page, KM_USER0) +
+ src_bvl->bv_offset + offs;
+ void *vto = page_address(dst_page) + dst_offs;
+ int len = min_t(int, copy_size, src_bvl->bv_len - offs);
+
+ BUG_ON(len < 0);
+ memcpy(vto, vfrom, len);
+ kunmap_atomic(src_bvl->bv_page, KM_USER0);
+
+ seg++;
+ offs = 0;
+ dst_offs += len;
+ copy_size -= len;
+ }
+}
+
+/*
+ * Copy all data for this packet to pkt->pages[], so that
+ * a) The number of required segments for the write bio is minimized, which
+ * is necessary for some scsi controllers.
+ * b) The data can be used as cache to avoid read requests if we receive a
+ * new write request for the same zone.
+ */
+static void pkt_make_local_copy(struct packet_data *pkt, struct page **pages, int *offsets)
+{
+ int f, p, offs;
+
+ /* Copy all data to pkt->pages[] */
+ p = 0;
+ offs = 0;
+ for (f = 0; f < pkt->frames; f++) {
+ if (pages[f] != pkt->pages[p]) {
+ void *vfrom = kmap_atomic(pages[f], KM_USER0) + offsets[f];
+ void *vto = page_address(pkt->pages[p]) + offs;
+ memcpy(vto, vfrom, CD_FRAMESIZE);
+ kunmap_atomic(pages[f], KM_USER0);
+ pages[f] = pkt->pages[p];
+ offsets[f] = offs;
+ } else {
+ BUG_ON(offsets[f] != offs);
+ }
+ offs += CD_FRAMESIZE;
+ if (offs >= PAGE_SIZE) {
+ BUG_ON(offs > PAGE_SIZE);
+ offs = 0;
+ p++;
+ }
+ }
+}
+
+static int pkt_end_io_read(struct bio *bio, unsigned int bytes_done, int err)
+{
+ struct packet_data *pkt = bio->bi_private;
+ struct pktcdvd_device *pd = pkt->pd;
+ BUG_ON(!pd);
+
+ if (bio->bi_size)
+ return 1;
+
+ VPRINTK("pkt_end_io_read: bio=%p sec0=%llx sec=%llx err=%d\n", bio,
+ (unsigned long long)pkt->sector, (unsigned long long)bio->bi_sector, err);
+
+ if (err)
+ atomic_inc(&pkt->io_errors);
+ if (atomic_dec_and_test(&pkt->io_wait)) {
+ atomic_inc(&pkt->run_sm);
+ wake_up(&pd->wqueue);
+ }
+ pkt_bio_finished(pd);
+
+ return 0;
+}
+
+static int pkt_end_io_packet_write(struct bio *bio, unsigned int bytes_done, int err)
+{
+ struct packet_data *pkt = bio->bi_private;
+ struct pktcdvd_device *pd = pkt->pd;
+ BUG_ON(!pd);
+
+ if (bio->bi_size)
+ return 1;
+
+ VPRINTK("pkt_end_io_packet_write: id=%d, err=%d\n", pkt->id, err);
+
+ pd->stats.pkt_ended++;
+
+ pkt_bio_finished(pd);
+ atomic_dec(&pkt->io_wait);
+ atomic_inc(&pkt->run_sm);
+ wake_up(&pd->wqueue);
+ return 0;
+}
+
+/*
+ * Schedule reads for the holes in a packet
+ */
+static void pkt_gather_data(struct pktcdvd_device *pd, struct packet_data *pkt)
+{
+ int frames_read = 0;
+ struct bio *bio;
+ int f;
+ char written[PACKET_MAX_SIZE];
+
+ BUG_ON(!pkt->orig_bios);
+
+ atomic_set(&pkt->io_wait, 0);
+ atomic_set(&pkt->io_errors, 0);
+
+ if (pkt->cache_valid) {
+ VPRINTK("pkt_gather_data: zone %llx cached\n",
+ (unsigned long long)pkt->sector);
+ goto out_account;
+ }
+
+ /*
+ * Figure out which frames we need to read before we can write.
+ */
+ memset(written, 0, sizeof(written));
+ spin_lock(&pkt->lock);
+ for (bio = pkt->orig_bios; bio; bio = bio->bi_next) {
+ int first_frame = (bio->bi_sector - pkt->sector) / (CD_FRAMESIZE >> 9);
+ int num_frames = bio->bi_size / CD_FRAMESIZE;
+ BUG_ON(first_frame < 0);
+ BUG_ON(first_frame + num_frames > pkt->frames);
+ for (f = first_frame; f < first_frame + num_frames; f++)
+ written[f] = 1;
+ }
+ spin_unlock(&pkt->lock);
+
+ /*
+ * Schedule reads for missing parts of the packet.
+ */
+ for (f = 0; f < pkt->frames; f++) {
+ int p, offset;
+ if (written[f])
+ continue;
+ bio = pkt->r_bios[f];
+ bio_init(bio);
+ bio->bi_max_vecs = 1;
+ bio->bi_sector = pkt->sector + f * (CD_FRAMESIZE >> 9);
+ bio->bi_bdev = pd->bdev;
+ bio->bi_end_io = pkt_end_io_read;
+ bio->bi_private = pkt;
+
+ p = (f * CD_FRAMESIZE) / PAGE_SIZE;
+ offset = (f * CD_FRAMESIZE) % PAGE_SIZE;
+ VPRINTK("pkt_gather_data: Adding frame %d, page:%p offs:%d\n",
+ f, pkt->pages[p], offset);
+ if (!bio_add_page(bio, pkt->pages[p], CD_FRAMESIZE, offset))
+ BUG();
+
+ atomic_inc(&pkt->io_wait);
+ bio->bi_rw = READ;
+ pkt_queue_bio(pd, bio, 0);
+ frames_read++;
+ }
+
+out_account:
+ VPRINTK("pkt_gather_data: need %d frames for zone %llx\n",
+ frames_read, (unsigned long long)pkt->sector);
+ pd->stats.pkt_started++;
+ pd->stats.secs_rg += frames_read * (CD_FRAMESIZE >> 9);
+ pd->stats.secs_w += pd->settings.size;
+}
+
+/*
+ * Find a packet matching zone, or the least recently used packet if
+ * there is no match.
+ */
+static struct packet_data *pkt_get_packet_data(struct pktcdvd_device *pd, int zone)
+{
+ struct packet_data *pkt;
+
+ list_for_each_entry(pkt, &pd->cdrw.pkt_free_list, list) {
+ if (pkt->sector == zone || pkt->list.next == &pd->cdrw.pkt_free_list) {
+ list_del_init(&pkt->list);
+ if (pkt->sector != zone)
+ pkt->cache_valid = 0;
+ break;
+ }
+ }
+ return pkt;
+}
+
+static void pkt_put_packet_data(struct pktcdvd_device *pd, struct packet_data *pkt)
+{
+ if (pkt->cache_valid) {
+ list_add(&pkt->list, &pd->cdrw.pkt_free_list);
+ } else {
+ list_add_tail(&pkt->list, &pd->cdrw.pkt_free_list);
+ }
+}
+
+/*
+ * recover a failed write, query for relocation if possible
+ *
+ * returns 1 if recovery is possible, or 0 if not
+ *
+ */
+static int pkt_start_recovery(struct packet_data *pkt)
+{
+ /*
+ * FIXME. We need help from the file system to implement
+ * recovery handling.
+ */
+ return 0;
+#if 0
+ struct request *rq = pkt->rq;
+ struct pktcdvd_device *pd = rq->rq_disk->private_data;
+ struct block_device *pkt_bdev;
+ struct super_block *sb = NULL;
+ unsigned long old_block, new_block;
+ sector_t new_sector;
+
+ pkt_bdev = bdget(kdev_t_to_nr(pd->pkt_dev));
+ if (pkt_bdev) {
+ sb = get_super(pkt_bdev);
+ bdput(pkt_bdev);
+ }
+
+ if (!sb)
+ return 0;
+
+ if (!sb->s_op || !sb->s_op->relocate_blocks)
+ goto out;
+
+ old_block = pkt->sector / (CD_FRAMESIZE >> 9);
+ if (sb->s_op->relocate_blocks(sb, old_block, &new_block))
+ goto out;
+
+ new_sector = new_block * (CD_FRAMESIZE >> 9);
+ pkt->sector = new_sector;
+
+ pkt->bio->bi_sector = new_sector;
+ pkt->bio->bi_next = NULL;
+ pkt->bio->bi_flags = 1 << BIO_UPTODATE;
+ pkt->bio->bi_idx = 0;
+
+ BUG_ON(pkt->bio->bi_rw != (1 << BIO_RW));
+ BUG_ON(pkt->bio->bi_vcnt != pkt->frames);
+ BUG_ON(pkt->bio->bi_size != pkt->frames * CD_FRAMESIZE);
+ BUG_ON(pkt->bio->bi_end_io != pkt_end_io_packet_write);
+ BUG_ON(pkt->bio->bi_private != pkt);
+
+ drop_super(sb);
+ return 1;
+
+out:
+ drop_super(sb);
+ return 0;
+#endif
+}
+
+static inline void pkt_set_state(struct packet_data *pkt, enum packet_data_state state)
+{
+#if PACKET_DEBUG > 1
+ static const char *state_name[] = {
+ "IDLE", "WAITING", "READ_WAIT", "WRITE_WAIT", "RECOVERY", "FINISHED"
+ };
+ enum packet_data_state old_state = pkt->state;
+ VPRINTK("pkt %2d : s=%6llx %s -> %s\n", pkt->id, (unsigned long long)pkt->sector,
+ state_name[old_state], state_name[state]);
+#endif
+ pkt->state = state;
+}
+
+/*
+ * Scan the work queue to see if we can start a new packet.
+ * returns non-zero if any work was done.
+ */
+static int pkt_handle_queue(struct pktcdvd_device *pd)
+{
+ struct packet_data *pkt, *p;
+ struct bio *bio = NULL;
+ sector_t zone = 0; /* Suppress gcc warning */
+ struct pkt_rb_node *node, *first_node;
+ struct rb_node *n;
+
+ VPRINTK("handle_queue\n");
+
+ atomic_set(&pd->scan_queue, 0);
+
+ if (list_empty(&pd->cdrw.pkt_free_list)) {
+ VPRINTK("handle_queue: no pkt\n");
+ return 0;
+ }
+
+ /*
+ * Try to find a zone we are not already working on.
+ */
+ spin_lock(&pd->lock);
+ first_node = pkt_rbtree_find(pd, pd->current_sector);
+ if (!first_node) {
+ n = rb_first(&pd->bio_queue);
+ if (n)
+ first_node = rb_entry(n, struct pkt_rb_node, rb_node);
+ }
+ node = first_node;
+ while (node) {
+ bio = node->bio;
+ zone = ZONE(bio->bi_sector, pd);
+ list_for_each_entry(p, &pd->cdrw.pkt_active_list, list) {
+ if (p->sector == zone)
+ goto try_next_bio;
+ }
+ break;
+try_next_bio:
+ node = pkt_rbtree_next(node);
+ if (!node) {
+ n = rb_first(&pd->bio_queue);
+ if (n)
+ node = rb_entry(n, struct pkt_rb_node, rb_node);
+ }
+ if (node == first_node)
+ node = NULL;
+ }
+ spin_unlock(&pd->lock);
+ if (!bio) {
+ VPRINTK("handle_queue: no bio\n");
+ return 0;
+ }
+
+ pkt = pkt_get_packet_data(pd, zone);
+ BUG_ON(!pkt);
+
+ pd->current_sector = zone + pd->settings.size;
+ pkt->sector = zone;
+ pkt->frames = pd->settings.size >> 2;
+ BUG_ON(pkt->frames > PACKET_MAX_SIZE);
+ pkt->write_size = 0;
+
+ /*
+ * Scan work queue for bios in the same zone and link them
+ * to this packet.
+ */
+ spin_lock(&pd->lock);
+ VPRINTK("pkt_handle_queue: looking for zone %llx\n", (unsigned long long)zone);
+ while ((node = pkt_rbtree_find(pd, zone)) != NULL) {
+ bio = node->bio;
+ VPRINTK("pkt_handle_queue: found zone=%llx\n",
+ (unsigned long long)ZONE(bio->bi_sector, pd));
+ if (ZONE(bio->bi_sector, pd) != zone)
+ break;
+ pkt_rbtree_erase(pd, node);
+ spin_lock(&pkt->lock);
+ pkt_add_list_last(bio, &pkt->orig_bios, &pkt->orig_bios_tail);
+ pkt->write_size += bio->bi_size / CD_FRAMESIZE;
+ spin_unlock(&pkt->lock);
+ }
+ spin_unlock(&pd->lock);
+
+ pkt->sleep_time = max(PACKET_WAIT_TIME, 1);
+ pkt_set_state(pkt, PACKET_WAITING_STATE);
+ atomic_set(&pkt->run_sm, 1);
+
+ spin_lock(&pd->cdrw.active_list_lock);
+ list_add(&pkt->list, &pd->cdrw.pkt_active_list);
+ spin_unlock(&pd->cdrw.active_list_lock);
+
+ return 1;
+}
+
+/*
+ * Assemble a bio to write one packet and queue the bio for processing
+ * by the underlying block device.
+ */
+static void pkt_start_write(struct pktcdvd_device *pd, struct packet_data *pkt)
+{
+ struct bio *bio;
+ struct page *pages[PACKET_MAX_SIZE];
+ int offsets[PACKET_MAX_SIZE];
+ int f;
+ int frames_write;
+
+ for (f = 0; f < pkt->frames; f++) {
+ pages[f] = pkt->pages[(f * CD_FRAMESIZE) / PAGE_SIZE];
+ offsets[f] = (f * CD_FRAMESIZE) % PAGE_SIZE;
+ }
+
+ /*
+ * Fill-in pages[] and offsets[] with data from orig_bios.
+ */
+ frames_write = 0;
+ spin_lock(&pkt->lock);
+ for (bio = pkt->orig_bios; bio; bio = bio->bi_next) {
+ int segment = bio->bi_idx;
+ int src_offs = 0;
+ int first_frame = (bio->bi_sector - pkt->sector) / (CD_FRAMESIZE >> 9);
+ int num_frames = bio->bi_size / CD_FRAMESIZE;
+ BUG_ON(first_frame < 0);
+ BUG_ON(first_frame + num_frames > pkt->frames);
+ for (f = first_frame; f < first_frame + num_frames; f++) {
+ struct bio_vec *src_bvl = bio_iovec_idx(bio, segment);
+
+ while (src_offs >= src_bvl->bv_len) {
+ src_offs -= src_bvl->bv_len;
+ segment++;
+ BUG_ON(segment >= bio->bi_vcnt);
+ src_bvl = bio_iovec_idx(bio, segment);
+ }
+
+ if (src_bvl->bv_len - src_offs >= CD_FRAMESIZE) {
+ pages[f] = src_bvl->bv_page;
+ offsets[f] = src_bvl->bv_offset + src_offs;
+ } else {
+ pkt_copy_bio_data(bio, segment, src_offs,
+ pages[f], offsets[f]);
+ }
+ src_offs += CD_FRAMESIZE;
+ frames_write++;
+ }
+ }
+ pkt_set_state(pkt, PACKET_WRITE_WAIT_STATE);
+ spin_unlock(&pkt->lock);
+
+ VPRINTK("pkt_start_write: Writing %d frames for zone %llx\n",
+ frames_write, (unsigned long long)pkt->sector);
+ BUG_ON(frames_write != pkt->write_size);
+
+ if (test_bit(PACKET_MERGE_SEGS, &pd->flags) || (pkt->write_size < pkt->frames)) {
+ pkt_make_local_copy(pkt, pages, offsets);
+ pkt->cache_valid = 1;
+ } else {
+ pkt->cache_valid = 0;
+ }
+
+ /* Start the write request */
+ bio_init(pkt->w_bio);
+ pkt->w_bio->bi_max_vecs = PACKET_MAX_SIZE;
+ pkt->w_bio->bi_sector = pkt->sector;
+ pkt->w_bio->bi_bdev = pd->bdev;
+ pkt->w_bio->bi_end_io = pkt_end_io_packet_write;
+ pkt->w_bio->bi_private = pkt;
+ for (f = 0; f < pkt->frames; f++) {
+ if ((f + 1 < pkt->frames) && (pages[f + 1] == pages[f]) &&
+ (offsets[f + 1] = offsets[f] + CD_FRAMESIZE)) {
+ if (!bio_add_page(pkt->w_bio, pages[f], CD_FRAMESIZE * 2, offsets[f]))
+ BUG();
+ f++;
+ } else {
+ if (!bio_add_page(pkt->w_bio, pages[f], CD_FRAMESIZE, offsets[f]))
+ BUG();
+ }
+ }
+ VPRINTK("pktcdvd: vcnt=%d\n", pkt->w_bio->bi_vcnt);
+
+ atomic_set(&pkt->io_wait, 1);
+ pkt->w_bio->bi_rw = WRITE;
+ pkt_queue_bio(pd, pkt->w_bio, 0);
+}
+
+static void pkt_finish_packet(struct packet_data *pkt, int uptodate)
+{
+ struct bio *bio, *next;
+
+ if (!uptodate)
+ pkt->cache_valid = 0;
+
+ /* Finish all bios corresponding to this packet */
+ bio = pkt->orig_bios;
+ while (bio) {
+ next = bio->bi_next;
+ bio->bi_next = NULL;
+ bio_endio(bio, bio->bi_size, uptodate ? 0 : -EIO);
+ bio = next;
+ }
+ pkt->orig_bios = pkt->orig_bios_tail = NULL;
+}
+
+static void pkt_run_state_machine(struct pktcdvd_device *pd, struct packet_data *pkt)
+{
+ int uptodate;
+
+ VPRINTK("run_state_machine: pkt %d\n", pkt->id);
+
+ for (;;) {
+ switch (pkt->state) {
+ case PACKET_WAITING_STATE:
+ if ((pkt->write_size < pkt->frames) && (pkt->sleep_time > 0))
+ return;
+
+ pkt->sleep_time = 0;
+ pkt_gather_data(pd, pkt);
+ pkt_set_state(pkt, PACKET_READ_WAIT_STATE);
+ break;
+
+ case PACKET_READ_WAIT_STATE:
+ if (atomic_read(&pkt->io_wait) > 0)
+ return;
+
+ if (atomic_read(&pkt->io_errors) > 0) {
+ pkt_set_state(pkt, PACKET_RECOVERY_STATE);
+ } else {
+ pkt_start_write(pd, pkt);
+ }
+ break;
+
+ case PACKET_WRITE_WAIT_STATE:
+ if (atomic_read(&pkt->io_wait) > 0)
+ return;
+
+ if (test_bit(BIO_UPTODATE, &pkt->w_bio->bi_flags)) {
+ pkt_set_state(pkt, PACKET_FINISHED_STATE);
+ } else {
+ pkt_set_state(pkt, PACKET_RECOVERY_STATE);
+ }
+ break;
+
+ case PACKET_RECOVERY_STATE:
+ if (pkt_start_recovery(pkt)) {
+ pkt_start_write(pd, pkt);
+ } else {
+ VPRINTK("No recovery possible\n");
+ pkt_set_state(pkt, PACKET_FINISHED_STATE);
+ }
+ break;
+
+ case PACKET_FINISHED_STATE:
+ uptodate = test_bit(BIO_UPTODATE, &pkt->w_bio->bi_flags);
+ pkt_finish_packet(pkt, uptodate);
+ return;
+
+ default:
+ BUG();
+ break;
+ }
+ }
+}
+
+static void pkt_handle_packets(struct pktcdvd_device *pd)
+{
+ struct packet_data *pkt, *next;
+
+ VPRINTK("pkt_handle_packets\n");
+
+ /*
+ * Run state machine for active packets
+ */
+ list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
+ if (atomic_read(&pkt->run_sm) > 0) {
+ atomic_set(&pkt->run_sm, 0);
+ pkt_run_state_machine(pd, pkt);
+ }
+ }
+
+ /*
+ * Move no longer active packets to the free list
+ */
+ spin_lock(&pd->cdrw.active_list_lock);
+ list_for_each_entry_safe(pkt, next, &pd->cdrw.pkt_active_list, list) {
+ if (pkt->state == PACKET_FINISHED_STATE) {
+ list_del(&pkt->list);
+ pkt_put_packet_data(pd, pkt);
+ pkt_set_state(pkt, PACKET_IDLE_STATE);
+ atomic_set(&pd->scan_queue, 1);
+ }
+ }
+ spin_unlock(&pd->cdrw.active_list_lock);
+}
+
+static void pkt_count_states(struct pktcdvd_device *pd, int *states)
+{
+ struct packet_data *pkt;
+ int i;
+
+ for (i = 0; i <= PACKET_NUM_STATES; i++)
+ states[i] = 0;
+
+ spin_lock(&pd->cdrw.active_list_lock);
+ list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
+ states[pkt->state]++;
+ }
+ spin_unlock(&pd->cdrw.active_list_lock);
+}
+
+/*
+ * kcdrwd is woken up when writes have been queued for one of our
+ * registered devices
+ */
+static int kcdrwd(void *foobar)
+{
+ struct pktcdvd_device *pd = foobar;
+ struct packet_data *pkt;
+ long min_sleep_time, residue;
+
+ set_user_nice(current, -20);
+
+ for (;;) {
+ DECLARE_WAITQUEUE(wait, current);
+
+ /*
+ * Wait until there is something to do
+ */
+ add_wait_queue(&pd->wqueue, &wait);
+ for (;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ /* Check if we need to run pkt_handle_queue */
+ if (atomic_read(&pd->scan_queue) > 0)
+ goto work_to_do;
+
+ /* Check if we need to run the state machine for some packet */
+ list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
+ if (atomic_read(&pkt->run_sm) > 0)
+ goto work_to_do;
+ }
+
+ /* Check if we need to process the iosched queues */
+ if (atomic_read(&pd->iosched.attention) != 0)
+ goto work_to_do;
+
+ /* Otherwise, go to sleep */
+ if (PACKET_DEBUG > 1) {
+ int states[PACKET_NUM_STATES];
+ pkt_count_states(pd, states);
+ VPRINTK("kcdrwd: i:%d ow:%d rw:%d ww:%d rec:%d fin:%d\n",
+ states[0], states[1], states[2], states[3],
+ states[4], states[5]);
+ }
+
+ min_sleep_time = MAX_SCHEDULE_TIMEOUT;
+ list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
+ if (pkt->sleep_time && pkt->sleep_time < min_sleep_time)
+ min_sleep_time = pkt->sleep_time;
+ }
+
+ generic_unplug_device(bdev_get_queue(pd->bdev));
+
+ VPRINTK("kcdrwd: sleeping\n");
+ residue = schedule_timeout(min_sleep_time);
+ VPRINTK("kcdrwd: wake up\n");
+
+ /* make swsusp happy with our thread */
+ if (current->flags & PF_FREEZE)
+ refrigerator(PF_FREEZE);
+
+ list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
+ if (!pkt->sleep_time)
+ continue;
+ pkt->sleep_time -= min_sleep_time - residue;
+ if (pkt->sleep_time <= 0) {
+ pkt->sleep_time = 0;
+ atomic_inc(&pkt->run_sm);
+ }
+ }
+
+ if (signal_pending(current)) {
+ flush_signals(current);
+ }
+ if (kthread_should_stop())
+ break;
+ }
+work_to_do:
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&pd->wqueue, &wait);
+
+ if (kthread_should_stop())
+ break;
+
+ /*
+ * if pkt_handle_queue returns true, we can queue
+ * another request.
+ */
+ while (pkt_handle_queue(pd))
+ ;
+
+ /*
+ * Handle packet state machine
+ */
+ pkt_handle_packets(pd);
+
+ /*
+ * Handle iosched queues
+ */
+ pkt_iosched_process_queue(pd);
+ }
+
+ return 0;
+}
+
+static void pkt_print_settings(struct pktcdvd_device *pd)
+{
+ printk("pktcdvd: %s packets, ", pd->settings.fp ? "Fixed" : "Variable");
+ printk("%u blocks, ", pd->settings.size >> 2);
+ printk("Mode-%c disc\n", pd->settings.block_mode == 8 ? '1' : '2');
+}
+
+static int pkt_mode_sense(struct pktcdvd_device *pd, struct packet_command *cgc,
+ int page_code, int page_control)
+{
+ memset(cgc->cmd, 0, sizeof(cgc->cmd));
+
+ cgc->cmd[0] = GPCMD_MODE_SENSE_10;
+ cgc->cmd[2] = page_code | (page_control << 6);
+ cgc->cmd[7] = cgc->buflen >> 8;
+ cgc->cmd[8] = cgc->buflen & 0xff;
+ cgc->data_direction = CGC_DATA_READ;
+ return pkt_generic_packet(pd, cgc);
+}
+
+static int pkt_mode_select(struct pktcdvd_device *pd, struct packet_command *cgc)
+{
+ memset(cgc->cmd, 0, sizeof(cgc->cmd));
+ memset(cgc->buffer, 0, 2);
+ cgc->cmd[0] = GPCMD_MODE_SELECT_10;
+ cgc->cmd[1] = 0x10; /* PF */
+ cgc->cmd[7] = cgc->buflen >> 8;
+ cgc->cmd[8] = cgc->buflen & 0xff;
+ cgc->data_direction = CGC_DATA_WRITE;
+ return pkt_generic_packet(pd, cgc);
+}
+
+static int pkt_get_disc_info(struct pktcdvd_device *pd, disc_information *di)
+{
+ struct packet_command cgc;
+ int ret;
+
+ /* set up command and get the disc info */
+ init_cdrom_command(&cgc, di, sizeof(*di), CGC_DATA_READ);
+ cgc.cmd[0] = GPCMD_READ_DISC_INFO;
+ cgc.cmd[8] = cgc.buflen = 2;
+ cgc.quiet = 1;
+
+ if ((ret = pkt_generic_packet(pd, &cgc)))
+ return ret;
+
+ /* not all drives have the same disc_info length, so requeue
+ * packet with the length the drive tells us it can supply
+ */
+ cgc.buflen = be16_to_cpu(di->disc_information_length) +
+ sizeof(di->disc_information_length);
+
+ if (cgc.buflen > sizeof(disc_information))
+ cgc.buflen = sizeof(disc_information);
+
+ cgc.cmd[8] = cgc.buflen;
+ return pkt_generic_packet(pd, &cgc);
+}
+
+static int pkt_get_track_info(struct pktcdvd_device *pd, __u16 track, __u8 type, track_information *ti)
+{
+ struct packet_command cgc;
+ int ret;
+
+ init_cdrom_command(&cgc, ti, 8, CGC_DATA_READ);
+ cgc.cmd[0] = GPCMD_READ_TRACK_RZONE_INFO;
+ cgc.cmd[1] = type & 3;
+ cgc.cmd[4] = (track & 0xff00) >> 8;
+ cgc.cmd[5] = track & 0xff;
+ cgc.cmd[8] = 8;
+ cgc.quiet = 1;
+
+ if ((ret = pkt_generic_packet(pd, &cgc)))
+ return ret;
+
+ cgc.buflen = be16_to_cpu(ti->track_information_length) +
+ sizeof(ti->track_information_length);
+
+ if (cgc.buflen > sizeof(track_information))
+ cgc.buflen = sizeof(track_information);
+
+ cgc.cmd[8] = cgc.buflen;
+ return pkt_generic_packet(pd, &cgc);
+}
+
+static int pkt_get_last_written(struct pktcdvd_device *pd, long *last_written)
+{
+ disc_information di;
+ track_information ti;
+ __u32 last_track;
+ int ret = -1;
+
+ if ((ret = pkt_get_disc_info(pd, &di)))
+ return ret;
+
+ last_track = (di.last_track_msb << 8) | di.last_track_lsb;
+ if ((ret = pkt_get_track_info(pd, last_track, 1, &ti)))
+ return ret;
+
+ /* if this track is blank, try the previous. */
+ if (ti.blank) {
+ last_track--;
+ if ((ret = pkt_get_track_info(pd, last_track, 1, &ti)))
+ return ret;
+ }
+
+ /* if last recorded field is valid, return it. */
+ if (ti.lra_v) {
+ *last_written = be32_to_cpu(ti.last_rec_address);
+ } else {
+ /* make it up instead */
+ *last_written = be32_to_cpu(ti.track_start) +
+ be32_to_cpu(ti.track_size);
+ if (ti.free_blocks)
+ *last_written -= (be32_to_cpu(ti.free_blocks) + 7);
+ }
+ return 0;
+}
+
+/*
+ * write mode select package based on pd->settings
+ */
+static int pkt_set_write_settings(struct pktcdvd_device *pd)
+{
+ struct packet_command cgc;
+ struct request_sense sense;
+ write_param_page *wp;
+ char buffer[128];
+ int ret, size;
+
+ /* doesn't apply to DVD+RW */
+ if (pd->mmc3_profile == 0x1a)
+ return 0;
+
+ memset(buffer, 0, sizeof(buffer));
+ init_cdrom_command(&cgc, buffer, sizeof(*wp), CGC_DATA_READ);
+ cgc.sense = &sense;
+ if ((ret = pkt_mode_sense(pd, &cgc, GPMODE_WRITE_PARMS_PAGE, 0))) {
+ pkt_dump_sense(&cgc);
+ return ret;
+ }
+
+ size = 2 + ((buffer[0] << 8) | (buffer[1] & 0xff));
+ pd->mode_offset = (buffer[6] << 8) | (buffer[7] & 0xff);
+ if (size > sizeof(buffer))
+ size = sizeof(buffer);
+
+ /*
+ * now get it all
+ */
+ init_cdrom_command(&cgc, buffer, size, CGC_DATA_READ);
+ cgc.sense = &sense;
+ if ((ret = pkt_mode_sense(pd, &cgc, GPMODE_WRITE_PARMS_PAGE, 0))) {
+ pkt_dump_sense(&cgc);
+ return ret;
+ }
+
+ /*
+ * write page is offset header + block descriptor length
+ */
+ wp = (write_param_page *) &buffer[sizeof(struct mode_page_header) + pd->mode_offset];
+
+ wp->fp = pd->settings.fp;
+ wp->track_mode = pd->settings.track_mode;
+ wp->write_type = pd->settings.write_type;
+ wp->data_block_type = pd->settings.block_mode;
+
+ wp->multi_session = 0;
+
+#ifdef PACKET_USE_LS
+ wp->link_size = 7;
+ wp->ls_v = 1;
+#endif
+
+ if (wp->data_block_type == PACKET_BLOCK_MODE1) {
+ wp->session_format = 0;
+ wp->subhdr2 = 0x20;
+ } else if (wp->data_block_type == PACKET_BLOCK_MODE2) {
+ wp->session_format = 0x20;
+ wp->subhdr2 = 8;
+#if 0
+ wp->mcn[0] = 0x80;
+ memcpy(&wp->mcn[1], PACKET_MCN, sizeof(wp->mcn) - 1);
+#endif
+ } else {
+ /*
+ * paranoia
+ */
+ printk("pktcdvd: write mode wrong %d\n", wp->data_block_type);
+ return 1;
+ }
+ wp->packet_size = cpu_to_be32(pd->settings.size >> 2);
+
+ cgc.buflen = cgc.cmd[8] = size;
+ if ((ret = pkt_mode_select(pd, &cgc))) {
+ pkt_dump_sense(&cgc);
+ return ret;
+ }
+
+ pkt_print_settings(pd);
+ return 0;
+}
+
+/*
+ * 0 -- we can write to this track, 1 -- we can't
+ */
+static int pkt_good_track(track_information *ti)
+{
+ /*
+ * only good for CD-RW at the moment, not DVD-RW
+ */
+
+ /*
+ * FIXME: only for FP
+ */
+ if (ti->fp == 0)
+ return 0;
+
+ /*
+ * "good" settings as per Mt Fuji.
+ */
+ if (ti->rt == 0 && ti->blank == 0 && ti->packet == 1)
+ return 0;
+
+ if (ti->rt == 0 && ti->blank == 1 && ti->packet == 1)
+ return 0;
+
+ if (ti->rt == 1 && ti->blank == 0 && ti->packet == 1)
+ return 0;
+
+ printk("pktcdvd: bad state %d-%d-%d\n", ti->rt, ti->blank, ti->packet);
+ return 1;
+}
+
+/*
+ * 0 -- we can write to this disc, 1 -- we can't
+ */
+static int pkt_good_disc(struct pktcdvd_device *pd, disc_information *di)
+{
+ switch (pd->mmc3_profile) {
+ case 0x0a: /* CD-RW */
+ case 0xffff: /* MMC3 not supported */
+ break;
+ case 0x1a: /* DVD+RW */
+ case 0x13: /* DVD-RW */
+ return 0;
+ default:
+ printk("pktcdvd: Wrong disc profile (%x)\n", pd->mmc3_profile);
+ return 1;
+ }
+
+ /*
+ * for disc type 0xff we should probably reserve a new track.
+ * but i'm not sure, should we leave this to user apps? probably.
+ */
+ if (di->disc_type == 0xff) {
+ printk("pktcdvd: Unknown disc. No track?\n");
+ return 1;
+ }
+
+ if (di->disc_type != 0x20 && di->disc_type != 0) {
+ printk("pktcdvd: Wrong disc type (%x)\n", di->disc_type);
+ return 1;
+ }
+
+ if (di->erasable == 0) {
+ printk("pktcdvd: Disc not erasable\n");
+ return 1;
+ }
+
+ if (di->border_status == PACKET_SESSION_RESERVED) {
+ printk("pktcdvd: Can't write to last track (reserved)\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+static int pkt_probe_settings(struct pktcdvd_device *pd)
+{
+ struct packet_command cgc;
+ unsigned char buf[12];
+ disc_information di;
+ track_information ti;
+ int ret, track;
+
+ init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
+ cgc.cmd[0] = GPCMD_GET_CONFIGURATION;
+ cgc.cmd[8] = 8;
+ ret = pkt_generic_packet(pd, &cgc);
+ pd->mmc3_profile = ret ? 0xffff : buf[6] << 8 | buf[7];
+
+ memset(&di, 0, sizeof(disc_information));
+ memset(&ti, 0, sizeof(track_information));
+
+ if ((ret = pkt_get_disc_info(pd, &di))) {
+ printk("failed get_disc\n");
+ return ret;
+ }
+
+ if (pkt_good_disc(pd, &di))
+ return -ENXIO;
+
+ switch (pd->mmc3_profile) {
+ case 0x1a: /* DVD+RW */
+ printk("pktcdvd: inserted media is DVD+RW\n");
+ break;
+ case 0x13: /* DVD-RW */
+ printk("pktcdvd: inserted media is DVD-RW\n");
+ break;
+ default:
+ printk("pktcdvd: inserted media is CD-R%s\n", di.erasable ? "W" : "");
+ break;
+ }
+ pd->type = di.erasable ? PACKET_CDRW : PACKET_CDR;
+
+ track = 1; /* (di.last_track_msb << 8) | di.last_track_lsb; */
+ if ((ret = pkt_get_track_info(pd, track, 1, &ti))) {
+ printk("pktcdvd: failed get_track\n");
+ return ret;
+ }
+
+ if (pkt_good_track(&ti)) {
+ printk("pktcdvd: can't write to this track\n");
+ return -ENXIO;
+ }
+
+ /*
+ * we keep packet size in 512 byte units, makes it easier to
+ * deal with request calculations.
+ */
+ pd->settings.size = be32_to_cpu(ti.fixed_packet_size) << 2;
+ if (pd->settings.size == 0) {
+ printk("pktcdvd: detected zero packet size!\n");
+ pd->settings.size = 128;
+ }
+ pd->settings.fp = ti.fp;
+ pd->offset = (be32_to_cpu(ti.track_start) << 2) & (pd->settings.size - 1);
+
+ if (ti.nwa_v) {
+ pd->nwa = be32_to_cpu(ti.next_writable);
+ set_bit(PACKET_NWA_VALID, &pd->flags);
+ }
+
+ /*
+ * in theory we could use lra on -RW media as well and just zero
+ * blocks that haven't been written yet, but in practice that
+ * is just a no-go. we'll use that for -R, naturally.
+ */
+ if (ti.lra_v) {
+ pd->lra = be32_to_cpu(ti.last_rec_address);
+ set_bit(PACKET_LRA_VALID, &pd->flags);
+ } else {
+ pd->lra = 0xffffffff;
+ set_bit(PACKET_LRA_VALID, &pd->flags);
+ }
+
+ /*
+ * fine for now
+ */
+ pd->settings.link_loss = 7;
+ pd->settings.write_type = 0; /* packet */
+ pd->settings.track_mode = ti.track_mode;
+
+ /*
+ * mode1 or mode2 disc
+ */
+ switch (ti.data_mode) {
+ case PACKET_MODE1:
+ pd->settings.block_mode = PACKET_BLOCK_MODE1;
+ break;
+ case PACKET_MODE2:
+ pd->settings.block_mode = PACKET_BLOCK_MODE2;
+ break;
+ default:
+ printk("pktcdvd: unknown data mode\n");
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * enable/disable write caching on drive
+ */
+static int pkt_write_caching(struct pktcdvd_device *pd, int set)
+{
+ struct packet_command cgc;
+ struct request_sense sense;
+ unsigned char buf[64];
+ int ret;
+
+ memset(buf, 0, sizeof(buf));
+ init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
+ cgc.sense = &sense;
+ cgc.buflen = pd->mode_offset + 12;
+
+ /*
+ * caching mode page might not be there, so quiet this command
+ */
+ cgc.quiet = 1;
+
+ if ((ret = pkt_mode_sense(pd, &cgc, GPMODE_WCACHING_PAGE, 0)))
+ return ret;
+
+ buf[pd->mode_offset + 10] |= (!!set << 2);
+
+ cgc.buflen = cgc.cmd[8] = 2 + ((buf[0] << 8) | (buf[1] & 0xff));
+ ret = pkt_mode_select(pd, &cgc);
+ if (ret) {
+ printk("pktcdvd: write caching control failed\n");
+ pkt_dump_sense(&cgc);
+ } else if (!ret && set)
+ printk("pktcdvd: enabled write caching on %s\n", pd->name);
+ return ret;
+}
+
+static int pkt_lock_door(struct pktcdvd_device *pd, int lockflag)
+{
+ struct packet_command cgc;
+
+ init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+ cgc.cmd[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL;
+ cgc.cmd[4] = lockflag ? 1 : 0;
+ return pkt_generic_packet(pd, &cgc);
+}
+
+/*
+ * Returns drive maximum write speed
+ */
+static int pkt_get_max_speed(struct pktcdvd_device *pd, unsigned *write_speed)
+{
+ struct packet_command cgc;
+ struct request_sense sense;
+ unsigned char buf[256+18];
+ unsigned char *cap_buf;
+ int ret, offset;
+
+ memset(buf, 0, sizeof(buf));
+ cap_buf = &buf[sizeof(struct mode_page_header) + pd->mode_offset];
+ init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_UNKNOWN);
+ cgc.sense = &sense;
+
+ ret = pkt_mode_sense(pd, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
+ if (ret) {
+ cgc.buflen = pd->mode_offset + cap_buf[1] + 2 +
+ sizeof(struct mode_page_header);
+ ret = pkt_mode_sense(pd, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
+ if (ret) {
+ pkt_dump_sense(&cgc);
+ return ret;
+ }
+ }
+
+ offset = 20; /* Obsoleted field, used by older drives */
+ if (cap_buf[1] >= 28)
+ offset = 28; /* Current write speed selected */
+ if (cap_buf[1] >= 30) {
+ /* If the drive reports at least one "Logical Unit Write
+ * Speed Performance Descriptor Block", use the information
+ * in the first block. (contains the highest speed)
+ */
+ int num_spdb = (cap_buf[30] << 8) + cap_buf[31];
+ if (num_spdb > 0)
+ offset = 34;
+ }
+
+ *write_speed = (cap_buf[offset] << 8) | cap_buf[offset + 1];
+ return 0;
+}
+
+/* These tables from cdrecord - I don't have orange book */
+/* standard speed CD-RW (1-4x) */
+static char clv_to_speed[16] = {
+ /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */
+ 0, 2, 4, 6, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+/* high speed CD-RW (-10x) */
+static char hs_clv_to_speed[16] = {
+ /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */
+ 0, 2, 4, 6, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+/* ultra high speed CD-RW */
+static char us_clv_to_speed[16] = {
+ /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */
+ 0, 2, 4, 8, 0, 0,16, 0,24,32,40,48, 0, 0, 0, 0
+};
+
+/*
+ * reads the maximum media speed from ATIP
+ */
+static int pkt_media_speed(struct pktcdvd_device *pd, unsigned *speed)
+{
+ struct packet_command cgc;
+ struct request_sense sense;
+ unsigned char buf[64];
+ unsigned int size, st, sp;
+ int ret;
+
+ init_cdrom_command(&cgc, buf, 2, CGC_DATA_READ);
+ cgc.sense = &sense;
+ cgc.cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
+ cgc.cmd[1] = 2;
+ cgc.cmd[2] = 4; /* READ ATIP */
+ cgc.cmd[8] = 2;
+ ret = pkt_generic_packet(pd, &cgc);
+ if (ret) {
+ pkt_dump_sense(&cgc);
+ return ret;
+ }
+ size = ((unsigned int) buf[0]<<8) + buf[1] + 2;
+ if (size > sizeof(buf))
+ size = sizeof(buf);
+
+ init_cdrom_command(&cgc, buf, size, CGC_DATA_READ);
+ cgc.sense = &sense;
+ cgc.cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
+ cgc.cmd[1] = 2;
+ cgc.cmd[2] = 4;
+ cgc.cmd[8] = size;
+ ret = pkt_generic_packet(pd, &cgc);
+ if (ret) {
+ pkt_dump_sense(&cgc);
+ return ret;
+ }
+
+ if (!buf[6] & 0x40) {
+ printk("pktcdvd: Disc type is not CD-RW\n");
+ return 1;
+ }
+ if (!buf[6] & 0x4) {
+ printk("pktcdvd: A1 values on media are not valid, maybe not CDRW?\n");
+ return 1;
+ }
+
+ st = (buf[6] >> 3) & 0x7; /* disc sub-type */
+
+ sp = buf[16] & 0xf; /* max speed from ATIP A1 field */
+
+ /* Info from cdrecord */
+ switch (st) {
+ case 0: /* standard speed */
+ *speed = clv_to_speed[sp];
+ break;
+ case 1: /* high speed */
+ *speed = hs_clv_to_speed[sp];
+ break;
+ case 2: /* ultra high speed */
+ *speed = us_clv_to_speed[sp];
+ break;
+ default:
+ printk("pktcdvd: Unknown disc sub-type %d\n",st);
+ return 1;
+ }
+ if (*speed) {
+ printk("pktcdvd: Max. media speed: %d\n",*speed);
+ return 0;
+ } else {
+ printk("pktcdvd: Unknown speed %d for sub-type %d\n",sp,st);
+ return 1;
+ }
+}
+
+static int pkt_perform_opc(struct pktcdvd_device *pd)
+{
+ struct packet_command cgc;
+ struct request_sense sense;
+ int ret;
+
+ VPRINTK("pktcdvd: Performing OPC\n");
+
+ init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+ cgc.sense = &sense;
+ cgc.timeout = 60*HZ;
+ cgc.cmd[0] = GPCMD_SEND_OPC;
+ cgc.cmd[1] = 1;
+ if ((ret = pkt_generic_packet(pd, &cgc)))
+ pkt_dump_sense(&cgc);
+ return ret;
+}
+
+static int pkt_open_write(struct pktcdvd_device *pd)
+{
+ int ret;
+ unsigned int write_speed, media_write_speed, read_speed;
+
+ if ((ret = pkt_probe_settings(pd))) {
+ DPRINTK("pktcdvd: %s failed probe\n", pd->name);
+ return -EIO;
+ }
+
+ if ((ret = pkt_set_write_settings(pd))) {
+ DPRINTK("pktcdvd: %s failed saving write settings\n", pd->name);
+ return -EIO;
+ }
+
+ pkt_write_caching(pd, USE_WCACHING);
+
+ if ((ret = pkt_get_max_speed(pd, &write_speed)))
+ write_speed = 16 * 177;
+ switch (pd->mmc3_profile) {
+ case 0x13: /* DVD-RW */
+ case 0x1a: /* DVD+RW */
+ DPRINTK("pktcdvd: write speed %ukB/s\n", write_speed);
+ break;
+ default:
+ if ((ret = pkt_media_speed(pd, &media_write_speed)))
+ media_write_speed = 16;
+ write_speed = min(write_speed, media_write_speed * 177);
+ DPRINTK("pktcdvd: write speed %ux\n", write_speed / 176);
+ break;
+ }
+ read_speed = write_speed;
+
+ if ((ret = pkt_set_speed(pd, write_speed, read_speed))) {
+ DPRINTK("pktcdvd: %s couldn't set write speed\n", pd->name);
+ return -EIO;
+ }
+ pd->write_speed = write_speed;
+ pd->read_speed = read_speed;
+
+ if ((ret = pkt_perform_opc(pd))) {
+ DPRINTK("pktcdvd: %s Optimum Power Calibration failed\n", pd->name);
+ }
+
+ return 0;
+}
+
+/*
+ * called at open time.
+ */
+static int pkt_open_dev(struct pktcdvd_device *pd, int write)
+{
+ int ret;
+ long lba;
+ request_queue_t *q;
+
+ /*
+ * We need to re-open the cdrom device without O_NONBLOCK to be able
+ * to read/write from/to it. It is already opened in O_NONBLOCK mode
+ * so bdget() can't fail.
+ */
+ bdget(pd->bdev->bd_dev);
+ if ((ret = blkdev_get(pd->bdev, FMODE_READ, O_RDONLY)))
+ goto out;
+
+ if ((ret = pkt_get_last_written(pd, &lba))) {
+ printk("pktcdvd: pkt_get_last_written failed\n");
+ goto out_putdev;
+ }
+
+ set_capacity(pd->disk, lba << 2);
+ set_capacity(pd->bdev->bd_disk, lba << 2);
+ bd_set_size(pd->bdev, (loff_t)lba << 11);
+
+ q = bdev_get_queue(pd->bdev);
+ if (write) {
+ if ((ret = pkt_open_write(pd)))
+ goto out_putdev;
+ /*
+ * Some CDRW drives can not handle writes larger than one packet,
+ * even if the size is a multiple of the packet size.
+ */
+ spin_lock_irq(q->queue_lock);
+ blk_queue_max_sectors(q, pd->settings.size);
+ spin_unlock_irq(q->queue_lock);
+ set_bit(PACKET_WRITABLE, &pd->flags);
+ } else {
+ pkt_set_speed(pd, MAX_SPEED, MAX_SPEED);
+ clear_bit(PACKET_WRITABLE, &pd->flags);
+ }
+
+ if ((ret = pkt_set_segment_merging(pd, q)))
+ goto out_putdev;
+
+ if (write)
+ printk("pktcdvd: %lukB available on disc\n", lba << 1);
+
+ return 0;
+
+out_putdev:
+ blkdev_put(pd->bdev);
+out:
+ return ret;
+}
+
+/*
+ * called when the device is closed. makes sure that the device flushes
+ * the internal cache before we close.
+ */
+static void pkt_release_dev(struct pktcdvd_device *pd, int flush)
+{
+ if (flush && pkt_flush_cache(pd))
+ DPRINTK("pktcdvd: %s not flushing cache\n", pd->name);
+
+ pkt_lock_door(pd, 0);
+
+ pkt_set_speed(pd, MAX_SPEED, MAX_SPEED);
+ blkdev_put(pd->bdev);
+}
+
+static struct pktcdvd_device *pkt_find_dev_from_minor(int dev_minor)
+{
+ if (dev_minor >= MAX_WRITERS)
+ return NULL;
+ return pkt_devs[dev_minor];
+}
+
+static int pkt_open(struct inode *inode, struct file *file)
+{
+ struct pktcdvd_device *pd = NULL;
+ int ret;
+
+ VPRINTK("pktcdvd: entering open\n");
+
+ down(&ctl_mutex);
+ pd = pkt_find_dev_from_minor(iminor(inode));
+ if (!pd) {
+ ret = -ENODEV;
+ goto out;
+ }
+ BUG_ON(pd->refcnt < 0);
+
+ pd->refcnt++;
+ if (pd->refcnt == 1) {
+ if (pkt_open_dev(pd, file->f_mode & FMODE_WRITE)) {
+ ret = -EIO;
+ goto out_dec;
+ }
+ /*
+ * needed here as well, since ext2 (among others) may change
+ * the blocksize at mount time
+ */
+ set_blocksize(inode->i_bdev, CD_FRAMESIZE);
+ }
+
+ up(&ctl_mutex);
+ return 0;
+
+out_dec:
+ pd->refcnt--;
+out:
+ VPRINTK("pktcdvd: failed open (%d)\n", ret);
+ up(&ctl_mutex);
+ return ret;
+}
+
+static int pkt_close(struct inode *inode, struct file *file)
+{
+ struct pktcdvd_device *pd = inode->i_bdev->bd_disk->private_data;
+ int ret = 0;
+
+ down(&ctl_mutex);
+ pd->refcnt--;
+ BUG_ON(pd->refcnt < 0);
+ if (pd->refcnt == 0) {
+ int flush = test_bit(PACKET_WRITABLE, &pd->flags);
+ pkt_release_dev(pd, flush);
+ }
+ up(&ctl_mutex);
+ return ret;
+}
+
+
+static void *psd_pool_alloc(int gfp_mask, void *data)
+{
+ return kmalloc(sizeof(struct packet_stacked_data), gfp_mask);
+}
+
+static void psd_pool_free(void *ptr, void *data)
+{
+ kfree(ptr);
+}
+
+static int pkt_end_io_read_cloned(struct bio *bio, unsigned int bytes_done, int err)
+{
+ struct packet_stacked_data *psd = bio->bi_private;
+ struct pktcdvd_device *pd = psd->pd;
+
+ if (bio->bi_size)
+ return 1;
+
+ bio_put(bio);
+ bio_endio(psd->bio, psd->bio->bi_size, err);
+ mempool_free(psd, psd_pool);
+ pkt_bio_finished(pd);
+ return 0;
+}
+
+static int pkt_make_request(request_queue_t *q, struct bio *bio)
+{
+ struct pktcdvd_device *pd;
+ char b[BDEVNAME_SIZE];
+ sector_t zone;
+ struct packet_data *pkt;
+ int was_empty, blocked_bio;
+ struct pkt_rb_node *node;
+
+ pd = q->queuedata;
+ if (!pd) {
+ printk("pktcdvd: %s incorrect request queue\n", bdevname(bio->bi_bdev, b));
+ goto end_io;
+ }
+
+ /*
+ * Clone READ bios so we can have our own bi_end_io callback.
+ */
+ if (bio_data_dir(bio) == READ) {
+ struct bio *cloned_bio = bio_clone(bio, GFP_NOIO);
+ struct packet_stacked_data *psd = mempool_alloc(psd_pool, GFP_NOIO);
+
+ psd->pd = pd;
+ psd->bio = bio;
+ cloned_bio->bi_bdev = pd->bdev;
+ cloned_bio->bi_private = psd;
+ cloned_bio->bi_end_io = pkt_end_io_read_cloned;
+ pd->stats.secs_r += bio->bi_size >> 9;
+ pkt_queue_bio(pd, cloned_bio, 1);
+ return 0;
+ }
+
+ if (!test_bit(PACKET_WRITABLE, &pd->flags)) {
+ printk("pktcdvd: WRITE for ro device %s (%llu)\n",
+ pd->name, (unsigned long long)bio->bi_sector);
+ goto end_io;
+ }
+
+ if (!bio->bi_size || (bio->bi_size % CD_FRAMESIZE)) {
+ printk("pktcdvd: wrong bio size\n");
+ goto end_io;
+ }
+
+ blk_queue_bounce(q, &bio);
+
+ zone = ZONE(bio->bi_sector, pd);
+ VPRINTK("pkt_make_request: start = %6llx stop = %6llx\n",
+ (unsigned long long)bio->bi_sector,
+ (unsigned long long)(bio->bi_sector + bio_sectors(bio)));
+
+ /* Check if we have to split the bio */
+ {
+ struct bio_pair *bp;
+ sector_t last_zone;
+ int first_sectors;
+
+ last_zone = ZONE(bio->bi_sector + bio_sectors(bio) - 1, pd);
+ if (last_zone != zone) {
+ BUG_ON(last_zone != zone + pd->settings.size);
+ first_sectors = last_zone - bio->bi_sector;
+ bp = bio_split(bio, bio_split_pool, first_sectors);
+ BUG_ON(!bp);
+ pkt_make_request(q, &bp->bio1);
+ pkt_make_request(q, &bp->bio2);
+ bio_pair_release(bp);
+ return 0;
+ }
+ }
+
+ /*
+ * If we find a matching packet in state WAITING or READ_WAIT, we can
+ * just append this bio to that packet.
+ */
+ spin_lock(&pd->cdrw.active_list_lock);
+ blocked_bio = 0;
+ list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
+ if (pkt->sector == zone) {
+ spin_lock(&pkt->lock);
+ if ((pkt->state == PACKET_WAITING_STATE) ||
+ (pkt->state == PACKET_READ_WAIT_STATE)) {
+ pkt_add_list_last(bio, &pkt->orig_bios,
+ &pkt->orig_bios_tail);
+ pkt->write_size += bio->bi_size / CD_FRAMESIZE;
+ if ((pkt->write_size >= pkt->frames) &&
+ (pkt->state == PACKET_WAITING_STATE)) {
+ atomic_inc(&pkt->run_sm);
+ wake_up(&pd->wqueue);
+ }
+ spin_unlock(&pkt->lock);
+ spin_unlock(&pd->cdrw.active_list_lock);
+ return 0;
+ } else {
+ blocked_bio = 1;
+ }
+ spin_unlock(&pkt->lock);
+ }
+ }
+ spin_unlock(&pd->cdrw.active_list_lock);
+
+ /*
+ * No matching packet found. Store the bio in the work queue.
+ */
+ node = mempool_alloc(pd->rb_pool, GFP_NOIO);
+ BUG_ON(!node);
+ node->bio = bio;
+ spin_lock(&pd->lock);
+ BUG_ON(pd->bio_queue_size < 0);
+ was_empty = (pd->bio_queue_size == 0);
+ pkt_rbtree_insert(pd, node);
+ spin_unlock(&pd->lock);
+
+ /*
+ * Wake up the worker thread.
+ */
+ atomic_set(&pd->scan_queue, 1);
+ if (was_empty) {
+ /* This wake_up is required for correct operation */
+ wake_up(&pd->wqueue);
+ } else if (!list_empty(&pd->cdrw.pkt_free_list) && !blocked_bio) {
+ /*
+ * This wake up is not required for correct operation,
+ * but improves performance in some cases.
+ */
+ wake_up(&pd->wqueue);
+ }
+ return 0;
+end_io:
+ bio_io_error(bio, bio->bi_size);
+ return 0;
+}
+
+
+
+static int pkt_merge_bvec(request_queue_t *q, struct bio *bio, struct bio_vec *bvec)
+{
+ struct pktcdvd_device *pd = q->queuedata;
+ sector_t zone = ZONE(bio->bi_sector, pd);
+ int used = ((bio->bi_sector - zone) << 9) + bio->bi_size;
+ int remaining = (pd->settings.size << 9) - used;
+ int remaining2;
+
+ /*
+ * A bio <= PAGE_SIZE must be allowed. If it crosses a packet
+ * boundary, pkt_make_request() will split the bio.
+ */
+ remaining2 = PAGE_SIZE - bio->bi_size;
+ remaining = max(remaining, remaining2);
+
+ BUG_ON(remaining < 0);
+ return remaining;
+}
+
+static void pkt_init_queue(struct pktcdvd_device *pd)
+{
+ request_queue_t *q = pd->disk->queue;
+
+ blk_queue_make_request(q, pkt_make_request);
+ blk_queue_hardsect_size(q, CD_FRAMESIZE);
+ blk_queue_max_sectors(q, PACKET_MAX_SECTORS);
+ blk_queue_merge_bvec(q, pkt_merge_bvec);
+ q->queuedata = pd;
+}
+
+static int pkt_seq_show(struct seq_file *m, void *p)
+{
+ struct pktcdvd_device *pd = m->private;
+ char *msg;
+ char bdev_buf[BDEVNAME_SIZE];
+ int states[PACKET_NUM_STATES];
+
+ seq_printf(m, "Writer %s mapped to %s:\n", pd->name,
+ bdevname(pd->bdev, bdev_buf));
+
+ seq_printf(m, "\nSettings:\n");
+ seq_printf(m, "\tpacket size:\t\t%dkB\n", pd->settings.size / 2);
+
+ if (pd->settings.write_type == 0)
+ msg = "Packet";
+ else
+ msg = "Unknown";
+ seq_printf(m, "\twrite type:\t\t%s\n", msg);
+
+ seq_printf(m, "\tpacket type:\t\t%s\n", pd->settings.fp ? "Fixed" : "Variable");
+ seq_printf(m, "\tlink loss:\t\t%d\n", pd->settings.link_loss);
+
+ seq_printf(m, "\ttrack mode:\t\t%d\n", pd->settings.track_mode);
+
+ if (pd->settings.block_mode == PACKET_BLOCK_MODE1)
+ msg = "Mode 1";
+ else if (pd->settings.block_mode == PACKET_BLOCK_MODE2)
+ msg = "Mode 2";
+ else
+ msg = "Unknown";
+ seq_printf(m, "\tblock mode:\t\t%s\n", msg);
+
+ seq_printf(m, "\nStatistics:\n");
+ seq_printf(m, "\tpackets started:\t%lu\n", pd->stats.pkt_started);
+ seq_printf(m, "\tpackets ended:\t\t%lu\n", pd->stats.pkt_ended);
+ seq_printf(m, "\twritten:\t\t%lukB\n", pd->stats.secs_w >> 1);
+ seq_printf(m, "\tread gather:\t\t%lukB\n", pd->stats.secs_rg >> 1);
+ seq_printf(m, "\tread:\t\t\t%lukB\n", pd->stats.secs_r >> 1);
+
+ seq_printf(m, "\nMisc:\n");
+ seq_printf(m, "\treference count:\t%d\n", pd->refcnt);
+ seq_printf(m, "\tflags:\t\t\t0x%lx\n", pd->flags);
+ seq_printf(m, "\tread speed:\t\t%ukB/s\n", pd->read_speed);
+ seq_printf(m, "\twrite speed:\t\t%ukB/s\n", pd->write_speed);
+ seq_printf(m, "\tstart offset:\t\t%lu\n", pd->offset);
+ seq_printf(m, "\tmode page offset:\t%u\n", pd->mode_offset);
+
+ seq_printf(m, "\nQueue state:\n");
+ seq_printf(m, "\tbios queued:\t\t%d\n", pd->bio_queue_size);
+ seq_printf(m, "\tbios pending:\t\t%d\n", atomic_read(&pd->cdrw.pending_bios));
+ seq_printf(m, "\tcurrent sector:\t\t0x%llx\n", (unsigned long long)pd->current_sector);
+
+ pkt_count_states(pd, states);
+ seq_printf(m, "\tstate:\t\t\ti:%d ow:%d rw:%d ww:%d rec:%d fin:%d\n",
+ states[0], states[1], states[2], states[3], states[4], states[5]);
+
+ return 0;
+}
+
+static int pkt_seq_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pkt_seq_show, PDE(inode)->data);
+}
+
+static struct file_operations pkt_proc_fops = {
+ .open = pkt_seq_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release
+};
+
+static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev)
+{
+ int i;
+ int ret = 0;
+ char b[BDEVNAME_SIZE];
+ struct proc_dir_entry *proc;
+ struct block_device *bdev;
+
+ if (pd->pkt_dev == dev) {
+ printk("pktcdvd: Recursive setup not allowed\n");
+ return -EBUSY;
+ }
+ for (i = 0; i < MAX_WRITERS; i++) {
+ struct pktcdvd_device *pd2 = pkt_devs[i];
+ if (!pd2)
+ continue;
+ if (pd2->bdev->bd_dev == dev) {
+ printk("pktcdvd: %s already setup\n", bdevname(pd2->bdev, b));
+ return -EBUSY;
+ }
+ if (pd2->pkt_dev == dev) {
+ printk("pktcdvd: Can't chain pktcdvd devices\n");
+ return -EBUSY;
+ }
+ }
+
+ bdev = bdget(dev);
+ if (!bdev)
+ return -ENOMEM;
+ ret = blkdev_get(bdev, FMODE_READ, O_RDONLY | O_NONBLOCK);
+ if (ret)
+ return ret;
+
+ /* This is safe, since we have a reference from open(). */
+ __module_get(THIS_MODULE);
+
+ if (!pkt_grow_pktlist(pd, CONFIG_CDROM_PKTCDVD_BUFFERS)) {
+ printk("pktcdvd: not enough memory for buffers\n");
+ ret = -ENOMEM;
+ goto out_mem;
+ }
+
+ pd->bdev = bdev;
+ set_blocksize(bdev, CD_FRAMESIZE);
+
+ pkt_init_queue(pd);
+
+ atomic_set(&pd->cdrw.pending_bios, 0);
+ pd->cdrw.thread = kthread_run(kcdrwd, pd, "%s", pd->name);
+ if (IS_ERR(pd->cdrw.thread)) {
+ printk("pktcdvd: can't start kernel thread\n");
+ ret = -ENOMEM;
+ goto out_thread;
+ }
+
+ proc = create_proc_entry(pd->name, 0, pkt_proc);
+ if (proc) {
+ proc->data = pd;
+ proc->proc_fops = &pkt_proc_fops;
+ }
+ DPRINTK("pktcdvd: writer %s mapped to %s\n", pd->name, bdevname(bdev, b));
+ return 0;
+
+out_thread:
+ pkt_shrink_pktlist(pd);
+out_mem:
+ blkdev_put(bdev);
+ /* This is safe: open() is still holding a reference. */
+ module_put(THIS_MODULE);
+ return ret;
+}
+
+static int pkt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct pktcdvd_device *pd = inode->i_bdev->bd_disk->private_data;
+
+ VPRINTK("pkt_ioctl: cmd %x, dev %d:%d\n", cmd, imajor(inode), iminor(inode));
+ BUG_ON(!pd);
+
+ switch (cmd) {
+ /*
+ * forward selected CDROM ioctls to CD-ROM, for UDF
+ */
+ case CDROMMULTISESSION:
+ case CDROMREADTOCENTRY:
+ case CDROM_LAST_WRITTEN:
+ case CDROM_SEND_PACKET:
+ case SCSI_IOCTL_SEND_COMMAND:
+ return ioctl_by_bdev(pd->bdev, cmd, arg);
+
+ case CDROMEJECT:
+ /*
+ * The door gets locked when the device is opened, so we
+ * have to unlock it or else the eject command fails.
+ */
+ pkt_lock_door(pd, 0);
+ return ioctl_by_bdev(pd->bdev, cmd, arg);
+
+ default:
+ printk("pktcdvd: Unknown ioctl for %s (%x)\n", pd->name, cmd);
+ return -ENOTTY;
+ }
+
+ return 0;
+}
+
+static int pkt_media_changed(struct gendisk *disk)
+{
+ struct pktcdvd_device *pd = disk->private_data;
+ struct gendisk *attached_disk;
+
+ if (!pd)
+ return 0;
+ if (!pd->bdev)
+ return 0;
+ attached_disk = pd->bdev->bd_disk;
+ if (!attached_disk)
+ return 0;
+ return attached_disk->fops->media_changed(attached_disk);
+}
+
+static struct block_device_operations pktcdvd_ops = {
+ .owner = THIS_MODULE,
+ .open = pkt_open,
+ .release = pkt_close,
+ .ioctl = pkt_ioctl,
+ .media_changed = pkt_media_changed,
+};
+
+/*
+ * Set up mapping from pktcdvd device to CD-ROM device.
+ */
+static int pkt_setup_dev(struct pkt_ctrl_command *ctrl_cmd)
+{
+ int idx;
+ int ret = -ENOMEM;
+ struct pktcdvd_device *pd;
+ struct gendisk *disk;
+ dev_t dev = new_decode_dev(ctrl_cmd->dev);
+
+ for (idx = 0; idx < MAX_WRITERS; idx++)
+ if (!pkt_devs[idx])
+ break;
+ if (idx == MAX_WRITERS) {
+ printk("pktcdvd: max %d writers supported\n", MAX_WRITERS);
+ return -EBUSY;
+ }
+
+ pd = kmalloc(sizeof(struct pktcdvd_device), GFP_KERNEL);
+ if (!pd)
+ return ret;
+ memset(pd, 0, sizeof(struct pktcdvd_device));
+
+ pd->rb_pool = mempool_create(PKT_RB_POOL_SIZE, pkt_rb_alloc, pkt_rb_free, NULL);
+ if (!pd->rb_pool)
+ goto out_mem;
+
+ disk = alloc_disk(1);
+ if (!disk)
+ goto out_mem;
+ pd->disk = disk;
+
+ spin_lock_init(&pd->lock);
+ spin_lock_init(&pd->iosched.lock);
+ sprintf(pd->name, "pktcdvd%d", idx);
+ init_waitqueue_head(&pd->wqueue);
+ pd->bio_queue = RB_ROOT;
+
+ disk->major = pkt_major;
+ disk->first_minor = idx;
+ disk->fops = &pktcdvd_ops;
+ disk->flags = GENHD_FL_REMOVABLE;
+ sprintf(disk->disk_name, "pktcdvd%d", idx);
+ disk->private_data = pd;
+ disk->queue = blk_alloc_queue(GFP_KERNEL);
+ if (!disk->queue)
+ goto out_mem2;
+
+ pd->pkt_dev = MKDEV(disk->major, disk->first_minor);
+ ret = pkt_new_dev(pd, dev);
+ if (ret)
+ goto out_new_dev;
+
+ add_disk(disk);
+ pkt_devs[idx] = pd;
+ ctrl_cmd->pkt_dev = new_encode_dev(pd->pkt_dev);
+ return 0;
+
+out_new_dev:
+ blk_put_queue(disk->queue);
+out_mem2:
+ put_disk(disk);
+out_mem:
+ if (pd->rb_pool)
+ mempool_destroy(pd->rb_pool);
+ kfree(pd);
+ return ret;
+}
+
+/*
+ * Tear down mapping from pktcdvd device to CD-ROM device.
+ */
+static int pkt_remove_dev(struct pkt_ctrl_command *ctrl_cmd)
+{
+ struct pktcdvd_device *pd;
+ int idx;
+ dev_t pkt_dev = new_decode_dev(ctrl_cmd->pkt_dev);
+
+ for (idx = 0; idx < MAX_WRITERS; idx++) {
+ pd = pkt_devs[idx];
+ if (pd && (pd->pkt_dev == pkt_dev))
+ break;
+ }
+ if (idx == MAX_WRITERS) {
+ DPRINTK("pktcdvd: dev not setup\n");
+ return -ENXIO;
+ }
+
+ if (pd->refcnt > 0)
+ return -EBUSY;
+
+ if (!IS_ERR(pd->cdrw.thread))
+ kthread_stop(pd->cdrw.thread);
+
+ blkdev_put(pd->bdev);
+
+ pkt_shrink_pktlist(pd);
+
+ remove_proc_entry(pd->name, pkt_proc);
+ DPRINTK("pktcdvd: writer %s unmapped\n", pd->name);
+
+ del_gendisk(pd->disk);
+ blk_put_queue(pd->disk->queue);
+ put_disk(pd->disk);
+
+ pkt_devs[idx] = NULL;
+ mempool_destroy(pd->rb_pool);
+ kfree(pd);
+
+ /* This is safe: open() is still holding a reference. */
+ module_put(THIS_MODULE);
+ return 0;
+}
+
+static void pkt_get_status(struct pkt_ctrl_command *ctrl_cmd)
+{
+ struct pktcdvd_device *pd = pkt_find_dev_from_minor(ctrl_cmd->dev_index);
+ if (pd) {
+ ctrl_cmd->dev = new_encode_dev(pd->bdev->bd_dev);
+ ctrl_cmd->pkt_dev = new_encode_dev(pd->pkt_dev);
+ } else {
+ ctrl_cmd->dev = 0;
+ ctrl_cmd->pkt_dev = 0;
+ }
+ ctrl_cmd->num_devices = MAX_WRITERS;
+}
+
+static int pkt_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ struct pkt_ctrl_command ctrl_cmd;
+ int ret = 0;
+
+ if (cmd != PACKET_CTRL_CMD)
+ return -ENOTTY;
+
+ if (copy_from_user(&ctrl_cmd, argp, sizeof(struct pkt_ctrl_command)))
+ return -EFAULT;
+
+ switch (ctrl_cmd.command) {
+ case PKT_CTRL_CMD_SETUP:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ down(&ctl_mutex);
+ ret = pkt_setup_dev(&ctrl_cmd);
+ up(&ctl_mutex);
+ break;
+ case PKT_CTRL_CMD_TEARDOWN:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ down(&ctl_mutex);
+ ret = pkt_remove_dev(&ctrl_cmd);
+ up(&ctl_mutex);
+ break;
+ case PKT_CTRL_CMD_STATUS:
+ down(&ctl_mutex);
+ pkt_get_status(&ctrl_cmd);
+ up(&ctl_mutex);
+ break;
+ default:
+ return -ENOTTY;
+ }
+
+ if (copy_to_user(argp, &ctrl_cmd, sizeof(struct pkt_ctrl_command)))
+ return -EFAULT;
+ return ret;
+}
+
+
+static struct file_operations pkt_ctl_fops = {
+ .ioctl = pkt_ctl_ioctl,
+ .owner = THIS_MODULE,
+};
+
+static struct miscdevice pkt_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "pktcdvd",
+ .devfs_name = "pktcdvd/control",
+ .fops = &pkt_ctl_fops
+};
+
+int pkt_init(void)
+{
+ int ret;
+
+ psd_pool = mempool_create(PSD_POOL_SIZE, psd_pool_alloc, psd_pool_free, NULL);
+ if (!psd_pool)
+ return -ENOMEM;
+
+ ret = register_blkdev(pkt_major, "pktcdvd");
+ if (ret < 0) {
+ printk("pktcdvd: Unable to register block device\n");
+ goto out2;
+ }
+ if (!pkt_major)
+ pkt_major = ret;
+
+ ret = misc_register(&pkt_misc);
+ if (ret) {
+ printk("pktcdvd: Unable to register misc device\n");
+ goto out;
+ }
+
+ init_MUTEX(&ctl_mutex);
+
+ pkt_proc = proc_mkdir("pktcdvd", proc_root_driver);
+
+ DPRINTK("pktcdvd: %s\n", VERSION_CODE);
+ return 0;
+
+out:
+ unregister_blkdev(pkt_major, "pktcdvd");
+out2:
+ mempool_destroy(psd_pool);
+ return ret;
+}
+
+void pkt_exit(void)
+{
+ remove_proc_entry("pktcdvd", proc_root_driver);
+ misc_deregister(&pkt_misc);
+ unregister_blkdev(pkt_major, "pktcdvd");
+ mempool_destroy(psd_pool);
+}
+
+MODULE_DESCRIPTION("Packet writing layer for CD/DVD drives");
+MODULE_AUTHOR("Jens Axboe <axboe@suse.de>");
+MODULE_LICENSE("GPL");
+
+module_init(pkt_init);
+module_exit(pkt_exit);
diff --git a/drivers/block/ub.c b/drivers/block/ub.c
index f605535d3f56..dd3be0a06219 100644
--- a/drivers/block/ub.c
+++ b/drivers/block/ub.c
@@ -25,6 +25,7 @@
* -- prune comments, they are too volumnous
* -- Exterminate P3 printks
* -- Resove XXX's
+ * -- Redo "benh's retries", perhaps have spin-up code to handle them. V:D=?
*/
#include <linux/kernel.h>
#include <linux/module.h>
@@ -62,9 +63,9 @@
/* command block wrapper */
struct bulk_cb_wrap {
- u32 Signature; /* contains 'USBC' */
+ __le32 Signature; /* contains 'USBC' */
u32 Tag; /* unique per command id */
- u32 DataTransferLength; /* size of data */
+ __le32 DataTransferLength; /* size of data */
u8 Flags; /* direction in bit 0 */
u8 Lun; /* LUN normally 0 */
u8 Length; /* of of the CDB */
@@ -78,9 +79,9 @@ struct bulk_cb_wrap {
/* command status wrapper */
struct bulk_cs_wrap {
- u32 Signature; /* should = 'USBS' */
+ __le32 Signature; /* should = 'USBS' */
u32 Tag; /* same as original command */
- u32 Residue; /* amount not transferred */
+ __le32 Residue; /* amount not transferred */
u8 Status; /* see below */
};
@@ -157,7 +158,8 @@ struct ub_scsi_cmd {
struct ub_scsi_cmd *next;
int error; /* Return code - valid upon done */
- int act_len; /* Return size */
+ unsigned int act_len; /* Return size */
+ unsigned char key, asc, ascq; /* May be valid if error==-EIO */
int stat_count; /* Retries getting status. */
@@ -490,6 +492,18 @@ static void ub_id_put(int id)
*/
static void ub_cleanup(struct ub_dev *sc)
{
+
+ /*
+ * If we zero disk->private_data BEFORE put_disk, we have to check
+ * for NULL all over the place in open, release, check_media and
+ * revalidate, because the block level semaphore is well inside the
+ * put_disk. But we cannot zero after the call, because *disk is gone.
+ * The sd.c is blatantly racy in this area.
+ */
+ /* disk->private_data = NULL; */
+ put_disk(sc->disk);
+ sc->disk = NULL;
+
ub_id_put(sc->id);
kfree(sc);
}
@@ -661,9 +675,12 @@ static inline int ub_bd_rq_fn_1(request_queue_t *q)
/*
* build the command
+ *
+ * The call to blk_queue_hardsect_size() guarantees that request
+ * is aligned, but it is given in terms of 512 byte units, always.
*/
- block = rq->sector;
- nblks = rq->nr_sectors;
+ block = rq->sector >> sc->capacity.bshift;
+ nblks = rq->nr_sectors >> sc->capacity.bshift;
memset(cmd, 0, sizeof(struct ub_scsi_cmd));
cmd->cdb[0] = (ub_dir == UB_DIR_READ)? READ_10: WRITE_10;
@@ -678,7 +695,7 @@ static inline int ub_bd_rq_fn_1(request_queue_t *q)
cmd->dir = ub_dir;
cmd->state = UB_CMDST_INIT;
cmd->data = rq->buffer;
- cmd->len = nblks * 512;
+ cmd->len = rq->nr_sectors * 512;
cmd->done = ub_rw_cmd_done;
cmd->back = rq;
@@ -786,17 +803,16 @@ static int ub_scsi_cmd_start(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
sc->work_urb.error_count = 0;
sc->work_urb.status = 0;
- sc->work_timer.expires = jiffies + UB_URB_TIMEOUT;
- add_timer(&sc->work_timer);
-
if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) {
/* XXX Clear stalls */
printk("ub: cmd #%d start failed (%d)\n", cmd->tag, rc); /* P3 */
- del_timer(&sc->work_timer);
ub_complete(&sc->work_done);
return rc;
}
+ sc->work_timer.expires = jiffies + UB_URB_TIMEOUT;
+ add_timer(&sc->work_timer);
+
cmd->state = UB_CMDST_CMD;
ub_cmdtr_state(sc, cmd);
return 0;
@@ -836,6 +852,7 @@ static void ub_scsi_action(unsigned long _dev)
unsigned long flags;
spin_lock_irqsave(&sc->lock, flags);
+ del_timer(&sc->work_timer);
ub_scsi_dispatch(sc);
spin_unlock_irqrestore(&sc->lock, flags);
}
@@ -968,18 +985,17 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
sc->work_urb.error_count = 0;
sc->work_urb.status = 0;
- sc->work_timer.expires = jiffies + UB_URB_TIMEOUT;
- add_timer(&sc->work_timer);
-
if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) {
/* XXX Clear stalls */
printk("ub: data #%d submit failed (%d)\n", cmd->tag, rc); /* P3 */
- del_timer(&sc->work_timer);
ub_complete(&sc->work_done);
ub_state_done(sc, cmd, rc);
return;
}
+ sc->work_timer.expires = jiffies + UB_URB_TIMEOUT;
+ add_timer(&sc->work_timer);
+
cmd->state = UB_CMDST_DATA;
ub_cmdtr_state(sc, cmd);
@@ -1063,19 +1079,18 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
sc->work_urb.error_count = 0;
sc->work_urb.status = 0;
- sc->work_timer.expires = jiffies + UB_URB_TIMEOUT;
- add_timer(&sc->work_timer);
-
rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC);
if (rc != 0) {
/* XXX Clear stalls */
printk("%s: CSW #%d submit failed (%d)\n",
sc->name, cmd->tag, rc); /* P3 */
- del_timer(&sc->work_timer);
ub_complete(&sc->work_done);
ub_state_done(sc, cmd, rc);
return;
}
+
+ sc->work_timer.expires = jiffies + UB_URB_TIMEOUT;
+ add_timer(&sc->work_timer);
return;
}
@@ -1132,16 +1147,8 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
(*cmd->done)(sc, cmd);
} else if (cmd->state == UB_CMDST_SENSE) {
- /*
- * We do not look at sense, because even if there was no sense,
- * we get into UB_CMDST_SENSE from a STALL or CSW FAIL only.
- * We request sense because we want to clear CHECK CONDITION
- * on devices with delusions of SCSI, and not because we
- * are curious in any way about the sense itself.
- */
- /* if ((cmd->top_sense[2] & 0x0F) == NO_SENSE) { foo } */
-
ub_state_done(sc, cmd, -EIO);
+
} else {
printk(KERN_WARNING "%s: "
"wrong command state %d on device %u\n",
@@ -1186,18 +1193,17 @@ static void ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
sc->work_urb.error_count = 0;
sc->work_urb.status = 0;
- sc->work_timer.expires = jiffies + UB_URB_TIMEOUT;
- add_timer(&sc->work_timer);
-
if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) {
/* XXX Clear stalls */
printk("ub: CSW #%d submit failed (%d)\n", cmd->tag, rc); /* P3 */
- del_timer(&sc->work_timer);
ub_complete(&sc->work_done);
ub_state_done(sc, cmd, rc);
return;
}
+ sc->work_timer.expires = jiffies + UB_URB_TIMEOUT;
+ add_timer(&sc->work_timer);
+
cmd->stat_count = 0;
cmd->state = UB_CMDST_STAT;
ub_cmdtr_state(sc, cmd);
@@ -1217,9 +1223,17 @@ static void ub_state_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
goto error;
}
+ /*
+ * ``If the allocation length is eighteen or greater, and a device
+ * server returns less than eithteen bytes of data, the application
+ * client should assume that the bytes not transferred would have been
+ * zeroes had the device server returned those bytes.''
+ */
memset(&sc->top_sense, 0, UB_SENSE_SIZE);
+
scmd = &sc->top_rqs_cmd;
scmd->cdb[0] = REQUEST_SENSE;
+ scmd->cdb[4] = UB_SENSE_SIZE;
scmd->cdb_len = 6;
scmd->dir = UB_DIR_READ;
scmd->state = UB_CMDST_INIT;
@@ -1271,14 +1285,13 @@ static int ub_submit_clear_stall(struct ub_dev *sc, struct ub_scsi_cmd *cmd,
sc->work_urb.error_count = 0;
sc->work_urb.status = 0;
- sc->work_timer.expires = jiffies + UB_CTRL_TIMEOUT;
- add_timer(&sc->work_timer);
-
if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) {
- del_timer(&sc->work_timer);
ub_complete(&sc->work_done);
return rc;
}
+
+ sc->work_timer.expires = jiffies + UB_CTRL_TIMEOUT;
+ add_timer(&sc->work_timer);
return 0;
}
@@ -1289,8 +1302,15 @@ static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd)
unsigned char *sense = scmd->data;
struct ub_scsi_cmd *cmd;
+ /*
+ * Ignoring scmd->act_len, because the buffer was pre-zeroed.
+ */
ub_cmdtr_sense(sc, scmd, sense);
+ /*
+ * Find the command which triggered the unit attention or a check,
+ * save the sense into it, and advance its state machine.
+ */
if ((cmd = ub_cmdq_peek(sc)) == NULL) {
printk(KERN_WARNING "%s: sense done while idle\n", sc->name);
return;
@@ -1308,6 +1328,10 @@ static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd)
return;
}
+ cmd->key = sense[2] & 0x0F;
+ cmd->asc = sense[12];
+ cmd->ascq = sense[13];
+
ub_scsi_urb_compl(sc, cmd);
}
@@ -1407,7 +1431,15 @@ static int ub_bd_open(struct inode *inode, struct file *filp)
if (sc->removable || sc->readonly)
check_disk_change(inode->i_bdev);
- /* XXX sd.c and floppy.c bail on open if media is not present. */
+ /*
+ * The sd.c considers ->media_present and ->changed not equivalent,
+ * under some pretty murky conditions (a failure of READ CAPACITY).
+ * We may need it one day.
+ */
+ if (sc->removable && sc->changed && !(filp->f_flags & O_NDELAY)) {
+ rc = -ENOMEDIUM;
+ goto err_open;
+ }
if (sc->readonly && (filp->f_mode & FMODE_WRITE)) {
rc = -EROFS;
@@ -1492,8 +1524,11 @@ static int ub_bd_revalidate(struct gendisk *disk)
printk(KERN_INFO "%s: device %u capacity nsec %ld bsize %u\n",
sc->name, sc->dev->devnum, sc->capacity.nsec, sc->capacity.bsize);
+ /* XXX Support sector size switching like in sr.c */
+ blk_queue_hardsect_size(disk->queue, sc->capacity.bsize);
set_capacity(disk, sc->capacity.nsec);
// set_disk_ro(sdkp->disk, sc->readonly);
+
return 0;
}
@@ -1592,6 +1627,9 @@ static int ub_sync_tur(struct ub_dev *sc)
rc = cmd->error;
+ if (rc == -EIO && cmd->key != 0) /* Retries for benh's key */
+ rc = cmd->key;
+
err_submit:
kfree(cmd);
err_alloc:
@@ -1654,8 +1692,8 @@ static int ub_sync_read_cap(struct ub_dev *sc, struct ub_capacity *ret)
}
/* sd.c special-cases sector size of 0 to mean 512. Needed? Safe? */
- nsec = be32_to_cpu(*(u32 *)p) + 1;
- bsize = be32_to_cpu(*(u32 *)(p + 4));
+ nsec = be32_to_cpu(*(__be32 *)p) + 1;
+ bsize = be32_to_cpu(*(__be32 *)(p + 4));
switch (bsize) {
case 512: shift = 0; break;
case 1024: shift = 1; break;
@@ -1725,28 +1763,22 @@ static int ub_probe_clear_stall(struct ub_dev *sc, int stalled_pipe)
sc->work_urb.error_count = 0;
sc->work_urb.status = 0;
- init_timer(&timer);
- timer.function = ub_probe_timeout;
- timer.data = (unsigned long) &compl;
- timer.expires = jiffies + UB_CTRL_TIMEOUT;
- add_timer(&timer);
-
if ((rc = usb_submit_urb(&sc->work_urb, GFP_KERNEL)) != 0) {
printk(KERN_WARNING
"%s: Unable to submit a probe clear (%d)\n", sc->name, rc);
- del_timer_sync(&timer);
return rc;
}
+ init_timer(&timer);
+ timer.function = ub_probe_timeout;
+ timer.data = (unsigned long) &compl;
+ timer.expires = jiffies + UB_CTRL_TIMEOUT;
+ add_timer(&timer);
+
wait_for_completion(&compl);
del_timer_sync(&timer);
- /*
- * Most of the time, URB was done and dev set to NULL, and so
- * the unlink bounces out with ENODEV. We do not call usb_kill_urb
- * because we still think about a backport to 2.4.
- */
- usb_unlink_urb(&sc->work_urb);
+ usb_kill_urb(&sc->work_urb);
/* reset the endpoint toggle */
usb_settoggle(sc->dev, endp, usb_pipeout(sc->last_pipe), 0);
@@ -1813,6 +1845,7 @@ static int ub_probe(struct usb_interface *intf,
request_queue_t *q;
struct gendisk *disk;
int rc;
+ int i;
rc = -ENOMEM;
if ((sc = kmalloc(sizeof(struct ub_dev), GFP_KERNEL)) == NULL)
@@ -1879,7 +1912,11 @@ static int ub_probe(struct usb_interface *intf,
* has to succeed, so we clear checks with an additional one here.
* In any case it's not our business how revaliadation is implemented.
*/
- ub_sync_tur(sc);
+ for (i = 0; i < 3; i++) { /* Retries for benh's key */
+ if ((rc = ub_sync_tur(sc)) <= 0) break;
+ if (rc != 0x6) break;
+ msleep(10);
+ }
sc->removable = 1; /* XXX Query this from the device */
@@ -1915,7 +1952,7 @@ static int ub_probe(struct usb_interface *intf,
blk_queue_max_phys_segments(q, UB_MAX_REQ_SG);
// blk_queue_segment_boundary(q, CARM_SG_BOUNDARY);
blk_queue_max_sectors(q, UB_MAX_SECTORS);
- // blk_queue_hardsect_size(q, xxxxx);
+ blk_queue_hardsect_size(q, sc->capacity.bsize);
/*
* This is a serious infraction, caused by a deficiency in the
@@ -2006,17 +2043,6 @@ static void ub_disconnect(struct usb_interface *intf)
blk_cleanup_queue(q);
/*
- * If we zero disk->private_data BEFORE put_disk, we have to check
- * for NULL all over the place in open, release, check_media and
- * revalidate, because the block level semaphore is well inside the
- * put_disk. But we cannot zero after the call, because *disk is gone.
- * The sd.c is blatantly racy in this area.
- */
- /* disk->private_data = NULL; */
- put_disk(disk);
- sc->disk = NULL;
-
- /*
* We really expect blk_cleanup_queue() to wait, so no amount
* of paranoya is too much.
*
@@ -2035,6 +2061,13 @@ static void ub_disconnect(struct usb_interface *intf)
spin_unlock_irqrestore(&sc->lock, flags);
/*
+ * There is virtually no chance that other CPU runs times so long
+ * after ub_urb_complete should have called del_timer, but only if HCD
+ * didn't forget to deliver a callback on unlink.
+ */
+ del_timer_sync(&sc->work_timer);
+
+ /*
* At this point there must be no commands coming from anyone
* and no URBs left in transit.
*/
diff --git a/drivers/cdrom/Makefile b/drivers/cdrom/Makefile
index 5c484f3b3e58..4a8351753e07 100644
--- a/drivers/cdrom/Makefile
+++ b/drivers/cdrom/Makefile
@@ -8,6 +8,7 @@
obj-$(CONFIG_BLK_DEV_IDECD) += cdrom.o
obj-$(CONFIG_BLK_DEV_SR) += cdrom.o
obj-$(CONFIG_PARIDE_PCD) += cdrom.o
+obj-$(CONFIG_CDROM_PKTCDVD) += cdrom.o
obj-$(CONFIG_AZTCD) += aztcd.o
obj-$(CONFIG_CDU31A) += cdu31a.o cdrom.o
diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c
index e57d19031f8e..4153153aeaf5 100644
--- a/drivers/cdrom/cdrom.c
+++ b/drivers/cdrom/cdrom.c
@@ -234,6 +234,12 @@
-- Mt Rainier support
-- DVD-RAM write open fixes
+ Nov 5 2001, Aug 8 2002. Modified by Andy Polyakov
+ <appro@fy.chalmers.se> to support MMC-3 compliant DVD+RW units.
+
+ Modified by Nigel Kukard <nkukard@lbsd.net> - support DVD+RW
+ 2.4.x patch by Andy Polyakov <appro@fy.chalmers.se>
+
-------------------------------------------------------------------------*/
#define REVISION "Revision: 3.20"
@@ -848,6 +854,41 @@ static int cdrom_ram_open_write(struct cdrom_device_info *cdi)
return ret;
}
+static void cdrom_mmc3_profile(struct cdrom_device_info *cdi)
+{
+ struct packet_command cgc;
+ char buffer[32];
+ int ret, mmc3_profile;
+
+ init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ);
+
+ cgc.cmd[0] = GPCMD_GET_CONFIGURATION;
+ cgc.cmd[1] = 0;
+ cgc.cmd[2] = cgc.cmd[3] = 0; /* Starting Feature Number */
+ cgc.cmd[8] = sizeof(buffer); /* Allocation Length */
+ cgc.quiet = 1;
+
+ if ((ret = cdi->ops->generic_packet(cdi, &cgc))) {
+ mmc3_profile = 0xffff;
+ } else {
+ mmc3_profile = (buffer[6] << 8) | buffer[7];
+ printk(KERN_INFO "cdrom: %s: mmc-3 profile capable, current profile: %Xh\n",
+ cdi->name, mmc3_profile);
+ }
+ cdi->mmc3_profile = mmc3_profile;
+}
+
+static int cdrom_is_dvd_rw(struct cdrom_device_info *cdi)
+{
+ switch (cdi->mmc3_profile) {
+ case 0x12: /* DVD-RAM */
+ case 0x1A: /* DVD+RW */
+ return 0;
+ default:
+ return 1;
+ }
+}
+
/*
* returns 0 for ok to open write, non-0 to disallow
*/
@@ -889,10 +930,50 @@ static int cdrom_open_write(struct cdrom_device_info *cdi)
ret = cdrom_ram_open_write(cdi);
else if (CDROM_CAN(CDC_MO_DRIVE))
ret = mo_open_write(cdi);
+ else if (!cdrom_is_dvd_rw(cdi))
+ ret = 0;
return ret;
}
+static void cdrom_dvd_rw_close_write(struct cdrom_device_info *cdi)
+{
+ struct packet_command cgc;
+
+ if (cdi->mmc3_profile != 0x1a) {
+ cdinfo(CD_CLOSE, "%s: No DVD+RW\n", cdi->name);
+ return;
+ }
+
+ if (!cdi->media_written) {
+ cdinfo(CD_CLOSE, "%s: DVD+RW media clean\n", cdi->name);
+ return;
+ }
+
+ printk(KERN_INFO "cdrom: %s: dirty DVD+RW media, \"finalizing\"\n",
+ cdi->name);
+
+ init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+ cgc.cmd[0] = GPCMD_FLUSH_CACHE;
+ cgc.timeout = 30*HZ;
+ cdi->ops->generic_packet(cdi, &cgc);
+
+ init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+ cgc.cmd[0] = GPCMD_CLOSE_TRACK;
+ cgc.timeout = 3000*HZ;
+ cgc.quiet = 1;
+ cdi->ops->generic_packet(cdi, &cgc);
+
+ init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+ cgc.cmd[0] = GPCMD_CLOSE_TRACK;
+ cgc.cmd[2] = 2; /* Close session */
+ cgc.quiet = 1;
+ cgc.timeout = 3000*HZ;
+ cdi->ops->generic_packet(cdi, &cgc);
+
+ cdi->media_written = 0;
+}
+
static int cdrom_close_write(struct cdrom_device_info *cdi)
{
#if 0
@@ -925,6 +1006,7 @@ int cdrom_open(struct cdrom_device_info *cdi, struct inode *ip, struct file *fp)
ret = open_for_data(cdi);
if (ret)
goto err;
+ cdrom_mmc3_profile(cdi);
if (fp->f_mode & FMODE_WRITE) {
ret = -EROFS;
if (cdrom_open_write(cdi))
@@ -932,6 +1014,7 @@ int cdrom_open(struct cdrom_device_info *cdi, struct inode *ip, struct file *fp)
if (!CDROM_CAN(CDC_RAM))
goto err;
ret = 0;
+ cdi->media_written = 0;
}
}
@@ -1123,6 +1206,8 @@ int cdrom_release(struct cdrom_device_info *cdi, struct file *fp)
cdi->use_count--;
if (cdi->use_count == 0)
cdinfo(CD_CLOSE, "Use count for \"/dev/%s\" now zero\n", cdi->name);
+ if (cdi->use_count == 0)
+ cdrom_dvd_rw_close_write(cdi);
if (cdi->use_count == 0 &&
(cdo->capability & CDC_LOCK) && !keeplocked) {
cdinfo(CD_CLOSE, "Unlocking door!\n");
@@ -1329,6 +1414,7 @@ int media_changed(struct cdrom_device_info *cdi, int queue)
if (cdi->ops->media_changed(cdi, CDSL_CURRENT)) {
cdi->mc_flags = 0x3; /* set bit on both queues */
ret |= 1;
+ cdi->media_written = 0;
}
cdi->mc_flags &= ~mask; /* clear bit */
return ret;
diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c
index 7bec505ad95e..b7b3c5617756 100644
--- a/drivers/char/agp/intel-agp.c
+++ b/drivers/char/agp/intel-agp.c
@@ -1782,16 +1782,8 @@ static struct pci_driver agp_intel_pci_driver = {
.resume = agp_intel_resume,
};
-/* intel_agp_init() must not be declared static for explicit
- early initialization to work (ie i810fb) */
-int __init agp_intel_init(void)
+static int __init agp_intel_init(void)
{
- static int agp_initialised=0;
-
- if (agp_initialised == 1)
- return 0;
- agp_initialised=1;
-
return pci_module_init(&agp_intel_pci_driver);
}
diff --git a/drivers/char/agp/sis-agp.c b/drivers/char/agp/sis-agp.c
index 019a31c36121..4042fd3b56bd 100644
--- a/drivers/char/agp/sis-agp.c
+++ b/drivers/char/agp/sis-agp.c
@@ -6,6 +6,7 @@
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/agp_backend.h>
+#include <linux/delay.h>
#include "agp.h"
#define SIS_ATTBASE 0x90
@@ -102,8 +103,7 @@ static void sis_delayed_enable(u32 mode)
*/
if (device->device == agp_bridge->dev->device) {
printk(KERN_INFO PFX "SiS delay workaround: giving bridge time to recover.\n");
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout (1+(HZ*10)/1000);
+ msleep(10);
}
}
}
diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c
index f11cf4ea0398..2355455786f4 100644
--- a/drivers/char/amiserial.c
+++ b/drivers/char/amiserial.c
@@ -32,6 +32,7 @@
*/
#include <linux/config.h>
+#include <linux/delay.h>
#undef SERIAL_PARANOIA_CHECK
#define SERIAL_DO_RESTART
@@ -1563,8 +1564,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
info->tty = 0;
if (info->blocked_open) {
if (info->close_delay) {
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(info->close_delay);
+ msleep_interruptible(jiffies_to_msecs(info->close_delay));
}
wake_up_interruptible(&info->open_wait);
}
@@ -1622,8 +1622,7 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
printk("serdatr = %d (jiff=%lu)...", lsr, jiffies);
#endif
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(char_time);
+ msleep_interruptible(jiffies_to_msecs(char_time));
if (signal_pending(current))
break;
if (timeout && time_after(jiffies, orig_jiffies + timeout))
diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c
index 6ceaee64b269..46002d5bada1 100644
--- a/drivers/char/cyclades.c
+++ b/drivers/char/cyclades.c
@@ -2690,20 +2690,16 @@ cy_wait_until_sent(struct tty_struct *tty, int timeout)
#ifdef CY_DEBUG_WAIT_UNTIL_SENT
printk("Not clean (jiff=%lu)...", jiffies);
#endif
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(char_time);
- if (signal_pending(current))
+ if (msleep_interruptible(jiffies_to_msecs(char_time)))
break;
if (timeout && time_after(jiffies, orig_jiffies + timeout))
break;
}
- current->state = TASK_RUNNING;
} else {
// Nothing to do!
}
/* Run one more char cycle */
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(char_time * 5);
+ msleep_interruptible(jiffies_to_msecs(char_time * 5));
#ifdef CY_DEBUG_WAIT_UNTIL_SENT
printk("Clean (jiff=%lu)...done\n", jiffies);
#endif
@@ -2828,8 +2824,7 @@ cy_close(struct tty_struct *tty, struct file *filp)
if (info->blocked_open) {
CY_UNLOCK(info, flags);
if (info->close_delay) {
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(info->close_delay);
+ msleep_interruptible(jiffies_to_msecs(info->close_delay));
}
wake_up_interruptible(&info->open_wait);
CY_LOCK(info, flags);
diff --git a/drivers/char/drm/radeon_mem.c b/drivers/char/drm/radeon_mem.c
index 289957406c9c..f82d0c42472f 100644
--- a/drivers/char/drm/radeon_mem.c
+++ b/drivers/char/drm/radeon_mem.c
@@ -85,7 +85,7 @@ static struct mem_block *alloc_block( struct mem_block *heap, int size,
struct mem_block *p;
int mask = (1 << align2)-1;
- for (p = heap->next ; p != heap ; p = p->next) {
+ list_for_each(p, heap) {
int start = (p->start + mask) & ~mask;
if (p->filp == 0 && start + size <= p->start + p->size)
return split_block( p, start, size, filp );
@@ -98,7 +98,7 @@ static struct mem_block *find_block( struct mem_block *heap, int start )
{
struct mem_block *p;
- for (p = heap->next ; p != heap ; p = p->next)
+ list_for_each(p, heap)
if (p->start == start)
return p;
@@ -166,7 +166,7 @@ void radeon_mem_release( DRMFILE filp, struct mem_block *heap )
if (!heap || !heap->next)
return;
- for (p = heap->next ; p != heap ; p = p->next) {
+ list_for_each(p, heap) {
if (p->filp == filp)
p->filp = NULL;
}
@@ -174,7 +174,7 @@ void radeon_mem_release( DRMFILE filp, struct mem_block *heap )
/* Assumes a single contiguous range. Needs a special filp in
* 'heap' to stop it being subsumed.
*/
- for (p = heap->next ; p != heap ; p = p->next) {
+ list_for_each(p, heap) {
while (p->filp == 0 && p->next->filp == 0) {
struct mem_block *q = p->next;
p->size += q->size;
diff --git a/drivers/char/dtlk.c b/drivers/char/dtlk.c
index e8f15f46eca4..903e4c3cc209 100644
--- a/drivers/char/dtlk.c
+++ b/drivers/char/dtlk.c
@@ -107,7 +107,6 @@ static struct file_operations dtlk_fops =
};
/* local prototypes */
-static void dtlk_delay(int ms);
static int dtlk_dev_probe(void);
static struct dtlk_settings *dtlk_interrogate(void);
static int dtlk_readable(void);
@@ -146,7 +145,7 @@ static ssize_t dtlk_read(struct file *file, char __user *buf,
return i;
if (file->f_flags & O_NONBLOCK)
break;
- dtlk_delay(100);
+ msleep_interruptible(100);
}
if (retries == loops_per_jiffy)
printk(KERN_ERR "dtlk_read times out\n");
@@ -191,7 +190,7 @@ static ssize_t dtlk_write(struct file *file, const char __user *buf,
rate to 500 bytes/sec, but that's
still enough to keep up with the
speech synthesizer. */
- dtlk_delay(1);
+ msleep_interruptible(1);
else {
/* the RDY bit goes zero 2-3 usec
after writing, and goes 1 again
@@ -212,7 +211,7 @@ static ssize_t dtlk_write(struct file *file, const char __user *buf,
if (file->f_flags & O_NONBLOCK)
break;
- dtlk_delay(1);
+ msleep_interruptible(1);
if (++retries > 10 * HZ) { /* wait no more than 10 sec
from last write */
@@ -351,8 +350,7 @@ static int __init dtlk_init(void)
static void __exit dtlk_cleanup (void)
{
dtlk_write_bytes("goodbye", 8);
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(5 * HZ / 10); /* nap 0.50 sec but
+ msleep_interruptible(500); /* nap 0.50 sec but
could be awakened
earlier by
signals... */
@@ -368,13 +366,6 @@ module_exit(dtlk_cleanup);
/* ------------------------------------------------------------------------ */
-/* sleep for ms milliseconds */
-static void dtlk_delay(int ms)
-{
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout((ms * HZ + 1000 - HZ) / 1000);
-}
-
static int dtlk_readable(void)
{
#ifdef TRACING
@@ -431,7 +422,7 @@ static int __init dtlk_dev_probe(void)
/* posting an index takes 18 msec. Here, we
wait up to 100 msec to see whether it
appears. */
- dtlk_delay(100);
+ msleep_interruptible(100);
dtlk_has_indexing = dtlk_readable();
#ifdef TRACING
printk(", indexing %d\n", dtlk_has_indexing);
diff --git a/drivers/char/epca.c b/drivers/char/epca.c
index 0f13bef975f5..4b1e0b38cd8c 100644
--- a/drivers/char/epca.c
+++ b/drivers/char/epca.c
@@ -561,8 +561,7 @@ static void pc_close(struct tty_struct * tty, struct file * filp)
if (ch->close_delay)
{
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(ch->close_delay);
+ msleep_interruptible(jiffies_to_msecs(ch->close_delay));
}
wake_up_interruptible(&ch->open_wait);
diff --git a/drivers/char/esp.c b/drivers/char/esp.c
index d67098c45ffd..7353c2678916 100644
--- a/drivers/char/esp.c
+++ b/drivers/char/esp.c
@@ -57,6 +57,7 @@
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/init.h>
+#include <linux/delay.h>
#include <asm/system.h>
#include <asm/io.h>
@@ -70,6 +71,7 @@
#define NR_PORTS 64 /* maximum number of ports */
#define NR_PRIMARY 8 /* maximum number of primary ports */
+#define REGION_SIZE 8 /* size of io region to request */
/* The following variables can be set by giving module options */
static int irq[NR_PRIMARY]; /* IRQ for each base port */
@@ -2066,8 +2068,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
if (info->blocked_open) {
if (info->close_delay) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(info->close_delay);
+ msleep_interruptible(jiffies_to_msecs(info->close_delay));
}
wake_up_interruptible(&info->open_wait);
}
@@ -2098,8 +2099,7 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
while ((serial_in(info, UART_ESI_STAT1) != 0x03) ||
(serial_in(info, UART_ESI_STAT2) != 0xff)) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(char_time);
+ msleep_interruptible(jiffies_to_msecs(char_time));
if (signal_pending(current))
break;
@@ -2344,19 +2344,21 @@ static _INLINE_ void show_serial_version(void)
* This routine is called by espserial_init() to initialize a specific serial
* port.
*/
-static _INLINE_ int autoconfig(struct esp_struct * info, int *region_start)
+static _INLINE_ int autoconfig(struct esp_struct * info)
{
int port_detected = 0;
unsigned long flags;
+ if (!request_region(info->port, REGION_SIZE, "esp serial"))
+ return -EIO;
+
save_flags(flags); cli();
/*
* Check for ESP card
*/
- if (!check_region(info->port, 8) &&
- serial_in(info, UART_ESI_BASE) == 0xf3) {
+ if (serial_in(info, UART_ESI_BASE) == 0xf3) {
serial_out(info, UART_ESI_CMD1, 0x00);
serial_out(info, UART_ESI_CMD1, 0x01);
@@ -2372,19 +2374,6 @@ static _INLINE_ int autoconfig(struct esp_struct * info, int *region_start)
info->irq = 4;
}
- if (ports && (ports->port == (info->port - 8))) {
- release_region(*region_start,
- info->port - *region_start);
- } else
- *region_start = info->port;
-
- if (!request_region(*region_start,
- info->port - *region_start + 8,
- "esp serial"))
- {
- restore_flags(flags);
- return -EIO;
- }
/* put card in enhanced mode */
/* this prevents access through */
@@ -2397,6 +2386,8 @@ static _INLINE_ int autoconfig(struct esp_struct * info, int *region_start)
serial_out(info, UART_ESI_CMD2, 0x00);
}
}
+ if (!port_detected)
+ release_region(info->port, REGION_SIZE);
restore_flags(flags);
return (port_detected);
@@ -2430,7 +2421,6 @@ static struct tty_operations esp_ops = {
int __init espserial_init(void)
{
int i, offset;
- int region_start;
struct esp_struct * info;
struct esp_struct *last_primary = NULL;
int esp[] = {0x100,0x140,0x180,0x200,0x240,0x280,0x300,0x380};
@@ -2516,7 +2506,7 @@ int __init espserial_init(void)
info->irq = irq[i];
info->line = (i * 8) + (offset / 8);
- if (!autoconfig(info, &region_start)) {
+ if (!autoconfig(info)) {
i++;
offset = 0;
continue;
@@ -2592,7 +2582,6 @@ static void __exit espserial_exit(void)
{
unsigned long flags;
int e1;
- unsigned int region_start, region_end;
struct esp_struct *temp_async;
struct esp_pio_buffer *pio_buf;
@@ -2607,27 +2596,8 @@ static void __exit espserial_exit(void)
while (ports) {
if (ports->port) {
- region_start = region_end = ports->port;
- temp_async = ports;
-
- while (temp_async) {
- if ((region_start - temp_async->port) == 8) {
- region_start = temp_async->port;
- temp_async->port = 0;
- temp_async = ports;
- } else if ((temp_async->port - region_end)
- == 8) {
- region_end = temp_async->port;
- temp_async->port = 0;
- temp_async = ports;
- } else
- temp_async = temp_async->next_port;
- }
-
- release_region(region_start,
- region_end - region_start + 8);
+ release_region(ports->port, REGION_SIZE);
}
-
temp_async = ports->next_port;
kfree(ports);
ports = temp_async;
diff --git a/drivers/char/ftape/lowlevel/fdc-io.c b/drivers/char/ftape/lowlevel/fdc-io.c
index 3b78eda30b19..a3b0f510b1e8 100644
--- a/drivers/char/ftape/lowlevel/fdc-io.c
+++ b/drivers/char/ftape/lowlevel/fdc-io.c
@@ -389,7 +389,7 @@ int fdc_interrupt_wait(unsigned int time)
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
- current->state = TASK_INTERRUPTIBLE;
+ set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&ftape_wait_intr, &wait);
while (!ft_interrupt_seen && (current->state == TASK_INTERRUPTIBLE)) {
timeout = schedule_timeout(timeout);
diff --git a/drivers/char/ftape/lowlevel/ftape-io.c b/drivers/char/ftape/lowlevel/ftape-io.c
index bd3b2f117ce0..b7910d429f34 100644
--- a/drivers/char/ftape/lowlevel/ftape-io.c
+++ b/drivers/char/ftape/lowlevel/ftape-io.c
@@ -32,6 +32,7 @@
#include <asm/system.h>
#include <linux/ioctl.h>
#include <linux/mtio.h>
+#include <linux/delay.h>
#include <linux/ftape.h>
#include <linux/qic117.h>
@@ -96,19 +97,12 @@ void ftape_sleep(unsigned int time)
timeout = ticks;
save_flags(flags);
sti();
- set_current_state(TASK_INTERRUPTIBLE);
- do {
- /* Mmm. Isn't current->blocked == 0xffffffff ?
- */
- if (signal_pending(current)) {
- TRACE(ft_t_err,
- "awoken by non-blocked signal :-(");
- break; /* exit on signal */
- }
- while (current->state != TASK_RUNNING) {
- timeout = schedule_timeout(timeout);
- }
- } while (timeout);
+ msleep_interruptible(jiffies_to_msecs(timeout));
+ /* Mmm. Isn't current->blocked == 0xffffffff ?
+ */
+ if (signal_pending(current)) {
+ TRACE(ft_t_err, "awoken by non-blocked signal :-(");
+ }
restore_flags(flags);
}
TRACE_EXIT;
diff --git a/drivers/char/ftape/zftape/zftape-buffers.c b/drivers/char/ftape/zftape/zftape-buffers.c
index 3d6483983423..ec4fdaabe39b 100644
--- a/drivers/char/ftape/zftape/zftape-buffers.c
+++ b/drivers/char/ftape/zftape/zftape-buffers.c
@@ -27,6 +27,7 @@
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/slab.h>
+#include <linux/delay.h>
#include <linux/zftape.h>
@@ -119,8 +120,7 @@ void *zft_kmalloc(size_t size)
void *new;
while ((new = kmalloc(size, GFP_KERNEL)) == NULL) {
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(HZ/10);
+ msleep_interruptible(100);
}
memset(new, 0, size);
used_memory += size;
diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c
index 45bc1be63669..fd9ed4c3434a 100644
--- a/drivers/char/generic_serial.c
+++ b/drivers/char/generic_serial.c
@@ -26,6 +26,7 @@
#include <linux/mm.h>
#include <linux/generic_serial.h>
#include <linux/interrupt.h>
+#include <linux/delay.h>
#include <asm/semaphore.h>
#include <asm/uaccess.h>
@@ -399,8 +400,7 @@ static int gs_wait_tx_flushed (void * ptr, int timeout)
gs_dprintk (GS_DEBUG_FLUSH, "Expect to finish in %d jiffies "
"(%d chars).\n", jiffies_to_transmit, charsleft);
- set_current_state (TASK_INTERRUPTIBLE);
- schedule_timeout(jiffies_to_transmit);
+ msleep_interruptible(jiffies_to_msecs(jiffies_to_transmit));
if (signal_pending (current)) {
gs_dprintk (GS_DEBUG_FLUSH, "Signal pending. Bombing out: ");
rv = -EINTR;
@@ -767,8 +767,7 @@ void gs_close(struct tty_struct * tty, struct file * filp)
if (port->blocked_open) {
if (port->close_delay) {
- set_current_state (TASK_INTERRUPTIBLE);
- schedule_timeout(port->close_delay);
+ msleep_interruptible(jiffies_to_msecs(port->close_delay));
}
wake_up_interruptible(&port->open_wait);
}
diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c
index 3c0af2cad6ee..8d569ed74bc8 100644
--- a/drivers/char/hvc_console.c
+++ b/drivers/char/hvc_console.c
@@ -37,6 +37,7 @@
#include <linux/tty_flip.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
+#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/hvconsole.h>
#include <asm/vio.h>
@@ -44,7 +45,7 @@
#define HVC_MAJOR 229
#define HVC_MINOR 0
-#define TIMEOUT ((HZ + 99) / 100)
+#define TIMEOUT (10)
/*
* Wait this long per iteration while trying to push buffered data to the
@@ -607,7 +608,7 @@ int khvcd(void *unused)
if (poll_mask == 0)
schedule();
else
- schedule_timeout(TIMEOUT);
+ msleep_interruptible(TIMEOUT);
}
__set_current_state(TASK_RUNNING);
} while (!kthread_should_stop());
diff --git a/drivers/char/ip2main.c b/drivers/char/ip2main.c
index fa3ca6eb5d53..e0cbd7552eb8 100644
--- a/drivers/char/ip2main.c
+++ b/drivers/char/ip2main.c
@@ -1632,8 +1632,7 @@ ip2_close( PTTY tty, struct file *pFile )
if (pCh->wopen) {
if (pCh->ClosingDelay) {
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(pCh->ClosingDelay);
+ msleep_interruptible(jiffies_to_msecs(pCh->ClosingDelay));
}
wake_up_interruptible(&pCh->open_wait);
}
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index 835d6a431a00..6a943d41093c 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -2283,6 +2283,7 @@ void __exit cleanup_one_si(struct smi_info *to_clean)
interface is in a clean state. */
while ((to_clean->curr_msg) || (to_clean->si_state != SI_NORMAL)) {
poll(to_clean);
+ set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(1);
}
diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c
index b454bf2084bf..eb8c18883a9d 100644
--- a/drivers/char/isicom.c
+++ b/drivers/char/isicom.c
@@ -1124,11 +1124,10 @@ static void isicom_close(struct tty_struct * tty, struct file * filp)
port->tty = NULL;
if (port->blocked_open) {
if (port->close_delay) {
- set_current_state(TASK_INTERRUPTIBLE);
#ifdef ISICOM_DEBUG
printk(KERN_DEBUG "ISICOM: scheduling until time out.\n");
#endif
- schedule_timeout(port->close_delay);
+ msleep_interruptible(jiffies_to_msecs(port->close_delay));
}
wake_up_interruptible(&port->open_wait);
}
diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c
index 9460f6d0bf97..832806725c1e 100644
--- a/drivers/char/istallion.c
+++ b/drivers/char/istallion.c
@@ -691,7 +691,6 @@ static int stli_rawopen(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, i
static int stli_rawclose(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait);
static int stli_waitcarrier(stlibrd_t *brdp, stliport_t *portp, struct file *filp);
static void stli_dohangup(void *arg);
-static void stli_delay(int len);
static int stli_setport(stliport_t *portp);
static int stli_cmdwait(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback);
static void stli_sendcmd(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback);
@@ -1180,7 +1179,7 @@ static void stli_close(struct tty_struct *tty, struct file *filp)
if (portp->openwaitcnt) {
if (portp->close_delay)
- stli_delay(portp->close_delay);
+ msleep_interruptible(jiffies_to_msecs(portp->close_delay));
wake_up_interruptible(&portp->open_wait);
}
@@ -1478,25 +1477,6 @@ static int stli_setport(stliport_t *portp)
/*****************************************************************************/
/*
- * Wait for a specified delay period, this is not a busy-loop. It will
- * give up the processor while waiting. Unfortunately this has some
- * rather intimate knowledge of the process management stuff.
- */
-
-static void stli_delay(int len)
-{
-#ifdef DEBUG
- printk("stli_delay(len=%d)\n", len);
-#endif
- if (len > 0) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(len);
- }
-}
-
-/*****************************************************************************/
-
-/*
* Possibly need to wait for carrier (DCD signal) to come high. Say
* maybe because if we are clocal then we don't need to wait...
*/
@@ -2504,7 +2484,7 @@ static void stli_waituntilsent(struct tty_struct *tty, int timeout)
while (test_bit(ST_TXBUSY, &portp->state)) {
if (signal_pending(current))
break;
- stli_delay(2);
+ msleep_interruptible(20);
if (time_after_eq(jiffies, tend))
break;
}
diff --git a/drivers/char/lcd.c b/drivers/char/lcd.c
index 64837783d180..717d812c2526 100644
--- a/drivers/char/lcd.c
+++ b/drivers/char/lcd.c
@@ -24,6 +24,7 @@
#include <linux/mc146818rtc.h>
#include <linux/netdevice.h>
#include <linux/sched.h>
+#include <linux/delay.h>
#include <asm/io.h>
#include <asm/uaccess.h>
@@ -583,8 +584,7 @@ static long lcd_read(struct inode *inode, struct file *file, char *buf,
lcd_waiters++;
while (((buttons_now = (long) button_pressed()) == 0) &&
!(signal_pending(current))) {
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(2 * HZ);
+ msleep_interruptible(2000);
}
lcd_waiters--;
diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c
index d45d381d444f..11373907f29f 100644
--- a/drivers/char/moxa.c
+++ b/drivers/char/moxa.c
@@ -625,8 +625,7 @@ static void moxa_close(struct tty_struct *tty, struct file *filp)
ch->tty = NULL;
if (ch->blocked_open) {
if (ch->close_delay) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(ch->close_delay);
+ msleep_interruptible(jiffies_to_msecs(ch->close_delay));
}
wake_up_interruptible(&ch->open_wait);
}
diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c
index 147c1ba2efe8..9ed493ed2220 100644
--- a/drivers/char/mxser.c
+++ b/drivers/char/mxser.c
@@ -59,6 +59,7 @@
#include <linux/smp_lock.h>
#include <linux/pci.h>
#include <linux/init.h>
+#include <linux/delay.h>
#include <asm/system.h>
#include <asm/io.h>
@@ -818,8 +819,7 @@ static void mxser_close(struct tty_struct *tty, struct file *filp)
info->tty = NULL;
if (info->blocked_open) {
if (info->close_delay) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(info->close_delay);
+ msleep_interruptible(jiffies_to_msecs(info->close_delay));
}
wake_up_interruptible(&info->open_wait);
}
diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c
index 5d7f2154247d..f334dbfcf21f 100644
--- a/drivers/char/pcmcia/synclink_cs.c
+++ b/drivers/char/pcmcia/synclink_cs.c
@@ -2609,8 +2609,7 @@ static void mgslpc_close(struct tty_struct *tty, struct file * filp)
if (info->blocked_open) {
if (info->close_delay) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(info->close_delay);
+ msleep_interruptible(jiffies_to_msecs(info->close_delay));
}
wake_up_interruptible(&info->open_wait);
}
@@ -2665,8 +2664,7 @@ static void mgslpc_wait_until_sent(struct tty_struct *tty, int timeout)
if (info->params.mode == MGSL_MODE_HDLC) {
while (info->tx_active) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(char_time);
+ msleep_interruptible(jiffies_to_msecs(char_time));
if (signal_pending(current))
break;
if (timeout && time_after(jiffies, orig_jiffies + timeout))
@@ -2675,8 +2673,7 @@ static void mgslpc_wait_until_sent(struct tty_struct *tty, int timeout)
} else {
while ((info->tx_count || info->tx_active) &&
info->tx_enabled) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(char_time);
+ msleep_interruptible(jiffies_to_msecs(char_time));
if (signal_pending(current))
break;
if (timeout && time_after(jiffies, orig_jiffies + timeout))
@@ -4129,8 +4126,7 @@ BOOLEAN irq_test(MGSLPC_INFO *info)
end_time=100;
while(end_time-- && !info->irq_occurred) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(msecs_to_jiffies(10));
+ msleep_interruptible(10);
}
info->testing_irq = FALSE;
diff --git a/drivers/char/pcxx.c b/drivers/char/pcxx.c
index 061237bb0c33..97fd9da9c5ef 100644
--- a/drivers/char/pcxx.c
+++ b/drivers/char/pcxx.c
@@ -538,8 +538,7 @@ static void pcxe_close(struct tty_struct * tty, struct file * filp)
info->tty = NULL;
if(info->blocked_open) {
if(info->close_delay) {
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(info->close_delay);
+ msleep_interruptible(jiffies_to_msecs(info->close_delay));
}
wake_up_interruptible(&info->open_wait);
}
diff --git a/drivers/char/rio/rio_linux.c b/drivers/char/rio/rio_linux.c
index 0cc02371a845..a8f443e1461c 100644
--- a/drivers/char/rio/rio_linux.c
+++ b/drivers/char/rio/rio_linux.c
@@ -330,8 +330,7 @@ int RIODelay (struct Port *PortP, int njiffies)
func_enter ();
rio_dprintk (RIO_DEBUG_DELAY, "delaying %d jiffies\n", njiffies);
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(njiffies);
+ msleep_interruptible(jiffies_to_msecs(njiffies));
func_exit();
if (signal_pending(current))
@@ -347,8 +346,7 @@ int RIODelay_ni (struct Port *PortP, int njiffies)
func_enter ();
rio_dprintk (RIO_DEBUG_DELAY, "delaying %d jiffies (ni)\n", njiffies);
- current->state = TASK_UNINTERRUPTIBLE;
- schedule_timeout(njiffies);
+ msleep(jiffies_to_msecs(njiffies));
func_exit();
return !RIO_FAIL;
}
diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c
index a35ea03a3227..616ecc65a06b 100644
--- a/drivers/char/riscom8.c
+++ b/drivers/char/riscom8.c
@@ -45,6 +45,7 @@
#include <linux/fcntl.h>
#include <linux/major.h>
#include <linux/init.h>
+#include <linux/delay.h>
#include <asm/uaccess.h>
@@ -1114,8 +1115,7 @@ static void rc_close(struct tty_struct * tty, struct file * filp)
*/
timeout = jiffies+HZ;
while(port->IER & IER_TXEMPTY) {
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(port->timeout);
+ msleep_interruptible(jiffies_to_msecs(port->timeout));
if (time_after(jiffies, timeout))
break;
}
@@ -1130,8 +1130,7 @@ static void rc_close(struct tty_struct * tty, struct file * filp)
port->tty = NULL;
if (port->blocked_open) {
if (port->close_delay) {
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(port->close_delay);
+ msleep_interruptible(jiffies_to_msecs(port->close_delay));
}
wake_up_interruptible(&port->open_wait);
}
diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c
index 72af1bec91e5..9f13fad3fc11 100644
--- a/drivers/char/rocket.c
+++ b/drivers/char/rocket.c
@@ -1112,8 +1112,7 @@ static void rp_close(struct tty_struct *tty, struct file *filp)
if (info->blocked_open) {
if (info->close_delay) {
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(info->close_delay);
+ msleep_interruptible(jiffies_to_msecs(info->close_delay));
}
wake_up_interruptible(&info->open_wait);
} else {
@@ -1538,8 +1537,7 @@ static void rp_wait_until_sent(struct tty_struct *tty, int timeout)
#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT
printk(KERN_INFO "txcnt = %d (jiff=%lu,check=%d)...", txcnt, jiffies, check_time);
#endif
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(check_time);
+ msleep_interruptible(jiffies_to_msecs(check_time));
if (signal_pending(current))
break;
}
diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c
index 885621edb4a6..b2538a6b85b0 100644
--- a/drivers/char/serial167.c
+++ b/drivers/char/serial167.c
@@ -1840,8 +1840,7 @@ cy_close(struct tty_struct * tty, struct file * filp)
info->tty = 0;
if (info->blocked_open) {
if (info->close_delay) {
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(info->close_delay);
+ msleep_interruptible(jiffies_to_msecs(info->close_delay));
}
wake_up_interruptible(&info->open_wait);
}
diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c
index bf82f06d4011..568edc38313c 100644
--- a/drivers/char/specialix.c
+++ b/drivers/char/specialix.c
@@ -1452,8 +1452,7 @@ static void sx_close(struct tty_struct * tty, struct file * filp)
*/
timeout = jiffies+HZ;
while(port->IER & IER_TXEMPTY) {
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(port->timeout);
+ msleep_interruptible(jiffies_to_msecs(port->timeout));
if (time_after(jiffies, timeout)) {
printk (KERN_INFO "Timeout waiting for close\n");
break;
@@ -1470,8 +1469,7 @@ static void sx_close(struct tty_struct * tty, struct file * filp)
port->tty = NULL;
if (port->blocked_open) {
if (port->close_delay) {
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(port->close_delay);
+ msleep_interruptible(jiffies_to_msecs(port->close_delay));
}
wake_up_interruptible(&port->open_wait);
}
diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c
index 90018ce17c57..38a7e846b813 100644
--- a/drivers/char/stallion.c
+++ b/drivers/char/stallion.c
@@ -42,6 +42,7 @@
#include <linux/smp_lock.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/device.h>
+#include <linux/delay.h>
#include <asm/io.h>
#include <asm/uaccess.h>
@@ -512,7 +513,6 @@ static int stl_clrportstats(stlport_t *portp, comstats_t __user *cp);
static int stl_getportstruct(stlport_t __user *arg);
static int stl_getbrdstruct(stlbrd_t __user *arg);
static int stl_waitcarrier(stlport_t *portp, struct file *filp);
-static void stl_delay(int len);
static void stl_eiointr(stlbrd_t *brdp);
static void stl_echatintr(stlbrd_t *brdp);
static void stl_echmcaintr(stlbrd_t *brdp);
@@ -1204,7 +1204,7 @@ static void stl_close(struct tty_struct *tty, struct file *filp)
if (portp->openwaitcnt) {
if (portp->close_delay)
- stl_delay(portp->close_delay);
+ msleep_interruptible(jiffies_to_msecs(portp->close_delay));
wake_up_interruptible(&portp->open_wait);
}
@@ -1216,25 +1216,6 @@ static void stl_close(struct tty_struct *tty, struct file *filp)
/*****************************************************************************/
/*
- * Wait for a specified delay period, this is not a busy-loop. It will
- * give up the processor while waiting. Unfortunately this has some
- * rather intimate knowledge of the process management stuff.
- */
-
-static void stl_delay(int len)
-{
-#ifdef DEBUG
- printk("stl_delay(len=%d)\n", len);
-#endif
- if (len > 0) {
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(len);
- }
-}
-
-/*****************************************************************************/
-
-/*
* Write routine. Take data and stuff it in to the TX ring queue.
* If transmit interrupts are not running then start them.
*/
@@ -1854,7 +1835,7 @@ static void stl_waituntilsent(struct tty_struct *tty, int timeout)
while (stl_datastate(portp)) {
if (signal_pending(current))
break;
- stl_delay(2);
+ msleep_interruptible(20);
if (time_after_eq(jiffies, tend))
break;
}
diff --git a/drivers/char/sx.c b/drivers/char/sx.c
index a990eb7aa6b5..3f95c4c6dff6 100644
--- a/drivers/char/sx.c
+++ b/drivers/char/sx.c
@@ -1499,7 +1499,7 @@ static void sx_close (void *ptr)
sx_send_command (port, HS_CLOSE, 0, 0);
while (to-- && (sx_read_channel_byte (port, hi_hstat) != HS_IDLE_CLOSED)) {
- current->state = TASK_INTERRUPTIBLE;
+ set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout (1);
if (signal_pending (current))
break;
diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c
index 0a1cc1d3630f..edf89fffe4e1 100644
--- a/drivers/char/synclink.c
+++ b/drivers/char/synclink.c
@@ -82,6 +82,7 @@
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/slab.h>
+#include <linux/delay.h>
#include <linux/netdevice.h>
@@ -3259,8 +3260,7 @@ static void mgsl_close(struct tty_struct *tty, struct file * filp)
if (info->blocked_open) {
if (info->close_delay) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(info->close_delay);
+ msleep_interruptible(jiffies_to_msecs(info->close_delay));
}
wake_up_interruptible(&info->open_wait);
}
@@ -3326,8 +3326,7 @@ static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout)
if ( info->params.mode == MGSL_MODE_HDLC ||
info->params.mode == MGSL_MODE_RAW ) {
while (info->tx_active) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(char_time);
+ msleep_interruptible(jiffies_to_msecs(char_time));
if (signal_pending(current))
break;
if (timeout && time_after(jiffies, orig_jiffies + timeout))
@@ -3336,8 +3335,7 @@ static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout)
} else {
while (!(usc_InReg(info,TCSR) & TXSTATUS_ALL_SENT) &&
info->tx_enabled) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(char_time);
+ msleep_interruptible(jiffies_to_msecs(char_time));
if (signal_pending(current))
break;
if (timeout && time_after(jiffies, orig_jiffies + timeout))
@@ -7200,8 +7198,7 @@ BOOLEAN mgsl_irq_test( struct mgsl_struct *info )
EndTime=100;
while( EndTime-- && !info->irq_occurred ) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(msecs_to_jiffies(10));
+ msleep_interruptible(10);
}
spin_lock_irqsave(&info->irq_spinlock,flags);
diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c
index 5648ed35562f..9ccce79fdf4e 100644
--- a/drivers/char/synclinkmp.c
+++ b/drivers/char/synclinkmp.c
@@ -878,8 +878,7 @@ static void close(struct tty_struct *tty, struct file *filp)
if (info->blocked_open) {
if (info->close_delay) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(info->close_delay);
+ msleep_interruptible(jiffies_to_msecs(info->close_delay));
}
wake_up_interruptible(&info->open_wait);
}
@@ -1164,8 +1163,7 @@ static void wait_until_sent(struct tty_struct *tty, int timeout)
if ( info->params.mode == MGSL_MODE_HDLC ) {
while (info->tx_active) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(char_time);
+ msleep_interruptible(jiffies_to_msecs(char_time));
if (signal_pending(current))
break;
if (timeout && time_after(jiffies, orig_jiffies + timeout))
@@ -1175,8 +1173,7 @@ static void wait_until_sent(struct tty_struct *tty, int timeout)
//TODO: determine if there is something similar to USC16C32
// TXSTATUS_ALL_SENT status
while ( info->tx_active && info->tx_enabled) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(char_time);
+ msleep_interruptible(jiffies_to_msecs(char_time));
if (signal_pending(current))
break;
if (timeout && time_after(jiffies, orig_jiffies + timeout))
@@ -5209,8 +5206,7 @@ int irq_test(SLMP_INFO *info)
timeout=100;
while( timeout-- && !info->irq_occurred ) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(msecs_to_jiffies(10));
+ msleep_interruptible(10);
}
spin_lock_irqsave(&info->lock,flags);
@@ -5360,8 +5356,7 @@ int loopback_test(SLMP_INFO *info)
/* wait for receive complete */
/* Set a timeout for waiting for interrupt. */
for ( timeout = 100; timeout; --timeout ) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(msecs_to_jiffies(10));
+ msleep_interruptible(10);
if (rx_get_frame(info)) {
rc = TRUE;
diff --git a/drivers/char/tpqic02.c b/drivers/char/tpqic02.c
index 66881e953399..d812253b4473 100644
--- a/drivers/char/tpqic02.c
+++ b/drivers/char/tpqic02.c
@@ -554,10 +554,9 @@ static int wait_for_ready(time_t timeout)
/* not ready and no exception && timeout not expired yet */
while (((stat = inb_p(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK) && time_before(jiffies, spin_t)) {
/* be `nice` to other processes on long operations... */
- current->state = TASK_INTERRUPTIBLE;
/* nap 0.30 sec between checks, */
/* but could be woken up earlier by signals... */
- schedule_timeout(3 * HZ / 10);
+ msleep_interruptible(300);
}
/* don't use jiffies for this test because it may have changed by now */
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index 89457cc2ed72..9d5e4ba9dc25 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -1981,10 +1981,10 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,
static int tioccons(struct file *file)
{
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
if (file->f_op->write == redirected_tty_write) {
struct file *f;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
spin_lock(&redirect_lock);
f = redirect;
redirect = NULL;
diff --git a/drivers/char/vt.c b/drivers/char/vt.c
index 31da37da5f40..cffa77b92629 100644
--- a/drivers/char/vt.c
+++ b/drivers/char/vt.c
@@ -3077,6 +3077,10 @@ int con_font_get(int currcons, struct console_font_op *op)
if (rc)
goto out;
+ op->height = font.height;
+ op->width = font.width;
+ op->charcount = font.charcount;
+
if (op->data && copy_to_user(op->data, font.data, c))
rc = -EFAULT;
diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c
index cc6b66e34e37..895f245fcced 100644
--- a/drivers/ide/ide-cd.c
+++ b/drivers/ide/ide-cd.c
@@ -1932,6 +1932,8 @@ static ide_startstop_t cdrom_start_write(ide_drive_t *drive, struct request *rq)
info->dma = drive->using_dma ? 1 : 0;
info->cmd = WRITE;
+ info->devinfo.media_written = 1;
+
/* Start sending the write request to the drive. */
return cdrom_start_packet_command(drive, 32768, cdrom_start_write_cont);
}
@@ -1999,7 +2001,7 @@ ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, sector_t block)
}
CDROM_CONFIG_FLAGS(drive)->seeking = 0;
}
- if (IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap) {
+ if ((rq_data_dir(rq) == READ) && IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap) {
action = cdrom_start_seek(drive, block);
} else {
if (rq_data_dir(rq) == READ)
@@ -2960,8 +2962,10 @@ int ide_cdrom_probe_capabilities (ide_drive_t *drive)
CDROM_CONFIG_FLAGS(drive)->no_eject = 0;
if (cap.cd_r_write)
CDROM_CONFIG_FLAGS(drive)->cd_r = 1;
- if (cap.cd_rw_write)
+ if (cap.cd_rw_write) {
CDROM_CONFIG_FLAGS(drive)->cd_rw = 1;
+ CDROM_CONFIG_FLAGS(drive)->ram = 1;
+ }
if (cap.test_write)
CDROM_CONFIG_FLAGS(drive)->test_write = 1;
if (cap.dvd_ram_read || cap.dvd_r_read || cap.dvd_rom)
diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c
index cc9c999f9a8b..2a9ff6f0229b 100644
--- a/drivers/ieee1394/nodemgr.c
+++ b/drivers/ieee1394/nodemgr.c
@@ -70,8 +70,7 @@ static int nodemgr_bus_read(struct csr1212_csr *csr, u64 addr, u16 length,
if (!ret)
break;
- set_current_state(TASK_INTERRUPTIBLE);
- if (schedule_timeout (HZ/3))
+ if (msleep_interruptible(334))
return -EINTR;
}
@@ -1496,7 +1495,7 @@ static int nodemgr_host_thread(void *__hi)
* to make sure things settle down. */
for (i = 0; i < 4 ; i++) {
set_current_state(TASK_INTERRUPTIBLE);
- if (schedule_timeout(HZ/16)) {
+ if (msleep_interruptible(63)) {
up(&nodemgr_serialize);
goto caught_signal;
}
diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c
index 47de79f21797..aa9ab3378e4c 100644
--- a/drivers/ieee1394/sbp2.c
+++ b/drivers/ieee1394/sbp2.c
@@ -357,8 +357,7 @@ static int sbp2util_down_timeout(atomic_t *done, int timeout)
int i;
for (i = timeout; (i > 0 && atomic_read(done) == 0); i-= HZ/10) {
- set_current_state(TASK_INTERRUPTIBLE);
- if (schedule_timeout(HZ/10)) /* 100ms */
+ if (msleep_interruptible(100)) /* 100ms */
return(1);
}
return ((i > 0) ? 0:1);
@@ -1088,7 +1087,7 @@ static int sbp2_query_logins(struct scsi_id_instance_data *scsi_id)
scsi_id->query_logins_orb->query_response_hi = ORB_SET_NODE_ID(hi->host->node_id);
SBP2_DEBUG("sbp2_query_logins: query_response_hi/lo initialized");
- scsi_id->query_logins_orb->lun_misc = ORB_SET_FUNCTION(QUERY_LOGINS_REQUEST);
+ scsi_id->query_logins_orb->lun_misc = ORB_SET_FUNCTION(SBP2_QUERY_LOGINS_REQUEST);
scsi_id->query_logins_orb->lun_misc |= ORB_SET_NOTIFY(1);
if (scsi_id->sbp2_device_type_and_lun != SBP2_DEVICE_TYPE_LUN_UNINITIALIZED) {
scsi_id->query_logins_orb->lun_misc |= ORB_SET_LUN(scsi_id->sbp2_device_type_and_lun);
@@ -1199,7 +1198,7 @@ static int sbp2_login_device(struct scsi_id_instance_data *scsi_id)
scsi_id->login_orb->login_response_hi = ORB_SET_NODE_ID(hi->host->node_id);
SBP2_DEBUG("sbp2_login_device: login_response_hi/lo initialized");
- scsi_id->login_orb->lun_misc = ORB_SET_FUNCTION(LOGIN_REQUEST);
+ scsi_id->login_orb->lun_misc = ORB_SET_FUNCTION(SBP2_LOGIN_REQUEST);
scsi_id->login_orb->lun_misc |= ORB_SET_RECONNECT(0); /* One second reconnect time */
scsi_id->login_orb->lun_misc |= ORB_SET_EXCLUSIVE(exclusive_login); /* Exclusive access to device */
scsi_id->login_orb->lun_misc |= ORB_SET_NOTIFY(1); /* Notify us of login complete */
@@ -1325,7 +1324,7 @@ static int sbp2_logout_device(struct scsi_id_instance_data *scsi_id)
scsi_id->logout_orb->reserved3 = 0x0;
scsi_id->logout_orb->reserved4 = 0x0;
- scsi_id->logout_orb->login_ID_misc = ORB_SET_FUNCTION(LOGOUT_REQUEST);
+ scsi_id->logout_orb->login_ID_misc = ORB_SET_FUNCTION(SBP2_LOGOUT_REQUEST);
scsi_id->logout_orb->login_ID_misc |= ORB_SET_LOGIN_ID(scsi_id->login_response->length_login_ID);
/* Notify us when complete */
@@ -1390,7 +1389,7 @@ static int sbp2_reconnect_device(struct scsi_id_instance_data *scsi_id)
scsi_id->reconnect_orb->reserved3 = 0x0;
scsi_id->reconnect_orb->reserved4 = 0x0;
- scsi_id->reconnect_orb->login_ID_misc = ORB_SET_FUNCTION(RECONNECT_REQUEST);
+ scsi_id->reconnect_orb->login_ID_misc = ORB_SET_FUNCTION(SBP2_RECONNECT_REQUEST);
scsi_id->reconnect_orb->login_ID_misc |=
ORB_SET_LOGIN_ID(scsi_id->login_response->length_login_ID);
diff --git a/drivers/ieee1394/sbp2.h b/drivers/ieee1394/sbp2.h
index d12f8e160a80..94b59f80bf90 100644
--- a/drivers/ieee1394/sbp2.h
+++ b/drivers/ieee1394/sbp2.h
@@ -52,15 +52,15 @@ struct sbp2_command_orb {
u8 cdb[12];
};
-#define LOGIN_REQUEST 0x0
-#define QUERY_LOGINS_REQUEST 0x1
-#define RECONNECT_REQUEST 0x3
-#define SET_PASSWORD_REQUEST 0x4
-#define LOGOUT_REQUEST 0x7
-#define ABORT_TASK_REQUEST 0xb
-#define ABORT_TASK_SET 0xc
-#define LOGICAL_UNIT_RESET 0xe
-#define TARGET_RESET_REQUEST 0xf
+#define SBP2_LOGIN_REQUEST 0x0
+#define SBP2_QUERY_LOGINS_REQUEST 0x1
+#define SBP2_RECONNECT_REQUEST 0x3
+#define SBP2_SET_PASSWORD_REQUEST 0x4
+#define SBP2_LOGOUT_REQUEST 0x7
+#define SBP2_ABORT_TASK_REQUEST 0xb
+#define SBP2_ABORT_TASK_SET 0xc
+#define SBP2_LOGICAL_UNIT_RESET 0xe
+#define SBP2_TARGET_RESET_REQUEST 0xf
#define ORB_SET_LUN(value) (value & 0xffff)
#define ORB_SET_FUNCTION(value) ((value & 0xf) << 16)
diff --git a/drivers/isdn/act2000/act2000_isa.c b/drivers/isdn/act2000/act2000_isa.c
index b363d0976465..df7923c5b843 100644
--- a/drivers/isdn/act2000/act2000_isa.c
+++ b/drivers/isdn/act2000/act2000_isa.c
@@ -18,13 +18,6 @@
static act2000_card *irq2card_map[16];
-static void
-act2000_isa_delay(long t)
-{
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(t);
-}
-
/*
* Reset Controller, then try to read the Card's signature.
+ Return:
@@ -419,7 +412,7 @@ act2000_isa_download(act2000_card * card, act2000_ddef __user * cb)
if (!act2000_isa_reset(card->port))
return -ENXIO;
- act2000_isa_delay(HZ / 2);
+ msleep_interruptible(500);
if(copy_from_user(&cblock, cb, sizeof(cblock)))
return -EFAULT;
length = cblock.length;
@@ -452,6 +445,6 @@ act2000_isa_download(act2000_card * card, act2000_ddef __user * cb)
p += l;
}
kfree(buf);
- act2000_isa_delay(HZ / 2);
+ msleep_interruptible(500);
return (act2000_isa_getid(card));
}
diff --git a/drivers/isdn/capi/kcapi.c b/drivers/isdn/capi/kcapi.c
index 8d58cfd45b0e..8cd4dde06a81 100644
--- a/drivers/isdn/capi/kcapi.c
+++ b/drivers/isdn/capi/kcapi.c
@@ -24,6 +24,7 @@
#include <linux/capi.h>
#include <linux/kernelcapi.h>
#include <linux/init.h>
+#include <linux/delay.h>
#include <asm/uaccess.h>
#include <linux/isdn/capicmd.h>
#include <linux/isdn/capiutil.h>
@@ -831,8 +832,7 @@ static int old_capi_manufacturer(unsigned int cmd, void __user *data)
while (card->cardstate != CARD_RUNNING) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ/10); /* 0.1 sec */
+ msleep_interruptible(100); /* 0.1 sec */
if (signal_pending(current)) {
capi_ctr_put(card);
@@ -856,8 +856,7 @@ static int old_capi_manufacturer(unsigned int cmd, void __user *data)
while (card->cardstate > CARD_DETECTED) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ/10); /* 0.1 sec */
+ msleep_interruptible(100); /* 0.1 sec */
if (signal_pending(current))
return -EINTR;
diff --git a/drivers/isdn/hisax/config.c b/drivers/isdn/hisax/config.c
index 26bb71d96b38..15b80c844205 100644
--- a/drivers/isdn/hisax/config.c
+++ b/drivers/isdn/hisax/config.c
@@ -843,9 +843,8 @@ static int init_card(struct IsdnCardState *cs)
}
while (cnt) {
cs->cardmsg(cs, CARD_INIT, NULL);
- set_current_state(TASK_UNINTERRUPTIBLE);
/* Timeout 10ms */
- schedule_timeout((10 * HZ) / 1000);
+ msleep(10);
printk(KERN_INFO "%s: IRQ %d count %d\n",
CardType[cs->typ], cs->irq, kstat_irqs(cs->irq));
if (kstat_irqs(cs->irq) == irq_cnt) {
diff --git a/drivers/isdn/hisax/elsa.c b/drivers/isdn/hisax/elsa.c
index 3b526d704fa6..21271465bd2f 100644
--- a/drivers/isdn/hisax/elsa.c
+++ b/drivers/isdn/hisax/elsa.c
@@ -691,8 +691,7 @@ Elsa_card_msg(struct IsdnCardState *cs, int mt, void *arg)
byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
byteout(cs->hw.elsa.timer, 0);
spin_unlock_irqrestore(&cs->lock, flags);
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout((110*HZ)/1000);
+ msleep(110);
spin_lock_irqsave(&cs->lock, flags);
cs->hw.elsa.ctrl_reg &= ~ELSA_ENA_TIMER_INT;
byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
diff --git a/drivers/isdn/hisax/hfc_pci.c b/drivers/isdn/hisax/hfc_pci.c
index 3946536d112b..c2db52696a86 100644
--- a/drivers/isdn/hisax/hfc_pci.c
+++ b/drivers/isdn/hisax/hfc_pci.c
@@ -1619,8 +1619,7 @@ hfcpci_card_msg(struct IsdnCardState *cs, int mt, void *arg)
inithfcpci(cs);
reset_hfcpci(cs);
spin_unlock_irqrestore(&cs->lock, flags);
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout((80 * HZ) / 1000); /* Timeout 80ms */
+ msleep(80); /* Timeout 80ms */
/* now switch timer interrupt off */
spin_lock_irqsave(&cs->lock, flags);
cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_TIMER;
diff --git a/drivers/isdn/hisax/hfc_sx.c b/drivers/isdn/hisax/hfc_sx.c
index 07a07aab792b..685fcc2d7256 100644
--- a/drivers/isdn/hisax/hfc_sx.c
+++ b/drivers/isdn/hisax/hfc_sx.c
@@ -314,8 +314,7 @@ release_io_hfcsx(struct IsdnCardState *cs)
cs->hw.hfcsx.int_m2 = 0; /* interrupt output off ! */
Write_hfc(cs, HFCSX_INT_M2, cs->hw.hfcsx.int_m2);
Write_hfc(cs, HFCSX_CIRM, HFCSX_RESET); /* Reset On */
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout((30 * HZ) / 1000); /* Timeout 30ms */
+ msleep(30); /* Timeout 30ms */
Write_hfc(cs, HFCSX_CIRM, 0); /* Reset Off */
del_timer(&cs->hw.hfcsx.timer);
release_region(cs->hw.hfcsx.base, 2); /* release IO-Block */
@@ -1367,8 +1366,7 @@ hfcsx_card_msg(struct IsdnCardState *cs, int mt, void *arg)
spin_lock_irqsave(&cs->lock, flags);
inithfcsx(cs);
spin_unlock_irqrestore(&cs->lock, flags);
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout((80 * HZ) / 1000); /* Timeout 80ms */
+ msleep(80); /* Timeout 80ms */
/* now switch timer interrupt off */
spin_lock_irqsave(&cs->lock, flags);
cs->hw.hfcsx.int_m1 &= ~HFCSX_INTS_TIMER;
diff --git a/drivers/isdn/hisax/hfcscard.c b/drivers/isdn/hisax/hfcscard.c
index ea8da997039f..6fc55fea1702 100644
--- a/drivers/isdn/hisax/hfcscard.c
+++ b/drivers/isdn/hisax/hfcscard.c
@@ -125,8 +125,7 @@ hfcs_card_msg(struct IsdnCardState *cs, int mt, void *arg)
init2bds0(cs);
spin_unlock_irqrestore(&cs->lock, flags);
delay = (80*HZ)/1000 +1;
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout((80*HZ)/1000);
+ msleep(80);
spin_lock_irqsave(&cs->lock, flags);
cs->hw.hfcD.ctmt |= HFCD_TIM800;
cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt);
diff --git a/drivers/isdn/hysdn/boardergo.c b/drivers/isdn/hysdn/boardergo.c
index 2f2731520d40..e19a01a305a9 100644
--- a/drivers/isdn/hysdn/boardergo.c
+++ b/drivers/isdn/hysdn/boardergo.c
@@ -21,6 +21,7 @@
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/vmalloc.h>
+#include <linux/delay.h>
#include <asm/io.h>
#include "hysdn_defs.h"
@@ -246,8 +247,7 @@ ergo_writebootimg(struct HYSDN_CARD *card, uchar * buf, ulong offs)
/* the interrupts are still masked */
sti();
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout((20 * HZ) / 1000); /* Timeout 20ms */
+ msleep_interruptible(20); /* Timeout 20ms */
if (((tDpramBootSpooler *) card->dpram)->Len != DPRAM_SPOOLER_DATA_SIZE) {
if (card->debug_flags & LOG_POF_CARD)
@@ -386,8 +386,7 @@ ergo_waitpofready(struct HYSDN_CARD *card)
return (0); /* success */
} /* data has arrived */
sti();
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout((50 * HZ) / 1000); /* Timeout 50ms */
+ msleep_interruptible(50); /* Timeout 50ms */
} /* wait until timeout */
if (card->debug_flags & LOG_POF_CARD)
diff --git a/drivers/isdn/hysdn/hysdn_sched.c b/drivers/isdn/hysdn/hysdn_sched.c
index 1a2015ecfda5..4fa3b01707cd 100644
--- a/drivers/isdn/hysdn/hysdn_sched.c
+++ b/drivers/isdn/hysdn/hysdn_sched.c
@@ -17,6 +17,7 @@
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
+#include <linux/delay.h>
#include <asm/io.h>
#include "hysdn_defs.h"
@@ -160,8 +161,7 @@ hysdn_tx_cfgline(hysdn_card * card, uchar * line, word chan)
if (card->debug_flags & LOG_SCHED_ASYN)
hysdn_addlog(card, "async tx-cfg delayed");
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout((20 * HZ) / 1000); /* Timeout 20ms */
+ msleep_interruptible(20); /* Timeout 20ms */
if (!--cnt) {
restore_flags(flags);
return (-ERR_ASYNC_TIME); /* timed out */
@@ -190,8 +190,7 @@ hysdn_tx_cfgline(hysdn_card * card, uchar * line, word chan)
if (card->debug_flags & LOG_SCHED_ASYN)
hysdn_addlog(card, "async tx-cfg waiting for tx-ready");
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout((20 * HZ) / 1000); /* Timeout 20ms */
+ msleep_interruptible(20); /* Timeout 20ms */
if (!--cnt) {
restore_flags(flags);
return (-ERR_ASYNC_TIME); /* timed out */
diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c
index 921c3c2ab97a..6b6d839930f1 100644
--- a/drivers/isdn/i4l/isdn_tty.c
+++ b/drivers/isdn/i4l/isdn_tty.c
@@ -13,6 +13,7 @@
#include <linux/config.h>
#include <linux/isdn.h>
+#include <linux/delay.h>
#include "isdn_common.h"
#include "isdn_tty.h"
#ifdef CONFIG_ISDN_AUDIO
@@ -1748,8 +1749,7 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp)
tty->closing = 0;
module_put(info->owner);
if (info->blocked_open) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ/2);
+ msleep_interruptible(500);
wake_up_interruptible(&info->open_wait);
}
info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CLOSING);
diff --git a/drivers/isdn/icn/icn.c b/drivers/isdn/icn/icn.c
index 0c256d698686..70989aa4c157 100644
--- a/drivers/isdn/icn/icn.c
+++ b/drivers/isdn/icn/icn.c
@@ -762,8 +762,7 @@ icn_check_loader(int cardnumber)
#ifdef BOOT_DEBUG
printk(KERN_DEBUG "Loader %d TO?\n", cardnumber);
#endif
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(ICN_BOOT_TIMEOUT1);
+ msleep_interruptible(ICN_BOOT_TIMEOUT1);
} else {
#ifdef BOOT_DEBUG
printk(KERN_DEBUG "Loader %d OK\n", cardnumber);
@@ -788,8 +787,7 @@ icn_check_loader(int cardnumber)
int slsec = sec; \
printk(KERN_DEBUG "SLEEP(%d)\n",slsec); \
while (slsec) { \
- current->state = TASK_INTERRUPTIBLE; \
- schedule_timeout(HZ); \
+ msleep_interruptible(1000); \
slsec--; \
} \
}
@@ -950,7 +948,7 @@ icn_loadproto(u_char __user * buffer, icn_card * card)
icn_maprelease_channel(card, 0);
return -EIO;
}
- current->state = TASK_INTERRUPTIBLE;
+ set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(10);
}
}
@@ -974,8 +972,7 @@ icn_loadproto(u_char __user * buffer, icn_card * card)
#ifdef BOOT_DEBUG
printk(KERN_DEBUG "Proto TO?\n");
#endif
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(ICN_BOOT_TIMEOUT1);
+ msleep_interruptible(ICN_BOOT_TIMEOUT1);
} else {
if ((card->secondhalf) || (!card->doubleS0)) {
#ifdef BOOT_DEBUG
@@ -1271,9 +1268,9 @@ icn_command(isdn_ctrl * c, icn_card * card)
if (!card->leased) {
card->leased = 1;
while (card->ptype == ISDN_PTYPE_UNKNOWN) {
- schedule_timeout(ICN_BOOT_TIMEOUT1);
+ msleep_interruptible(ICN_BOOT_TIMEOUT1);
}
- schedule_timeout(ICN_BOOT_TIMEOUT1);
+ msleep_interruptible(ICN_BOOT_TIMEOUT1);
sprintf(cbuf, "00;FV2ON\n01;EAZ%c\n02;EAZ%c\n",
(a & 1)?'1':'C', (a & 2)?'2':'C');
i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
diff --git a/drivers/isdn/icn/icn.h b/drivers/isdn/icn/icn.h
index 4ab15c917ed4..28548318a389 100644
--- a/drivers/isdn/icn/icn.h
+++ b/drivers/isdn/icn/icn.h
@@ -70,8 +70,7 @@ typedef struct icn_cdef {
#define ICN_FLAGS_RUNNING 4 /* Cards driver activated */
#define ICN_FLAGS_RBTIMER 8 /* cyclic scheduling of B-Channel-poll */
-#define ICN_BOOT_TIMEOUT1 (HZ) /* Delay for Boot-download (jiffies) */
-#define ICN_CHANLOCK_DELAY (HZ/10) /* Delay for Channel-mapping (jiffies) */
+#define ICN_BOOT_TIMEOUT1 1000 /* Delay for Boot-download (msecs) */
#define ICN_TIMER_BCREAD (HZ/100) /* B-Channel poll-cycle */
#define ICN_TIMER_DCREAD (HZ/2) /* D-Channel poll-cycle */
diff --git a/drivers/isdn/isdnloop/isdnloop.c b/drivers/isdn/isdnloop/isdnloop.c
index 544f41b413ed..7f17ab1ac7ee 100644
--- a/drivers/isdn/isdnloop/isdnloop.c
+++ b/drivers/isdn/isdnloop/isdnloop.c
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/init.h>
+#include <linux/sched.h>
#include "isdnloop.h"
static char *revision = "$Revision: 1.11.6.7 $";
@@ -1161,8 +1162,10 @@ isdnloop_command(isdn_ctrl * c, isdnloop_card * card)
if (!card->leased) {
card->leased = 1;
while (card->ptype == ISDN_PTYPE_UNKNOWN) {
+ set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(10);
}
+ set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(10);
sprintf(cbuf, "00;FV2ON\n01;EAZ1\n02;EAZ2\n");
i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
diff --git a/drivers/isdn/sc/card.h b/drivers/isdn/sc/card.h
index bdb27fae6f20..8e44928cdf1c 100644
--- a/drivers/isdn/sc/card.h
+++ b/drivers/isdn/sc/card.h
@@ -24,24 +24,25 @@
* We need these if they're not already included
*/
#include <linux/timer.h>
+#include <linux/time.h>
#include <linux/isdnif.h>
#include "message.h"
/*
* Amount of time to wait for a reset to complete
*/
-#define CHECKRESET_TIME milliseconds(4000)
+#define CHECKRESET_TIME msecs_to_jiffies(4000)
/*
* Amount of time between line status checks
*/
-#define CHECKSTAT_TIME milliseconds(8000)
+#define CHECKSTAT_TIME msecs_to_jiffies(8000)
/*
* The maximum amount of time to wait for a message response
* to arrive. Use exclusively by send_and_receive
*/
-#define SAR_TIMEOUT milliseconds(10000)
+#define SAR_TIMEOUT msecs_to_jiffies(10000)
/*
* Macro to determine is a card id is valid
diff --git a/drivers/isdn/sc/hardware.h b/drivers/isdn/sc/hardware.h
index adde8fb14665..9e6d5302bf8e 100644
--- a/drivers/isdn/sc/hardware.h
+++ b/drivers/isdn/sc/hardware.h
@@ -104,9 +104,6 @@
* Some handy macros
*/
-/* Return the number of jiffies in a given number of msecs */
-#define milliseconds(x) (((x)*HZ)/1000)
-
/* Determine if a channel number is valid for the adapter */
#define IS_VALID_CHANNEL(y,x) ((x>0) && (x <= sc_adapter[y]->channels))
diff --git a/drivers/isdn/sc/init.c b/drivers/isdn/sc/init.c
index 930845cff800..a10c6af42a97 100644
--- a/drivers/isdn/sc/init.c
+++ b/drivers/isdn/sc/init.c
@@ -7,6 +7,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
+#include <linux/delay.h>
#include "includes.h"
#include "hardware.h"
#include "card.h"
@@ -167,8 +168,7 @@ static int __init sc_init(void)
if(do_reset) {
pr_debug("Doing a SAFE probe reset\n");
outb(0xFF, io[b] + RESET_OFFSET);
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(milliseconds(10000));
+ msleep_interruptible(10000);
}
pr_debug("RAM Base for board %d is 0x%x, %s probe\n", b, ram[b],
ram[b] == 0 ? "will" : "won't");
@@ -500,8 +500,7 @@ int identify_board(unsigned long rambase, unsigned int iobase)
* Try to identify a PRI card
*/
outb(PRI_BASEPG_VAL, pgport);
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ);
+ msleep_interruptible(1000);
sig = readl(rambase + SIG_OFFSET);
pr_debug("Looking for a signature, got 0x%x\n", sig);
if(sig == SIGNATURE)
@@ -511,8 +510,7 @@ int identify_board(unsigned long rambase, unsigned int iobase)
* Try to identify a PRI card
*/
outb(BRI_BASEPG_VAL, pgport);
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ);
+ msleep_interruptible(1000);
sig = readl(rambase + SIG_OFFSET);
pr_debug("Looking for a signature, got 0x%x\n", sig);
if(sig == SIGNATURE)
diff --git a/drivers/isdn/tpam/tpam.h b/drivers/isdn/tpam/tpam.h
index c1456d4b6170..da3319453cd0 100644
--- a/drivers/isdn/tpam/tpam.h
+++ b/drivers/isdn/tpam/tpam.h
@@ -14,6 +14,8 @@
#ifndef _TPAM_PRIV_H_
#define _TPAM_PRIV_H_
+//#define DEBUG /* uncomment if you want debugging output */
+#include <linux/kernel.h>
#include <linux/isdnif.h>
#include <linux/init.h>
#include <linux/workqueue.h>
@@ -224,13 +226,4 @@ extern void hdlc_encode_modem(u8 *, u32, u8 *, u32 *);
extern void hdlc_no_accm_encode(u8 *, u32, u8 *, u32 *);
extern u32 hdlc_no_accm_decode(u8 *, u32);
-/* Define this to enable debug tracing prints */
-#undef DEBUG
-
-#ifdef DEBUG
-#define dprintk printk
-#else
-#define dprintk while(0) printk
-#endif
-
#endif /* _TPAM_H_ */
diff --git a/drivers/isdn/tpam/tpam_commands.c b/drivers/isdn/tpam/tpam_commands.c
index 9cf6ef14fb1d..60f75184a320 100644
--- a/drivers/isdn/tpam/tpam_commands.c
+++ b/drivers/isdn/tpam/tpam_commands.c
@@ -45,7 +45,7 @@ int tpam_command(isdn_ctrl *c) {
tpam_card *card;
unsigned long argp;
- dprintk("TurboPAM(tpam_command) card=%d, command=%d\n",
+ pr_debug("TurboPAM(tpam_command) card=%d, command=%d\n",
c->driver, c->command);
/* search for the board */
@@ -75,7 +75,7 @@ int tpam_command(isdn_ctrl *c) {
return tpam_command_ioctl_loopmode(card,
0);
default:
- dprintk("TurboPAM(tpam_command): "
+ pr_debug("TurboPAM(tpam_command): "
"invalid tpam ioctl %ld\n",
c->arg);
return -EINVAL;
@@ -95,7 +95,7 @@ int tpam_command(isdn_ctrl *c) {
case ISDN_CMD_PROCEED:
return tpam_command_proceed(card, c->arg);
default:
- dprintk("TurboPAM(tpam_command): "
+ pr_debug("TurboPAM(tpam_command): "
"unknown or unused isdn ioctl %d\n",
c->command);
return -EINVAL;
@@ -117,7 +117,7 @@ int tpam_command(isdn_ctrl *c) {
static int tpam_command_ioctl_dspload(tpam_card *card, u32 arg) {
tpam_dsp_ioctl tdl;
- dprintk("TurboPAM(tpam_command_ioctl_dspload): card=%d\n", card->id);
+ pr_debug("TurboPAM(tpam_command_ioctl_dspload): card=%d\n", card->id);
/* get the IOCTL parameter from userspace */
if (copy_from_user(&tdl, (void __user *)arg, sizeof(tpam_dsp_ioctl)))
@@ -147,7 +147,7 @@ static int tpam_command_ioctl_dspload(tpam_card *card, u32 arg) {
static int tpam_command_ioctl_dspsave(tpam_card *card, u32 arg) {
tpam_dsp_ioctl tdl;
- dprintk("TurboPAM(tpam_command_ioctl_dspsave): card=%d\n", card->id);
+ pr_debug("TurboPAM(tpam_command_ioctl_dspsave): card=%d\n", card->id);
/* get the IOCTL parameter from userspace */
if (copy_from_user(&tdl, (void __user *)arg, sizeof(tpam_dsp_ioctl)))
@@ -178,7 +178,7 @@ static int tpam_command_ioctl_dsprun(tpam_card *card) {
isdn_ctrl ctrl;
struct sk_buff *skb;
- dprintk("TurboPAM(tpam_command_ioctl_dsprun): card=%d\n", card->id);
+ pr_debug("TurboPAM(tpam_command_ioctl_dsprun): card=%d\n", card->id);
/* board must _not_ be running */
if (card->running)
@@ -297,7 +297,7 @@ static int tpam_command_dial(tpam_card *card, u32 channel, u8 *phone) {
struct sk_buff *skb;
isdn_ctrl ctrl;
- dprintk("TurboPAM(tpam_command_dial): card=%d, channel=%lu, phone=%s\n",
+ pr_debug("TurboPAM(tpam_command_dial): card=%d, channel=%lu, phone=%s\n",
card->id, (unsigned long)channel, phone);
/* board must be running */
@@ -341,7 +341,7 @@ static int tpam_command_dial(tpam_card *card, u32 channel, u8 *phone) {
*/
static int tpam_command_setl2(tpam_card *card, u32 channel, u8 proto) {
- dprintk("TurboPAM(tpam_command_setl2): card=%d, channel=%lu, proto=%d\n",
+ pr_debug("TurboPAM(tpam_command_setl2): card=%d, channel=%lu, proto=%d\n",
card->id, (unsigned long)channel, proto);
/* board must be running */
@@ -376,7 +376,7 @@ static int tpam_command_acceptd(tpam_card *card, u32 channel) {
isdn_ctrl ctrl;
struct sk_buff *skb;
- dprintk("TurboPAM(tpam_command_acceptd): card=%d, channel=%lu\n",
+ pr_debug("TurboPAM(tpam_command_acceptd): card=%d, channel=%lu\n",
card->id, (unsigned long)channel);
/* board must be running */
@@ -410,7 +410,7 @@ static int tpam_command_acceptd(tpam_card *card, u32 channel) {
static int tpam_command_acceptb(tpam_card *card, u32 channel) {
isdn_ctrl ctrl;
- dprintk("TurboPAM(tpam_command_acceptb): card=%d, channel=%lu\n",
+ pr_debug("TurboPAM(tpam_command_acceptb): card=%d, channel=%lu\n",
card->id, (unsigned long)channel);
/* board must be running */
@@ -437,7 +437,7 @@ static int tpam_command_acceptb(tpam_card *card, u32 channel) {
static int tpam_command_hangup(tpam_card *card, u32 channel) {
struct sk_buff *skb;
- dprintk("TurboPAM(tpam_command_hangup): card=%d, channel=%lu\n",
+ pr_debug("TurboPAM(tpam_command_hangup): card=%d, channel=%lu\n",
card->id, (unsigned long)channel);
/* board must be running */
@@ -464,7 +464,7 @@ static int tpam_command_hangup(tpam_card *card, u32 channel) {
static int tpam_command_proceed(tpam_card *card, u32 channel) {
struct sk_buff *skb;
- dprintk("TurboPAM(tpam_command_proceed): card=%d, channel=%lu\n",
+ pr_debug("TurboPAM(tpam_command_proceed): card=%d, channel=%lu\n",
card->id, (unsigned long)channel);
/* board must be running */
@@ -496,7 +496,7 @@ int tpam_writebuf_skb(int driverId, int channel, int ack, struct sk_buff *skb) {
void *finaldata;
u32 finallen;
- dprintk("TurboPAM(tpam_writebuf_skb): "
+ pr_debug("TurboPAM(tpam_writebuf_skb): "
"card=%d, channel=%ld, ack=%d, data size=%d\n",
driverId, (unsigned long)channel, ack, skb->len);
@@ -569,7 +569,7 @@ void tpam_recv_ACreateNCOCnf(tpam_card *card, struct sk_buff *skb) {
u8 status;
u32 channel;
- dprintk("TurboPAM(tpam_recv_ACreateNCOCnf): card=%d\n", card->id);
+ pr_debug("TurboPAM(tpam_recv_ACreateNCOCnf): card=%d\n", card->id);
/* parse the message contents */
if (parse_ACreateNCOCnf(skb, &status, &ncoid))
@@ -614,7 +614,7 @@ void tpam_recv_ADestroyNCOCnf(tpam_card *card, struct sk_buff *skb) {
u8 status;
u32 channel;
- dprintk("TurboPAM(tpam_recv_ADestroyNCOCnf): card=%d\n", card->id);
+ pr_debug("TurboPAM(tpam_recv_ADestroyNCOCnf): card=%d\n", card->id);
/* parse the message contents */
if (parse_ADestroyNCOCnf(skb, &status, &ncoid))
@@ -647,7 +647,7 @@ void tpam_recv_CConnectCnf(tpam_card *card, struct sk_buff *skb) {
u32 channel;
isdn_ctrl ctrl;
- dprintk("TurboPAM(tpam_recv_CConnectCnf): card=%d\n", card->id);
+ pr_debug("TurboPAM(tpam_recv_CConnectCnf): card=%d\n", card->id);
/* parse the message contents */
if (parse_CConnectCnf(skb, &ncoid))
@@ -685,7 +685,7 @@ void tpam_recv_CConnectInd(tpam_card *card, struct sk_buff *skb) {
isdn_ctrl ctrl;
int status;
- dprintk("TurboPAM(tpam_recv_CConnectInd): card=%d\n", card->id);
+ pr_debug("TurboPAM(tpam_recv_CConnectInd): card=%d\n", card->id);
/* parse the message contents */
if (parse_CConnectInd(skb, &ncoid, &hdlc, calling, called, &plan, &screen))
@@ -720,13 +720,13 @@ void tpam_recv_CConnectInd(tpam_card *card, struct sk_buff *skb) {
case 4:
/* call accepted, link layer will send us a ACCEPTD
* command later */
- dprintk("TurboPAM(tpam_recv_CConnectInd): "
+ pr_debug("TurboPAM(tpam_recv_CConnectInd): "
"card=%d, channel=%d, icall waiting, status=%d\n",
card->id, channel, status);
break;
default:
/* call denied, we build and send a CDisconnectReq */
- dprintk("TurboPAM(tpam_recv_CConnectInd): "
+ pr_debug("TurboPAM(tpam_recv_CConnectInd): "
"card=%d, channel=%d, icall denied, status=%d\n",
card->id, channel, status);
skb = build_CDisconnectReq(ncoid);
@@ -749,7 +749,7 @@ void tpam_recv_CDisconnectInd(tpam_card *card, struct sk_buff *skb) {
u32 cause;
isdn_ctrl ctrl;
- dprintk("TurboPAM(tpam_recv_CDisconnectInd): card=%d\n", card->id);
+ pr_debug("TurboPAM(tpam_recv_CDisconnectInd): card=%d\n", card->id);
/* parse the message contents */
if (parse_CDisconnectInd(skb, &ncoid, &cause))
@@ -794,7 +794,7 @@ void tpam_recv_CDisconnectCnf(tpam_card *card, struct sk_buff *skb) {
u32 cause;
isdn_ctrl ctrl;
- dprintk("TurboPAM(tpam_recv_CDisconnectCnf): card=%d\n", card->id);
+ pr_debug("TurboPAM(tpam_recv_CDisconnectCnf): card=%d\n", card->id);
/* parse the message contents */
if (parse_CDisconnectCnf(skb, &ncoid, &cause))
@@ -835,7 +835,7 @@ void tpam_recv_U3DataInd(tpam_card *card, struct sk_buff *skb) {
u16 len;
struct sk_buff *result;
- dprintk("TurboPAM(tpam_recv_U3DataInd): card=%d, datalen=%d\n",
+ pr_debug("TurboPAM(tpam_recv_U3DataInd): card=%d, datalen=%d\n",
card->id, skb->len);
/* parse the message contents */
@@ -914,7 +914,7 @@ void tpam_recv_U3ReadyToReceiveInd(tpam_card *card, struct sk_buff *skb) {
u32 channel;
u8 ready;
- dprintk("TurboPAM(tpam_recv_U3ReadyToReceiveInd): card=%d\n", card->id);
+ pr_debug("TurboPAM(tpam_recv_U3ReadyToReceiveInd): card=%d\n", card->id);
/* parse the message contents */
if (parse_U3ReadyToReceiveInd(skb, &ncoid, &ready))
@@ -943,7 +943,7 @@ void tpam_recv_U3ReadyToReceiveInd(tpam_card *card, struct sk_buff *skb) {
static void tpam_statcallb_run(unsigned long parm) {
tpam_statcallb_data *ds = (tpam_statcallb_data *)parm;
- dprintk("TurboPAM(tpam_statcallb_run)\n");
+ pr_debug("TurboPAM(tpam_statcallb_run)\n");
(* ds->card->interface.statcallb)(&ds->ctrl);
@@ -961,7 +961,7 @@ static void tpam_statcallb(tpam_card *card, isdn_ctrl ctrl) {
struct timer_list *timer;
tpam_statcallb_data *ds;
- dprintk("TurboPAM(tpam_statcallb): card=%d\n", card->id);
+ pr_debug("TurboPAM(tpam_statcallb): card=%d\n", card->id);
if (!(timer = (struct timer_list *) kmalloc(sizeof(struct timer_list),
GFP_ATOMIC))) {
diff --git a/drivers/isdn/tpam/tpam_nco.c b/drivers/isdn/tpam/tpam_nco.c
index 69a6bd4ba60e..de4904f35296 100644
--- a/drivers/isdn/tpam/tpam_nco.c
+++ b/drivers/isdn/tpam/tpam_nco.c
@@ -84,7 +84,7 @@ struct sk_buff *build_ACreateNCOReq(const u8 *phone) {
struct sk_buff *skb;
u8 *tlv;
- dprintk("TurboPAM(build_ACreateNCOReq): phone=%s\n", phone);
+ pr_debug("TurboPAM(build_ACreateNCOReq): phone=%s\n", phone);
/* build the NCO packet */
if (!(skb = build_NCOpacket(ID_ACreateNCOReq, 23 + strlen(phone), 0, 0, 0)))
@@ -141,7 +141,7 @@ struct sk_buff *build_ADestroyNCOReq(u32 ncoid) {
struct sk_buff *skb;
u8 *tlv;
- dprintk("TurboPAM(build_ADestroyNCOReq): ncoid=%lu\n",
+ pr_debug("TurboPAM(build_ADestroyNCOReq): ncoid=%lu\n",
(unsigned long)ncoid);
/* build the NCO packet */
@@ -170,7 +170,7 @@ struct sk_buff *build_CConnectReq(u32 ncoid, const u8 *called, u8 hdlc) {
struct sk_buff *skb;
u8 *tlv;
- dprintk("TurboPAM(build_CConnectReq): ncoid=%lu, called=%s, hdlc=%d\n",
+ pr_debug("TurboPAM(build_CConnectReq): ncoid=%lu, called=%s, hdlc=%d\n",
(unsigned long)ncoid, called, hdlc);
/* build the NCO packet */
@@ -220,7 +220,7 @@ struct sk_buff *build_CConnectRsp(u32 ncoid) {
struct sk_buff *skb;
u8 *tlv;
- dprintk("TurboPAM(build_CConnectRsp): ncoid=%lu\n",
+ pr_debug("TurboPAM(build_CConnectRsp): ncoid=%lu\n",
(unsigned long)ncoid);
/* build the NCO packet */
@@ -247,7 +247,7 @@ struct sk_buff *build_CDisconnectReq(u32 ncoid) {
struct sk_buff *skb;
u8 *tlv;
- dprintk("TurboPAM(build_CDisconnectReq): ncoid=%lu\n",
+ pr_debug("TurboPAM(build_CDisconnectReq): ncoid=%lu\n",
(unsigned long)ncoid);
/* build the NCO packet */
@@ -274,7 +274,7 @@ struct sk_buff *build_CDisconnectRsp(u32 ncoid) {
struct sk_buff *skb;
u8 *tlv;
- dprintk("TurboPAM(build_CDisconnectRsp): ncoid=%lu\n",
+ pr_debug("TurboPAM(build_CDisconnectRsp): ncoid=%lu\n",
(unsigned long)ncoid);
/* build the NCO packet */
@@ -307,7 +307,7 @@ struct sk_buff *build_U3DataReq(u32 ncoid, void *data, u16 len,
u8 *tlv;
void *p;
- dprintk("TurboPAM(build_U3DataReq): "
+ pr_debug("TurboPAM(build_U3DataReq): "
"ncoid=%lu, len=%d, ack=%d, ack_size=%d\n",
(unsigned long)ncoid, len, ack, ack_size);
@@ -397,7 +397,7 @@ int parse_ACreateNCOCnf(struct sk_buff *skb, u8 *status, u32 *ncoid) {
}
if (*status) {
- dprintk("TurboPAM(parse_ACreateNCOCnf): status=%d\n", *status);
+ pr_debug("TurboPAM(parse_ACreateNCOCnf): status=%d\n", *status);
return 0;
}
@@ -408,7 +408,7 @@ int parse_ACreateNCOCnf(struct sk_buff *skb, u8 *status, u32 *ncoid) {
return -1;
}
- dprintk("TurboPAM(parse_ACreateNCOCnf): ncoid=%lu, status=%d\n",
+ pr_debug("TurboPAM(parse_ACreateNCOCnf): ncoid=%lu, status=%d\n",
(unsigned long)*ncoid, *status);
return 0;
}
@@ -432,7 +432,7 @@ int parse_ADestroyNCOCnf(struct sk_buff *skb, u8 *status, u32 *ncoid) {
}
if (*status) {
- dprintk("TurboPAM(parse_ADestroyNCOCnf): status=%d\n", *status);
+ pr_debug("TurboPAM(parse_ADestroyNCOCnf): status=%d\n", *status);
return 0;
}
@@ -443,7 +443,7 @@ int parse_ADestroyNCOCnf(struct sk_buff *skb, u8 *status, u32 *ncoid) {
return -1;
}
- dprintk("TurboPAM(parse_ADestroyNCOCnf): ncoid=%lu, status=%d\n",
+ pr_debug("TurboPAM(parse_ADestroyNCOCnf): ncoid=%lu, status=%d\n",
(unsigned long)*ncoid, *status);
return 0;
}
@@ -464,7 +464,7 @@ int parse_CConnectCnf(struct sk_buff *skb, u32 *ncoid) {
"NCOID not found\n");
return -1;
}
- dprintk("TurboPAM(parse_CConnectCnf): ncoid=%lu\n",
+ pr_debug("TurboPAM(parse_CConnectCnf): ncoid=%lu\n",
(unsigned long)*ncoid);
return 0;
}
@@ -522,7 +522,7 @@ int parse_CConnectInd(struct sk_buff *skb, u32 *ncoid, u8 *hdlc,
}
memcpy(called, phone + 2, PHONE_MAXIMUMSIZE);
- dprintk("TurboPAM(parse_CConnectInd): "
+ pr_debug("TurboPAM(parse_CConnectInd): "
"ncoid=%lu, hdlc=%d, plan=%d, scr=%d, calling=%s, called=%s\n",
(unsigned long)*ncoid, *hdlc, *plan, *screen, calling, called);
return 0;
@@ -553,7 +553,7 @@ int parse_CDisconnectCnf(struct sk_buff *skb, u32 *ncoid, u32 *causetopuf) {
return -1;
}
- dprintk("TurboPAM(parse_CDisconnectCnf): ncoid=%lu, causetopuf=%lu\n",
+ pr_debug("TurboPAM(parse_CDisconnectCnf): ncoid=%lu, causetopuf=%lu\n",
(unsigned long)*ncoid, (unsigned long)*causetopuf);
return 0;
}
@@ -583,7 +583,7 @@ int parse_CDisconnectInd(struct sk_buff *skb, u32 *ncoid, u32 *causetopuf) {
return -1;
}
- dprintk("TurboPAM(parse_CDisconnectInd): ncoid=%lu, causetopuf=%lu\n",
+ pr_debug("TurboPAM(parse_CDisconnectInd): ncoid=%lu, causetopuf=%lu\n",
(unsigned long)*ncoid, (unsigned long)*causetopuf);
return 0;
}
@@ -613,7 +613,7 @@ int parse_U3ReadyToReceiveInd(struct sk_buff *skb, u32 *ncoid, u8 *ready) {
return -1;
}
- dprintk("TurboPAM(parse_U3ReadyToReceiveInd): ncoid=%lu, ready=%d\n",
+ pr_debug("TurboPAM(parse_U3ReadyToReceiveInd): ncoid=%lu, ready=%d\n",
(unsigned long)*ncoid, *ready);
return 0;
}
@@ -644,7 +644,7 @@ int parse_U3DataInd(struct sk_buff *skb, u32 *ncoid, u8 **data, u16 *len) {
sizeof(skb_header) + sizeof(pci_mpb) + p->actualBlockTLVSize);
*data = skb->data;
- dprintk("TurboPAM(parse_U3DataInd): ncoid=%lu, datalen=%d\n",
+ pr_debug("TurboPAM(parse_U3DataInd): ncoid=%lu, datalen=%d\n",
(unsigned long)*ncoid, *len);
return 0;
}
diff --git a/drivers/isdn/tpam/tpam_queues.c b/drivers/isdn/tpam/tpam_queues.c
index 9b55684dde0c..a9d8bb08007e 100644
--- a/drivers/isdn/tpam/tpam_queues.c
+++ b/drivers/isdn/tpam/tpam_queues.c
@@ -30,7 +30,7 @@ static int tpam_sendpacket(tpam_card *card, tpam_channel *channel);
*/
void tpam_enqueue(tpam_card *card, struct sk_buff *skb) {
- dprintk("TurboPAM(tpam_enqueue): card=%d\n", card->id);
+ pr_debug("TurboPAM(tpam_enqueue): card=%d\n", card->id);
/* queue the sk_buff on the board's send queue */
skb_queue_tail(&card->sendq, skb);
@@ -49,7 +49,7 @@ void tpam_enqueue(tpam_card *card, struct sk_buff *skb) {
*/
void tpam_enqueue_data(tpam_channel *channel, struct sk_buff *skb) {
- dprintk("TurboPAM(tpam_enqueue_data): card=%d, channel=%d\n",
+ pr_debug("TurboPAM(tpam_enqueue_data): card=%d, channel=%d\n",
channel->card->id, channel->num);
/* if existant, queue the sk_buff on the channel's send queue */
@@ -84,7 +84,7 @@ irqreturn_t tpam_irq(int irq, void *dev_id, struct pt_regs *regs)
pci_mpb mpb;
skb_header *skbh;
- dprintk("TurboPAM(tpam_irq): IRQ received, card=%d\n", card->id);
+ pr_debug("TurboPAM(tpam_irq): IRQ received, card=%d\n", card->id);
/* grab the board lock */
spin_lock(&card->lock);
@@ -99,7 +99,7 @@ irqreturn_t tpam_irq(int irq, void *dev_id, struct pt_regs *regs)
if (!ackupload) {
/* it is a new message from the board */
- dprintk("TurboPAM(tpam_irq): message received, card=%d\n",
+ pr_debug("TurboPAM(tpam_irq): message received, card=%d\n",
card->id);
/* get the upload pointer */
@@ -176,7 +176,7 @@ irqreturn_t tpam_irq(int irq, void *dev_id, struct pt_regs *regs)
else {
/* it is a ack from the board */
- dprintk("TurboPAM(tpam_irq): message acknowledged, card=%d\n",
+ pr_debug("TurboPAM(tpam_irq): message acknowledged, card=%d\n",
card->id);
/* board is not busy anymore */
@@ -231,7 +231,7 @@ void tpam_recv_tq(tpam_card *card) {
tpam_recv_U3DataInd(card, skb);
break;
default:
- dprintk("TurboPAM(tpam_recv_tq): "
+ pr_debug("TurboPAM(tpam_recv_tq): "
"unknown messageID %d, card=%d\n",
p->messageID, card->id);
break;
@@ -286,13 +286,13 @@ static int tpam_sendpacket(tpam_card *card, tpam_channel *channel) {
skb_header *skbh;
u32 waiting_too_long;
- dprintk("TurboPAM(tpam_sendpacket), card=%d, channel=%d\n",
+ pr_debug("TurboPAM(tpam_sendpacket), card=%d, channel=%d\n",
card->id, channel ? channel->num : -1);
if (channel) {
/* dequeue a packet from the channel's send queue */
if (!(skb = skb_dequeue(&channel->sendq))) {
- dprintk("TurboPAM(tpam_sendpacket): "
+ pr_debug("TurboPAM(tpam_sendpacket): "
"card=%d, channel=%d, no packet\n",
card->id, channel->num);
return 0;
@@ -301,7 +301,7 @@ static int tpam_sendpacket(tpam_card *card, tpam_channel *channel) {
/* if the channel is not ready to receive, requeue the packet
* and return 0 to give a chance to another channel */
if (!channel->readytoreceive) {
- dprintk("TurboPAM(tpam_sendpacket): "
+ pr_debug("TurboPAM(tpam_sendpacket): "
"card=%d, channel=%d, channel not ready\n",
card->id, channel->num);
skb_queue_head(&channel->sendq, skb);
@@ -314,7 +314,7 @@ static int tpam_sendpacket(tpam_card *card, tpam_channel *channel) {
/* if the board is busy, requeue the packet and return 1 since
* there is no need to try another channel */
if (card->busy) {
- dprintk("TurboPAM(tpam_sendpacket): "
+ pr_debug("TurboPAM(tpam_sendpacket): "
"card=%d, channel=%d, card busy\n",
card->id, channel->num);
skb_queue_head(&channel->sendq, skb);
@@ -325,7 +325,7 @@ static int tpam_sendpacket(tpam_card *card, tpam_channel *channel) {
else {
/* dequeue a packet from the board's send queue */
if (!(skb = skb_dequeue(&card->sendq))) {
- dprintk("TurboPAM(tpam_sendpacket): "
+ pr_debug("TurboPAM(tpam_sendpacket): "
"card=%d, no packet\n", card->id);
return 0;
}
@@ -336,7 +336,7 @@ static int tpam_sendpacket(tpam_card *card, tpam_channel *channel) {
/* if the board is busy, requeue the packet and return 1 since
* there is no need to try another channel */
if (card->busy) {
- dprintk("TurboPAM(tpam_sendpacket): "
+ pr_debug("TurboPAM(tpam_sendpacket): "
"card=%d, card busy\n", card->id);
skb_queue_head(&card->sendq, skb);
spin_unlock_irq(&card->lock);
@@ -357,7 +357,7 @@ static int tpam_sendpacket(tpam_card *card, tpam_channel *channel) {
} while (hpic & 0x00000002);
skbh = (skb_header *)skb->data;
- dprintk("TurboPAM(tpam_sendpacket): "
+ pr_debug("TurboPAM(tpam_sendpacket): "
"card=%d, card ready, sending %d/%d bytes\n",
card->id, skbh->size, skbh->data_size);
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 745f7e7bd3ee..10efe7942775 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -3468,8 +3468,7 @@ static void md_do_sync(mddev_t *mddev)
if (currspeed > sysctl_speed_limit_min) {
if ((currspeed > sysctl_speed_limit_max) ||
!is_mddev_idle(mddev)) {
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(HZ/4);
+ msleep_interruptible(250);
goto repeat;
}
}
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 1ed82ea4be8e..553e16da088c 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -1013,7 +1013,7 @@ static int sync_request(mddev_t *mddev, sector_t sector_nr, int go_faster)
* put in a delay to throttle resync.
*/
if (!go_faster && waitqueue_active(&conf->wait_resume))
- schedule_timeout(HZ);
+ msleep_interruptible(1000);
device_barrier(conf, sector_nr + RESYNC_SECTORS);
/*
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 6c3fde9ba192..0ba3a4e1831c 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -1358,7 +1358,7 @@ static int sync_request(mddev_t *mddev, sector_t sector_nr, int go_faster)
* put in a delay to throttle resync.
*/
if (!go_faster && waitqueue_active(&conf->wait_resume))
- schedule_timeout(HZ);
+ msleep_interruptible(1000);
device_barrier(conf, sector_nr + RESYNC_SECTORS);
/* Again, very different code for resync and recovery.
diff --git a/drivers/media/video/bw-qcam.c b/drivers/media/video/bw-qcam.c
index 31412e8d32db..293173885bd9 100644
--- a/drivers/media/video/bw-qcam.c
+++ b/drivers/media/video/bw-qcam.c
@@ -249,8 +249,7 @@ static int qc_waithand(struct qcam_device *q, int val)
if(runs++>maxpoll)
{
- current->state=TASK_INTERRUPTIBLE;
- schedule_timeout(HZ/200);
+ msleep_interruptible(5);
}
if(runs>(maxpoll+1000)) /* 5 seconds */
return -1;
@@ -269,8 +268,7 @@ static int qc_waithand(struct qcam_device *q, int val)
if(runs++>maxpoll)
{
- current->state=TASK_INTERRUPTIBLE;
- schedule_timeout(HZ/200);
+ msleep_interruptible(5);
}
if(runs++>(maxpoll+1000)) /* 5 seconds */
return -1;
@@ -302,8 +300,7 @@ static unsigned int qc_waithand2(struct qcam_device *q, int val)
if(runs++>maxpoll)
{
- current->state=TASK_INTERRUPTIBLE;
- schedule_timeout(HZ/200);
+ msleep_interruptible(5);
}
if(runs++>(maxpoll+1000)) /* 5 seconds */
return 0;
@@ -669,8 +666,7 @@ long qc_capture(struct qcam_device * q, char __user *buf, unsigned long len)
time will be 240 / 200 = 1.2 seconds. The compile-time
default is to yield every 4 lines. */
if (i >= yield) {
- current->state=TASK_INTERRUPTIBLE;
- schedule_timeout(HZ/200);
+ msleep_interruptible(5);
yield = i + yieldlines;
}
}
diff --git a/drivers/media/video/c-qcam.c b/drivers/media/video/c-qcam.c
index bd9fef419a88..703c4cba97b6 100644
--- a/drivers/media/video/c-qcam.c
+++ b/drivers/media/video/c-qcam.c
@@ -103,8 +103,7 @@ static unsigned int qcam_await_ready1(struct qcam_device *qcam,
{
if (qcam_ready1(qcam) == value)
return 0;
- current->state=TASK_INTERRUPTIBLE;
- schedule_timeout(HZ/10);
+ msleep_interruptible(100);
}
/* Probably somebody pulled the plug out. Not much we can do. */
@@ -129,8 +128,7 @@ static unsigned int qcam_await_ready2(struct qcam_device *qcam, int value)
{
if (qcam_ready2(qcam) == value)
return 0;
- current->state=TASK_INTERRUPTIBLE;
- schedule_timeout(HZ/10);
+ msleep_interruptible(100);
}
/* Probably somebody pulled the plug out. Not much we can do. */
diff --git a/drivers/media/video/cpia.c b/drivers/media/video/cpia.c
index 21db4b390277..388bc09366bd 100644
--- a/drivers/media/video/cpia.c
+++ b/drivers/media/video/cpia.c
@@ -37,6 +37,7 @@
#include <linux/proc_fs.h>
#include <linux/ctype.h>
#include <linux/pagemap.h>
+#include <linux/delay.h>
#include <asm/io.h>
#include <asm/semaphore.h>
@@ -2886,9 +2887,7 @@ static int fetch_frame(void *data)
cond_resched();
/* sleep for 10 ms, hopefully ;) */
- current->state = TASK_INTERRUPTIBLE;
-
- schedule_timeout(10*HZ/1000);
+ msleep_interruptible(10);
if (signal_pending(current))
return -EINTR;
@@ -2951,8 +2950,7 @@ static int fetch_frame(void *data)
CPIA_GRAB_SINGLE, 0, 0, 0);
/* FIXME: Trial & error - need up to 70ms for
the grab mode change to complete ? */
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(70*HZ / 1000);
+ msleep_interruptible(70);
if (signal_pending(current))
return -EINTR;
}
@@ -3003,8 +3001,7 @@ static int goto_high_power(struct cam_data *cam)
{
if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0))
return -EIO;
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(40*HZ/1000); /* windows driver does it too */
+ msleep_interruptible(40); /* windows driver does it too */
if(signal_pending(current))
return -EINTR;
if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
@@ -3074,10 +3071,8 @@ static int set_camera_state(struct cam_data *cam)
/* Wait 6 frames for the sensor to get all settings and
AEC/ACB to settle */
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout((6*(cam->params.sensorFps.baserate ? 33 : 40) *
- (1 << cam->params.sensorFps.divisor) + 10) *
- HZ / 1000);
+ msleep_interruptible(6*(cam->params.sensorFps.baserate ? 33 : 40) *
+ (1 << cam->params.sensorFps.divisor) + 10);
if(signal_pending(current))
return -EINTR;
diff --git a/drivers/media/video/ovcamchip/ovcamchip_core.c b/drivers/media/video/ovcamchip/ovcamchip_core.c
index d88956a26aee..67f2fc4a4f9f 100644
--- a/drivers/media/video/ovcamchip/ovcamchip_core.c
+++ b/drivers/media/video/ovcamchip/ovcamchip_core.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
+#include <linux/delay.h>
#include "ovcamchip_priv.h"
#define DRIVER_VERSION "v2.27 for Linux 2.6"
@@ -128,8 +129,7 @@ static int init_camchip(struct i2c_client *c)
ov_write(c, 0x12, 0x80);
/* Wait for it to initialize */
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(1 + 150 * HZ / 1000);
+ msleep(150);
for (i = 0, success = 0; i < I2C_DETECT_RETRIES && !success; i++) {
if (ov_read(c, GENERIC_REG_ID_HIGH, &high) >= 0) {
@@ -145,8 +145,7 @@ static int init_camchip(struct i2c_client *c)
ov_write(c, 0x12, 0x80);
/* Wait for it to initialize */
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(1 + 150 * HZ / 1000);
+ msleep(150);
/* Dummy read to sync I2C */
ov_read(c, 0x00, &low);
diff --git a/drivers/media/video/planb.c b/drivers/media/video/planb.c
index ebf130f167e3..3a828546952a 100644
--- a/drivers/media/video/planb.c
+++ b/drivers/media/video/planb.c
@@ -178,8 +178,7 @@ static unsigned char saa_status(int byte, struct planb *pb)
saa_write_reg (SAA7196_STDC, saa_regs[pb->win.norm][SAA7196_STDC]);
/* Let's wait 30msec for this one */
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(30 * HZ / 1000);
+ msleep_interruptible(30);
return (unsigned char)in_8 (&planb_regs->saa_status);
}
diff --git a/drivers/media/video/saa5249.c b/drivers/media/video/saa5249.c
index d486fb7faf67..7d5ca87c1619 100644
--- a/drivers/media/video/saa5249.c
+++ b/drivers/media/video/saa5249.c
@@ -273,8 +273,7 @@ static void jdelay(unsigned long delay)
sigfillset(&current->blocked);
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(delay);
+ msleep_interruptible(jiffies_to_msecs(delay));
spin_lock_irq(&current->sighand->siglock);
current->blocked = oldblocked;
diff --git a/drivers/media/video/tda9887.c b/drivers/media/video/tda9887.c
index e46fbdbc46e6..31977e770b23 100644
--- a/drivers/media/video/tda9887.c
+++ b/drivers/media/video/tda9887.c
@@ -6,6 +6,7 @@
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/slab.h>
+#include <linux/delay.h>
#include <media/audiochip.h>
#include <media/tuner.h>
@@ -543,8 +544,7 @@ static int tda9887_configure(struct tda9887 *t)
printk(PREFIX "i2c i/o error: rc == %d (should be 4)\n",rc);
if (debug > 2) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ);
+ msleep_interruptible(1000);
tda9887_status(t);
}
return 0;
diff --git a/drivers/media/video/videocodec.c b/drivers/media/video/videocodec.c
index 081eb06a4c7a..ae1063e09a01 100644
--- a/drivers/media/video/videocodec.c
+++ b/drivers/media/video/videocodec.c
@@ -43,11 +43,6 @@
#include <asm/uaccess.h>
#endif
-#include <linux/version.h>
-#ifndef KERNEL_VERSION
-#define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c))
-#endif
-
#include "videocodec.h"
static int debug = 0;
diff --git a/drivers/media/video/zoran_driver.c b/drivers/media/video/zoran_driver.c
index d6294bd7909f..bb2b4201a24d 100644
--- a/drivers/media/video/zoran_driver.c
+++ b/drivers/media/video/zoran_driver.c
@@ -1917,8 +1917,7 @@ zoran_set_norm (struct zoran *zr,
decoder_command(zr, DECODER_SET_NORM, &norm);
/* let changes come into effect */
- current->state = TASK_UNINTERRUPTIBLE;
- schedule_timeout(2 * HZ);
+ ssleep(2);
decoder_command(zr, DECODER_GET_STATUS, &status);
if (!(status & DECODER_STATUS_GOOD)) {
@@ -2639,8 +2638,7 @@ zoran_do_ioctl (struct inode *inode,
decoder_command(zr, DECODER_SET_NORM, &norm);
/* sleep 1 second */
- current->state = TASK_UNINTERRUPTIBLE;
- schedule_timeout(1 * HZ);
+ ssleep(1);
/* Get status of video decoder */
decoder_command(zr, DECODER_GET_STATUS, &status);
diff --git a/drivers/media/video/zr36120.c b/drivers/media/video/zr36120.c
index 9b55341baf40..e99226434e73 100644
--- a/drivers/media/video/zr36120.c
+++ b/drivers/media/video/zr36120.c
@@ -819,8 +819,7 @@ void zoran_close(struct video_device* dev)
* be sure its safe to free the buffer. We wait 5-6 fields
* which is more than sufficient to be sure.
*/
- current->state = TASK_UNINTERRUPTIBLE;
- schedule_timeout(HZ/10); /* Wait 1/10th of a second */
+ msleep(100); /* Wait 1/10th of a second */
/* free the allocated framebuffer */
if (ztv->fbuffer)
@@ -1568,8 +1567,7 @@ void vbi_close(struct video_device *dev)
* be sure its safe to free the buffer. We wait 5-6 fields
* which is more than sufficient to be sure.
*/
- current->state = TASK_UNINTERRUPTIBLE;
- schedule_timeout(HZ/10); /* Wait 1/10th of a second */
+ msleep(100); /* Wait 1/10th of a second */
for (item=ztv->readinfo; item!=ztv->readinfo+ZORAN_VBI_BUFFERS; item++)
{
diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c
index c467480b958c..d252c2bda137 100644
--- a/drivers/message/fusion/mptbase.c
+++ b/drivers/message/fusion/mptbase.c
@@ -2195,8 +2195,7 @@ MakeIocReady(MPT_ADAPTER *ioc, int force, int sleepFlag)
}
if (sleepFlag == CAN_SLEEP) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(1 * HZ / 1000);
+ msleep_interruptible(1);
} else {
mdelay (1); /* 1 msec delay */
}
@@ -2565,8 +2564,7 @@ SendIocInit(MPT_ADAPTER *ioc, int sleepFlag)
state = mpt_GetIocState(ioc, 1);
while (state != MPI_IOC_STATE_OPERATIONAL && --cntdn) {
if (sleepFlag == CAN_SLEEP) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(1 * HZ / 1000);
+ msleep_interruptible(1);
} else {
mdelay(1);
}
@@ -2833,8 +2831,7 @@ mpt_downloadboot(MPT_ADAPTER *ioc, int sleepFlag)
/* wait 1 msec */
if (sleepFlag == CAN_SLEEP) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(1 * HZ / 1000);
+ msleep_interruptible(1);
} else {
mdelay (1);
}
@@ -2851,8 +2848,7 @@ mpt_downloadboot(MPT_ADAPTER *ioc, int sleepFlag)
}
/* wait 1 sec */
if (sleepFlag == CAN_SLEEP) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(1000 * HZ / 1000);
+ msleep_interruptible (1000);
} else {
mdelay (1000);
}
@@ -2952,8 +2948,7 @@ mpt_downloadboot(MPT_ADAPTER *ioc, int sleepFlag)
return 0;
}
if (sleepFlag == CAN_SLEEP) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(10 * HZ / 1000);
+ msleep_interruptible (10);
} else {
mdelay (10);
}
@@ -3004,8 +2999,7 @@ KickStart(MPT_ADAPTER *ioc, int force, int sleepFlag)
SendIocReset(ioc, MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET, sleepFlag);
if (sleepFlag == CAN_SLEEP) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(1000 * HZ / 1000);
+ msleep_interruptible (1000);
} else {
mdelay (1000);
}
@@ -3027,8 +3021,7 @@ KickStart(MPT_ADAPTER *ioc, int force, int sleepFlag)
return hard_reset_done;
}
if (sleepFlag == CAN_SLEEP) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(10 * HZ / 1000);
+ msleep_interruptible (10);
} else {
mdelay (10);
}
@@ -3099,8 +3092,7 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag)
/* wait 100 msec */
if (sleepFlag == CAN_SLEEP) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(100 * HZ / 1000);
+ msleep_interruptible (100);
} else {
mdelay (100);
}
@@ -3207,8 +3199,7 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag)
/* wait 1 sec */
if (sleepFlag == CAN_SLEEP) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(1000 * HZ / 1000);
+ msleep_interruptible (1000);
} else {
mdelay (1000);
}
@@ -3242,8 +3233,7 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag)
/* wait 100 msec */
if (sleepFlag == CAN_SLEEP) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(100 * HZ / 1000);
+ msleep_interruptible (100);
} else {
mdelay (100);
}
@@ -3337,8 +3327,7 @@ SendIocReset(MPT_ADAPTER *ioc, u8 reset_type, int sleepFlag)
}
if (sleepFlag == CAN_SLEEP) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(1 * HZ / 1000);
+ msleep_interruptible(1);
} else {
mdelay (1); /* 1 msec delay */
}
@@ -3775,8 +3764,7 @@ WaitForDoorbellAck(MPT_ADAPTER *ioc, int howlong, int sleepFlag)
intstat = CHIPREG_READ32(&ioc->chip->IntStatus);
if (! (intstat & MPI_HIS_IOP_DOORBELL_STATUS))
break;
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(1 * HZ / 1000);
+ msleep_interruptible (1);
count++;
}
} else {
@@ -3825,8 +3813,7 @@ WaitForDoorbellInt(MPT_ADAPTER *ioc, int howlong, int sleepFlag)
intstat = CHIPREG_READ32(&ioc->chip->IntStatus);
if (intstat & MPI_HIS_DOORBELL_INTERRUPT)
break;
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(1 * HZ / 1000);
+ msleep_interruptible(1);
count++;
}
} else {
diff --git a/drivers/message/i2o/debug.c b/drivers/message/i2o/debug.c
index 7227a8a1e0bd..85db8c4c58e0 100644
--- a/drivers/message/i2o/debug.c
+++ b/drivers/message/i2o/debug.c
@@ -54,7 +54,7 @@ void i2o_report_status(const char *severity, const char *str,
if (cmd == I2O_CMD_UTIL_EVT_REGISTER)
return; // No status in this reply
- printk("%s%s: ", severity, str);
+ printk(KERN_DEBUG "%s%s: ", severity, str);
if (cmd < 0x1F) // Utility cmd
i2o_report_util_cmd(cmd);
@@ -62,7 +62,7 @@ void i2o_report_status(const char *severity, const char *str,
else if (cmd >= 0xA0 && cmd <= 0xEF) // Executive cmd
i2o_report_exec_cmd(cmd);
else
- printk("Cmd = %0#2x, ", cmd); // Other cmds
+ printk(KERN_DEBUG "Cmd = %0#2x, ", cmd); // Other cmds
if (msg[0] & MSG_FAIL) {
i2o_report_fail_status(req_status, msg);
@@ -74,7 +74,8 @@ void i2o_report_status(const char *severity, const char *str,
if (cmd < 0x1F || (cmd >= 0xA0 && cmd <= 0xEF))
i2o_report_common_dsc(detailed_status);
else
- printk(" / DetailedStatus = %0#4x.\n", detailed_status);
+ printk(KERN_DEBUG " / DetailedStatus = %0#4x.\n",
+ detailed_status);
}
/* Used to dump a message to syslog during debugging */
@@ -129,20 +130,20 @@ void i2o_report_controller_unit(struct i2o_controller *c, struct i2o_device *d)
printk(KERN_INFO " Class: ");
//sprintf(str, "%-21s", i2o_get_class_name(d->lct_data.class_id));
- printk("%s\n", str);
+ printk(KERN_DEBUG "%s\n", str);
printk(KERN_INFO " Subclass: 0x%04X\n", d->lct_data.sub_class);
printk(KERN_INFO " Flags: ");
if (d->lct_data.device_flags & (1 << 0))
- printk("C"); // ConfigDialog requested
+ printk(KERN_DEBUG "C"); // ConfigDialog requested
if (d->lct_data.device_flags & (1 << 1))
- printk("U"); // Multi-user capable
+ printk(KERN_DEBUG "U"); // Multi-user capable
if (!(d->lct_data.device_flags & (1 << 4)))
- printk("P"); // Peer service enabled!
+ printk(KERN_DEBUG "P"); // Peer service enabled!
if (!(d->lct_data.device_flags & (1 << 5)))
- printk("M"); // Mgmt service enabled!
- printk("\n");
+ printk(KERN_DEBUG "M"); // Mgmt service enabled!
+ printk(KERN_DEBUG "\n");
}
/*
@@ -177,9 +178,11 @@ void i2o_report_fail_status(u8 req_status, u32 * msg)
};
if (req_status == I2O_FSC_TRANSPORT_UNKNOWN_FAILURE)
- printk("TRANSPORT_UNKNOWN_FAILURE (%0#2x)\n.", req_status);
+ printk(KERN_DEBUG "TRANSPORT_UNKNOWN_FAILURE (%0#2x)\n.",
+ req_status);
else
- printk("TRANSPORT_%s.\n", FAIL_STATUS[req_status & 0x0F]);
+ printk(KERN_DEBUG "TRANSPORT_%s.\n",
+ FAIL_STATUS[req_status & 0x0F]);
/* Dump some details */
@@ -192,16 +195,17 @@ void i2o_report_fail_status(u8 req_status, u32 * msg)
printk(KERN_ERR " Severity: 0x%02X ", (msg[4] >> 16) & 0xFF);
if (msg[4] & (1 << 16))
- printk("(FormatError), "
+ printk(KERN_DEBUG "(FormatError), "
"this msg can never be delivered/processed.\n");
if (msg[4] & (1 << 17))
- printk("(PathError), "
+ printk(KERN_DEBUG "(PathError), "
"this msg can no longer be delivered/processed.\n");
if (msg[4] & (1 << 18))
- printk("(PathState), "
+ printk(KERN_DEBUG "(PathState), "
"the system state does not allow delivery.\n");
if (msg[4] & (1 << 19))
- printk("(Congestion), resources temporarily not available;"
+ printk(KERN_DEBUG
+ "(Congestion), resources temporarily not available;"
"do not retry immediately.\n");
}
@@ -227,9 +231,9 @@ void i2o_report_common_status(u8 req_status)
};
if (req_status >= ARRAY_SIZE(REPLY_STATUS))
- printk("RequestStatus = %0#2x", req_status);
+ printk(KERN_DEBUG "RequestStatus = %0#2x", req_status);
else
- printk("%s", REPLY_STATUS[req_status]);
+ printk(KERN_DEBUG "%s", REPLY_STATUS[req_status]);
}
/*
@@ -272,9 +276,10 @@ static void i2o_report_common_dsc(u16 detailed_status)
};
if (detailed_status > I2O_DSC_DEVICE_NOT_AVAILABLE)
- printk(" / DetailedStatus = %0#4x.\n", detailed_status);
+ printk(KERN_DEBUG " / DetailedStatus = %0#4x.\n",
+ detailed_status);
else
- printk(" / %s.\n", COMMON_DSC[detailed_status]);
+ printk(KERN_DEBUG " / %s.\n", COMMON_DSC[detailed_status]);
}
/*
@@ -284,49 +289,49 @@ static void i2o_report_util_cmd(u8 cmd)
{
switch (cmd) {
case I2O_CMD_UTIL_NOP:
- printk("UTIL_NOP, ");
+ printk(KERN_DEBUG "UTIL_NOP, ");
break;
case I2O_CMD_UTIL_ABORT:
- printk("UTIL_ABORT, ");
+ printk(KERN_DEBUG "UTIL_ABORT, ");
break;
case I2O_CMD_UTIL_CLAIM:
- printk("UTIL_CLAIM, ");
+ printk(KERN_DEBUG "UTIL_CLAIM, ");
break;
case I2O_CMD_UTIL_RELEASE:
- printk("UTIL_CLAIM_RELEASE, ");
+ printk(KERN_DEBUG "UTIL_CLAIM_RELEASE, ");
break;
case I2O_CMD_UTIL_CONFIG_DIALOG:
- printk("UTIL_CONFIG_DIALOG, ");
+ printk(KERN_DEBUG "UTIL_CONFIG_DIALOG, ");
break;
case I2O_CMD_UTIL_DEVICE_RESERVE:
- printk("UTIL_DEVICE_RESERVE, ");
+ printk(KERN_DEBUG "UTIL_DEVICE_RESERVE, ");
break;
case I2O_CMD_UTIL_DEVICE_RELEASE:
- printk("UTIL_DEVICE_RELEASE, ");
+ printk(KERN_DEBUG "UTIL_DEVICE_RELEASE, ");
break;
case I2O_CMD_UTIL_EVT_ACK:
- printk("UTIL_EVENT_ACKNOWLEDGE, ");
+ printk(KERN_DEBUG "UTIL_EVENT_ACKNOWLEDGE, ");
break;
case I2O_CMD_UTIL_EVT_REGISTER:
- printk("UTIL_EVENT_REGISTER, ");
+ printk(KERN_DEBUG "UTIL_EVENT_REGISTER, ");
break;
case I2O_CMD_UTIL_LOCK:
- printk("UTIL_LOCK, ");
+ printk(KERN_DEBUG "UTIL_LOCK, ");
break;
case I2O_CMD_UTIL_LOCK_RELEASE:
- printk("UTIL_LOCK_RELEASE, ");
+ printk(KERN_DEBUG "UTIL_LOCK_RELEASE, ");
break;
case I2O_CMD_UTIL_PARAMS_GET:
- printk("UTIL_PARAMS_GET, ");
+ printk(KERN_DEBUG "UTIL_PARAMS_GET, ");
break;
case I2O_CMD_UTIL_PARAMS_SET:
- printk("UTIL_PARAMS_SET, ");
+ printk(KERN_DEBUG "UTIL_PARAMS_SET, ");
break;
case I2O_CMD_UTIL_REPLY_FAULT_NOTIFY:
- printk("UTIL_REPLY_FAULT_NOTIFY, ");
+ printk(KERN_DEBUG "UTIL_REPLY_FAULT_NOTIFY, ");
break;
default:
- printk("Cmd = %0#2x, ", cmd);
+ printk(KERN_DEBUG "Cmd = %0#2x, ", cmd);
}
}
@@ -337,106 +342,106 @@ static void i2o_report_exec_cmd(u8 cmd)
{
switch (cmd) {
case I2O_CMD_ADAPTER_ASSIGN:
- printk("EXEC_ADAPTER_ASSIGN, ");
+ printk(KERN_DEBUG "EXEC_ADAPTER_ASSIGN, ");
break;
case I2O_CMD_ADAPTER_READ:
- printk("EXEC_ADAPTER_READ, ");
+ printk(KERN_DEBUG "EXEC_ADAPTER_READ, ");
break;
case I2O_CMD_ADAPTER_RELEASE:
- printk("EXEC_ADAPTER_RELEASE, ");
+ printk(KERN_DEBUG "EXEC_ADAPTER_RELEASE, ");
break;
case I2O_CMD_BIOS_INFO_SET:
- printk("EXEC_BIOS_INFO_SET, ");
+ printk(KERN_DEBUG "EXEC_BIOS_INFO_SET, ");
break;
case I2O_CMD_BOOT_DEVICE_SET:
- printk("EXEC_BOOT_DEVICE_SET, ");
+ printk(KERN_DEBUG "EXEC_BOOT_DEVICE_SET, ");
break;
case I2O_CMD_CONFIG_VALIDATE:
- printk("EXEC_CONFIG_VALIDATE, ");
+ printk(KERN_DEBUG "EXEC_CONFIG_VALIDATE, ");
break;
case I2O_CMD_CONN_SETUP:
- printk("EXEC_CONN_SETUP, ");
+ printk(KERN_DEBUG "EXEC_CONN_SETUP, ");
break;
case I2O_CMD_DDM_DESTROY:
- printk("EXEC_DDM_DESTROY, ");
+ printk(KERN_DEBUG "EXEC_DDM_DESTROY, ");
break;
case I2O_CMD_DDM_ENABLE:
- printk("EXEC_DDM_ENABLE, ");
+ printk(KERN_DEBUG "EXEC_DDM_ENABLE, ");
break;
case I2O_CMD_DDM_QUIESCE:
- printk("EXEC_DDM_QUIESCE, ");
+ printk(KERN_DEBUG "EXEC_DDM_QUIESCE, ");
break;
case I2O_CMD_DDM_RESET:
- printk("EXEC_DDM_RESET, ");
+ printk(KERN_DEBUG "EXEC_DDM_RESET, ");
break;
case I2O_CMD_DDM_SUSPEND:
- printk("EXEC_DDM_SUSPEND, ");
+ printk(KERN_DEBUG "EXEC_DDM_SUSPEND, ");
break;
case I2O_CMD_DEVICE_ASSIGN:
- printk("EXEC_DEVICE_ASSIGN, ");
+ printk(KERN_DEBUG "EXEC_DEVICE_ASSIGN, ");
break;
case I2O_CMD_DEVICE_RELEASE:
- printk("EXEC_DEVICE_RELEASE, ");
+ printk(KERN_DEBUG "EXEC_DEVICE_RELEASE, ");
break;
case I2O_CMD_HRT_GET:
- printk("EXEC_HRT_GET, ");
+ printk(KERN_DEBUG "EXEC_HRT_GET, ");
break;
case I2O_CMD_ADAPTER_CLEAR:
- printk("EXEC_IOP_CLEAR, ");
+ printk(KERN_DEBUG "EXEC_IOP_CLEAR, ");
break;
case I2O_CMD_ADAPTER_CONNECT:
- printk("EXEC_IOP_CONNECT, ");
+ printk(KERN_DEBUG "EXEC_IOP_CONNECT, ");
break;
case I2O_CMD_ADAPTER_RESET:
- printk("EXEC_IOP_RESET, ");
+ printk(KERN_DEBUG "EXEC_IOP_RESET, ");
break;
case I2O_CMD_LCT_NOTIFY:
- printk("EXEC_LCT_NOTIFY, ");
+ printk(KERN_DEBUG "EXEC_LCT_NOTIFY, ");
break;
case I2O_CMD_OUTBOUND_INIT:
- printk("EXEC_OUTBOUND_INIT, ");
+ printk(KERN_DEBUG "EXEC_OUTBOUND_INIT, ");
break;
case I2O_CMD_PATH_ENABLE:
- printk("EXEC_PATH_ENABLE, ");
+ printk(KERN_DEBUG "EXEC_PATH_ENABLE, ");
break;
case I2O_CMD_PATH_QUIESCE:
- printk("EXEC_PATH_QUIESCE, ");
+ printk(KERN_DEBUG "EXEC_PATH_QUIESCE, ");
break;
case I2O_CMD_PATH_RESET:
- printk("EXEC_PATH_RESET, ");
+ printk(KERN_DEBUG "EXEC_PATH_RESET, ");
break;
case I2O_CMD_STATIC_MF_CREATE:
- printk("EXEC_STATIC_MF_CREATE, ");
+ printk(KERN_DEBUG "EXEC_STATIC_MF_CREATE, ");
break;
case I2O_CMD_STATIC_MF_RELEASE:
- printk("EXEC_STATIC_MF_RELEASE, ");
+ printk(KERN_DEBUG "EXEC_STATIC_MF_RELEASE, ");
break;
case I2O_CMD_STATUS_GET:
- printk("EXEC_STATUS_GET, ");
+ printk(KERN_DEBUG "EXEC_STATUS_GET, ");
break;
case I2O_CMD_SW_DOWNLOAD:
- printk("EXEC_SW_DOWNLOAD, ");
+ printk(KERN_DEBUG "EXEC_SW_DOWNLOAD, ");
break;
case I2O_CMD_SW_UPLOAD:
- printk("EXEC_SW_UPLOAD, ");
+ printk(KERN_DEBUG "EXEC_SW_UPLOAD, ");
break;
case I2O_CMD_SW_REMOVE:
- printk("EXEC_SW_REMOVE, ");
+ printk(KERN_DEBUG "EXEC_SW_REMOVE, ");
break;
case I2O_CMD_SYS_ENABLE:
- printk("EXEC_SYS_ENABLE, ");
+ printk(KERN_DEBUG "EXEC_SYS_ENABLE, ");
break;
case I2O_CMD_SYS_MODIFY:
- printk("EXEC_SYS_MODIFY, ");
+ printk(KERN_DEBUG "EXEC_SYS_MODIFY, ");
break;
case I2O_CMD_SYS_QUIESCE:
- printk("EXEC_SYS_QUIESCE, ");
+ printk(KERN_DEBUG "EXEC_SYS_QUIESCE, ");
break;
case I2O_CMD_SYS_TAB_SET:
- printk("EXEC_SYS_TAB_SET, ");
+ printk(KERN_DEBUG "EXEC_SYS_TAB_SET, ");
break;
default:
- printk("Cmd = %#02x, ", cmd);
+ printk(KERN_DEBUG "Cmd = %#02x, ", cmd);
}
}
@@ -445,28 +450,28 @@ void i2o_debug_state(struct i2o_controller *c)
printk(KERN_INFO "%s: State = ", c->name);
switch (((i2o_status_block *) c->status_block.virt)->iop_state) {
case 0x01:
- printk("INIT\n");
+ printk(KERN_DEBUG "INIT\n");
break;
case 0x02:
- printk("RESET\n");
+ printk(KERN_DEBUG "RESET\n");
break;
case 0x04:
- printk("HOLD\n");
+ printk(KERN_DEBUG "HOLD\n");
break;
case 0x05:
- printk("READY\n");
+ printk(KERN_DEBUG "READY\n");
break;
case 0x08:
- printk("OPERATIONAL\n");
+ printk(KERN_DEBUG "OPERATIONAL\n");
break;
case 0x10:
- printk("FAILED\n");
+ printk(KERN_DEBUG "FAILED\n");
break;
case 0x11:
- printk("FAULTED\n");
+ printk(KERN_DEBUG "FAULTED\n");
break;
default:
- printk("%x (unknown !!)\n",
+ printk(KERN_DEBUG "%x (unknown !!)\n",
((i2o_status_block *) c->status_block.virt)->iop_state);
}
};
@@ -516,53 +521,58 @@ void i2o_dump_hrt(struct i2o_controller *c)
d = (u8 *) (rows + 2);
state = p[1] << 8 | p[0];
- printk("TID %04X:[", state & 0xFFF);
+ printk(KERN_DEBUG "TID %04X:[", state & 0xFFF);
state >>= 12;
if (state & (1 << 0))
- printk("H"); /* Hidden */
+ printk(KERN_DEBUG "H"); /* Hidden */
if (state & (1 << 2)) {
- printk("P"); /* Present */
+ printk(KERN_DEBUG "P"); /* Present */
if (state & (1 << 1))
- printk("C"); /* Controlled */
+ printk(KERN_DEBUG "C"); /* Controlled */
}
if (state > 9)
- printk("*"); /* Hard */
+ printk(KERN_DEBUG "*"); /* Hard */
- printk("]:");
+ printk(KERN_DEBUG "]:");
switch (p[3] & 0xFFFF) {
case 0:
/* Adapter private bus - easy */
- printk("Local bus %d: I/O at 0x%04X Mem 0x%08X",
- p[2], d[1] << 8 | d[0], *(u32 *) (d + 4));
+ printk(KERN_DEBUG
+ "Local bus %d: I/O at 0x%04X Mem 0x%08X", p[2],
+ d[1] << 8 | d[0], *(u32 *) (d + 4));
break;
case 1:
/* ISA bus */
- printk("ISA %d: CSN %d I/O at 0x%04X Mem 0x%08X",
- p[2], d[2], d[1] << 8 | d[0], *(u32 *) (d + 4));
+ printk(KERN_DEBUG
+ "ISA %d: CSN %d I/O at 0x%04X Mem 0x%08X", p[2],
+ d[2], d[1] << 8 | d[0], *(u32 *) (d + 4));
break;
case 2: /* EISA bus */
- printk("EISA %d: Slot %d I/O at 0x%04X Mem 0x%08X",
+ printk(KERN_DEBUG
+ "EISA %d: Slot %d I/O at 0x%04X Mem 0x%08X",
p[2], d[3], d[1] << 8 | d[0], *(u32 *) (d + 4));
break;
case 3: /* MCA bus */
- printk("MCA %d: Slot %d I/O at 0x%04X Mem 0x%08X",
- p[2], d[3], d[1] << 8 | d[0], *(u32 *) (d + 4));
+ printk(KERN_DEBUG
+ "MCA %d: Slot %d I/O at 0x%04X Mem 0x%08X", p[2],
+ d[3], d[1] << 8 | d[0], *(u32 *) (d + 4));
break;
case 4: /* PCI bus */
- printk("PCI %d: Bus %d Device %d Function %d",
- p[2], d[2], d[1], d[0]);
+ printk(KERN_DEBUG
+ "PCI %d: Bus %d Device %d Function %d", p[2],
+ d[2], d[1], d[0]);
break;
case 0x80: /* Other */
default:
- printk("Unsupported bus type.");
+ printk(KERN_DEBUG "Unsupported bus type.");
break;
}
- printk("\n");
+ printk(KERN_DEBUG "\n");
rows += length;
}
}
diff --git a/drivers/message/i2o/device.c b/drivers/message/i2o/device.c
index ff4822ed4df8..def6ea54416e 100644
--- a/drivers/message/i2o/device.c
+++ b/drivers/message/i2o/device.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/i2o.h>
+#include <linux/delay.h>
/* Exec OSM functions */
extern struct bus_type i2o_bus_type;
@@ -106,8 +107,7 @@ int i2o_device_claim_release(struct i2o_device *dev)
if (!rc)
break;
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(HZ);
+ ssleep(1);
}
if (!rc)
diff --git a/drivers/message/i2o/driver.c b/drivers/message/i2o/driver.c
index bc69d66c2623..1102013ab768 100644
--- a/drivers/message/i2o/driver.c
+++ b/drivers/message/i2o/driver.c
@@ -18,7 +18,6 @@
#include <linux/rwsem.h>
#include <linux/i2o.h>
-
/* max_drivers - Maximum I2O drivers (OSMs) which could be registered */
unsigned int i2o_max_drivers = I2O_MAX_DRIVERS;
module_param_named(max_drivers, i2o_max_drivers, uint, 0);
@@ -146,7 +145,7 @@ void i2o_driver_unregister(struct i2o_driver *drv)
struct i2o_device *i2o_dev;
list_for_each_entry(i2o_dev, &c->devices, list)
- i2o_driver_notify_device_remove(drv, i2o_dev);
+ i2o_driver_notify_device_remove(drv, i2o_dev);
i2o_driver_notify_controller_remove(drv, c);
}
@@ -246,14 +245,15 @@ int i2o_driver_dispatch(struct i2o_controller *c, u32 m,
* Send notifications to all registered drivers that a new controller was
* added.
*/
-void i2o_driver_notify_controller_add_all(struct i2o_controller *c) {
+void i2o_driver_notify_controller_add_all(struct i2o_controller *c)
+{
int i;
struct i2o_driver *drv;
- for(i = 0; i < I2O_MAX_DRIVERS; i ++) {
+ for (i = 0; i < I2O_MAX_DRIVERS; i++) {
drv = i2o_drivers[i];
- if(drv)
+ if (drv)
i2o_driver_notify_controller_add(drv, c);
}
}
@@ -265,14 +265,15 @@ void i2o_driver_notify_controller_add_all(struct i2o_controller *c) {
* Send notifications to all registered drivers that a controller was
* removed.
*/
-void i2o_driver_notify_controller_remove_all(struct i2o_controller *c) {
+void i2o_driver_notify_controller_remove_all(struct i2o_controller *c)
+{
int i;
struct i2o_driver *drv;
- for(i = 0; i < I2O_MAX_DRIVERS; i ++) {
+ for (i = 0; i < I2O_MAX_DRIVERS; i++) {
drv = i2o_drivers[i];
- if(drv)
+ if (drv)
i2o_driver_notify_controller_remove(drv, c);
}
}
@@ -283,14 +284,15 @@ void i2o_driver_notify_controller_remove_all(struct i2o_controller *c) {
*
* Send notifications to all registered drivers that a device was added.
*/
-void i2o_driver_notify_device_add_all(struct i2o_device *i2o_dev) {
+void i2o_driver_notify_device_add_all(struct i2o_device *i2o_dev)
+{
int i;
struct i2o_driver *drv;
- for(i = 0; i < I2O_MAX_DRIVERS; i ++) {
+ for (i = 0; i < I2O_MAX_DRIVERS; i++) {
drv = i2o_drivers[i];
- if(drv)
+ if (drv)
i2o_driver_notify_device_add(drv, i2o_dev);
}
}
@@ -301,14 +303,15 @@ void i2o_driver_notify_device_add_all(struct i2o_device *i2o_dev) {
*
* Send notifications to all registered drivers that a device was removed.
*/
-void i2o_driver_notify_device_remove_all(struct i2o_device *i2o_dev) {
+void i2o_driver_notify_device_remove_all(struct i2o_device *i2o_dev)
+{
int i;
struct i2o_driver *drv;
- for(i = 0; i < I2O_MAX_DRIVERS; i ++) {
+ for (i = 0; i < I2O_MAX_DRIVERS; i++) {
drv = i2o_drivers[i];
- if(drv)
+ if (drv)
i2o_driver_notify_device_remove(drv, i2o_dev);
}
}
diff --git a/drivers/message/i2o/exec-osm.c b/drivers/message/i2o/exec-osm.c
index 117f26106491..6d865f89642f 100644
--- a/drivers/message/i2o/exec-osm.c
+++ b/drivers/message/i2o/exec-osm.c
@@ -29,6 +29,7 @@
#include <linux/module.h>
#include <linux/i2o.h>
+#include <linux/delay.h>
struct i2o_driver i2o_exec_driver;
@@ -151,7 +152,7 @@ int i2o_msg_post_wait_mem(struct i2o_controller *c, u32 m, unsigned long
prepare_to_wait(&wq, &wait, TASK_INTERRUPTIBLE);
if (!iwait->complete)
- schedule_timeout(timeout * HZ);
+ msleep_interruptible(timeout * 1000);
finish_wait(&wq, &wait);
@@ -322,13 +323,13 @@ static void i2o_exec_lct_modified(struct i2o_controller *c)
static int i2o_exec_reply(struct i2o_controller *c, u32 m,
struct i2o_message *msg)
{
- if (readl(&msg->u.head[0]) & MSG_FAIL) { // Fail bit is set
+ if (le32_to_cpu(msg->u.head[0]) & MSG_FAIL) { // Fail bit is set
struct i2o_message *pmsg; /* preserved message */
u32 pm;
- pm = readl(&msg->body[3]);
+ pm = le32_to_cpu(msg->body[3]);
- pmsg = c->in_queue.virt + pm;
+ pmsg = i2o_msg_in_to_virt(c, pm);
i2o_report_status(KERN_INFO, "i2o_core", msg);
@@ -339,10 +340,10 @@ static int i2o_exec_reply(struct i2o_controller *c, u32 m,
return -1;
}
- if (readl(&msg->u.s.tcntxt) & 0x80000000)
+ if (le32_to_cpu(msg->u.s.tcntxt) & 0x80000000)
return i2o_msg_post_wait_complete(c, m, msg);
- if ((readl(&msg->u.head[1]) >> 24) == I2O_CMD_LCT_NOTIFY) {
+ if ((le32_to_cpu(msg->u.head[1]) >> 24) == I2O_CMD_LCT_NOTIFY) {
struct work_struct *work;
pr_debug("%s: LCT notify received\n", c->name);
diff --git a/drivers/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c
index 102fb83164c0..04080c80ba37 100644
--- a/drivers/message/i2o/i2o_block.c
+++ b/drivers/message/i2o/i2o_block.c
@@ -416,11 +416,10 @@ static int i2o_block_reply(struct i2o_controller *c, u32 m,
unsigned long flags;
/* FAILed message */
- if (unlikely(readl(&msg->u.head[0]) & (1 << 13))) {
+ if (unlikely(le32_to_cpu(msg->u.head[0]) & (1 << 13))) {
struct i2o_message *pmsg;
u32 pm;
- printk(KERN_WARNING "FAIL");
/*
* FAILed message from controller
* We increment the error count and abort it
@@ -431,10 +430,10 @@ static int i2o_block_reply(struct i2o_controller *c, u32 m,
* better be on the safe side since no one really follows
* the spec to the book :)
*/
- pm = readl(&msg->body[3]);
- pmsg = c->in_queue.virt + pm;
+ pm = le32_to_cpu(msg->body[3]);
+ pmsg = i2o_msg_in_to_virt(c, pm);
- req = i2o_cntxt_list_get(c, readl(&pmsg->u.s.tcntxt));
+ req = i2o_cntxt_list_get(c, le32_to_cpu(pmsg->u.s.tcntxt));
if (unlikely(!req)) {
printk(KERN_ERR "block-osm: NULL reply received!\n");
return -1;
@@ -449,7 +448,7 @@ static int i2o_block_reply(struct i2o_controller *c, u32 m,
spin_lock_irqsave(q->queue_lock, flags);
while (end_that_request_chunk(req, !req->errors,
- readl(&pmsg->body[1]))) ;
+ le32_to_cpu(pmsg->body[1]))) ;
end_that_request_last(req);
dev->open_queue_depth--;
@@ -464,7 +463,7 @@ static int i2o_block_reply(struct i2o_controller *c, u32 m,
return -1;
}
- req = i2o_cntxt_list_get(c, readl(&msg->u.s.tcntxt));
+ req = i2o_cntxt_list_get(c, le32_to_cpu(msg->u.s.tcntxt));
if (unlikely(!req)) {
printk(KERN_ERR "block-osm: NULL reply received!\n");
return -1;
@@ -487,7 +486,7 @@ static int i2o_block_reply(struct i2o_controller *c, u32 m,
"I2O Block: Data transfer to deleted device!\n");
spin_lock_irqsave(q->queue_lock, flags);
while (end_that_request_chunk
- (req, !req->errors, readl(&msg->body[1]))) ;
+ (req, !req->errors, le32_to_cpu(msg->body[1]))) ;
end_that_request_last(req);
dev->open_queue_depth--;
@@ -503,7 +502,7 @@ static int i2o_block_reply(struct i2o_controller *c, u32 m,
* request in the context.
*/
- st = readl(&msg->body[0]) >> 24;
+ st = le32_to_cpu(msg->body[0]) >> 24;
if (st != 0) {
int err;
@@ -524,7 +523,7 @@ static int i2o_block_reply(struct i2o_controller *c, u32 m,
"Volume has changed, waiting for acknowledgement"
};
- err = readl(&msg->body[0]) & 0xffff;
+ err = le32_to_cpu(msg->body[0]) & 0xffff;
/*
* Device not ready means two things. One is that the
@@ -538,17 +537,18 @@ static int i2o_block_reply(struct i2o_controller *c, u32 m,
* Don't stick a supertrak100 into cache aggressive modes
*/
- printk(KERN_ERR "\n/dev/%s error: %s", dev->gd->disk_name,
- bsa_errors[readl(&msg->body[0]) & 0xffff]);
- if (readl(&msg->body[0]) & 0x00ff0000)
- printk(" - DDM attempted %d retries",
- (readl(&msg->body[0]) >> 16) & 0x00ff);
- printk(".\n");
+ printk(KERN_ERR "/dev/%s error: %s", dev->gd->disk_name,
+ bsa_errors[le32_to_cpu(msg->body[0]) & 0xffff]);
+ if (le32_to_cpu(msg->body[0]) & 0x00ff0000)
+ printk(KERN_ERR " - DDM attempted %d retries",
+ (le32_to_cpu(msg->body[0]) >> 16) & 0x00ff);
+ printk(KERN_ERR ".\n");
req->errors++;
} else
req->errors = 0;
- if (!end_that_request_chunk(req, !req->errors, readl(&msg->body[1]))) {
+ if (!end_that_request_chunk
+ (req, !req->errors, le32_to_cpu(msg->body[1]))) {
add_disk_randomness(req->rq_disk);
spin_lock_irqsave(q->queue_lock, flags);
@@ -563,7 +563,7 @@ static int i2o_block_reply(struct i2o_controller *c, u32 m,
i2o_block_sglist_free(ireq);
i2o_block_request_free(ireq);
} else
- printk(KERN_ERR "still remaining chunks\n");
+ printk(KERN_ERR "i2o_block: still remaining chunks\n");
return 1;
};
@@ -573,174 +573,6 @@ static void i2o_block_event(struct i2o_event *evt)
printk(KERN_INFO "block-osm: event received\n");
};
-#if 0
-static int i2o_block_event(void *dummy)
-{
- unsigned int evt;
- unsigned long flags;
- struct i2o_block_device *dev;
- int unit;
- //The only event that has data is the SCSI_SMART event.
- struct i2o_reply {
- u32 header[4];
- u32 evt_indicator;
- u8 ASC;
- u8 ASCQ;
- u16 pad;
- u8 data[16];
- } *evt_local;
-
- daemonize("i2oblock");
- allow_signal(SIGKILL);
-
- evt_running = 1;
-
- while (1) {
- if (down_interruptible(&i2ob_evt_sem)) {
- evt_running = 0;
- printk("exiting...");
- break;
- }
-
- /*
- * Keep another CPU/interrupt from overwriting the
- * message while we're reading it
- *
- * We stuffed the unit in the TxContext and grab the event mask
- * None of the BSA we care about events have EventData
- */
- spin_lock_irqsave(&i2ob_evt_lock, flags);
- evt_local = (struct i2o_reply *)evt_msg;
- spin_unlock_irqrestore(&i2ob_evt_lock, flags);
-
- unit = le32_to_cpu(evt_local->header[3]);
- evt = le32_to_cpu(evt_local->evt_indicator);
-
- dev = &i2o_blk_dev[unit];
- switch (evt) {
- /*
- * New volume loaded on same TID, so we just re-install.
- * The TID/controller don't change as it is the same
- * I2O device. It's just new media that we have to
- * rescan.
- */
- case I2O_EVT_IND_BSA_VOLUME_LOAD:
- {
- i2ob_install_device(dev->i2o_device->iop,
- dev->i2o_device, unit);
- add_disk(dev->gendisk);
- break;
- }
-
- /*
- * No media, so set all parameters to 0 and set the media
- * change flag. The I2O device is still valid, just doesn't
- * have media, so we don't want to clear the controller or
- * device pointer.
- */
- case I2O_EVT_IND_BSA_VOLUME_UNLOAD:
- {
- struct gendisk *p = dev->gendisk;
- blk_queue_max_sectors(dev->gendisk->queue, 0);
- del_gendisk(p);
- put_disk(p);
- dev->gendisk = NULL;
- dev->media_change_flag = 1;
- break;
- }
-
- case I2O_EVT_IND_BSA_VOLUME_UNLOAD_REQ:
- printk(KERN_WARNING
- "%s: Attempt to eject locked media\n",
- dev->i2o_device->dev_name);
- break;
-
- /*
- * The capacity has changed and we are going to be
- * updating the max_sectors and other information
- * about this disk. We try a revalidate first. If
- * the block device is in use, we don't want to
- * do that as there may be I/Os bound for the disk
- * at the moment. In that case we read the size
- * from the device and update the information ourselves
- * and the user can later force a partition table
- * update through an ioctl.
- */
- case I2O_EVT_IND_BSA_CAPACITY_CHANGE:
- {
- u64 size;
-
- if (i2ob_query_device(dev, 0x0004, 0, &size, 8)
- != 0)
- i2ob_query_device(dev, 0x0000, 4, &size,
- 8);
-
- spin_lock_irqsave(dev->req_queue->queue_lock,
- flags);
- set_capacity(dev->gendisk, size >> 9);
- spin_unlock_irqrestore(dev->req_queue->
- queue_lock, flags);
- break;
- }
-
- /*
- * We got a SCSI SMART event, we just log the relevant
- * information and let the user decide what they want
- * to do with the information.
- */
- case I2O_EVT_IND_BSA_SCSI_SMART:
- {
- char buf[16];
- printk(KERN_INFO
- "I2O Block: %s received a SCSI SMART Event\n",
- dev->i2o_device->dev_name);
- evt_local->data[16] = '\0';
- sprintf(buf, "%s", &evt_local->data[0]);
- printk(KERN_INFO " Disk Serial#:%s\n",
- buf);
- printk(KERN_INFO " ASC 0x%02x \n",
- evt_local->ASC);
- printk(KERN_INFO " ASCQ 0x%02x \n",
- evt_local->ASCQ);
- break;
- }
-
- /*
- * Non event
- */
-
- case 0:
- break;
-
- /*
- * An event we didn't ask for. Call the card manufacturer
- * and tell them to fix their firmware :)
- */
-
- case 0x20:
- /*
- * If a promise card reports 0x20 event then the brown stuff
- * hit the fan big time. The card seems to recover but loses
- * the pending writes. Deeply ungood except for testing fsck
- */
- if (dev->i2o_device->iop->promise)
- panic
- ("I2O controller firmware failed. Reboot and force a filesystem check.\n");
- default:
- printk(KERN_INFO
- "%s: Received event 0x%X we didn't register for\n"
- KERN_INFO
- " Blame the I2O card manufacturer 8)\n",
- dev->i2o_device->dev_name, evt);
- break;
- }
- };
-
- complete_and_exit(&i2ob_thread_dead, 0);
- return 0;
-}
-#endif
-
/*
* SCSI-CAM for ioctl geometry mapping
* Duplicated with SCSI - this should be moved into somewhere common
diff --git a/drivers/message/i2o/i2o_config.c b/drivers/message/i2o/i2o_config.c
index 4c240102a50e..353762860251 100644
--- a/drivers/message/i2o/i2o_config.c
+++ b/drivers/message/i2o/i2o_config.c
@@ -74,96 +74,6 @@ struct i2o_cfg_info {
static struct i2o_cfg_info *open_files = NULL;
static ulong i2o_cfg_info_id = 0;
-#if 0
-/*
- * This is the callback for any message we have posted. The message itself
- * will be returned to the message pool when we return from the IRQ
- *
- * This runs in irq context so be short and sweet.
- */
-static void i2o_cfg_reply(struct i2o_handler *h, struct i2o_controller *c,
- struct i2o_message *m)
-{
- u32 *msg = (u32 *) m;
-
- if (msg[0] & MSG_FAIL) {
- u32 *preserved_msg = (u32 *) (c->msg_virt + msg[7]);
-
- printk(KERN_ERR "i2o_config: IOP failed to process the msg.\n");
-
- /* Release the preserved msg frame by resubmitting it as a NOP */
-
- preserved_msg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0;
- preserved_msg[1] = I2O_CMD_UTIL_NOP << 24 | HOST_TID << 12 | 0;
- preserved_msg[2] = 0;
- i2o_post_message(c, msg[7]);
- }
-
- if (msg[4] >> 24) // ReqStatus != SUCCESS
- i2o_report_status(KERN_INFO, "i2o_config", msg);
-
- if (m->function == I2O_CMD_UTIL_EVT_REGISTER) {
- struct i2o_cfg_info *inf;
-
- for (inf = open_files; inf; inf = inf->next)
- if (inf->q_id == i2o_cntxt_list_get(c, msg[3]))
- break;
-
- //
- // If this is the case, it means that we're getting
- // events for a file descriptor that's been close()'d
- // w/o the user unregistering for events first.
- // The code currently assumes that the user will
- // take care of unregistering for events before closing
- // a file.
- //
- // TODO:
- // Should we track event registartion and deregister
- // for events when a file is close()'d so this doesn't
- // happen? That would get rid of the search through
- // the linked list since file->private_data could point
- // directly to the i2o_config_info data structure...but
- // it would mean having all sorts of tables to track
- // what each file is registered for...I think the
- // current method is simpler. - DS
- //
- if (!inf)
- return;
-
- inf->event_q[inf->q_in].id.iop = c->unit;
- inf->event_q[inf->q_in].id.tid = m->target_tid;
- inf->event_q[inf->q_in].id.evt_mask = msg[4];
-
- //
- // Data size = msg size - reply header
- //
- inf->event_q[inf->q_in].data_size = (m->size - 5) * 4;
- if (inf->event_q[inf->q_in].data_size)
- memcpy(inf->event_q[inf->q_in].evt_data,
- (unsigned char *)(msg + 5),
- inf->event_q[inf->q_in].data_size);
-
- spin_lock(&i2o_config_lock);
- MODINC(inf->q_in, I2O_EVT_Q_LEN);
- if (inf->q_len == I2O_EVT_Q_LEN) {
- MODINC(inf->q_out, I2O_EVT_Q_LEN);
- inf->q_lost++;
- } else {
- // Keep I2OEVTGET on another CPU from touching this
- inf->q_len++;
- }
- spin_unlock(&i2o_config_lock);
-
-// printk(KERN_INFO "File %p w/id %d has %d events\n",
-// inf->fp, inf->q_id, inf->q_len);
-
- kill_fasync(&inf->fasync, SIGIO, POLL_IN);
- }
-
- return;
-}
-#endif
-
/*
* Each of these describes an i2o message handler. They are
* multiplexed by the i2o_core code
@@ -388,7 +298,7 @@ static int i2o_cfg_swdl(unsigned long arg)
writel(0xD0000000 | fragsize, &msg->body[3]);
writel(buffer.phys, &msg->body[4]);
-// printk("i2o_config: swdl frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize);
+// printk(KERN_INFO "i2o_config: swdl frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize);
status = i2o_msg_post_wait_mem(c, m, 60, &buffer);
if (status != -ETIMEDOUT)
@@ -461,7 +371,7 @@ static int i2o_cfg_swul(unsigned long arg)
writel(0xD0000000 | fragsize, &msg->body[3]);
writel(buffer.phys, &msg->body[4]);
-// printk("i2o_config: swul frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize);
+// printk(KERN_INFO "i2o_config: swul frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize);
status = i2o_msg_post_wait_mem(c, m, 60, &buffer);
if (status != I2O_POST_WAIT_OK) {
diff --git a/drivers/message/i2o/i2o_proc.c b/drivers/message/i2o/i2o_proc.c
index a535c7a1f66f..25e9e3df3075 100644
--- a/drivers/message/i2o/i2o_proc.c
+++ b/drivers/message/i2o/i2o_proc.c
@@ -938,11 +938,6 @@ int i2o_seq_show_drivers_stored(struct seq_file *seq, void *v)
seq_printf(seq, " ");
}
-#if 0
- if (c->i2oversion == 0x02)
- seq_printf(seq, "%-d", dst->module_state);
-#endif
-
seq_printf(seq, "%-#7x", dst->i2o_vendor_id);
seq_printf(seq, "%-#8x", dst->module_id);
seq_printf(seq, "%-29s", chtostr(dst->module_name_version, 28));
@@ -950,10 +945,6 @@ int i2o_seq_show_drivers_stored(struct seq_file *seq, void *v)
seq_printf(seq, "%8d ", dst->module_size);
seq_printf(seq, "%8d ", dst->mpb_size);
seq_printf(seq, "0x%04x", dst->module_flags);
-#if 0
- if (c->i2oversion == 0x02)
- seq_printf(seq, "%d", dst->notification_level);
-#endif
seq_printf(seq, "\n");
}
diff --git a/drivers/message/i2o/i2o_scsi.c b/drivers/message/i2o/i2o_scsi.c
index 9be68f380b1b..7186d004ec1d 100644
--- a/drivers/message/i2o/i2o_scsi.c
+++ b/drivers/message/i2o/i2o_scsi.c
@@ -274,53 +274,6 @@ static const char *i2o_scsi_info(struct Scsi_Host *SChost)
return hostdata->iop->name;
}
-#if 0
-/**
- * i2o_retry_run - retry on timeout
- * @f: unused
- *
- * Retry congested frames. This actually needs pushing down into
- * i2o core. We should only bother the OSM with this when we can't
- * queue and retry the frame. Or perhaps we should call the OSM
- * and its default handler should be this in the core, and this
- * call a 2nd "I give up" handler in the OSM ?
- */
-
-static void i2o_retry_run(unsigned long f)
-{
- int i;
- unsigned long flags;
-
- spin_lock_irqsave(&retry_lock, flags);
- for (i = 0; i < retry_ct; i++)
- i2o_post_message(retry_ctrl[i], virt_to_bus(retry[i]));
- retry_ct = 0;
- spin_unlock_irqrestore(&retry_lock, flags);
-}
-
-/**
- * flush_pending - empty the retry queue
- *
- * Turn each of the pending commands into a NOP and post it back
- * to the controller to clear it.
- */
-
-static void flush_pending(void)
-{
- int i;
- unsigned long flags;
-
- spin_lock_irqsave(&retry_lock, flags);
- for (i = 0; i < retry_ct; i++) {
- retry[i][0] &= ~0xFFFFFF;
- retry[i][0] |= I2O_CMD_UTIL_NOP << 24;
- i2o_post_message(retry_ctrl[i], virt_to_bus(retry[i]));
- }
- retry_ct = 0;
- spin_unlock_irqrestore(&retry_lock, flags);
-}
-#endif
-
/**
* i2o_scsi_reply - SCSI OSM message reply handler
* @c: controller issuing the reply
@@ -343,38 +296,41 @@ static int i2o_scsi_reply(struct i2o_controller *c, u32 m,
struct device *dev;
u8 as, ds, st;
- cmd = i2o_cntxt_list_get(c, readl(&msg->u.s.tcntxt));
+ cmd = i2o_cntxt_list_get(c, le32_to_cpu(msg->u.s.tcntxt));
if (msg->u.head[0] & (1 << 13)) {
struct i2o_message *pmsg; /* preserved message */
u32 pm;
+ int err = DID_ERROR;
- pm = readl(&msg->body[3]);
+ pm = le32_to_cpu(msg->body[3]);
- pmsg = c->in_queue.virt + pm;
+ pmsg = i2o_msg_in_to_virt(c, pm);
- printk("IOP fail.\n");
- printk("From %d To %d Cmd %d.\n",
+ printk(KERN_ERR "IOP fail.\n");
+ printk(KERN_ERR "From %d To %d Cmd %d.\n",
(msg->u.head[1] >> 12) & 0xFFF,
msg->u.head[1] & 0xFFF, msg->u.head[1] >> 24);
- printk("Failure Code %d.\n", msg->body[0] >> 24);
+ printk(KERN_ERR "Failure Code %d.\n", msg->body[0] >> 24);
if (msg->body[0] & (1 << 16))
- printk("Format error.\n");
+ printk(KERN_ERR "Format error.\n");
if (msg->body[0] & (1 << 17))
- printk("Path error.\n");
+ printk(KERN_ERR "Path error.\n");
if (msg->body[0] & (1 << 18))
- printk("Path State.\n");
+ printk(KERN_ERR "Path State.\n");
if (msg->body[0] & (1 << 18))
- printk("Congestion.\n");
+ {
+ printk(KERN_ERR "Congestion.\n");
+ err = DID_BUS_BUSY;
+ }
- printk("Failing message is %p.\n", pmsg);
+ printk(KERN_DEBUG "Failing message is %p.\n", pmsg);
cmd = i2o_cntxt_list_get(c, readl(&pmsg->u.s.tcntxt));
if (!cmd)
return 1;
- printk("Aborted %ld\n", cmd->serial_number);
- cmd->result = DID_ERROR << 16;
+ cmd->result = err << 16;
cmd->scsi_done(cmd);
/* Now flush the message by making it a NOP */
@@ -387,9 +343,9 @@ static int i2o_scsi_reply(struct i2o_controller *c, u32 m,
* Low byte is device status, next is adapter status,
* (then one byte reserved), then request status.
*/
- ds = (u8) readl(&msg->body[0]);
- as = (u8) (readl(&msg->body[0]) >> 8);
- st = (u8) (readl(&msg->body[0]) >> 24);
+ ds = (u8) le32_to_cpu(msg->body[0]);
+ as = (u8) (le32_to_cpu(msg->body[0]) >> 8);
+ st = (u8) (le32_to_cpu(msg->body[0]) >> 24);
/*
* Is this a control request coming back - eg an abort ?
@@ -398,7 +354,7 @@ static int i2o_scsi_reply(struct i2o_controller *c, u32 m,
if (!cmd) {
if (st)
printk(KERN_WARNING "SCSI abort: %08X",
- readl(&msg->body[0]));
+ le32_to_cpu(msg->body[0]));
printk(KERN_INFO "SCSI abort completed.\n");
return -EFAULT;
}
@@ -411,21 +367,22 @@ static int i2o_scsi_reply(struct i2o_controller *c, u32 m,
switch (st) {
case 0x06:
- count = readl(&msg->body[1]);
+ count = le32_to_cpu(msg->body[1]);
if (count < cmd->underflow) {
int i;
printk(KERN_ERR "SCSI: underflow 0x%08X 0x%08X"
"\n", count, cmd->underflow);
- printk("Cmd: ");
+ printk(KERN_DEBUG "Cmd: ");
for (i = 0; i < 15; i++)
- printk("%02X ", cmd->cmnd[i]);
- printk(".\n");
+ printk(KERN_DEBUG "%02X ",
+ cmd->cmnd[i]);
+ printk(KERN_DEBUG ".\n");
cmd->result = (DID_ERROR << 16);
}
break;
default:
- error = readl(&msg->body[0]);
+ error = le32_to_cpu(msg->body[0]);
printk(KERN_ERR "scsi-osm: SCSI error %08x\n", error);
@@ -517,8 +474,7 @@ void i2o_scsi_notify_controller_add(struct i2o_controller *c)
rc = scsi_add_host(i2o_shost->scsi_host, &c->device);
if (rc) {
- printk(KERN_ERR "scsi-osm: Could not add SCSI "
- "host\n");
+ printk(KERN_ERR "scsi-osm: Could not add SCSI " "host\n");
scsi_host_put(i2o_shost->scsi_host);
return;
}
diff --git a/drivers/message/i2o/iop.c b/drivers/message/i2o/iop.c
index 699723e3a3c3..46fa7dfd6eb1 100644
--- a/drivers/message/i2o/iop.c
+++ b/drivers/message/i2o/iop.c
@@ -27,6 +27,7 @@
#include <linux/module.h>
#include <linux/i2o.h>
+#include <linux/delay.h>
/* global I2O controller list */
LIST_HEAD(i2o_controllers);
@@ -117,7 +118,7 @@ u32 i2o_msg_get_wait(struct i2o_controller *c, struct i2o_message **msg,
*
* Returns context id > 0 on success or 0 on failure.
*/
-u32 i2o_cntxt_list_add(struct i2o_controller *c, void *ptr)
+u32 i2o_cntxt_list_add(struct i2o_controller * c, void *ptr)
{
struct i2o_context_list_element *entry;
unsigned long flags;
@@ -162,7 +163,7 @@ u32 i2o_cntxt_list_add(struct i2o_controller *c, void *ptr)
*
* Returns context id on succes or 0 on failure.
*/
-u32 i2o_cntxt_list_remove(struct i2o_controller *c, void *ptr)
+u32 i2o_cntxt_list_remove(struct i2o_controller * c, void *ptr)
{
struct i2o_context_list_element *entry;
u32 context = 0;
@@ -470,7 +471,7 @@ static int i2o_iop_reset(struct i2o_controller *c)
if (m == I2O_QUEUE_EMPTY)
return -ETIMEDOUT;
- memset(status, 0, 4);
+ memset(status, 0, 8);
/* Quiesce all IOPs first */
i2o_iop_quiesce_all();
@@ -495,6 +496,13 @@ static int i2o_iop_reset(struct i2o_controller *c)
rc = -ETIMEDOUT;
goto exit;
}
+
+ /* Promise bug */
+ if (status[1] || status[4]) {
+ *status = 0;
+ break;
+ }
+
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(1);
@@ -605,6 +613,7 @@ int i2o_iop_init_outbound_queue(struct i2o_controller *c)
/* Post frames */
for (i = 0; i < NMBR_MSG_FRAMES; i++) {
i2o_flush_reply(c, m);
+ udelay(1); /* Promise */
m += MSG_FRAME_SIZE * 4;
}
@@ -612,6 +621,23 @@ int i2o_iop_init_outbound_queue(struct i2o_controller *c)
}
/**
+ * i2o_iop_send_nop - send a core NOP message
+ * @c: controller
+ *
+ * Send a no-operation message with a reply set to cause no
+ * action either. Needed for bringing up promise controllers.
+ */
+static int i2o_iop_send_nop(struct i2o_controller *c)
+{
+ struct i2o_message *msg;
+ u32 m = i2o_msg_get_wait(c, &msg, HZ);
+ if (m == I2O_QUEUE_EMPTY)
+ return -ETIMEDOUT;
+ i2o_msg_nop(c, m);
+ return 0;
+}
+
+/**
* i2o_iop_activate - Bring controller up to HOLD
* @c: controller
*
@@ -622,8 +648,27 @@ int i2o_iop_init_outbound_queue(struct i2o_controller *c)
*/
static int i2o_iop_activate(struct i2o_controller *c)
{
+ struct pci_dev *i960 = NULL;
i2o_status_block *sb = c->status_block.virt;
int rc;
+
+ if (c->promise) {
+ /* Beat up the hardware first of all */
+ i960 =
+ pci_find_slot(c->pdev->bus->number,
+ PCI_DEVFN(PCI_SLOT(c->pdev->devfn), 0));
+ if (i960)
+ pci_write_config_word(i960, 0x42, 0);
+
+ /* Follow this sequence precisely or the controller
+ ceases to perform useful functions until reboot */
+ if ((rc = i2o_iop_send_nop(c)))
+ return rc;
+
+ if ((rc = i2o_iop_reset(c)))
+ return rc;
+ }
+
/* In INIT state, Wait Inbound Q to initialize (in i2o_status_get) */
/* In READY state, Get status */
@@ -659,13 +704,22 @@ static int i2o_iop_activate(struct i2o_controller *c)
if (rc)
return rc;
+ if (c->promise) {
+ if ((rc = i2o_iop_send_nop(c)))
+ return rc;
+
+ if ((rc = i2o_status_get(c)))
+ return rc;
+
+ if (i960)
+ pci_write_config_word(i960, 0x42, 0x3FF);
+ }
+
/* In HOLD state */
rc = i2o_hrt_get(c);
- if (rc)
- return rc;
- return 0;
+ return rc;
};
/**
@@ -691,10 +745,11 @@ static int i2o_iop_systab_set(struct i2o_controller *c)
res->flags = IORESOURCE_MEM;
res->start = 0;
res->end = 0;
- printk("%s: requires private memory resources.\n", c->name);
+ printk(KERN_INFO "%s: requires private memory resources.\n",
+ c->name);
root = pci_find_parent_resource(c->pdev, res);
if (root == NULL)
- printk("Can't find parent resource!\n");
+ printk(KERN_WARNING "Can't find parent resource!\n");
if (root && allocate_resource(root, res, sb->desired_mem_size, sb->desired_mem_size, sb->desired_mem_size, 1 << 20, /* Unspecified, so use 1Mb and play safe */
NULL, NULL) >= 0) {
c->mem_alloc = 1;
@@ -712,10 +767,11 @@ static int i2o_iop_systab_set(struct i2o_controller *c)
res->flags = IORESOURCE_IO;
res->start = 0;
res->end = 0;
- printk("%s: requires private memory resources.\n", c->name);
+ printk(KERN_INFO "%s: requires private memory resources.\n",
+ c->name);
root = pci_find_parent_resource(c->pdev, res);
if (root == NULL)
- printk("Can't find parent resource!\n");
+ printk(KERN_WARNING "Can't find parent resource!\n");
if (root && allocate_resource(root, res, sb->desired_io_size, sb->desired_io_size, sb->desired_io_size, 1 << 20, /* Unspecified, so use 1Mb and play safe */
NULL, NULL) >= 0) {
c->io_alloc = 1;
diff --git a/drivers/message/i2o/pci.c b/drivers/message/i2o/pci.c
index 9ee58b6cf55b..f98849abbe2a 100644
--- a/drivers/message/i2o/pci.c
+++ b/drivers/message/i2o/pci.c
@@ -138,13 +138,13 @@ static int __devinit i2o_pci_alloc(struct i2o_controller *c)
* If we know what card it is, set the size
* correctly. Code is taken from dpt_i2o.c
*/
- if(pdev->device == 0xa501) {
- if(pdev->subsystem_device >= 0xc032 &&
- pdev->subsystem_device <= 0xc03b) {
- if(c->base.len > 0x400000)
+ if (pdev->device == 0xa501) {
+ if (pdev->subsystem_device >= 0xc032 &&
+ pdev->subsystem_device <= 0xc03b) {
+ if (c->base.len > 0x400000)
c->base.len = 0x400000;
} else {
- if(c->base.len > 0x100000)
+ if (c->base.len > 0x100000)
c->base.len = 0x100000;
}
}
@@ -231,7 +231,7 @@ static int __devinit i2o_pci_alloc(struct i2o_controller *c)
}
#endif
- if (i2o_dma_alloc(dev, &c->status, 4, GFP_KERNEL)) {
+ if (i2o_dma_alloc(dev, &c->status, 8, GFP_KERNEL)) {
i2o_pci_free(c);
return -ENOMEM;
}
@@ -277,7 +277,6 @@ static irqreturn_t i2o_pci_interrupt(int irq, void *dev_id, struct pt_regs *r)
struct device *dev = &c->pdev->dev;
struct i2o_message *m;
u32 mv;
- u32 *msg;
/*
* Old 960 steppings had a bug in the I2O unit that caused
@@ -298,11 +297,7 @@ static irqreturn_t i2o_pci_interrupt(int irq, void *dev_id, struct pt_regs *r)
* Because bus_to_virt is deprecated, we have calculate the
* location by ourself!
*/
- m = (struct i2o_message *)(mv -
- (unsigned long)c->out_queue.phys +
- (unsigned long)c->out_queue.virt);
-
- msg = (u32 *) m;
+ m = i2o_msg_out_to_virt(c, mv);
/*
* Ensure this message is seen coherently but cachably by
diff --git a/drivers/net/bsd_comp.c b/drivers/net/bsd_comp.c
index 409b75442bbf..3d88ad622bdb 100644
--- a/drivers/net/bsd_comp.c
+++ b/drivers/net/bsd_comp.c
@@ -1160,7 +1160,7 @@ static struct compressor ppp_bsd_compress = {
* Module support routines
*************************************************************/
-int __init bsdcomp_init(void)
+static int __init bsdcomp_init(void)
{
int answer = ppp_register_compressor(&ppp_bsd_compress);
if (answer == 0)
@@ -1168,7 +1168,7 @@ int __init bsdcomp_init(void)
return answer;
}
-void __exit bsdcomp_cleanup(void)
+static void __exit bsdcomp_cleanup(void)
{
ppp_unregister_compressor(&ppp_bsd_compress);
}
diff --git a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c
index 4beb7ac2a861..94191fdb8e13 100644
--- a/drivers/net/hamradio/dmascc.c
+++ b/drivers/net/hamradio/dmascc.c
@@ -37,7 +37,6 @@
#include <linux/rtnetlink.h>
#include <linux/sockios.h>
#include <linux/workqueue.h>
-#include <linux/version.h>
#include <asm/atomic.h>
#include <asm/bitops.h>
#include <asm/dma.h>
diff --git a/drivers/net/irda/stir4200.c b/drivers/net/irda/stir4200.c
index 81282d94f91d..5f26d9ff30e9 100644
--- a/drivers/net/irda/stir4200.c
+++ b/drivers/net/irda/stir4200.c
@@ -168,6 +168,7 @@ enum StirTestMask {
struct stir_cb {
struct usb_device *usbdev; /* init: probe_irda */
+ struct usb_interface *usbintf;
struct net_device *netdev; /* network layer */
struct irlap_cb *irlap; /* The link layer we are binded to */
struct net_device_stats stats; /* network statistics */
@@ -508,6 +509,7 @@ static int change_speed(struct stir_cb *stir, unsigned speed)
{
int i, err;
__u8 mode;
+ int rc;
for (i = 0; i < ARRAY_SIZE(stir_modes); ++i) {
if (speed == stir_modes[i].speed)
@@ -521,7 +523,14 @@ static int change_speed(struct stir_cb *stir, unsigned speed)
pr_debug("speed change from %d to %d\n", stir->speed, speed);
/* sometimes needed to get chip out of stuck state */
+ rc = usb_lock_device_for_reset(stir->usbdev, stir->usbintf);
+ if (rc < 0) {
+ err = rc;
+ goto out;
+ }
err = usb_reset_device(stir->usbdev);
+ if (rc)
+ usb_unlock_device(stir->usbdev);
if (err)
goto out;
@@ -1066,6 +1075,7 @@ static int stir_probe(struct usb_interface *intf,
stir = net->priv;
stir->netdev = net;
stir->usbdev = dev;
+ stir->usbintf = intf;
ret = usb_reset_configuration(dev);
if (ret != 0) {
diff --git a/drivers/net/mac89x0.c b/drivers/net/mac89x0.c
index 8c08255899ea..f65b0db111b8 100644
--- a/drivers/net/mac89x0.c
+++ b/drivers/net/mac89x0.c
@@ -98,6 +98,7 @@ static char *version =
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
+#include <linux/delay.h>
#include <asm/system.h>
#include <asm/bitops.h>
@@ -308,8 +309,7 @@ void __init reset_chip(struct net_device *dev)
writereg(dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) | POWER_ON_RESET);
/* wait 30 ms */
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(30*HZ/1000);
+ msleep_interruptible(30);
/* Wait until the chip is reset */
reset_start_time = jiffies;
diff --git a/drivers/net/ppp_deflate.c b/drivers/net/ppp_deflate.c
index c51291a3c40a..df75c94e1d0c 100644
--- a/drivers/net/ppp_deflate.c
+++ b/drivers/net/ppp_deflate.c
@@ -636,7 +636,7 @@ struct compressor ppp_deflate_draft = {
.owner = THIS_MODULE
};
-int __init deflate_init(void)
+static int __init deflate_init(void)
{
int answer = ppp_register_compressor(&ppp_deflate);
if (answer == 0)
@@ -646,7 +646,7 @@ int __init deflate_init(void)
return answer;
}
-void __exit deflate_cleanup(void)
+static void __exit deflate_cleanup(void)
{
ppp_unregister_compressor(&ppp_deflate);
ppp_unregister_compressor(&ppp_deflate_draft);
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index faaf5d04c05b..cf2b3ee2ce3f 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -827,6 +827,236 @@ static void __init quirk_sis_96x_smbus(struct pci_dev *dev)
pci_read_config_byte(dev, 0x77, &val);
}
+
+#define UHCI_USBLEGSUP 0xc0 /* legacy support */
+#define UHCI_USBCMD 0 /* command register */
+#define UHCI_USBSTS 2 /* status register */
+#define UHCI_USBINTR 4 /* interrupt register */
+#define UHCI_USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */
+#define UHCI_USBCMD_RUN (1 << 0) /* RUN/STOP bit */
+#define UHCI_USBCMD_GRESET (1 << 2) /* Global reset */
+#define UHCI_USBCMD_CONFIGURE (1 << 6) /* config semaphore */
+#define UHCI_USBSTS_HALTED (1 << 5) /* HCHalted bit */
+
+#define OHCI_CONTROL 0x04
+#define OHCI_CMDSTATUS 0x08
+#define OHCI_INTRSTATUS 0x0c
+#define OHCI_INTRENABLE 0x10
+#define OHCI_INTRDISABLE 0x14
+#define OHCI_OCR (1 << 3) /* ownership change request */
+#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */
+#define OHCI_INTR_OC (1 << 30) /* ownership change */
+
+#define EHCI_HCC_PARAMS 0x08 /* extended capabilities */
+#define EHCI_USBCMD 0 /* command register */
+#define EHCI_USBCMD_RUN (1 << 0) /* RUN/STOP bit */
+#define EHCI_USBSTS 4 /* status register */
+#define EHCI_USBSTS_HALTED (1 << 12) /* HCHalted bit */
+#define EHCI_USBINTR 8 /* interrupt register */
+#define EHCI_USBLEGSUP 0 /* legacy support register */
+#define EHCI_USBLEGSUP_BIOS (1 << 16) /* BIOS semaphore */
+#define EHCI_USBLEGSUP_OS (1 << 24) /* OS semaphore */
+#define EHCI_USBLEGCTLSTS 4 /* legacy control/status */
+#define EHCI_USBLEGCTLSTS_SOOE (1 << 13) /* SMI on ownership change */
+
+int usb_early_handoff __initdata = 0;
+static int __init usb_handoff_early(char *str)
+{
+ usb_early_handoff = 1;
+ return 0;
+}
+__setup("usb-handoff", usb_handoff_early);
+
+static void __devinit quirk_usb_handoff_uhci(struct pci_dev *pdev)
+{
+ unsigned long base = 0;
+ int wait_time, delta;
+ u16 val, sts;
+ int i;
+
+ for (i = 0; i < PCI_ROM_RESOURCE; i++)
+ if ((pci_resource_flags(pdev, i) & IORESOURCE_IO)) {
+ base = pci_resource_start(pdev, i);
+ break;
+ }
+
+ if (!base)
+ return;
+
+ /*
+ * stop controller
+ */
+ sts = inw(base + UHCI_USBSTS);
+ val = inw(base + UHCI_USBCMD);
+ val &= ~(u16)(UHCI_USBCMD_RUN | UHCI_USBCMD_CONFIGURE);
+ outw(val, base + UHCI_USBCMD);
+
+ /*
+ * wait while it stops if it was running
+ */
+ if ((sts & UHCI_USBSTS_HALTED) == 0)
+ {
+ wait_time = 1000;
+ delta = 100;
+
+ do {
+ outw(0x1f, base + UHCI_USBSTS);
+ udelay(delta);
+ wait_time -= delta;
+ val = inw(base + UHCI_USBSTS);
+ if (val & UHCI_USBSTS_HALTED)
+ break;
+ } while (wait_time > 0);
+ }
+
+ /*
+ * disable interrupts & legacy support
+ */
+ outw(0, base + UHCI_USBINTR);
+ outw(0x1f, base + UHCI_USBSTS);
+ pci_read_config_word(pdev, UHCI_USBLEGSUP, &val);
+ if (val & 0xbf)
+ pci_write_config_word(pdev, UHCI_USBLEGSUP, UHCI_USBLEGSUP_DEFAULT);
+
+}
+
+static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev)
+{
+ void __iomem *base;
+ int wait_time;
+
+ base = ioremap_nocache(pci_resource_start(pdev, 0),
+ pci_resource_len(pdev, 0));
+ if (base == NULL) return;
+
+ if (readl(base + OHCI_CONTROL) & OHCI_CTRL_IR) {
+ wait_time = 500; /* 0.5 seconds */
+ writel(OHCI_INTR_OC, base + OHCI_INTRENABLE);
+ writel(OHCI_OCR, base + OHCI_CMDSTATUS);
+ while (wait_time > 0 &&
+ readl(base + OHCI_CONTROL) & OHCI_CTRL_IR) {
+ wait_time -= 10;
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout((HZ*10 + 999) / 1000);
+ }
+ }
+
+ /*
+ * disable interrupts
+ */
+ writel(~(u32)0, base + OHCI_INTRDISABLE);
+ writel(~(u32)0, base + OHCI_INTRSTATUS);
+
+ iounmap(base);
+}
+
+static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev)
+{
+ int wait_time, delta;
+ void __iomem *base, *op_reg_base;
+ u32 hcc_params, val, temp;
+ u8 cap_length;
+
+ base = ioremap_nocache(pci_resource_start(pdev, 0),
+ pci_resource_len(pdev, 0));
+ if (base == NULL) return;
+
+ cap_length = readb(base);
+ op_reg_base = base + cap_length;
+ hcc_params = readl(base + EHCI_HCC_PARAMS);
+ hcc_params = (hcc_params >> 8) & 0xff;
+ if (hcc_params) {
+ pci_read_config_dword(pdev,
+ hcc_params + EHCI_USBLEGSUP,
+ &val);
+ if (((val & 0xff) == 1) && (val & EHCI_USBLEGSUP_BIOS)) {
+ /*
+ * Ok, BIOS is in smm mode, try to hand off...
+ */
+ pci_read_config_dword(pdev,
+ hcc_params + EHCI_USBLEGCTLSTS,
+ &temp);
+ pci_write_config_dword(pdev,
+ hcc_params + EHCI_USBLEGCTLSTS,
+ temp | EHCI_USBLEGCTLSTS_SOOE);
+ val |= EHCI_USBLEGSUP_OS;
+ pci_write_config_dword(pdev,
+ hcc_params + EHCI_USBLEGSUP,
+ val);
+
+ wait_time = 500;
+ do {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout((HZ*10+999)/1000);
+ wait_time -= 10;
+ pci_read_config_dword(pdev,
+ hcc_params + EHCI_USBLEGSUP,
+ &val);
+ } while (wait_time && (val & EHCI_USBLEGSUP_BIOS));
+ if (!wait_time) {
+ /*
+ * well, possibly buggy BIOS...
+ */
+ printk(KERN_WARNING "EHCI early BIOS handoff "
+ "failed (BIOS bug ?)\n");
+ pci_write_config_dword(pdev,
+ hcc_params + EHCI_USBLEGSUP,
+ EHCI_USBLEGSUP_OS);
+ pci_write_config_dword(pdev,
+ hcc_params + EHCI_USBLEGCTLSTS,
+ 0);
+ }
+ }
+ }
+
+ /*
+ * halt EHCI & disable its interrupts in any case
+ */
+ val = readl(op_reg_base + EHCI_USBSTS);
+ if ((val & EHCI_USBSTS_HALTED) == 0) {
+ val = readl(op_reg_base + EHCI_USBCMD);
+ val &= ~EHCI_USBCMD_RUN;
+ writel(val, op_reg_base + EHCI_USBCMD);
+
+ wait_time = 2000;
+ delta = 100;
+ do {
+ writel(0x3f, op_reg_base + EHCI_USBSTS);
+ udelay(delta);
+ wait_time -= delta;
+ val = readl(op_reg_base + EHCI_USBSTS);
+ if ((val == ~(u32)0) || (val & EHCI_USBSTS_HALTED)) {
+ break;
+ }
+ } while (wait_time > 0);
+ }
+ writel(0, op_reg_base + EHCI_USBINTR);
+ writel(0x3f, op_reg_base + EHCI_USBSTS);
+
+ iounmap(base);
+
+ return;
+}
+
+
+
+static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev)
+{
+ if (!usb_early_handoff)
+ return;
+
+ if (pdev->class == ((PCI_CLASS_SERIAL_USB << 8) | 0x00)) { /* UHCI */
+ quirk_usb_handoff_uhci(pdev);
+ } else if (pdev->class == ((PCI_CLASS_SERIAL_USB << 8) | 0x10)) { /* OHCI */
+ quirk_usb_handoff_ohci(pdev);
+ } else if (pdev->class == ((PCI_CLASS_SERIAL_USB << 8) | 0x20)) { /* EHCI */
+ quirk_usb_disable_ehci(pdev);
+ }
+
+ return;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, quirk_usb_early_handoff);
+
/*
* ... This is further complicated by the fact that some SiS96x south
* bridges pretend to be 85C503/5513 instead. In that case see if we
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 2249b78487bd..b3714fbd0083 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -1595,8 +1595,8 @@ dasd_alloc_queue(struct dasd_device * device)
device->request_queue->queuedata = device;
#if 0
- elevator_exit(device->request_queue);
- rc = elevator_init(device->request_queue, &elevator_noop);
+ elevator_exit(device->request_queue->elevator);
+ rc = elevator_init(device->request_queue, "noop");
if (rc) {
blk_cleanup_queue(device->request_queue);
return rc;
diff --git a/drivers/s390/char/tape_block.c b/drivers/s390/char/tape_block.c
index b7f4e7b8be74..1efc9f21229e 100644
--- a/drivers/s390/char/tape_block.c
+++ b/drivers/s390/char/tape_block.c
@@ -225,8 +225,8 @@ tapeblock_setup_device(struct tape_device * device)
if (!blkdat->request_queue)
return -ENOMEM;
- elevator_exit(blkdat->request_queue);
- rc = elevator_init(blkdat->request_queue, &elevator_noop);
+ elevator_exit(blkdat->request_queue->elevator);
+ rc = elevator_init(blkdat->request_queue, "noop");
if (rc)
goto cleanup_queue;
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index b6c76b5680a2..da06adf48834 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -377,6 +377,7 @@ static int sr_init_command(struct scsi_cmnd * SCpnt)
return 0;
SCpnt->cmnd[0] = WRITE_10;
SCpnt->sc_data_direction = DMA_TO_DEVICE;
+ cd->cdi.media_written = 1;
} else if (rq_data_dir(SCpnt->request) == READ) {
SCpnt->cmnd[0] = READ_10;
SCpnt->sc_data_direction = DMA_FROM_DEVICE;
@@ -875,10 +876,10 @@ static void get_capabilities(struct scsi_cd *cd)
cd->cdi.mask |= CDC_CLOSE_TRAY; */
/*
- * if DVD-RAM of MRW-W, we are randomly writeable
+ * if DVD-RAM, MRW-W or CD-RW, we are randomly writable
*/
- if ((cd->cdi.mask & (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM)) !=
- (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM)) {
+ if ((cd->cdi.mask & (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) !=
+ (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) {
cd->device->writeable = 1;
}
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 0e805e4f3aa1..7bb3fa4c38b6 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -7,7 +7,7 @@ menu "USB support"
# ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
config USB
tristate "Support for Host-side USB"
- depends on PCI || SA1111 || ARCH_OMAP1510 || ARCH_OMAP1610 || ARCH_LH7A404
+ depends on PCI || SA1111 || ARCH_OMAP1510 || ARCH_OMAP1610 || ARCH_LH7A404 || PXA27x
---help---
Universal Serial Bus (USB) is a specification for a serial bus
subsystem which offers higher speeds and more features than the
@@ -91,6 +91,8 @@ source "drivers/usb/serial/Kconfig"
source "drivers/usb/misc/Kconfig"
+source "drivers/usb/atm/Kconfig"
+
source "drivers/usb/gadget/Kconfig"
endmenu
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index e3787f35cd21..63a7f2d80f83 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -62,8 +62,10 @@ obj-$(CONFIG_USB_LCD) += misc/
obj-$(CONFIG_USB_LED) += misc/
obj-$(CONFIG_USB_LEGOTOWER) += misc/
obj-$(CONFIG_USB_RIO500) += misc/
-obj-$(CONFIG_USB_SPEEDTOUCH) += misc/
obj-$(CONFIG_USB_TEST) += misc/
obj-$(CONFIG_USB_TIGL) += misc/
obj-$(CONFIG_USB_USS720) += misc/
obj-$(CONFIG_USB_PHIDGETSERVO) += misc/
+
+obj-$(CONFIG_USB_ATM) += atm/
+obj-$(CONFIG_USB_SPEEDTOUCH) += atm/
diff --git a/drivers/usb/atm/Kconfig b/drivers/usb/atm/Kconfig
new file mode 100644
index 000000000000..0d9f5379b8cf
--- /dev/null
+++ b/drivers/usb/atm/Kconfig
@@ -0,0 +1,30 @@
+#
+# USB ATM driver configuration
+#
+comment "USB ATM/DSL drivers"
+ depends on USB
+
+config USB_ATM
+ tristate "Generic USB ATM/DSL core I/O support"
+ depends on USB && ATM
+ select CRC32
+ default n
+ help
+ This provides a library which is used for packet I/O by USB DSL
+ modems, such as the SpeedTouch driver below.
+
+ To compile this driver as a module, choose M here: the
+ module will be called usb_atm.
+
+config USB_SPEEDTOUCH
+ tristate "Alcatel Speedtouch USB support"
+ depends on USB && ATM
+ select USB_ATM
+ help
+ Say Y here if you have an Alcatel SpeedTouch USB or SpeedTouch 330
+ modem. In order to use your modem you will need to install the
+ two parts of the firmware, extracted by the user space tools; see
+ <http://www.linux-usb.org/SpeedTouch/> for details.
+
+ To compile this driver as a module, choose M here: the
+ module will be called speedtch.
diff --git a/drivers/usb/atm/Makefile b/drivers/usb/atm/Makefile
new file mode 100644
index 000000000000..9213b8b97587
--- /dev/null
+++ b/drivers/usb/atm/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the rest of the USB drivers
+# (the ones that don't fit into any other categories)
+#
+
+obj-$(CONFIG_USB_ATM) += usb_atm.o
+obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o
diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c
new file mode 100644
index 000000000000..f17e576d12ec
--- /dev/null
+++ b/drivers/usb/atm/speedtch.c
@@ -0,0 +1,866 @@
+/******************************************************************************
+ * speedtch.c - Alcatel SpeedTouch USB xDSL modem driver
+ *
+ * Copyright (C) 2001, Alcatel
+ * Copyright (C) 2003, Duncan Sands
+ * Copyright (C) 2004, David Woodhouse
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ ******************************************************************************/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <asm/processor.h>
+#include <asm/uaccess.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include <linux/crc32.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+
+#include "usb_atm.h"
+
+/*
+#define DEBUG
+#define VERBOSE_DEBUG
+*/
+
+#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG)
+# define DEBUG
+#endif
+
+#include <linux/usb.h>
+
+#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
+# define USE_FW_LOADER
+#endif
+
+#ifdef VERBOSE_DEBUG
+static int udsl_print_packet(const unsigned char *data, int len);
+#define PACKETDEBUG(arg...) udsl_print_packet (arg)
+#define vdbg(arg...) dbg (arg)
+#else
+#define PACKETDEBUG(arg...)
+#define vdbg(arg...)
+#endif
+
+#define DRIVER_AUTHOR "Johan Verrept, Duncan Sands <duncan.sands@free.fr>"
+#define DRIVER_VERSION "1.8"
+#define DRIVER_DESC "Alcatel SpeedTouch USB driver version " DRIVER_VERSION
+
+static const char speedtch_driver_name[] = "speedtch";
+
+#define SPEEDTOUCH_VENDORID 0x06b9
+#define SPEEDTOUCH_PRODUCTID 0x4061
+
+/* Timeout in jiffies */
+#define CTRL_TIMEOUT (2*HZ)
+#define DATA_TIMEOUT (2*HZ)
+
+#define OFFSET_7 0 /* size 1 */
+#define OFFSET_b 1 /* size 8 */
+#define OFFSET_d 9 /* size 4 */
+#define OFFSET_e 13 /* size 1 */
+#define OFFSET_f 14 /* size 1 */
+#define TOTAL 15
+
+#define SIZE_7 1
+#define SIZE_b 8
+#define SIZE_d 4
+#define SIZE_e 1
+#define SIZE_f 1
+
+static int dl_512_first = 0;
+static int sw_buffering = 0;
+
+module_param(dl_512_first, bool, 0444);
+MODULE_PARM_DESC(dl_512_first, "Read 512 bytes before sending firmware");
+
+module_param(sw_buffering, uint, 0444);
+MODULE_PARM_DESC(sw_buffering, "Enable software buffering");
+
+#define UDSL_IOCTL_LINE_UP 1
+#define UDSL_IOCTL_LINE_DOWN 2
+
+#define SPEEDTCH_ENDPOINT_INT 0x81
+#define SPEEDTCH_ENDPOINT_DATA 0x07
+#define SPEEDTCH_ENDPOINT_FIRMWARE 0x05
+
+#define hex2int(c) ( (c >= '0') && (c <= '9') ? (c - '0') : ((c & 0xf) + 9) )
+
+static struct usb_device_id speedtch_usb_ids[] = {
+ {USB_DEVICE(SPEEDTOUCH_VENDORID, SPEEDTOUCH_PRODUCTID)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, speedtch_usb_ids);
+
+struct speedtch_instance_data {
+ struct udsl_instance_data u;
+
+ /* Status */
+ struct urb *int_urb;
+ unsigned char int_data[16];
+ struct work_struct poll_work;
+ struct timer_list poll_timer;
+};
+/* USB */
+
+static int speedtch_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id);
+static void speedtch_usb_disconnect(struct usb_interface *intf);
+static int speedtch_usb_ioctl(struct usb_interface *intf, unsigned int code,
+ void *user_data);
+static void speedtch_handle_int(struct urb *urb, struct pt_regs *regs);
+static void speedtch_poll_status(struct speedtch_instance_data *instance);
+
+static struct usb_driver speedtch_usb_driver = {
+ .owner = THIS_MODULE,
+ .name = speedtch_driver_name,
+ .probe = speedtch_usb_probe,
+ .disconnect = speedtch_usb_disconnect,
+ .ioctl = speedtch_usb_ioctl,
+ .id_table = speedtch_usb_ids,
+};
+
+/***************
+** firmware **
+***************/
+
+static void speedtch_got_firmware(struct speedtch_instance_data *instance,
+ int got_it)
+{
+ int err;
+ struct usb_interface *intf;
+
+ down(&instance->u.serialize); /* vs self, speedtch_firmware_start */
+ if (instance->u.status == UDSL_LOADED_FIRMWARE)
+ goto out;
+ if (!got_it) {
+ instance->u.status = UDSL_NO_FIRMWARE;
+ goto out;
+ }
+ if ((err = usb_set_interface(instance->u.usb_dev, 1, 1)) < 0) {
+ dbg("speedtch_got_firmware: usb_set_interface returned %d!", err);
+ instance->u.status = UDSL_NO_FIRMWARE;
+ goto out;
+ }
+
+ /* Set up interrupt endpoint */
+ intf = usb_ifnum_to_if(instance->u.usb_dev, 0);
+ if (intf && !usb_driver_claim_interface(&speedtch_usb_driver, intf, NULL)) {
+
+ instance->int_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (instance->int_urb) {
+
+ usb_fill_int_urb(instance->int_urb, instance->u.usb_dev,
+ usb_rcvintpipe(instance->u.usb_dev, SPEEDTCH_ENDPOINT_INT),
+ instance->int_data,
+ sizeof(instance->int_data),
+ speedtch_handle_int, instance, 50);
+ err = usb_submit_urb(instance->int_urb, GFP_KERNEL);
+ if (err) {
+ /* Doesn't matter; we'll poll anyway */
+ dbg("speedtch_got_firmware: Submission of interrupt URB failed %d", err);
+ usb_free_urb(instance->int_urb);
+ instance->int_urb = NULL;
+ usb_driver_release_interface(&speedtch_usb_driver, intf);
+ }
+ }
+ }
+ /* Start status polling */
+ mod_timer(&instance->poll_timer, jiffies + (1 * HZ));
+
+ instance->u.status = UDSL_LOADED_FIRMWARE;
+ tasklet_schedule(&instance->u.receive_tasklet);
+ out:
+ up(&instance->u.serialize);
+ wake_up_interruptible(&instance->u.firmware_waiters);
+}
+
+static int speedtch_set_swbuff(struct speedtch_instance_data *instance,
+ int state)
+{
+ struct usb_device *dev = instance->u.usb_dev;
+ int ret;
+
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x32, 0x40, state ? 0x01 : 0x00,
+ 0x00, NULL, 0, 100);
+ if (ret < 0) {
+ printk("Warning: %sabling SW buffering: usb_control_msg returned %d\n",
+ state ? "En" : "Dis", ret);
+ return ret;
+ }
+
+ dbg("speedtch_set_swbuff: %sbled SW buffering", state ? "En" : "Dis");
+ return 0;
+}
+
+static void speedtch_test_sequence(struct speedtch_instance_data *instance)
+{
+ struct usb_device *dev = instance->u.usb_dev;
+ unsigned char buf[10];
+ int ret;
+
+ /* URB 147 */
+ buf[0] = 0x1c;
+ buf[1] = 0x50;
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x01, 0x40, 0x0b, 0x00, buf, 2, 100);
+ if (ret < 0)
+ printk(KERN_WARNING "%s failed on URB147: %d\n", __func__, ret);
+
+ /* URB 148 */
+ buf[0] = 0x32;
+ buf[1] = 0x00;
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x01, 0x40, 0x02, 0x00, buf, 2, 100);
+ if (ret < 0)
+ printk(KERN_WARNING "%s failed on URB148: %d\n", __func__, ret);
+
+ /* URB 149 */
+ buf[0] = 0x01;
+ buf[1] = 0x00;
+ buf[2] = 0x01;
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x01, 0x40, 0x03, 0x00, buf, 3, 100);
+ if (ret < 0)
+ printk(KERN_WARNING "%s failed on URB149: %d\n", __func__, ret);
+
+ /* URB 150 */
+ buf[0] = 0x01;
+ buf[1] = 0x00;
+ buf[2] = 0x01;
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x01, 0x40, 0x04, 0x00, buf, 3, 100);
+ if (ret < 0)
+ printk(KERN_WARNING "%s failed on URB150: %d\n", __func__, ret);
+}
+
+static int speedtch_start_synchro(struct speedtch_instance_data *instance)
+{
+ struct usb_device *dev = instance->u.usb_dev;
+ unsigned char buf[2];
+ int ret;
+
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ 0x12, 0xc0, 0x04, 0x00,
+ buf, sizeof(buf), CTRL_TIMEOUT);
+ if (ret < 0) {
+ printk(KERN_WARNING "SpeedTouch: Failed to start ADSL synchronisation: %d\n", ret);
+ return ret;
+ }
+
+ dbg("speedtch_start_synchro: modem prodded. %d Bytes returned: %02x %02x", ret, buf[0], buf[1]);
+ return 0;
+}
+
+static void speedtch_handle_int(struct urb *urb, struct pt_regs *regs)
+{
+ struct speedtch_instance_data *instance = urb->context;
+ unsigned int count = urb->actual_length;
+ int ret;
+
+ /* The magic interrupt for "up state" */
+ const static unsigned char up_int[6] = { 0xa1, 0x00, 0x01, 0x00, 0x00, 0x00 };
+ /* The magic interrupt for "down state" */
+ const static unsigned char down_int[6] = { 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated; clean up */
+ dbg("%s - urb shutting down with status: %d", __func__, urb->status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d", __func__, urb->status);
+ goto exit;
+ }
+
+ if (count < 6) {
+ dbg("%s - int packet too short", __func__);
+ goto exit;
+ }
+
+ if (!memcmp(up_int, instance->int_data, 6)) {
+ del_timer(&instance->poll_timer);
+ printk(KERN_NOTICE "DSL line goes up\n");
+ } else if (!memcmp(down_int, instance->int_data, 6)) {
+ printk(KERN_NOTICE "DSL line goes down\n");
+ } else {
+ int i;
+
+ printk(KERN_DEBUG "Unknown interrupt packet of %d bytes:", count);
+ for (i = 0; i < count; i++)
+ printk(" %02x", instance->int_data[i]);
+ printk("\n");
+ }
+ schedule_work(&instance->poll_work);
+
+ exit:
+ rmb();
+ if (!instance->int_urb)
+ return;
+
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret)
+ err("%s - usb_submit_urb failed with result %d", __func__, ret);
+}
+
+static int speedtch_get_status(struct speedtch_instance_data *instance,
+ unsigned char *buf)
+{
+ struct usb_device *dev = instance->u.usb_dev;
+ int ret;
+
+ memset(buf, 0, TOTAL);
+
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ 0x12, 0xc0, 0x07, 0x00, buf + OFFSET_7, SIZE_7,
+ CTRL_TIMEOUT);
+ if (ret < 0) {
+ dbg("MSG 7 failed");
+ return ret;
+ }
+
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ 0x12, 0xc0, 0x0b, 0x00, buf + OFFSET_b, SIZE_b,
+ CTRL_TIMEOUT);
+ if (ret < 0) {
+ dbg("MSG B failed");
+ return ret;
+ }
+
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ 0x12, 0xc0, 0x0d, 0x00, buf + OFFSET_d, SIZE_d,
+ CTRL_TIMEOUT);
+ if (ret < 0) {
+ dbg("MSG D failed");
+ return ret;
+ }
+
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ 0x01, 0xc0, 0x0e, 0x00, buf + OFFSET_e, SIZE_e,
+ CTRL_TIMEOUT);
+ if (ret < 0) {
+ dbg("MSG E failed");
+ return ret;
+ }
+
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ 0x01, 0xc0, 0x0f, 0x00, buf + OFFSET_f, SIZE_f,
+ CTRL_TIMEOUT);
+ if (ret < 0) {
+ dbg("MSG F failed");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void speedtch_poll_status(struct speedtch_instance_data *instance)
+{
+ unsigned char buf[TOTAL];
+ int ret;
+
+ ret = speedtch_get_status(instance, buf);
+ if (ret) {
+ printk(KERN_WARNING
+ "SpeedTouch: Error %d fetching device status\n", ret);
+ return;
+ }
+
+ dbg("Line state %02x", buf[OFFSET_7]);
+
+ switch (buf[OFFSET_7]) {
+ case 0:
+ if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST) {
+ instance->u.atm_dev->signal = ATM_PHY_SIG_LOST;
+ printk(KERN_NOTICE "ADSL line is down\n");
+ }
+ break;
+
+ case 0x08:
+ if (instance->u.atm_dev->signal != ATM_PHY_SIG_UNKNOWN) {
+ instance->u.atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
+ printk(KERN_NOTICE "ADSL line is blocked?\n");
+ }
+ break;
+
+ case 0x10:
+ if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST) {
+ instance->u.atm_dev->signal = ATM_PHY_SIG_LOST;
+ printk(KERN_NOTICE "ADSL line is synchronising\n");
+ }
+ break;
+
+ case 0x20:
+ if (instance->u.atm_dev->signal != ATM_PHY_SIG_FOUND) {
+ int down_speed = buf[OFFSET_b] | (buf[OFFSET_b + 1] << 8)
+ | (buf[OFFSET_b + 2] << 16) | (buf[OFFSET_b + 3] << 24);
+ int up_speed = buf[OFFSET_b + 4] | (buf[OFFSET_b + 5] << 8)
+ | (buf[OFFSET_b + 6] << 16) | (buf[OFFSET_b + 7] << 24);
+
+ if (!(down_speed & 0x0000ffff) &&
+ !(up_speed & 0x0000ffff)) {
+ down_speed >>= 16;
+ up_speed >>= 16;
+ }
+ instance->u.atm_dev->link_rate = down_speed * 1000 / 424;
+ instance->u.atm_dev->signal = ATM_PHY_SIG_FOUND;
+
+ printk(KERN_NOTICE
+ "ADSL line is up (%d Kib/s down | %d Kib/s up)\n",
+ down_speed, up_speed);
+ }
+ break;
+
+ default:
+ if (instance->u.atm_dev->signal != ATM_PHY_SIG_UNKNOWN) {
+ instance->u.atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
+ printk(KERN_NOTICE "Unknown line state %02x\n", buf[OFFSET_7]);
+ }
+ break;
+ }
+}
+
+static void speedtch_timer_poll(unsigned long data)
+{
+ struct speedtch_instance_data *instance = (void *)data;
+
+ schedule_work(&instance->poll_work);
+ mod_timer(&instance->poll_timer, jiffies + (5 * HZ));
+}
+
+#ifdef USE_FW_LOADER
+static void speedtch_upload_firmware(struct speedtch_instance_data *instance,
+ const struct firmware *fw1,
+ const struct firmware *fw2)
+{
+ unsigned char *buffer;
+ struct usb_device *usb_dev = instance->u.usb_dev;
+ struct usb_interface *intf;
+ int actual_length, ret;
+ int offset;
+
+ dbg("speedtch_upload_firmware");
+
+ if (!(intf = usb_ifnum_to_if(usb_dev, 2))) {
+ dbg("speedtch_upload_firmware: interface not found!");
+ goto fail;
+ }
+
+ if (!(buffer = (unsigned char *)__get_free_page(GFP_KERNEL))) {
+ dbg("speedtch_upload_firmware: no memory for buffer!");
+ goto fail;
+ }
+
+ /* A user-space firmware loader may already have claimed interface #2 */
+ if ((ret =
+ usb_driver_claim_interface(&speedtch_usb_driver, intf, NULL)) < 0) {
+ dbg("speedtch_upload_firmware: interface in use (%d)!", ret);
+ goto fail_free;
+ }
+
+ /* URB 7 */
+ if (dl_512_first) { /* some modems need a read before writing the firmware */
+ ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
+ buffer, 0x200, &actual_length, 2 * HZ);
+
+ if (ret < 0 && ret != -ETIMEDOUT)
+ dbg("speedtch_upload_firmware: read BLOCK0 from modem failed (%d)!", ret);
+ else
+ dbg("speedtch_upload_firmware: BLOCK0 downloaded (%d bytes)", ret);
+ }
+
+ /* URB 8 : both leds are static green */
+ for (offset = 0; offset < fw1->size; offset += PAGE_SIZE) {
+ int thislen = min_t(int, PAGE_SIZE, fw1->size - offset);
+ memcpy(buffer, fw1->data + offset, thislen);
+
+ ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
+ buffer, thislen, &actual_length, DATA_TIMEOUT);
+
+ if (ret < 0) {
+ dbg("speedtch_upload_firmware: write BLOCK1 to modem failed (%d)!", ret);
+ goto fail_release;
+ }
+ dbg("speedtch_upload_firmware: BLOCK1 uploaded (%d bytes)", fw1->size);
+ }
+
+ /* USB led blinking green, ADSL led off */
+
+ /* URB 11 */
+ ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
+ buffer, 0x200, &actual_length, DATA_TIMEOUT);
+
+ if (ret < 0) {
+ dbg("speedtch_upload_firmware: read BLOCK2 from modem failed (%d)!", ret);
+ goto fail_release;
+ }
+ dbg("speedtch_upload_firmware: BLOCK2 downloaded (%d bytes)", actual_length);
+
+ /* URBs 12 to 139 - USB led blinking green, ADSL led off */
+ for (offset = 0; offset < fw2->size; offset += PAGE_SIZE) {
+ int thislen = min_t(int, PAGE_SIZE, fw2->size - offset);
+ memcpy(buffer, fw2->data + offset, thislen);
+
+ ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
+ buffer, thislen, &actual_length, DATA_TIMEOUT);
+
+ if (ret < 0) {
+ dbg("speedtch_upload_firmware: write BLOCK3 to modem failed (%d)!", ret);
+ goto fail_release;
+ }
+ }
+ dbg("speedtch_upload_firmware: BLOCK3 uploaded (%d bytes)", fw2->size);
+
+ /* USB led static green, ADSL led static red */
+
+ /* URB 142 */
+ ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
+ buffer, 0x200, &actual_length, DATA_TIMEOUT);
+
+ if (ret < 0) {
+ dbg("speedtch_upload_firmware: read BLOCK4 from modem failed (%d)!", ret);
+ goto fail_release;
+ }
+
+ /* success */
+ dbg("speedtch_upload_firmware: BLOCK4 downloaded (%d bytes)", actual_length);
+
+ /* Delay to allow firmware to start up. We can do this here
+ because we're in our own kernel thread anyway. */
+ msleep(1000);
+
+ /* Enable software buffering, if requested */
+ if (sw_buffering)
+ speedtch_set_swbuff(instance, 1);
+
+ /* Magic spell; don't ask us what this does */
+ speedtch_test_sequence(instance);
+
+ /* Start modem synchronisation */
+ if (speedtch_start_synchro(instance))
+ dbg("speedtch_start_synchro: failed");
+
+ speedtch_got_firmware(instance, 1);
+
+ free_page((unsigned long)buffer);
+ return;
+
+ fail_release:
+ /* Only release interface #2 if uploading failed; we don't release it
+ we succeeded. This prevents the userspace tools from trying to load
+ the firmware themselves */
+ usb_driver_release_interface(&speedtch_usb_driver, intf);
+ fail_free:
+ free_page((unsigned long)buffer);
+ fail:
+ speedtch_got_firmware(instance, 0);
+}
+
+static int speedtch_find_firmware(struct speedtch_instance_data
+ *instance, int phase,
+ const struct firmware **fw_p)
+{
+ char buf[24];
+ const u16 bcdDevice = instance->u.usb_dev->descriptor.bcdDevice;
+ const u8 major_revision = bcdDevice >> 8;
+ const u8 minor_revision = bcdDevice & 0xff;
+
+ sprintf(buf, "speedtch-%d.bin.%x.%02x", phase, major_revision, minor_revision);
+ dbg("speedtch_find_firmware: looking for %s", buf);
+
+ if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) {
+ sprintf(buf, "speedtch-%d.bin.%x", phase, major_revision);
+ dbg("speedtch_find_firmware: looking for %s", buf);
+
+ if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) {
+ sprintf(buf, "speedtch-%d.bin", phase);
+ dbg("speedtch_find_firmware: looking for %s", buf);
+
+ if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) {
+ dev_warn(&instance->u.usb_dev->dev, "no stage %d firmware found!", phase);
+ return -ENOENT;
+ }
+ }
+ }
+
+ dev_info(&instance->u.usb_dev->dev, "found stage %d firmware %s\n", phase, buf);
+
+ return 0;
+}
+
+static int speedtch_load_firmware(void *arg)
+{
+ const struct firmware *fw1, *fw2;
+ struct speedtch_instance_data *instance = arg;
+
+ BUG_ON(!instance);
+
+ daemonize("firmware/speedtch");
+
+ if (!speedtch_find_firmware(instance, 1, &fw1)) {
+ if (!speedtch_find_firmware(instance, 2, &fw2)) {
+ speedtch_upload_firmware(instance, fw1, fw2);
+ release_firmware(fw2);
+ }
+ release_firmware(fw1);
+ }
+
+ /* In case we failed, set state back to NO_FIRMWARE so that
+ another later attempt may work. Otherwise, we never actually
+ manage to recover if, for example, the firmware is on /usr and
+ we look for it too early. */
+ speedtch_got_firmware(instance, 0);
+
+ module_put(THIS_MODULE);
+ udsl_put_instance(&instance->u);
+ return 0;
+}
+#endif /* USE_FW_LOADER */
+
+static void speedtch_firmware_start(struct speedtch_instance_data *instance)
+{
+#ifdef USE_FW_LOADER
+ int ret;
+#endif
+
+ dbg("speedtch_firmware_start");
+
+ down(&instance->u.serialize); /* vs self, speedtch_got_firmware */
+
+ if (instance->u.status >= UDSL_LOADING_FIRMWARE) {
+ up(&instance->u.serialize);
+ return;
+ }
+
+ instance->u.status = UDSL_LOADING_FIRMWARE;
+ up(&instance->u.serialize);
+
+#ifdef USE_FW_LOADER
+ udsl_get_instance(&instance->u);
+ try_module_get(THIS_MODULE);
+
+ ret = kernel_thread(speedtch_load_firmware, instance,
+ CLONE_FS | CLONE_FILES);
+
+ if (ret >= 0)
+ return; /* OK */
+
+ dbg("speedtch_firmware_start: kernel_thread failed (%d)!", ret);
+
+ module_put(THIS_MODULE);
+ udsl_put_instance(&instance->u);
+ /* Just pretend it never happened... hope modem_run happens */
+#endif /* USE_FW_LOADER */
+
+ speedtch_got_firmware(instance, 0);
+}
+
+static int speedtch_firmware_wait(struct udsl_instance_data *instance)
+{
+ speedtch_firmware_start((void *)instance);
+
+ if (wait_event_interruptible(instance->firmware_waiters, instance->status != UDSL_LOADING_FIRMWARE) < 0)
+ return -ERESTARTSYS;
+
+ return (instance->status == UDSL_LOADED_FIRMWARE) ? 0 : -EAGAIN;
+}
+
+/**********
+** USB **
+**********/
+
+static int speedtch_usb_ioctl(struct usb_interface *intf, unsigned int code,
+ void *user_data)
+{
+ struct speedtch_instance_data *instance = usb_get_intfdata(intf);
+
+ dbg("speedtch_usb_ioctl entered");
+
+ if (!instance) {
+ dbg("speedtch_usb_ioctl: NULL instance!");
+ return -ENODEV;
+ }
+
+ switch (code) {
+ case UDSL_IOCTL_LINE_UP:
+ instance->u.atm_dev->signal = ATM_PHY_SIG_FOUND;
+ speedtch_got_firmware(instance, 1);
+ return (instance->u.status == UDSL_LOADED_FIRMWARE) ? 0 : -EIO;
+ case UDSL_IOCTL_LINE_DOWN:
+ instance->u.atm_dev->signal = ATM_PHY_SIG_LOST;
+ return 0;
+ default:
+ return -ENOTTY;
+ }
+}
+
+static int speedtch_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ int ifnum = intf->altsetting->desc.bInterfaceNumber;
+ struct speedtch_instance_data *instance;
+ unsigned char mac_str[13];
+ int ret, i;
+ char buf7[SIZE_7];
+
+ dbg("speedtch_usb_probe: trying device with vendor=0x%x, product=0x%x, ifnum %d", dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum);
+
+ if ((dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) ||
+ (dev->descriptor.idVendor != SPEEDTOUCH_VENDORID) ||
+ (dev->descriptor.idProduct != SPEEDTOUCH_PRODUCTID) || (ifnum != 1))
+ return -ENODEV;
+
+ dbg("speedtch_usb_probe: device accepted");
+
+ /* instance init */
+ instance = kmalloc(sizeof(*instance), GFP_KERNEL);
+ if (!instance) {
+ dbg("speedtch_usb_probe: no memory for instance data!");
+ return -ENOMEM;
+ }
+
+ memset(instance, 0, sizeof(struct speedtch_instance_data));
+
+ if ((ret = usb_set_interface(dev, 0, 0)) < 0)
+ goto fail;
+
+ if ((ret = usb_set_interface(dev, 2, 0)) < 0)
+ goto fail;
+
+ instance->u.data_endpoint = SPEEDTCH_ENDPOINT_DATA;
+ instance->u.firmware_wait = speedtch_firmware_wait;
+ instance->u.driver_name = speedtch_driver_name;
+
+ ret = udsl_instance_setup(dev, &instance->u);
+ if (ret)
+ goto fail;
+
+ init_timer(&instance->poll_timer);
+ instance->poll_timer.function = speedtch_timer_poll;
+ instance->poll_timer.data = (unsigned long)instance;
+
+ INIT_WORK(&instance->poll_work, (void *)speedtch_poll_status, instance);
+
+ /* set MAC address, it is stored in the serial number */
+ memset(instance->u.atm_dev->esi, 0, sizeof(instance->u.atm_dev->esi));
+ if (usb_string(dev, dev->descriptor.iSerialNumber, mac_str, sizeof(mac_str)) == 12) {
+ for (i = 0; i < 6; i++)
+ instance->u.atm_dev->esi[i] =
+ (hex2int(mac_str[i * 2]) * 16) + (hex2int(mac_str[i * 2 + 1]));
+ }
+
+ /* First check whether the modem already seems to be alive */
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ 0x12, 0xc0, 0x07, 0x00, buf7, SIZE_7, HZ / 2);
+
+ if (ret == SIZE_7) {
+ dbg("firmware appears to be already loaded");
+ speedtch_got_firmware(instance, 1);
+ speedtch_poll_status(instance);
+ } else {
+ speedtch_firmware_start(instance);
+ }
+
+ usb_set_intfdata(intf, instance);
+
+ return 0;
+
+ fail:
+ kfree(instance);
+
+ return -ENOMEM;
+}
+
+static void speedtch_usb_disconnect(struct usb_interface *intf)
+{
+ struct speedtch_instance_data *instance = usb_get_intfdata(intf);
+
+ dbg("speedtch_usb_disconnect entered");
+
+ if (!instance) {
+ dbg("speedtch_usb_disconnect: NULL instance!");
+ return;
+ }
+
+/*QQ need to handle disconnects on interface #2 while uploading firmware */
+/*QQ and what about interface #1? */
+
+ if (instance->int_urb) {
+ struct urb *int_urb = instance->int_urb;
+ instance->int_urb = NULL;
+ wmb();
+ usb_unlink_urb(int_urb);
+ usb_free_urb(int_urb);
+ }
+
+ instance->int_data[0] = 1;
+ del_timer_sync(&instance->poll_timer);
+ wmb();
+ flush_scheduled_work();
+
+ udsl_instance_disconnect(&instance->u);
+
+ /* clean up */
+ usb_set_intfdata(intf, NULL);
+ udsl_put_instance(&instance->u);
+}
+
+/***********
+** init **
+***********/
+
+static int __init speedtch_usb_init(void)
+{
+ dbg("speedtch_usb_init: driver version " DRIVER_VERSION);
+
+ return usb_register(&speedtch_usb_driver);
+}
+
+static void __exit speedtch_usb_cleanup(void)
+{
+ dbg("speedtch_usb_cleanup entered");
+
+ usb_deregister(&speedtch_usb_driver);
+}
+
+module_init(speedtch_usb_init);
+module_exit(speedtch_usb_cleanup);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
diff --git a/drivers/usb/atm/usb_atm.c b/drivers/usb/atm/usb_atm.c
new file mode 100644
index 000000000000..8d1d365846a1
--- /dev/null
+++ b/drivers/usb/atm/usb_atm.c
@@ -0,0 +1,1205 @@
+/******************************************************************************
+ * usb_atm.c - Generic USB xDSL driver core
+ *
+ * Copyright (C) 2001, Alcatel
+ * Copyright (C) 2003, Duncan Sands, SolNegro, Josep Comas
+ * Copyright (C) 2004, David Woodhouse
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ ******************************************************************************/
+
+/*
+ * Written by Johan Verrept, maintained by Duncan Sands (duncan.sands@free.fr)
+ *
+ * 1.7+: - See the check-in logs
+ *
+ * 1.6: - No longer opens a connection if the firmware is not loaded
+ * - Added support for the speedtouch 330
+ * - Removed the limit on the number of devices
+ * - Module now autoloads on device plugin
+ * - Merged relevant parts of sarlib
+ * - Replaced the kernel thread with a tasklet
+ * - New packet transmission code
+ * - Changed proc file contents
+ * - Fixed all known SMP races
+ * - Many fixes and cleanups
+ * - Various fixes by Oliver Neukum (oliver@neukum.name)
+ *
+ * 1.5A: - Version for inclusion in 2.5 series kernel
+ * - Modifications by Richard Purdie (rpurdie@rpsys.net)
+ * - made compatible with kernel 2.5.6 onwards by changing
+ * udsl_usb_send_data_context->urb to a pointer and adding code
+ * to alloc and free it
+ * - remove_wait_queue() added to udsl_atm_processqueue_thread()
+ *
+ * 1.5: - fixed memory leak when atmsar_decode_aal5 returned NULL.
+ * (reported by stephen.robinson@zen.co.uk)
+ *
+ * 1.4: - changed the spin_lock() under interrupt to spin_lock_irqsave()
+ * - unlink all active send urbs of a vcc that is being closed.
+ *
+ * 1.3.1: - added the version number
+ *
+ * 1.3: - Added multiple send urb support
+ * - fixed memory leak and vcc->tx_inuse starvation bug
+ * when not enough memory left in vcc.
+ *
+ * 1.2: - Fixed race condition in udsl_usb_send_data()
+ * 1.1: - Turned off packet debugging
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <asm/uaccess.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include <linux/crc32.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+
+#include "usb_atm.h"
+
+/*
+#define DEBUG
+#define VERBOSE_DEBUG
+*/
+
+#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG)
+# define DEBUG
+#endif
+
+#include <linux/usb.h>
+
+#ifdef DEBUG
+#define UDSL_ASSERT(x) BUG_ON(!(x))
+#else
+#define UDSL_ASSERT(x) do { if (!(x)) warn("failed assertion '" #x "' at line %d", __LINE__); } while(0)
+#endif
+
+#ifdef VERBOSE_DEBUG
+static int udsl_print_packet(const unsigned char *data, int len);
+#define PACKETDEBUG(arg...) udsl_print_packet (arg)
+#define vdbg(arg...) dbg (arg)
+#else
+#define PACKETDEBUG(arg...)
+#define vdbg(arg...)
+#endif
+
+#define DRIVER_AUTHOR "Johan Verrept, Duncan Sands <duncan.sands@free.fr>"
+#define DRIVER_VERSION "1.8"
+#define DRIVER_DESC "Alcatel SpeedTouch USB driver version " DRIVER_VERSION
+
+static unsigned int num_rcv_urbs = UDSL_DEFAULT_RCV_URBS;
+static unsigned int num_snd_urbs = UDSL_DEFAULT_SND_URBS;
+static unsigned int num_rcv_bufs = UDSL_DEFAULT_RCV_BUFS;
+static unsigned int num_snd_bufs = UDSL_DEFAULT_SND_BUFS;
+static unsigned int rcv_buf_size = UDSL_DEFAULT_RCV_BUF_SIZE;
+static unsigned int snd_buf_size = UDSL_DEFAULT_SND_BUF_SIZE;
+
+module_param(num_rcv_urbs, uint, 0444);
+MODULE_PARM_DESC(num_rcv_urbs,
+ "Number of urbs used for reception (range: 0-"
+ __MODULE_STRING(UDSL_MAX_RCV_URBS) ", default: "
+ __MODULE_STRING(UDSL_DEFAULT_RCV_URBS) ")");
+
+module_param(num_snd_urbs, uint, 0444);
+MODULE_PARM_DESC(num_snd_urbs,
+ "Number of urbs used for transmission (range: 0-"
+ __MODULE_STRING(UDSL_MAX_SND_URBS) ", default: "
+ __MODULE_STRING(UDSL_DEFAULT_SND_URBS) ")");
+
+module_param(num_rcv_bufs, uint, 0444);
+MODULE_PARM_DESC(num_rcv_bufs,
+ "Number of buffers used for reception (range: 0-"
+ __MODULE_STRING(UDSL_MAX_RCV_BUFS) ", default: "
+ __MODULE_STRING(UDSL_DEFAULT_RCV_BUFS) ")");
+
+module_param(num_snd_bufs, uint, 0444);
+MODULE_PARM_DESC(num_snd_bufs,
+ "Number of buffers used for transmission (range: 0-"
+ __MODULE_STRING(UDSL_MAX_SND_BUFS) ", default: "
+ __MODULE_STRING(UDSL_DEFAULT_SND_BUFS) ")");
+
+module_param(rcv_buf_size, uint, 0444);
+MODULE_PARM_DESC(rcv_buf_size,
+ "Size of the buffers used for reception (range: 0-"
+ __MODULE_STRING(UDSL_MAX_RCV_BUF_SIZE) ", default: "
+ __MODULE_STRING(UDSL_DEFAULT_RCV_BUF_SIZE) ")");
+
+module_param(snd_buf_size, uint, 0444);
+MODULE_PARM_DESC(snd_buf_size,
+ "Size of the buffers used for transmission (range: 0-"
+ __MODULE_STRING(UDSL_MAX_SND_BUF_SIZE) ", default: "
+ __MODULE_STRING(UDSL_DEFAULT_SND_BUF_SIZE) ")");
+
+/* ATM */
+
+static void udsl_atm_dev_close(struct atm_dev *dev);
+static int udsl_atm_open(struct atm_vcc *vcc);
+static void udsl_atm_close(struct atm_vcc *vcc);
+static int udsl_atm_ioctl(struct atm_dev *dev, unsigned int cmd, void __user * arg);
+static int udsl_atm_send(struct atm_vcc *vcc, struct sk_buff *skb);
+static int udsl_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *page);
+
+static struct atmdev_ops udsl_atm_devops = {
+ .dev_close = udsl_atm_dev_close,
+ .open = udsl_atm_open,
+ .close = udsl_atm_close,
+ .ioctl = udsl_atm_ioctl,
+ .send = udsl_atm_send,
+ .proc_read = udsl_atm_proc_read,
+ .owner = THIS_MODULE,
+};
+
+/***********
+** misc **
+***********/
+
+static inline void udsl_pop(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+ if (vcc->pop)
+ vcc->pop(vcc, skb);
+ else
+ dev_kfree_skb(skb);
+}
+
+/*************
+** decode **
+*************/
+
+static inline struct udsl_vcc_data *udsl_find_vcc(struct udsl_instance_data *instance,
+ short vpi, int vci)
+{
+ struct udsl_vcc_data *vcc;
+
+ list_for_each_entry(vcc, &instance->vcc_list, list)
+ if ((vcc->vci == vci) && (vcc->vpi == vpi))
+ return vcc;
+ return NULL;
+}
+
+static void udsl_extract_cells(struct udsl_instance_data *instance,
+ unsigned char *source, unsigned int howmany)
+{
+ struct udsl_vcc_data *cached_vcc = NULL;
+ struct atm_vcc *vcc;
+ struct sk_buff *sarb;
+ struct udsl_vcc_data *vcc_data;
+ int cached_vci = 0;
+ unsigned int i;
+ int pti;
+ int vci;
+ short cached_vpi = 0;
+ short vpi;
+
+ for (i = 0; i < howmany;
+ i++, source += ATM_CELL_SIZE + instance->rcv_padding) {
+ vpi = ((source[0] & 0x0f) << 4) | (source[1] >> 4);
+ vci = ((source[1] & 0x0f) << 12) | (source[2] << 4) | (source[3] >> 4);
+ pti = (source[3] & 0x2) != 0;
+
+ vdbg("udsl_extract_cells: vpi %hd, vci %d, pti %d", vpi, vci, pti);
+
+ if (cached_vcc && (vci == cached_vci) && (vpi == cached_vpi))
+ vcc_data = cached_vcc;
+ else if ((vcc_data = udsl_find_vcc(instance, vpi, vci))) {
+ cached_vcc = vcc_data;
+ cached_vpi = vpi;
+ cached_vci = vci;
+ } else {
+ dbg("udsl_extract_cells: unknown vpi/vci (%hd/%d)!", vpi, vci);
+ continue;
+ }
+
+ vcc = vcc_data->vcc;
+ sarb = vcc_data->sarb;
+
+ if (sarb->tail + ATM_CELL_PAYLOAD > sarb->end) {
+ dbg("udsl_extract_cells: buffer overrun (sarb->len %u, vcc: 0x%p)!", sarb->len, vcc);
+ /* discard cells already received */
+ skb_trim(sarb, 0);
+ }
+
+ memcpy(sarb->tail, source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD);
+ __skb_put(sarb, ATM_CELL_PAYLOAD);
+
+ if (pti) {
+ struct sk_buff *skb;
+ unsigned int length;
+ unsigned int pdu_length;
+
+ length = (source[ATM_CELL_SIZE - 6] << 8) + source[ATM_CELL_SIZE - 5];
+
+ /* guard against overflow */
+ if (length > ATM_MAX_AAL5_PDU) {
+ dbg("udsl_extract_cells: bogus length %u (vcc: 0x%p)!", length, vcc);
+ atomic_inc(&vcc->stats->rx_err);
+ goto out;
+ }
+
+ pdu_length = UDSL_NUM_CELLS(length) * ATM_CELL_PAYLOAD;
+
+ if (sarb->len < pdu_length) {
+ dbg("udsl_extract_cells: bogus pdu_length %u (sarb->len: %u, vcc: 0x%p)!", pdu_length, sarb->len, vcc);
+ atomic_inc(&vcc->stats->rx_err);
+ goto out;
+ }
+
+ if (crc32_be(~0, sarb->tail - pdu_length, pdu_length) != 0xc704dd7b) {
+ dbg("udsl_extract_cells: packet failed crc check (vcc: 0x%p)!", vcc);
+ atomic_inc(&vcc->stats->rx_err);
+ goto out;
+ }
+
+ vdbg("udsl_extract_cells: got packet (length: %u, pdu_length: %u, vcc: 0x%p)", length, pdu_length, vcc);
+
+ if (!(skb = dev_alloc_skb(length))) {
+ dbg("udsl_extract_cells: no memory for skb (length: %u)!", length);
+ atomic_inc(&vcc->stats->rx_drop);
+ goto out;
+ }
+
+ vdbg("udsl_extract_cells: allocated new sk_buff (skb: 0x%p, skb->truesize: %u)", skb, skb->truesize);
+
+ if (!atm_charge(vcc, skb->truesize)) {
+ dbg("udsl_extract_cells: failed atm_charge (skb->truesize: %u)!", skb->truesize);
+ dev_kfree_skb(skb);
+ goto out; /* atm_charge increments rx_drop */
+ }
+
+ memcpy(skb->data, sarb->tail - pdu_length, length);
+ __skb_put(skb, length);
+
+ vdbg("udsl_extract_cells: sending skb 0x%p, skb->len %u, skb->truesize %u", skb, skb->len, skb->truesize);
+
+ PACKETDEBUG(skb->data, skb->len);
+
+ vcc->push(vcc, skb);
+
+ atomic_inc(&vcc->stats->rx);
+ out:
+ skb_trim(sarb, 0);
+ }
+ }
+}
+
+/*************
+** encode **
+*************/
+
+static inline void udsl_fill_cell_header(unsigned char *target, struct atm_vcc *vcc)
+{
+ target[0] = vcc->vpi >> 4;
+ target[1] = (vcc->vpi << 4) | (vcc->vci >> 12);
+ target[2] = vcc->vci >> 4;
+ target[3] = vcc->vci << 4;
+ target[4] = 0xec;
+}
+
+static const unsigned char zeros[ATM_CELL_PAYLOAD];
+
+static void udsl_groom_skb(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+ struct udsl_control *ctrl = UDSL_SKB(skb);
+ unsigned int zero_padding;
+ u32 crc;
+
+ ctrl->atm_data.vcc = vcc;
+
+ ctrl->num_cells = UDSL_NUM_CELLS(skb->len);
+ ctrl->num_entire = skb->len / ATM_CELL_PAYLOAD;
+
+ zero_padding = ctrl->num_cells * ATM_CELL_PAYLOAD - skb->len - ATM_AAL5_TRAILER;
+
+ if (ctrl->num_entire + 1 < ctrl->num_cells)
+ ctrl->pdu_padding = zero_padding - (ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER);
+ else
+ ctrl->pdu_padding = zero_padding;
+
+ ctrl->aal5_trailer[0] = 0; /* UU = 0 */
+ ctrl->aal5_trailer[1] = 0; /* CPI = 0 */
+ ctrl->aal5_trailer[2] = skb->len >> 8;
+ ctrl->aal5_trailer[3] = skb->len;
+
+ crc = crc32_be(~0, skb->data, skb->len);
+ crc = crc32_be(crc, zeros, zero_padding);
+ crc = crc32_be(crc, ctrl->aal5_trailer, 4);
+ crc = ~crc;
+
+ ctrl->aal5_trailer[4] = crc >> 24;
+ ctrl->aal5_trailer[5] = crc >> 16;
+ ctrl->aal5_trailer[6] = crc >> 8;
+ ctrl->aal5_trailer[7] = crc;
+}
+
+static unsigned int udsl_write_cells(struct udsl_instance_data *instance,
+ 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;
+
+ vdbg("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);
+
+ nc = ctrl->num_cells;
+ ne = min(howmany, ctrl->num_entire);
+
+ for (i = 0; i < ne; i++) {
+ udsl_fill_cell_header(target, ctrl->atm_data.vcc);
+ target += ATM_CELL_HEADER;
+ memcpy(target, skb->data, ATM_CELL_PAYLOAD);
+ target += ATM_CELL_PAYLOAD;
+ if (instance->snd_padding) {
+ memset(target, 0, instance->snd_padding);
+ target += instance->snd_padding;
+ }
+ __skb_pull(skb, ATM_CELL_PAYLOAD);
+ }
+
+ ctrl->num_entire -= ne;
+
+ if (!(ctrl->num_cells -= ne) || !(howmany -= ne))
+ goto out;
+
+ if (instance->snd_padding) {
+ memset(target, 0, instance->snd_padding);
+ target += instance->snd_padding;
+ }
+ udsl_fill_cell_header(target, ctrl->atm_data.vcc);
+ 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) {
+ if (!--howmany) {
+ ctrl->pdu_padding = ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER;
+ goto out;
+ }
+
+ udsl_fill_cell_header(target, ctrl->atm_data.vcc);
+ target += ATM_CELL_HEADER;
+ memset(target, 0, ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER);
+ target += ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER;
+
+ --ctrl->num_cells;
+ UDSL_ASSERT(!ctrl->num_cells);
+ }
+
+ 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 (instance->snd_padding) {
+ memset(target, 0, instance->snd_padding);
+ target += instance->snd_padding;
+ }
+ out:
+ *target_p = target;
+ return nc - ctrl->num_cells;
+}
+
+/**************
+** receive **
+**************/
+
+static void udsl_complete_receive(struct urb *urb, struct pt_regs *regs)
+{
+ struct udsl_receive_buffer *buf;
+ struct udsl_instance_data *instance;
+ struct udsl_receiver *rcv;
+ unsigned long flags;
+
+ if (!urb || !(rcv = urb->context)) {
+ dbg("udsl_complete_receive: bad urb!");
+ return;
+ }
+
+ instance = rcv->instance;
+ buf = rcv->buffer;
+
+ buf->filled_cells = urb->actual_length / (ATM_CELL_SIZE + instance->rcv_padding);
+
+ vdbg("udsl_complete_receive: urb 0x%p, status %d, actual_length %d, filled_cells %u, rcv 0x%p, buf 0x%p", urb, urb->status, urb->actual_length, buf->filled_cells, rcv, buf);
+
+ UDSL_ASSERT(buf->filled_cells <= rcv_buf_size);
+
+ /* may not be in_interrupt() */
+ spin_lock_irqsave(&instance->receive_lock, flags);
+ list_add(&rcv->list, &instance->spare_receivers);
+ list_add_tail(&buf->list, &instance->filled_receive_buffers);
+ if (likely(!urb->status))
+ tasklet_schedule(&instance->receive_tasklet);
+ spin_unlock_irqrestore(&instance->receive_lock, flags);
+}
+
+static void udsl_process_receive(unsigned long data)
+{
+ struct udsl_receive_buffer *buf;
+ struct udsl_instance_data *instance = (struct udsl_instance_data *)data;
+ struct udsl_receiver *rcv;
+ int err;
+
+ made_progress:
+ while (!list_empty(&instance->spare_receive_buffers)) {
+ spin_lock_irq(&instance->receive_lock);
+ if (list_empty(&instance->spare_receivers)) {
+ spin_unlock_irq(&instance->receive_lock);
+ break;
+ }
+ rcv = list_entry(instance->spare_receivers.next,
+ struct udsl_receiver, list);
+ list_del(&rcv->list);
+ spin_unlock_irq(&instance->receive_lock);
+
+ buf = list_entry(instance->spare_receive_buffers.next,
+ struct udsl_receive_buffer, list);
+ list_del(&buf->list);
+
+ rcv->buffer = buf;
+
+ usb_fill_bulk_urb(rcv->urb, instance->usb_dev,
+ usb_rcvbulkpipe(instance->usb_dev, instance->data_endpoint),
+ buf->base,
+ rcv_buf_size * (ATM_CELL_SIZE + instance->rcv_padding),
+ udsl_complete_receive, rcv);
+
+ vdbg("udsl_process_receive: sending urb 0x%p, rcv 0x%p, buf 0x%p",
+ rcv->urb, rcv, buf);
+
+ if ((err = usb_submit_urb(rcv->urb, GFP_ATOMIC)) < 0) {
+ dbg("udsl_process_receive: urb submission failed (%d)!", err);
+ list_add(&buf->list, &instance->spare_receive_buffers);
+ spin_lock_irq(&instance->receive_lock);
+ list_add(&rcv->list, &instance->spare_receivers);
+ spin_unlock_irq(&instance->receive_lock);
+ break;
+ }
+ }
+
+ spin_lock_irq(&instance->receive_lock);
+ if (list_empty(&instance->filled_receive_buffers)) {
+ spin_unlock_irq(&instance->receive_lock);
+ return; /* done - no more buffers */
+ }
+ buf = list_entry(instance->filled_receive_buffers.next,
+ struct udsl_receive_buffer, list);
+ list_del(&buf->list);
+ spin_unlock_irq(&instance->receive_lock);
+
+ vdbg("udsl_process_receive: processing buf 0x%p", buf);
+ udsl_extract_cells(instance, buf->base, buf->filled_cells);
+ list_add(&buf->list, &instance->spare_receive_buffers);
+ goto made_progress;
+}
+
+/***********
+** send **
+***********/
+
+static void udsl_complete_send(struct urb *urb, struct pt_regs *regs)
+{
+ struct udsl_instance_data *instance;
+ struct udsl_sender *snd;
+ unsigned long flags;
+
+ if (!urb || !(snd = urb->context) || !(instance = snd->instance)) {
+ dbg("udsl_complete_send: bad urb!");
+ return;
+ }
+
+ vdbg("udsl_complete_send: urb 0x%p, status %d, snd 0x%p, buf 0x%p", urb,
+ urb->status, snd, snd->buffer);
+
+ /* may not be in_interrupt() */
+ spin_lock_irqsave(&instance->send_lock, flags);
+ list_add(&snd->list, &instance->spare_senders);
+ list_add(&snd->buffer->list, &instance->spare_send_buffers);
+ tasklet_schedule(&instance->send_tasklet);
+ spin_unlock_irqrestore(&instance->send_lock, flags);
+}
+
+static void udsl_process_send(unsigned long data)
+{
+ struct udsl_send_buffer *buf;
+ struct udsl_instance_data *instance = (struct udsl_instance_data *)data;
+ struct sk_buff *skb;
+ struct udsl_sender *snd;
+ int err;
+ unsigned int num_written;
+
+ made_progress:
+ spin_lock_irq(&instance->send_lock);
+ while (!list_empty(&instance->spare_senders)) {
+ if (!list_empty(&instance->filled_send_buffers)) {
+ buf = list_entry(instance->filled_send_buffers.next,
+ struct udsl_send_buffer, list);
+ list_del(&buf->list);
+ } else if ((buf = instance->current_buffer)) {
+ instance->current_buffer = NULL;
+ } else /* all buffers empty */
+ break;
+
+ snd = list_entry(instance->spare_senders.next,
+ struct udsl_sender, list);
+ list_del(&snd->list);
+ spin_unlock_irq(&instance->send_lock);
+
+ snd->buffer = buf;
+ usb_fill_bulk_urb(snd->urb, instance->usb_dev,
+ usb_sndbulkpipe(instance->usb_dev, instance->data_endpoint),
+ buf->base,
+ (snd_buf_size - buf->free_cells) * (ATM_CELL_SIZE + instance->snd_padding),
+ udsl_complete_send, snd);
+
+ vdbg("udsl_process_send: submitting urb 0x%p (%d cells), snd 0x%p, buf 0x%p",
+ snd->urb, snd_buf_size - buf->free_cells, snd, buf);
+
+ if ((err = usb_submit_urb(snd->urb, GFP_ATOMIC)) < 0) {
+ dbg("udsl_process_send: urb submission failed (%d)!", err);
+ spin_lock_irq(&instance->send_lock);
+ list_add(&snd->list, &instance->spare_senders);
+ spin_unlock_irq(&instance->send_lock);
+ list_add(&buf->list, &instance->filled_send_buffers);
+ return; /* bail out */
+ }
+
+ spin_lock_irq(&instance->send_lock);
+ } /* while */
+ spin_unlock_irq(&instance->send_lock);
+
+ if (!instance->current_skb)
+ instance->current_skb = skb_dequeue(&instance->sndqueue);
+ if (!instance->current_skb)
+ return; /* done - no more skbs */
+
+ skb = instance->current_skb;
+
+ if (!(buf = instance->current_buffer)) {
+ spin_lock_irq(&instance->send_lock);
+ if (list_empty(&instance->spare_send_buffers)) {
+ instance->current_buffer = NULL;
+ spin_unlock_irq(&instance->send_lock);
+ return; /* done - no more buffers */
+ }
+ buf = list_entry(instance->spare_send_buffers.next,
+ struct udsl_send_buffer, list);
+ list_del(&buf->list);
+ spin_unlock_irq(&instance->send_lock);
+
+ buf->free_start = buf->base;
+ buf->free_cells = snd_buf_size;
+
+ instance->current_buffer = buf;
+ }
+
+ num_written = udsl_write_cells(instance, buf->free_cells, skb, &buf->free_start);
+
+ vdbg("udsl_process_send: wrote %u cells from skb 0x%p to buffer 0x%p",
+ num_written, skb, buf);
+
+ if (!(buf->free_cells -= num_written)) {
+ list_add_tail(&buf->list, &instance->filled_send_buffers);
+ instance->current_buffer = NULL;
+ }
+
+ vdbg("udsl_process_send: buffer contains %d cells, %d left",
+ snd_buf_size - buf->free_cells, buf->free_cells);
+
+ if (!UDSL_SKB(skb)->num_cells) {
+ struct atm_vcc *vcc = UDSL_SKB(skb)->atm_data.vcc;
+
+ udsl_pop(vcc, skb);
+ instance->current_skb = NULL;
+
+ atomic_inc(&vcc->stats->tx);
+ }
+
+ goto made_progress;
+}
+
+static void udsl_cancel_send(struct udsl_instance_data *instance,
+ struct atm_vcc *vcc)
+{
+ struct sk_buff *skb, *n;
+
+ dbg("udsl_cancel_send entered");
+ spin_lock_irq(&instance->sndqueue.lock);
+ for (skb = instance->sndqueue.next, n = skb->next;
+ skb != (struct sk_buff *)&instance->sndqueue;
+ skb = n, n = skb->next)
+ if (UDSL_SKB(skb)->atm_data.vcc == vcc) {
+ dbg("udsl_cancel_send: popping skb 0x%p", skb);
+ __skb_unlink(skb, &instance->sndqueue);
+ udsl_pop(vcc, skb);
+ }
+ spin_unlock_irq(&instance->sndqueue.lock);
+
+ tasklet_disable(&instance->send_tasklet);
+ if ((skb = instance->current_skb) && (UDSL_SKB(skb)->atm_data.vcc == vcc)) {
+ dbg("udsl_cancel_send: popping current skb (0x%p)", skb);
+ instance->current_skb = NULL;
+ udsl_pop(vcc, skb);
+ }
+ tasklet_enable(&instance->send_tasklet);
+ dbg("udsl_cancel_send done");
+}
+
+static int udsl_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+ struct udsl_instance_data *instance = vcc->dev->dev_data;
+ int err;
+
+ vdbg("udsl_atm_send called (skb 0x%p, len %u)", skb, skb->len);
+
+ if (!instance) {
+ dbg("udsl_atm_send: NULL data!");
+ err = -ENODEV;
+ goto fail;
+ }
+
+ if (vcc->qos.aal != ATM_AAL5) {
+ dbg("udsl_atm_send: unsupported ATM type %d!", vcc->qos.aal);
+ err = -EINVAL;
+ goto fail;
+ }
+
+ if (skb->len > ATM_MAX_AAL5_PDU) {
+ dbg("udsl_atm_send: packet too long (%d vs %d)!", skb->len,
+ ATM_MAX_AAL5_PDU);
+ err = -EINVAL;
+ goto fail;
+ }
+
+ PACKETDEBUG(skb->data, skb->len);
+
+ udsl_groom_skb(vcc, skb);
+ skb_queue_tail(&instance->sndqueue, skb);
+ tasklet_schedule(&instance->send_tasklet);
+
+ return 0;
+
+ fail:
+ udsl_pop(vcc, skb);
+ return err;
+}
+
+/********************
+** bean counting **
+********************/
+
+static void udsl_destroy_instance(struct kref *kref)
+{
+ struct udsl_instance_data *instance =
+ container_of(kref, struct udsl_instance_data, refcount);
+
+ tasklet_kill(&instance->receive_tasklet);
+ tasklet_kill(&instance->send_tasklet);
+ usb_put_dev(instance->usb_dev);
+ kfree(instance);
+}
+
+void udsl_get_instance(struct udsl_instance_data *instance)
+{
+ kref_get(&instance->refcount);
+}
+
+void udsl_put_instance(struct udsl_instance_data *instance)
+{
+ kref_put(&instance->refcount, udsl_destroy_instance);
+}
+
+/**********
+** ATM **
+**********/
+
+static void udsl_atm_dev_close(struct atm_dev *dev)
+{
+ struct udsl_instance_data *instance = dev->dev_data;
+
+ dev->dev_data = NULL;
+ udsl_put_instance(instance);
+}
+
+static int udsl_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *page)
+{
+ struct udsl_instance_data *instance = atm_dev->dev_data;
+ int left = *pos;
+
+ if (!instance) {
+ dbg("udsl_atm_proc_read: NULL instance!");
+ return -ENODEV;
+ }
+
+ if (!left--)
+ return sprintf(page, "%s\n", instance->description);
+
+ if (!left--)
+ return sprintf(page, "MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ atm_dev->esi[0], atm_dev->esi[1],
+ atm_dev->esi[2], atm_dev->esi[3],
+ atm_dev->esi[4], atm_dev->esi[5]);
+
+ if (!left--)
+ return sprintf(page,
+ "AAL5: tx %d ( %d err ), rx %d ( %d err, %d drop )\n",
+ atomic_read(&atm_dev->stats.aal5.tx),
+ atomic_read(&atm_dev->stats.aal5.tx_err),
+ atomic_read(&atm_dev->stats.aal5.rx),
+ atomic_read(&atm_dev->stats.aal5.rx_err),
+ atomic_read(&atm_dev->stats.aal5.rx_drop));
+
+ if (!left--) {
+ switch (atm_dev->signal) {
+ case ATM_PHY_SIG_FOUND:
+ sprintf(page, "Line up");
+ break;
+ case ATM_PHY_SIG_LOST:
+ sprintf(page, "Line down");
+ break;
+ default:
+ sprintf(page, "Line state unknown");
+ break;
+ }
+
+ if (instance->usb_dev->state == USB_STATE_NOTATTACHED)
+ strcat(page, ", disconnected\n");
+ else {
+ if (instance->status == UDSL_LOADED_FIRMWARE)
+ strcat(page, ", firmware loaded\n");
+ else if (instance->status == UDSL_LOADING_FIRMWARE)
+ strcat(page, ", firmware loading\n");
+ else
+ strcat(page, ", no firmware\n");
+ }
+
+ return strlen(page);
+ }
+
+ return 0;
+}
+
+static int udsl_atm_open(struct atm_vcc *vcc)
+{
+ struct udsl_instance_data *instance = vcc->dev->dev_data;
+ struct udsl_vcc_data *new;
+ unsigned int max_pdu;
+ int vci = vcc->vci;
+ short vpi = vcc->vpi;
+ int err;
+
+ dbg("udsl_atm_open: vpi %hd, vci %d", vpi, vci);
+
+ if (!instance) {
+ dbg("udsl_atm_open: NULL data!");
+ return -ENODEV;
+ }
+
+ /* only support AAL5 */
+ if ((vcc->qos.aal != ATM_AAL5) || (vcc->qos.rxtp.max_sdu < 0)
+ || (vcc->qos.rxtp.max_sdu > ATM_MAX_AAL5_PDU)) {
+ dbg("udsl_atm_open: unsupported ATM type %d!", vcc->qos.aal);
+ return -EINVAL;
+ }
+
+ if (instance->firmware_wait &&
+ (err = instance->firmware_wait(instance)) < 0) {
+ dbg("udsl_atm_open: firmware not loaded (%d)!", err);
+ return err;
+ }
+
+ down(&instance->serialize); /* vs self, udsl_atm_close */
+
+ if (udsl_find_vcc(instance, vpi, vci)) {
+ dbg("udsl_atm_open: %hd/%d already in use!", vpi, vci);
+ up(&instance->serialize);
+ return -EADDRINUSE;
+ }
+
+ if (!(new = kmalloc(sizeof(struct udsl_vcc_data), GFP_KERNEL))) {
+ dbg("udsl_atm_open: no memory for vcc_data!");
+ up(&instance->serialize);
+ return -ENOMEM;
+ }
+
+ memset(new, 0, sizeof(struct udsl_vcc_data));
+ new->vcc = vcc;
+ new->vpi = vpi;
+ new->vci = vci;
+
+ /* udsl_extract_cells requires at least one cell */
+ max_pdu = max(1, UDSL_NUM_CELLS(vcc->qos.rxtp.max_sdu)) * ATM_CELL_PAYLOAD;
+ if (!(new->sarb = alloc_skb(max_pdu, GFP_KERNEL))) {
+ dbg("udsl_atm_open: no memory for SAR buffer!");
+ kfree(new);
+ up(&instance->serialize);
+ return -ENOMEM;
+ }
+
+ vcc->dev_data = new;
+
+ tasklet_disable(&instance->receive_tasklet);
+ list_add(&new->list, &instance->vcc_list);
+ tasklet_enable(&instance->receive_tasklet);
+
+ set_bit(ATM_VF_ADDR, &vcc->flags);
+ set_bit(ATM_VF_PARTIAL, &vcc->flags);
+ set_bit(ATM_VF_READY, &vcc->flags);
+
+ up(&instance->serialize);
+
+ tasklet_schedule(&instance->receive_tasklet);
+
+ dbg("udsl_atm_open: allocated vcc data 0x%p (max_pdu: %u)", new, max_pdu);
+
+ return 0;
+}
+
+static void udsl_atm_close(struct atm_vcc *vcc)
+{
+ struct udsl_instance_data *instance = vcc->dev->dev_data;
+ struct udsl_vcc_data *vcc_data = vcc->dev_data;
+
+ dbg("udsl_atm_close called");
+
+ if (!instance || !vcc_data) {
+ dbg("udsl_atm_close: NULL data!");
+ return;
+ }
+
+ dbg("udsl_atm_close: deallocating vcc 0x%p with vpi %d vci %d",
+ vcc_data, vcc_data->vpi, vcc_data->vci);
+
+ udsl_cancel_send(instance, vcc);
+
+ down(&instance->serialize); /* vs self, udsl_atm_open */
+
+ tasklet_disable(&instance->receive_tasklet);
+ list_del(&vcc_data->list);
+ tasklet_enable(&instance->receive_tasklet);
+
+ kfree_skb(vcc_data->sarb);
+ vcc_data->sarb = NULL;
+
+ kfree(vcc_data);
+ vcc->dev_data = NULL;
+
+ vcc->vpi = ATM_VPI_UNSPEC;
+ vcc->vci = ATM_VCI_UNSPEC;
+ clear_bit(ATM_VF_READY, &vcc->flags);
+ clear_bit(ATM_VF_PARTIAL, &vcc->flags);
+ clear_bit(ATM_VF_ADDR, &vcc->flags);
+
+ up(&instance->serialize);
+
+ dbg("udsl_atm_close successful");
+}
+
+static int udsl_atm_ioctl(struct atm_dev *dev, unsigned int cmd,
+ void __user * arg)
+{
+ switch (cmd) {
+ case ATM_QUERYLOOP:
+ return put_user(ATM_LM_NONE, (int __user *)arg) ? -EFAULT : 0;
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+/**********
+** USB **
+**********/
+
+int udsl_instance_setup(struct usb_device *dev,
+ struct udsl_instance_data *instance)
+{
+ char *buf;
+ int i, length;
+
+ kref_init(&instance->refcount); /* one for USB */
+ udsl_get_instance(instance); /* one for ATM */
+
+ init_MUTEX(&instance->serialize);
+
+ instance->usb_dev = dev;
+
+ INIT_LIST_HEAD(&instance->vcc_list);
+
+ instance->status = UDSL_NO_FIRMWARE;
+ init_waitqueue_head(&instance->firmware_waiters);
+
+ spin_lock_init(&instance->receive_lock);
+ INIT_LIST_HEAD(&instance->spare_receivers);
+ INIT_LIST_HEAD(&instance->filled_receive_buffers);
+
+ tasklet_init(&instance->receive_tasklet, udsl_process_receive, (unsigned long)instance);
+ INIT_LIST_HEAD(&instance->spare_receive_buffers);
+
+ skb_queue_head_init(&instance->sndqueue);
+
+ spin_lock_init(&instance->send_lock);
+ INIT_LIST_HEAD(&instance->spare_senders);
+ INIT_LIST_HEAD(&instance->spare_send_buffers);
+
+ tasklet_init(&instance->send_tasklet, udsl_process_send,
+ (unsigned long)instance);
+ INIT_LIST_HEAD(&instance->filled_send_buffers);
+
+ /* receive init */
+ for (i = 0; i < num_rcv_urbs; i++) {
+ struct udsl_receiver *rcv = &(instance->receivers[i]);
+
+ if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) {
+ dbg("udsl_usb_probe: no memory for receive urb %d!", i);
+ goto fail;
+ }
+
+ rcv->instance = instance;
+
+ list_add(&rcv->list, &instance->spare_receivers);
+ }
+
+ for (i = 0; i < num_rcv_bufs; i++) {
+ struct udsl_receive_buffer *buf =
+ &(instance->receive_buffers[i]);
+
+ buf->base = kmalloc(rcv_buf_size * (ATM_CELL_SIZE + instance->rcv_padding),
+ GFP_KERNEL);
+ if (!buf->base) {
+ dbg("udsl_usb_probe: no memory for receive buffer %d!", i);
+ goto fail;
+ }
+
+ list_add(&buf->list, &instance->spare_receive_buffers);
+ }
+
+ /* send init */
+ for (i = 0; i < num_snd_urbs; i++) {
+ struct udsl_sender *snd = &(instance->senders[i]);
+
+ if (!(snd->urb = usb_alloc_urb(0, GFP_KERNEL))) {
+ dbg("udsl_usb_probe: no memory for send urb %d!", i);
+ goto fail;
+ }
+
+ snd->instance = instance;
+
+ list_add(&snd->list, &instance->spare_senders);
+ }
+
+ for (i = 0; i < num_snd_bufs; i++) {
+ struct udsl_send_buffer *buf = &(instance->send_buffers[i]);
+
+ buf->base = kmalloc(snd_buf_size * (ATM_CELL_SIZE + instance->snd_padding),
+ GFP_KERNEL);
+ if (!buf->base) {
+ dbg("udsl_usb_probe: no memory for send buffer %d!", i);
+ goto fail;
+ }
+
+ list_add(&buf->list, &instance->spare_send_buffers);
+ }
+
+ /* ATM init */
+ instance->atm_dev = atm_dev_register(instance->driver_name,
+ &udsl_atm_devops, -1, NULL);
+ if (!instance->atm_dev) {
+ dbg("udsl_usb_probe: failed to register ATM device!");
+ goto fail;
+ }
+
+ instance->atm_dev->ci_range.vpi_bits = ATM_CI_MAX;
+ instance->atm_dev->ci_range.vci_bits = ATM_CI_MAX;
+ instance->atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
+
+ /* temp init ATM device, set to 128kbit */
+ instance->atm_dev->link_rate = 128 * 1000 / 424;
+
+ /* device description */
+ buf = instance->description;
+ length = sizeof(instance->description);
+
+ if ((i = usb_string(dev, dev->descriptor.iProduct, buf, length)) < 0)
+ goto finish;
+
+ buf += i;
+ length -= i;
+
+ i = scnprintf(buf, length, " (");
+ buf += i;
+ length -= i;
+
+ if (length <= 0 || (i = usb_make_path(dev, buf, length)) < 0)
+ goto finish;
+
+ buf += i;
+ length -= i;
+
+ snprintf(buf, length, ")");
+
+ finish:
+ /* ready for ATM callbacks */
+ wmb();
+ instance->atm_dev->dev_data = instance;
+
+ usb_get_dev(dev);
+
+ return 0;
+
+ fail:
+ for (i = 0; i < num_snd_bufs; i++)
+ kfree(instance->send_buffers[i].base);
+
+ for (i = 0; i < num_snd_urbs; i++)
+ usb_free_urb(instance->senders[i].urb);
+
+ for (i = 0; i < num_rcv_bufs; i++)
+ kfree(instance->receive_buffers[i].base);
+
+ for (i = 0; i < num_rcv_urbs; i++)
+ usb_free_urb(instance->receivers[i].urb);
+
+ return -ENOMEM;
+}
+
+void udsl_instance_disconnect(struct udsl_instance_data *instance)
+{
+ int i;
+
+ dbg("udsl_instance_disconnect entered");
+
+ if (!instance) {
+ dbg("udsl_instance_disconnect: NULL instance!");
+ return;
+ }
+
+ /* receive finalize */
+ tasklet_disable(&instance->receive_tasklet);
+
+ for (i = 0; i < num_rcv_urbs; i++)
+ usb_kill_urb(instance->receivers[i].urb);
+
+ /* no need to take the spinlock */
+ INIT_LIST_HEAD(&instance->filled_receive_buffers);
+ INIT_LIST_HEAD(&instance->spare_receive_buffers);
+
+ tasklet_enable(&instance->receive_tasklet);
+
+ for (i = 0; i < num_rcv_urbs; i++)
+ usb_free_urb(instance->receivers[i].urb);
+
+ for (i = 0; i < num_rcv_bufs; i++)
+ kfree(instance->receive_buffers[i].base);
+
+ /* send finalize */
+ tasklet_disable(&instance->send_tasklet);
+
+ for (i = 0; i < num_snd_urbs; i++)
+ usb_kill_urb(instance->senders[i].urb);
+
+ /* no need to take the spinlock */
+ INIT_LIST_HEAD(&instance->spare_senders);
+ INIT_LIST_HEAD(&instance->spare_send_buffers);
+ instance->current_buffer = NULL;
+
+ tasklet_enable(&instance->send_tasklet);
+
+ for (i = 0; i < num_snd_urbs; i++)
+ usb_free_urb(instance->senders[i].urb);
+
+ for (i = 0; i < num_snd_bufs; i++)
+ kfree(instance->send_buffers[i].base);
+
+ /* ATM finalize */
+ shutdown_atm_dev(instance->atm_dev);
+}
+
+EXPORT_SYMBOL_GPL(udsl_get_instance);
+EXPORT_SYMBOL_GPL(udsl_put_instance);
+EXPORT_SYMBOL_GPL(udsl_instance_setup);
+EXPORT_SYMBOL_GPL(udsl_instance_disconnect);
+
+/***********
+** init **
+***********/
+
+static int __init udsl_usb_init(void)
+{
+ dbg("udsl_usb_init: driver version " DRIVER_VERSION);
+
+ if (sizeof(struct udsl_control) > sizeof(((struct sk_buff *) 0)->cb)) {
+ printk(KERN_ERR __FILE__ ": unusable with this kernel!\n");
+ return -EIO;
+ }
+
+ if ((num_rcv_urbs > UDSL_MAX_RCV_URBS)
+ || (num_snd_urbs > UDSL_MAX_SND_URBS)
+ || (num_rcv_bufs > UDSL_MAX_RCV_BUFS)
+ || (num_snd_bufs > UDSL_MAX_SND_BUFS)
+ || (rcv_buf_size > UDSL_MAX_RCV_BUF_SIZE)
+ || (snd_buf_size > UDSL_MAX_SND_BUF_SIZE))
+ return -EINVAL;
+
+ return 0;
+}
+
+static void __exit udsl_usb_exit(void)
+{
+}
+
+module_init(udsl_usb_init);
+module_exit(udsl_usb_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
+
+/************
+** debug **
+************/
+
+#ifdef VERBOSE_DEBUG
+static int udsl_print_packet(const unsigned char *data, int len)
+{
+ unsigned char buffer[256];
+ int i = 0, j = 0;
+
+ for (i = 0; i < len;) {
+ buffer[0] = '\0';
+ sprintf(buffer, "%.3d :", i);
+ for (j = 0; (j < 16) && (i < len); j++, i++) {
+ sprintf(buffer, "%s %2.2x", buffer, data[i]);
+ }
+ dbg("%s", buffer);
+ }
+ return i;
+}
+#endif
diff --git a/drivers/usb/atm/usb_atm.h b/drivers/usb/atm/usb_atm.h
new file mode 100644
index 000000000000..219763cc3242
--- /dev/null
+++ b/drivers/usb/atm/usb_atm.h
@@ -0,0 +1,159 @@
+/******************************************************************************
+ * usb_atm.h - Generic USB xDSL driver core
+ *
+ * Copyright (C) 2001, Alcatel
+ * Copyright (C) 2003, Duncan Sands, SolNegro, Josep Comas
+ * Copyright (C) 2004, David Woodhouse
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ ******************************************************************************/
+
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <linux/kref.h>
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include <asm/semaphore.h>
+
+#define UDSL_MAX_RCV_URBS 4
+#define UDSL_MAX_SND_URBS 4
+#define UDSL_MAX_RCV_BUFS 8
+#define UDSL_MAX_SND_BUFS 8
+#define UDSL_MAX_RCV_BUF_SIZE 1024 /* ATM cells */
+#define UDSL_MAX_SND_BUF_SIZE 1024 /* ATM cells */
+#define UDSL_DEFAULT_RCV_URBS 2
+#define UDSL_DEFAULT_SND_URBS 2
+#define UDSL_DEFAULT_RCV_BUFS 4
+#define UDSL_DEFAULT_SND_BUFS 4
+#define UDSL_DEFAULT_RCV_BUF_SIZE 64 /* ATM cells */
+#define UDSL_DEFAULT_SND_BUF_SIZE 64 /* ATM cells */
+
+#define ATM_CELL_HEADER (ATM_CELL_SIZE - ATM_CELL_PAYLOAD)
+#define UDSL_NUM_CELLS(x) (((x) + ATM_AAL5_TRAILER + ATM_CELL_PAYLOAD - 1) / ATM_CELL_PAYLOAD)
+
+/* receive */
+
+struct udsl_receive_buffer {
+ struct list_head list;
+ unsigned char *base;
+ unsigned int filled_cells;
+};
+
+struct udsl_receiver {
+ struct list_head list;
+ struct udsl_receive_buffer *buffer;
+ struct urb *urb;
+ struct udsl_instance_data *instance;
+};
+
+struct udsl_vcc_data {
+ /* vpi/vci lookup */
+ struct list_head list;
+ short vpi;
+ int vci;
+ struct atm_vcc *vcc;
+
+ /* raw cell reassembly */
+ struct sk_buff *sarb;
+};
+
+/* send */
+
+struct udsl_send_buffer {
+ struct list_head list;
+ unsigned char *base;
+ unsigned char *free_start;
+ unsigned int free_cells;
+};
+
+struct udsl_sender {
+ struct list_head list;
+ struct udsl_send_buffer *buffer;
+ struct urb *urb;
+ struct udsl_instance_data *instance;
+};
+
+struct udsl_control {
+ struct atm_skb_data atm_data;
+ unsigned int num_cells;
+ unsigned int num_entire;
+ unsigned int pdu_padding;
+ unsigned char aal5_trailer[ATM_AAL5_TRAILER];
+};
+
+#define UDSL_SKB(x) ((struct udsl_control *)(x)->cb)
+
+/* main driver data */
+
+enum udsl_status {
+ UDSL_NO_FIRMWARE,
+ UDSL_LOADING_FIRMWARE,
+ UDSL_LOADED_FIRMWARE
+};
+
+struct udsl_instance_data {
+ struct kref refcount;
+ struct semaphore serialize;
+
+ /* USB device part */
+ struct usb_device *usb_dev;
+ char description[64];
+ int data_endpoint;
+ int snd_padding;
+ int rcv_padding;
+ const char *driver_name;
+
+ /* ATM device part */
+ struct atm_dev *atm_dev;
+ struct list_head vcc_list;
+
+ /* firmware */
+ int (*firmware_wait) (struct udsl_instance_data *);
+ enum udsl_status status;
+ wait_queue_head_t firmware_waiters;
+
+ /* receive */
+ struct udsl_receiver receivers[UDSL_MAX_RCV_URBS];
+ struct udsl_receive_buffer receive_buffers[UDSL_MAX_RCV_BUFS];
+
+ spinlock_t receive_lock;
+ struct list_head spare_receivers;
+ struct list_head filled_receive_buffers;
+
+ struct tasklet_struct receive_tasklet;
+ struct list_head spare_receive_buffers;
+
+ /* send */
+ struct udsl_sender senders[UDSL_MAX_SND_URBS];
+ struct udsl_send_buffer send_buffers[UDSL_MAX_SND_BUFS];
+
+ struct sk_buff_head sndqueue;
+
+ spinlock_t send_lock;
+ struct list_head spare_senders;
+ struct list_head spare_send_buffers;
+
+ struct tasklet_struct send_tasklet;
+ struct sk_buff *current_skb; /* being emptied */
+ struct udsl_send_buffer *current_buffer; /* being filled */
+ struct list_head filled_send_buffers;
+};
+
+extern int udsl_instance_setup(struct usb_device *dev,
+ struct udsl_instance_data *instance);
+extern void udsl_instance_disconnect(struct udsl_instance_data *instance);
+extern void udsl_get_instance(struct udsl_instance_data *instance);
+extern void udsl_put_instance(struct udsl_instance_data *instance);
diff --git a/drivers/usb/class/Kconfig b/drivers/usb/class/Kconfig
index 3d49e9d8cb67..0561d0234f23 100644
--- a/drivers/usb/class/Kconfig
+++ b/drivers/usb/class/Kconfig
@@ -9,7 +9,8 @@ config USB_AUDIO
depends on USB && SOUND
help
Say Y here if you want to connect USB audio equipment such as
- speakers to your computer's USB port.
+ speakers to your computer's USB port. You only need this if you use
+ the OSS sound driver; ALSA has its own option for usb audio support.
To compile this driver as a module, choose M here: the
module will be called audio.
diff --git a/drivers/usb/class/audio.c b/drivers/usb/class/audio.c
index 17a6dd74713f..88628d85dff2 100644
--- a/drivers/usb/class/audio.c
+++ b/drivers/usb/class/audio.c
@@ -635,13 +635,13 @@ static void usbin_stop(struct usb_audiodev *as)
spin_unlock_irqrestore(&as->lock, flags);
if (notkilled && signal_pending(current)) {
if (i & FLG_URB0RUNNING)
- usb_unlink_urb(u->durb[0].urb);
+ usb_kill_urb(u->durb[0].urb);
if (i & FLG_URB1RUNNING)
- usb_unlink_urb(u->durb[1].urb);
+ usb_kill_urb(u->durb[1].urb);
if (i & FLG_SYNC0RUNNING)
- usb_unlink_urb(u->surb[0].urb);
+ usb_kill_urb(u->surb[0].urb);
if (i & FLG_SYNC1RUNNING)
- usb_unlink_urb(u->surb[1].urb);
+ usb_kill_urb(u->surb[1].urb);
notkilled = 0;
}
}
@@ -1114,13 +1114,13 @@ static void usbout_stop(struct usb_audiodev *as)
spin_unlock_irqrestore(&as->lock, flags);
if (notkilled && signal_pending(current)) {
if (i & FLG_URB0RUNNING)
- usb_unlink_urb(u->durb[0].urb);
+ usb_kill_urb(u->durb[0].urb);
if (i & FLG_URB1RUNNING)
- usb_unlink_urb(u->durb[1].urb);
+ usb_kill_urb(u->durb[1].urb);
if (i & FLG_SYNC0RUNNING)
- usb_unlink_urb(u->surb[0].urb);
+ usb_kill_urb(u->surb[0].urb);
if (i & FLG_SYNC1RUNNING)
- usb_unlink_urb(u->surb[1].urb);
+ usb_kill_urb(u->surb[1].urb);
notkilled = 0;
}
}
@@ -1949,15 +1949,12 @@ static inline int prog_dmabuf_out(struct usb_audiodev *as)
static int usb_audio_open_mixdev(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
- struct list_head *devs, *mdevs;
struct usb_mixerdev *ms;
struct usb_audio_state *s;
down(&open_sem);
- list_for_each(devs, &audiodevs) {
- s = list_entry(devs, struct usb_audio_state, audiodev);
- list_for_each(mdevs, &s->mixerlist) {
- ms = list_entry(mdevs, struct usb_mixerdev, list);
+ list_for_each_entry(s, &audiodevs, audiodev) {
+ list_for_each_entry(ms, &s->mixerlist, list) {
if (ms->dev_mixer == minor)
goto mixer_found;
}
@@ -2634,16 +2631,13 @@ static int usb_audio_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
DECLARE_WAITQUEUE(wait, current);
- struct list_head *devs, *adevs;
struct usb_audiodev *as;
struct usb_audio_state *s;
for (;;) {
down(&open_sem);
- list_for_each(devs, &audiodevs) {
- s = list_entry(devs, struct usb_audio_state, audiodev);
- list_for_each(adevs, &s->audiolist) {
- as = list_entry(adevs, struct usb_audiodev, list);
+ list_for_each_entry(s, &audiodevs, audiodev) {
+ list_for_each_entry(as, &s->audiolist, list) {
if (!((as->dev_audio ^ minor) & ~0xf))
goto device_found;
}
@@ -3809,7 +3803,6 @@ static int usb_audio_probe(struct usb_interface *intf,
static void usb_audio_disconnect(struct usb_interface *intf)
{
struct usb_audio_state *s = usb_get_intfdata (intf);
- struct list_head *list;
struct usb_audiodev *as;
struct usb_mixerdev *ms;
@@ -3831,8 +3824,7 @@ static void usb_audio_disconnect(struct usb_interface *intf)
usb_set_intfdata (intf, NULL);
/* deregister all audio and mixer devices, so no new processes can open this device */
- list_for_each(list, &s->audiolist) {
- as = list_entry(list, struct usb_audiodev, list);
+ list_for_each_entry(as, &s->audiolist, list) {
usbin_disc(as);
usbout_disc(as);
wake_up(&as->usbin.dma.wait);
@@ -3843,8 +3835,7 @@ static void usb_audio_disconnect(struct usb_interface *intf)
}
as->dev_audio = -1;
}
- list_for_each(list, &s->mixerlist) {
- ms = list_entry(list, struct usb_mixerdev, list);
+ list_for_each_entry(ms, &s->mixerlist, list) {
if (ms->dev_mixer >= 0) {
unregister_sound_mixer(ms->dev_mixer);
printk(KERN_INFO "usbaudio: unregister mixer 14,%d\n", ms->dev_mixer);
diff --git a/drivers/usb/class/bluetty.c b/drivers/usb/class/bluetty.c
index 4d8e01895ce9..6baf68badc06 100644
--- a/drivers/usb/class/bluetty.c
+++ b/drivers/usb/class/bluetty.c
@@ -426,8 +426,8 @@ static void bluetooth_close (struct tty_struct *tty, struct file * filp)
bluetooth->open_count = 0;
/* shutdown any in-flight urbs that we know about */
- usb_unlink_urb (bluetooth->read_urb);
- usb_unlink_urb (bluetooth->interrupt_in_urb);
+ usb_kill_urb (bluetooth->read_urb);
+ usb_kill_urb (bluetooth->interrupt_in_urb);
}
up(&bluetooth->lock);
}
@@ -705,7 +705,7 @@ void btusb_disable_bulk_read(struct tty_struct *tty){
}
if ((bluetooth->read_urb) && (bluetooth->read_urb->actual_length))
- usb_unlink_urb(bluetooth->read_urb);
+ usb_kill_urb(bluetooth->read_urb);
}
#endif
@@ -1179,14 +1179,14 @@ static void usb_bluetooth_disconnect(struct usb_interface *intf)
bluetooth->open_count = 0;
if (bluetooth->read_urb) {
- usb_unlink_urb (bluetooth->read_urb);
+ usb_kill_urb (bluetooth->read_urb);
usb_free_urb (bluetooth->read_urb);
}
if (bluetooth->bulk_in_buffer)
kfree (bluetooth->bulk_in_buffer);
if (bluetooth->interrupt_in_urb) {
- usb_unlink_urb (bluetooth->interrupt_in_urb);
+ usb_kill_urb (bluetooth->interrupt_in_urb);
usb_free_urb (bluetooth->interrupt_in_urb);
}
if (bluetooth->interrupt_in_buffer)
@@ -1196,7 +1196,7 @@ static void usb_bluetooth_disconnect(struct usb_interface *intf)
for (i = 0; i < NUM_CONTROL_URBS; ++i) {
if (bluetooth->control_urb_pool[i]) {
- usb_unlink_urb (bluetooth->control_urb_pool[i]);
+ usb_kill_urb (bluetooth->control_urb_pool[i]);
if (bluetooth->control_urb_pool[i]->transfer_buffer)
kfree (bluetooth->control_urb_pool[i]->transfer_buffer);
usb_free_urb (bluetooth->control_urb_pool[i]);
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index a929a521242b..0d0d49923bee 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -301,9 +301,9 @@ done:
return 0;
full_bailout:
- usb_unlink_urb(acm->readurb);
+ usb_kill_urb(acm->readurb);
bail_out_and_unlink:
- usb_unlink_urb(acm->ctrlurb);
+ usb_kill_urb(acm->ctrlurb);
bail_out:
up(&open_sem);
return -EIO;
@@ -320,9 +320,9 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp)
if (!--acm->used) {
if (acm->dev) {
acm_set_control(acm, acm->ctrlout = 0);
- usb_unlink_urb(acm->ctrlurb);
- usb_unlink_urb(acm->writeurb);
- usb_unlink_urb(acm->readurb);
+ usb_kill_urb(acm->ctrlurb);
+ usb_kill_urb(acm->writeurb);
+ usb_kill_urb(acm->readurb);
} else {
tty_unregister_device(acm_tty_driver, acm->minor);
acm_table[acm->minor] = NULL;
@@ -542,6 +542,17 @@ static int acm_probe (struct usb_interface *intf,
return -EINVAL;
}
+ if (!buflen) {
+ if (intf->cur_altsetting->endpoint->extralen && intf->cur_altsetting->endpoint->extra) {
+ dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint");
+ buflen = intf->cur_altsetting->endpoint->extralen;
+ buffer = intf->cur_altsetting->endpoint->extra;
+ } else {
+ err("Zero length descriptor references");
+ return -EINVAL;
+ }
+ }
+
while (buflen > 0) {
if (buffer [1] != USB_DT_CS_INTERFACE) {
err("skipping garbage");
@@ -558,6 +569,8 @@ static int acm_probe (struct usb_interface *intf,
break;
case CDC_COUNTRY_TYPE: /* maybe somehow export */
break; /* for now we ignore it */
+ case CDC_HEADER_TYPE: /* maybe check version */
+ break; /* for now we ignore it */
case CDC_AC_MANAGEMENT_TYPE:
ac_management_function = buffer[3];
break;
@@ -569,7 +582,7 @@ static int acm_probe (struct usb_interface *intf,
break;
default:
- err("Ignoring extra header");
+ err("Ignoring extra header, type %d, length %d", buffer[2], buffer[0]);
break;
}
next_desc:
@@ -637,7 +650,7 @@ next_desc:
dbg("interfaces are valid");
for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
- if (acm_table[minor]) {
+ if (minor == ACM_TTY_MINORS) {
err("no more free acm devices");
return -ENODEV;
}
@@ -762,9 +775,9 @@ static void acm_disconnect(struct usb_interface *intf)
acm->dev = NULL;
usb_set_intfdata (intf, NULL);
- usb_unlink_urb(acm->ctrlurb);
- usb_unlink_urb(acm->readurb);
- usb_unlink_urb(acm->writeurb);
+ usb_kill_urb(acm->ctrlurb);
+ usb_kill_urb(acm->readurb);
+ usb_kill_urb(acm->writeurb);
flush_scheduled_work(); /* wait for acm_softint */
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index cc26d9517398..da945b367a85 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -117,6 +117,7 @@ struct union_desc {
} __attribute__ ((packed));
/* class specific descriptor types */
+#define CDC_HEADER_TYPE 0x00
#define CDC_CALL_MANAGEMENT_TYPE 0x01
#define CDC_AC_MANAGEMENT_TYPE 0x02
#define CDC_UNION_TYPE 0x06
diff --git a/drivers/usb/class/usb-midi.c b/drivers/usb/class/usb-midi.c
index a9dc047a04a7..1dc952e9bc81 100644
--- a/drivers/usb/class/usb-midi.c
+++ b/drivers/usb/class/usb-midi.c
@@ -805,7 +805,6 @@ static int usb_midi_open(struct inode *inode, struct file *file)
{
int minor = iminor(inode);
DECLARE_WAITQUEUE(wait, current);
- struct list_head *devs, *mdevs;
struct usb_midi_state *s;
struct usb_mididev *m;
unsigned long flags;
@@ -817,10 +816,8 @@ static int usb_midi_open(struct inode *inode, struct file *file)
for(;;) {
down(&open_sem);
- list_for_each(devs, &mididevs) {
- s = list_entry(devs, struct usb_midi_state, mididev);
- list_for_each(mdevs, &s->midiDevList) {
- m = list_entry(mdevs, struct usb_mididev, list);
+ list_for_each_entry(s, &mididevs, mididev) {
+ list_for_each_entry(m, &s->midiDevList, list) {
if ( !((m->dev_midi ^ minor) & ~0xf) )
goto device_found;
}
@@ -939,7 +936,7 @@ static int usb_midi_release(struct inode *inode, struct file *file)
if ( m->open_mode & FMODE_WRITE ) {
m->open_mode &= ~FMODE_WRITE;
- usb_unlink_urb( m->mout.ep->urb );
+ usb_kill_urb( m->mout.ep->urb );
}
if ( m->open_mode & FMODE_READ ) {
@@ -951,7 +948,7 @@ static int usb_midi_release(struct inode *inode, struct file *file)
if ( m->min.ep->readers == 0 &&
m->min.ep->urbSubmitted ) {
m->min.ep->urbSubmitted = 0;
- usb_unlink_urb(m->min.ep->urb);
+ usb_kill_urb(m->min.ep->urb);
}
spin_unlock_irqrestore( &m->min.ep->lock, flagsep );
}
@@ -1042,7 +1039,7 @@ static struct midi_in_endpoint *alloc_midi_in_endpoint( struct usb_device *d, in
static int remove_midi_in_endpoint( struct midi_in_endpoint *min )
{
- usb_unlink_urb( min->urb );
+ usb_kill_urb( min->urb );
usb_free_urb( min->urb );
kfree( min->recvBuf );
kfree( min );
@@ -1102,7 +1099,7 @@ static struct midi_out_endpoint *alloc_midi_out_endpoint( struct usb_device *d,
static int remove_midi_out_endpoint( struct midi_out_endpoint *mout )
{
- usb_unlink_urb( mout->urb );
+ usb_kill_urb( mout->urb );
usb_free_urb( mout->urb );
kfree( mout->buf );
kfree( mout );
@@ -1994,7 +1991,6 @@ static int usb_midi_probe(struct usb_interface *intf,
static void usb_midi_disconnect(struct usb_interface *intf)
{
struct usb_midi_state *s = usb_get_intfdata (intf);
- struct list_head *list;
struct usb_mididev *m;
if ( !s )
@@ -2012,8 +2008,7 @@ static void usb_midi_disconnect(struct usb_interface *intf)
s->usbdev = NULL;
usb_set_intfdata (intf, NULL);
- list_for_each(list, &s->midiDevList) {
- m = list_entry(list, struct usb_mididev, list);
+ list_for_each_entry(m, &s->midiDevList, list) {
wake_up(&(m->min.ep->wait));
wake_up(&(m->mout.ep->wait));
if ( m->dev_midi >= 0 ) {
diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c
index da91bbd6735c..da145c91c1fa 100644
--- a/drivers/usb/class/usblp.c
+++ b/drivers/usb/class/usblp.c
@@ -406,9 +406,9 @@ static void usblp_cleanup (struct usblp *usblp)
static void usblp_unlink_urbs(struct usblp *usblp)
{
- usb_unlink_urb(usblp->writeurb);
+ usb_kill_urb(usblp->writeurb);
if (usblp->bidir)
- usb_unlink_urb(usblp->readurb);
+ usb_kill_urb(usblp->readurb);
}
static int usblp_release(struct inode *inode, struct file *file)
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index 8e535789fce7..50009ed51e8d 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -149,7 +149,7 @@ static const struct class_info clas_info[] =
/*****************************************************************/
-void usbdevfs_conn_disc_event(void)
+void usbfs_conn_disc_event(void)
{
conndiscevcnt++;
wake_up(&deviceconndiscwq);
@@ -451,7 +451,7 @@ static char *usb_dump_string(char *start, char *end, const struct usb_device *de
* nbytes - the maximum number of bytes to write
* skip_bytes - the number of bytes to skip before writing anything
* file_offset - the offset into the devices file on completion
- * The caller must own the usbdev->serialize semaphore.
+ * The caller must own the device lock.
*/
static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, loff_t *skip_bytes, loff_t *file_offset,
struct usb_device *usbdev, struct usb_bus *bus, int level, int index, int count)
@@ -569,7 +569,6 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, loff_t *ski
static ssize_t usb_device_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
{
- struct list_head *buslist;
struct usb_bus *bus;
ssize_t ret, total_written = 0;
loff_t skip_bytes = *ppos;
@@ -581,18 +580,15 @@ static ssize_t usb_device_read(struct file *file, char __user *buf, size_t nbyte
if (!access_ok(VERIFY_WRITE, buf, nbytes))
return -EFAULT;
- /* enumerate busses */
down (&usb_bus_list_lock);
- list_for_each(buslist, &usb_bus_list) {
- /* print devices for this bus */
- bus = list_entry(buslist, struct usb_bus, bus_list);
-
+ /* print devices for all busses */
+ list_for_each_entry(bus, &usb_bus_list, bus_list) {
/* recurse through all children of the root hub */
if (!bus->root_hub)
continue;
- down(&bus->root_hub->serialize);
+ usb_lock_device(bus->root_hub);
ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos, bus->root_hub, bus, 0, 0, 0);
- up(&bus->root_hub->serialize);
+ usb_unlock_device(bus->root_hub);
if (ret < 0) {
up(&usb_bus_list_lock);
return ret;
@@ -682,7 +678,7 @@ static loff_t usb_device_lseek(struct file * file, loff_t offset, int orig)
return ret;
}
-struct file_operations usbdevfs_devices_fops = {
+struct file_operations usbfs_devices_fops = {
.llseek = usb_device_lseek,
.read = usb_device_read,
.poll = usb_device_poll,
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 776c1bf0df9b..0e0ea0806606 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -21,7 +21,7 @@
*
* $Id: devio.c,v 1.7 2000/02/01 17:28:48 fliegl Exp $
*
- * This file implements the usbdevfs/x/y files, where
+ * This file implements the usbfs/x/y files, where
* x is the bus number and y the device number.
*
* It allows user space programs/"drivers" to communicate directly
@@ -113,7 +113,7 @@ static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, l
int i;
pos = *ppos;
- down(&dev->serialize);
+ usb_lock_device(dev);
if (!connected(dev)) {
ret = -ENODEV;
goto err;
@@ -175,7 +175,7 @@ static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, l
}
err:
- up(&dev->serialize);
+ usb_unlock_device(dev);
return ret;
}
@@ -286,9 +286,10 @@ static void destroy_async (struct dev_state *ps, struct list_head *list)
while (!list_empty(list)) {
as = list_entry(list->next, struct async, asynclist);
list_del_init(&as->asynclist);
+
+ /* drop the spinlock so the completion handler can run */
spin_unlock_irqrestore(&ps->lock, flags);
- /* usb_unlink_urb calls the completion handler with status == -ENOENT */
- usb_unlink_urb(as->urb);
+ usb_kill_urb(as->urb);
spin_lock_irqsave(&ps->lock, flags);
}
spin_unlock_irqrestore(&ps->lock, flags);
@@ -353,7 +354,7 @@ static void driver_disconnect(struct usb_interface *intf)
destroy_async_on_interface(ps, ifnum);
}
-struct usb_driver usbdevfs_driver = {
+struct usb_driver usbfs_driver = {
.owner = THIS_MODULE,
.name = "usbfs",
.probe = driver_probe,
@@ -378,7 +379,7 @@ static int claimintf(struct dev_state *ps, unsigned int ifnum)
if (!intf)
err = -ENOENT;
else
- err = usb_driver_claim_interface(&usbdevfs_driver, intf, ps);
+ err = usb_driver_claim_interface(&usbfs_driver, intf, ps);
up_write(&usb_bus_type.subsys.rwsem);
if (err == 0)
set_bit(ifnum, &ps->ifclaimed);
@@ -401,7 +402,7 @@ static int releaseintf(struct dev_state *ps, unsigned int ifnum)
if (!intf)
err = -ENOENT;
else if (test_and_clear_bit(ifnum, &ps->ifclaimed)) {
- usb_driver_release_interface(&usbdevfs_driver, intf);
+ usb_driver_release_interface(&usbfs_driver, intf);
err = 0;
}
up_write(&usb_bus_type.subsys.rwsem);
@@ -516,7 +517,7 @@ static int usbdev_release(struct inode *inode, struct file *file)
struct usb_device *dev = ps->dev;
unsigned int ifnum;
- down(&dev->serialize);
+ usb_lock_device(dev);
list_del_init(&ps->list);
if (connected(dev)) {
@@ -525,7 +526,7 @@ static int usbdev_release(struct inode *inode, struct file *file)
releaseintf(ps, ifnum);
destroy_all_async(ps);
}
- up(&dev->serialize);
+ usb_unlock_device(dev);
usb_put_dev(dev);
ps->dev = NULL;
kfree(ps);
@@ -557,10 +558,10 @@ static int proc_control(struct dev_state *ps, void __user *arg)
snoop(&dev->dev, "control read: bRequest=%02x bRrequestType=%02x wValue=%04x wIndex=%04x\n",
ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex);
- up(&dev->serialize);
+ usb_unlock_device(dev);
i = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ctrl.bRequest, ctrl.bRequestType,
ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo);
- down(&dev->serialize);
+ usb_lock_device(dev);
if ((i > 0) && ctrl.wLength) {
if (usbfs_snoop) {
dev_info(&dev->dev, "control read: data ");
@@ -588,10 +589,10 @@ static int proc_control(struct dev_state *ps, void __user *arg)
printk ("%02x ", (unsigned char)(tbuf)[j]);
printk("\n");
}
- up(&dev->serialize);
+ usb_unlock_device(dev);
i = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.bRequest, ctrl.bRequestType,
ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo);
- down(&dev->serialize);
+ usb_lock_device(dev);
}
free_page((unsigned long)tbuf);
if (i<0) {
@@ -635,9 +636,9 @@ static int proc_bulk(struct dev_state *ps, void __user *arg)
kfree(tbuf);
return -EINVAL;
}
- up(&dev->serialize);
+ usb_unlock_device(dev);
i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);
- down(&dev->serialize);
+ usb_lock_device(dev);
if (!i && len2) {
if (copy_to_user(bulk.data, tbuf, len2)) {
kfree(tbuf);
@@ -651,9 +652,9 @@ static int proc_bulk(struct dev_state *ps, void __user *arg)
return -EFAULT;
}
}
- up(&dev->serialize);
+ usb_unlock_device(dev);
i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);
- down(&dev->serialize);
+ usb_lock_device(dev);
}
kfree(tbuf);
if (i < 0) {
@@ -734,7 +735,7 @@ static int proc_connectinfo(struct dev_state *ps, void __user *arg)
static int proc_resetdevice(struct dev_state *ps)
{
- return __usb_reset_device(ps->dev);
+ return usb_reset_device(ps->dev);
}
@@ -976,7 +977,7 @@ static int proc_unlinkurb(struct dev_state *ps, void __user *arg)
as = async_getpending(ps, arg);
if (!as)
return -EINVAL;
- usb_unlink_urb(as->urb);
+ usb_kill_urb(as->urb);
return 0;
}
@@ -1024,9 +1025,9 @@ static int proc_reapurb(struct dev_state *ps, void __user *arg)
break;
if (signal_pending(current))
break;
- up(&dev->serialize);
+ usb_unlock_device(dev);
schedule();
- down(&dev->serialize);
+ usb_lock_device(dev);
}
remove_wait_queue(&ps->wait, &wait);
set_current_state(TASK_RUNNING);
@@ -1149,7 +1150,11 @@ static int proc_ioctl (struct dev_state *ps, void __user *arg)
/* let kernel drivers try to (re)bind to the interface */
case USBDEVFS_CONNECT:
+ usb_unlock_device(ps->dev);
+ usb_lock_all_devices();
bus_rescan_devices(intf->dev.bus);
+ usb_unlock_all_devices();
+ usb_lock_device(ps->dev);
break;
/* talk directly to the interface's driver */
@@ -1192,9 +1197,9 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
if (!(file->f_mode & FMODE_WRITE))
return -EPERM;
- down(&dev->serialize);
+ usb_lock_device(dev);
if (!connected(dev)) {
- up(&dev->serialize);
+ usb_unlock_device(dev);
return -ENODEV;
}
@@ -1294,7 +1299,7 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
ret = proc_ioctl(ps, p);
break;
}
- up(&dev->serialize);
+ usb_unlock_device(dev);
if (ret >= 0)
inode->i_atime = CURRENT_TIME;
return ret;
@@ -1314,7 +1319,7 @@ static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wai
return mask;
}
-struct file_operations usbdevfs_device_file_operations = {
+struct file_operations usbfs_device_file_operations = {
.llseek = usbdev_lseek,
.read = usbdev_read,
.poll = usbdev_poll,
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index 5fdaae079cf6..837bb267f194 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -188,9 +188,9 @@ clean_3:
}
hcd->irq = dev->irq;
- dev_info (hcd->self.controller, "irq %s, %s %p\n", bufp,
+ dev_info (hcd->self.controller, "irq %s, %s 0x%lx\n", bufp,
(driver->flags & HCD_MEMORY) ? "pci mem" : "io base",
- base);
+ resource);
usb_bus_init (&hcd->self);
hcd->self.op = &usb_hcd_operations;
@@ -260,6 +260,8 @@ void usb_hcd_pci_remove (struct pci_dev *dev)
}
usb_deregister_bus (&hcd->self);
+
+ pci_disable_device(dev);
}
EXPORT_SYMBOL (usb_hcd_pci_remove);
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index d970e2c0c066..8ab1163e59b9 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -798,9 +798,9 @@ int usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev
return (retval < 0) ? retval : -EMSGSIZE;
}
- down (&usb_dev->serialize);
+ usb_lock_device (usb_dev);
retval = usb_new_device (usb_dev);
- up (&usb_dev->serialize);
+ usb_unlock_device (usb_dev);
if (retval) {
usb_dev->bus->root_hub = NULL;
dev_err (parent_dev, "can't register root hub for %s, %d\n",
@@ -1264,7 +1264,7 @@ static int hcd_unlink_urb (struct urb *urb, int status)
* never get completion IRQs ... maybe even the ones we need to
* finish unlinking the initial failed usb_set_address().
*/
- if (!hcd->saw_irq) {
+ if (!hcd->saw_irq && hcd->rh_timer.data != (unsigned long) urb) {
dev_warn (hcd->self.controller, "Unlink after no-IRQ? "
"Different ACPI or APIC settings may help."
"\n");
@@ -1573,13 +1573,12 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r)
struct usb_hcd *hcd = __hcd;
int start = hcd->state;
- if (unlikely (hcd->state == USB_STATE_HALT)) /* irq sharing? */
+ if (start == USB_STATE_HALT)
return IRQ_NONE;
-
- hcd->saw_irq = 1;
if (hcd->driver->irq (hcd, r) == IRQ_NONE)
return IRQ_NONE;
+ hcd->saw_irq = 1;
if (hcd->state != start && hcd->state == USB_STATE_HALT)
usb_hc_died (hcd);
return IRQ_HANDLED;
@@ -1588,22 +1587,6 @@ EXPORT_SYMBOL (usb_hcd_irq);
/*-------------------------------------------------------------------------*/
-static void hcd_panic (void *_hcd)
-{
- struct usb_hcd *hcd = _hcd;
- struct usb_device *hub = hcd->self.root_hub;
- unsigned i;
-
- /* hc's root hub is removed later removed in hcd->stop() */
- down (&hub->serialize);
- usb_set_device_state(hub, USB_STATE_NOTATTACHED);
- for (i = 0; i < hub->maxchild; i++) {
- if (hub->children [i])
- usb_disconnect (&hub->children [i]);
- }
- up (&hub->serialize);
-}
-
/**
* usb_hc_died - report abnormal shutdown of a host controller (bus glue)
* @hcd: pointer to the HCD representing the controller
@@ -1616,9 +1599,9 @@ void usb_hc_died (struct usb_hcd *hcd)
{
dev_err (hcd->self.controller, "HC died; cleaning up\n");
- /* clean up old urbs and devices; needs a task context */
- INIT_WORK (&hcd->work, hcd_panic, hcd);
- (void) schedule_work (&hcd->work);
+ /* make khubd clean up old urbs and devices */
+ usb_set_device_state(hcd->self.root_hub, USB_STATE_NOTATTACHED);
+ mod_timer(&hcd->rh_timer, jiffies);
}
EXPORT_SYMBOL (usb_hc_died);
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index 340977b72925..eb8ae109096e 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -67,7 +67,6 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */
struct timer_list rh_timer; /* drives root hub */
struct list_head dev_list; /* devices on this bus */
- struct work_struct work;
/*
* hardware info/state
@@ -363,6 +362,9 @@ static inline int hcd_register_root (struct usb_device *usb_dev,
return usb_register_root_hub (usb_dev, hcd->self.controller);
}
+extern void usb_set_device_state(struct usb_device *udev,
+ enum usb_device_state new_state);
+
/*-------------------------------------------------------------------------*/
/* exported only within usbcore */
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 7f72ee96c7fe..10baa8f52566 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -36,15 +36,18 @@
#include "hcd.h"
#include "hub.h"
-/* Protect struct usb_device state and children members */
+/* Protect struct usb_device->state and ->children members
+ * Note: Both are also protected by ->serialize, except that ->state can
+ * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */
static spinlock_t device_state_lock = SPIN_LOCK_UNLOCKED;
-/* Wakes up khubd */
+/* khubd's worklist and its lock */
static spinlock_t hub_event_lock = SPIN_LOCK_UNLOCKED;
-
static LIST_HEAD(hub_event_list); /* List of hubs needing servicing */
+/* Wakes up khubd */
static DECLARE_WAIT_QUEUE_HEAD(khubd_wait);
+
static pid_t khubd_pid = 0; /* PID of khubd */
static DECLARE_COMPLETION(khubd_exited);
@@ -226,6 +229,19 @@ static int get_port_status(struct usb_device *hdev, int port,
data, sizeof(*data), HZ * USB_CTRL_GET_TIMEOUT);
}
+static void kick_khubd(struct usb_hub *hub)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hub_event_lock, flags);
+ if (list_empty(&hub->event_list)) {
+ list_add_tail(&hub->event_list, &hub_event_list);
+ wake_up(&khubd_wait);
+ }
+ spin_unlock_irqrestore(&hub_event_lock, flags);
+}
+
+
/* completion function, fires on port status changes and various faults */
static void hub_irq(struct urb *urb, struct pt_regs *regs)
{
@@ -261,12 +277,7 @@ static void hub_irq(struct urb *urb, struct pt_regs *regs)
hub->nerrors = 0;
/* Something happened, let khubd figure it out */
- spin_lock(&hub_event_lock);
- if (list_empty(&hub->event_list)) {
- list_add_tail(&hub->event_list, &hub_event_list);
- wake_up(&khubd_wait);
- }
- spin_unlock(&hub_event_lock);
+ kick_khubd(hub);
resubmit:
if (hub->quiescing)
@@ -384,6 +395,33 @@ static void hub_power_on(struct usb_hub *hub)
msleep(hub->descriptor->bPwrOn2PwrGood * 2);
}
+static void hub_quiesce(struct usb_hub *hub)
+{
+ /* stop khubd and related activity */
+ hub->quiescing = 1;
+ usb_kill_urb(hub->urb);
+ if (hub->has_indicators)
+ cancel_delayed_work(&hub->leds);
+ if (hub->has_indicators || hub->tt.hub)
+ flush_scheduled_work();
+}
+
+static void hub_activate(struct usb_hub *hub)
+{
+ int status;
+
+ hub->quiescing = 0;
+ status = usb_submit_urb(hub->urb, GFP_NOIO);
+ if (status < 0)
+ dev_err(&hub->intf->dev, "activate --> %d\n", status);
+ if (hub->has_indicators && blinkenlights)
+ schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
+
+ /* scan all ports ASAP */
+ hub->event_bits[0] = ~0;
+ kick_khubd(hub);
+}
+
static int hub_hub_status(struct usb_hub *hub,
u16 *status, u16 *change)
{
@@ -579,7 +617,7 @@ static int hub_configure(struct usb_hub *hub,
dev_dbg(hub_dev, "%sover-current condition exists\n",
(hubstatus & HUB_STATUS_OVERCURRENT) ? "" : "no ");
- /* Start the interrupt endpoint */
+ /* set up the interrupt endpoint */
pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe));
@@ -597,24 +635,13 @@ static int hub_configure(struct usb_hub *hub,
hub, endpoint->bInterval);
hub->urb->transfer_dma = hub->buffer_dma;
hub->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
- ret = usb_submit_urb(hub->urb, GFP_KERNEL);
- if (ret) {
- message = "couldn't submit status urb";
- goto fail;
- }
-
- /* Wake up khubd */
- wake_up(&khubd_wait);
- /* maybe start cycling the hub leds */
- if (hub->has_indicators && blinkenlights) {
- set_port_led(hdev, 1, HUB_LED_GREEN);
+ /* maybe cycle the hub leds */
+ if (hub->has_indicators && blinkenlights)
hub->indicator [0] = INDICATOR_CYCLE;
- schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
- }
hub_power_on(hub);
-
+ hub_activate(hub);
return 0;
fail:
@@ -626,33 +653,6 @@ fail:
static unsigned highspeed_hubs;
-static void hub_quiesce(struct usb_hub *hub)
-{
- /* stop khubd and related activity */
- hub->quiescing = 1;
- usb_kill_urb(hub->urb);
- if (hub->has_indicators)
- cancel_delayed_work(&hub->leds);
- if (hub->has_indicators || hub->tt.hub)
- flush_scheduled_work();
-}
-
-#ifdef CONFIG_USB_SUSPEND
-
-static void hub_reactivate(struct usb_hub *hub)
-{
- int status;
-
- hub->quiescing = 0;
- status = usb_submit_urb(hub->urb, GFP_NOIO);
- if (status < 0)
- dev_err(&hub->intf->dev, "reactivate --> %d\n", status);
- if (hub->has_indicators && blinkenlights)
- schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
-}
-
-#endif
-
static void hub_disconnect(struct usb_interface *intf)
{
struct usb_hub *hub = usb_get_intfdata (intf);
@@ -794,68 +794,29 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
}
}
-/* caller has locked the hub and must own the device lock */
-static int hub_reset(struct usb_hub *hub)
+/* caller has locked the hub device */
+static void hub_pre_reset(struct usb_device *hdev)
{
- struct usb_device *hdev = hub->hdev;
+ struct usb_hub *hub = usb_get_intfdata(hdev->actconfig->interface[0]);
int i;
- /* Disconnect any attached devices */
- for (i = 0; i < hub->descriptor->bNbrPorts; i++) {
+ for (i = 0; i < hdev->maxchild; ++i) {
if (hdev->children[i])
usb_disconnect(&hdev->children[i]);
}
-
- /* Attempt to reset the hub */
- if (hub->urb)
- usb_kill_urb(hub->urb);
- else
- return -1;
-
- if (__usb_reset_device(hdev))
- return -1;
-
- hub->urb->dev = hdev;
- if (usb_submit_urb(hub->urb, GFP_KERNEL))
- return -1;
-
- hub_power_on(hub);
-
- return 0;
+ hub_quiesce(hub);
}
-/* caller has locked the hub */
-/* FIXME! This routine should be subsumed into hub_reset */
-static void hub_start_disconnect(struct usb_device *hdev)
+/* caller has locked the hub device */
+static void hub_post_reset(struct usb_device *hdev)
{
- struct usb_device *parent = hdev->parent;
- int i;
-
- /* Find the device pointer to disconnect */
- if (parent) {
- for (i = 0; i < parent->maxchild; i++) {
- if (parent->children[i] == hdev) {
- usb_disconnect(&parent->children[i]);
- return;
- }
- }
- }
+ struct usb_hub *hub = usb_get_intfdata(hdev->actconfig->interface[0]);
- dev_err(&hdev->dev, "cannot disconnect hub!\n");
+ hub_activate(hub);
+ hub_power_on(hub);
}
-static void recursively_mark_NOTATTACHED(struct usb_device *udev)
-{
- int i;
-
- for (i = 0; i < udev->maxchild; ++i) {
- if (udev->children[i])
- recursively_mark_NOTATTACHED(udev->children[i]);
- }
- udev->state = USB_STATE_NOTATTACHED;
-}
-
/* grab device/port lock, returning index of that port (zero based).
* protects the upstream link used by this device from concurrent
* tree operations like suspend, resume, reset, and disconnect, which
@@ -872,21 +833,16 @@ static int locktree(struct usb_device *udev)
/* root hub is always the first lock in the series */
hdev = udev->parent;
if (!hdev) {
- down(&udev->serialize);
+ usb_lock_device(udev);
return 0;
}
/* on the path from root to us, lock everything from
* top down, dropping parent locks when not needed
- *
- * NOTE: if disconnect were to ignore the locking, we'd need
- * to get extra refcounts to everything since hdev->children
- * and udev->parent could be invalidated while we work...
*/
t = locktree(hdev);
if (t < 0)
return t;
- spin_lock_irq(&device_state_lock);
for (t = 0; t < hdev->maxchild; t++) {
if (hdev->children[t] == udev) {
/* everything is fail-fast once disconnect
@@ -898,33 +854,45 @@ static int locktree(struct usb_device *udev)
/* when everyone grabs locks top->bottom,
* non-overlapping work may be concurrent
*/
- spin_unlock_irq(&device_state_lock);
down(&udev->serialize);
up(&hdev->serialize);
return t;
}
}
- spin_unlock_irq(&device_state_lock);
- up(&hdev->serialize);
+ usb_unlock_device(hdev);
return -ENODEV;
}
+static void recursively_mark_NOTATTACHED(struct usb_device *udev)
+{
+ int i;
+
+ for (i = 0; i < udev->maxchild; ++i) {
+ if (udev->children[i])
+ recursively_mark_NOTATTACHED(udev->children[i]);
+ }
+ udev->state = USB_STATE_NOTATTACHED;
+}
+
/**
- * usb_set_device_state - change a device's current state (usbcore-internal)
+ * usb_set_device_state - change a device's current state (usbcore, hcds)
* @udev: pointer to device whose state should be changed
* @new_state: new state value to be stored
*
- * udev->state is _not_ protected by the device lock. This
+ * udev->state is _not_ fully protected by the device lock. Although
+ * most transitions are made only while holding the lock, the state can
+ * can change to USB_STATE_NOTATTACHED at almost any time. This
* is so that devices can be marked as disconnected as soon as possible,
- * without having to wait for the semaphore to be released. Instead,
- * changes to the state must be protected by the device_state_lock spinlock.
+ * without having to wait for any semaphores to be released. As a result,
+ * all changes to any device's state must be protected by the
+ * device_state_lock spinlock.
*
* Once a device has been added to the device tree, all changes to its state
* should be made using this routine. The state should _not_ be set directly.
*
* If udev->state is already USB_STATE_NOTATTACHED then no change is made.
* Otherwise udev->state is set to new_state, and if new_state is
- * USB_STATE_NOTATTACHED then all of udev's descendant's states are also set
+ * USB_STATE_NOTATTACHED then all of udev's descendants' states are also set
* to USB_STATE_NOTATTACHED.
*/
void usb_set_device_state(struct usb_device *udev,
@@ -941,6 +909,7 @@ void usb_set_device_state(struct usb_device *udev,
recursively_mark_NOTATTACHED(udev);
spin_unlock_irqrestore(&device_state_lock, flags);
}
+EXPORT_SYMBOL(usb_set_device_state);
static void choose_address(struct usb_device *udev)
@@ -974,11 +943,12 @@ static void release_address(struct usb_device *udev)
/**
* usb_disconnect - disconnect a device (usbcore-internal)
- * @pdev: pointer to device being disconnected, into a locked hub
+ * @pdev: pointer to device being disconnected
* Context: !in_interrupt ()
*
- * Something got disconnected. Get rid of it, and all of its children.
- * If *pdev is a normal device then the parent hub should be locked.
+ * Something got disconnected. Get rid of it and all of its children.
+ *
+ * If *pdev is a normal device then the parent hub must already be locked.
* If *pdev is a root hub then this routine will acquire the
* usb_bus_list_lock on behalf of the caller.
*
@@ -1004,9 +974,11 @@ void usb_disconnect(struct usb_device **pdev)
usb_set_device_state(udev, USB_STATE_NOTATTACHED);
/* lock the bus list on behalf of HCDs unregistering their root hubs */
- if (!udev->parent)
+ if (!udev->parent) {
down(&usb_bus_list_lock);
- down(&udev->serialize);
+ usb_lock_device(udev);
+ } else
+ down(&udev->serialize);
dev_info (&udev->dev, "USB disconnect, address %d\n", udev->devnum);
@@ -1031,14 +1003,16 @@ void usb_disconnect(struct usb_device **pdev)
usbfs_remove_device(udev);
usb_remove_sysfs_dev_files(udev);
- /* Avoid races with recursively_mark_NOTATTACHED() and locktree() */
+ /* Avoid races with recursively_mark_NOTATTACHED() */
spin_lock_irq(&device_state_lock);
*pdev = NULL;
spin_unlock_irq(&device_state_lock);
- up(&udev->serialize);
- if (!udev->parent)
+ if (!udev->parent) {
+ usb_unlock_device(udev);
up(&usb_bus_list_lock);
+ } else
+ up(&udev->serialize);
device_unregister(&udev->dev);
}
@@ -1061,11 +1035,19 @@ static int choose_configuration(struct usb_device *udev)
->altsetting->desc;
if (desc->bInterfaceClass == USB_CLASS_VENDOR_SPEC)
continue;
- /* COMM/2/all is CDC ACM, except 0xff is MSFT RNDIS */
+ /* COMM/2/all is CDC ACM, except 0xff is MSFT RNDIS.
+ * MSFT needs this to be the first config; never use
+ * it as the default unless Linux has host-side RNDIS.
+ * A second config would ideally be CDC-Ethernet, but
+ * may instead be the "vendor specific" CDC subset
+ * long used by ARM Linux for sa1100 or pxa255.
+ */
if (desc->bInterfaceClass == USB_CLASS_COMM
&& desc->bInterfaceSubClass == 2
- && desc->bInterfaceProtocol == 0xff)
+ && desc->bInterfaceProtocol == 0xff) {
+ c = udev->config[1].desc.bConfigurationValue;
continue;
+ }
c = udev->config[i].desc.bConfigurationValue;
break;
}
@@ -1384,7 +1366,6 @@ static int hub_port_disable(struct usb_device *hdev, int port)
int ret;
if (hdev->children[port]) {
- /* FIXME need disconnect() for NOTATTACHED device */
usb_set_device_state(hdev->children[port],
USB_STATE_NOTATTACHED);
}
@@ -1396,6 +1377,33 @@ static int hub_port_disable(struct usb_device *hdev, int port)
return ret;
}
+/*
+ * Disable a port and mark a logical connnect-change event, so that some
+ * time later khubd will disconnect() any existing usb_device on the port
+ * and will re-enumerate if there actually is a device attached.
+ */
+static void hub_port_logical_disconnect(struct usb_device *hdev, int port)
+{
+ struct usb_hub *hub;
+
+ dev_dbg(hubdev(hdev), "logical disconnect on port %d\n", port + 1);
+ hub_port_disable(hdev, port);
+
+ /* FIXME let caller ask to power down the port:
+ * - some devices won't enumerate without a VBUS power cycle
+ * - SRP saves power that way
+ * - usb_suspend_device(dev,PM_SUSPEND_DISK)
+ * That's easy if this hub can switch power per-port, and
+ * khubd reactivates the port later (timer, SRP, etc).
+ * Powerdown must be optional, because of reset/DFU.
+ */
+
+ hub = usb_get_intfdata(hdev->actconfig->interface[0]);
+ set_bit(port, hub->change_bits);
+ kick_khubd(hub);
+}
+
+
#ifdef CONFIG_USB_SUSPEND
/*
@@ -1413,8 +1421,8 @@ static int hub_port_suspend(struct usb_device *hdev, int port)
int status;
struct usb_device *udev;
- udev = hdev->children[port - 1];
- // dev_dbg(hubdev(hdev), "suspend port %d\n", port);
+ udev = hdev->children[port];
+ // dev_dbg(hubdev(hdev), "suspend port %d\n", port + 1);
/* enable remote wakeup when appropriate; this lets the device
* wake up the upstream hub (including maybe the root hub).
@@ -1439,11 +1447,11 @@ static int hub_port_suspend(struct usb_device *hdev, int port)
}
/* see 7.1.7.6 */
- status = set_port_feature(hdev, port, USB_PORT_FEAT_SUSPEND);
+ status = set_port_feature(hdev, port + 1, USB_PORT_FEAT_SUSPEND);
if (status) {
dev_dbg(hubdev(hdev),
"can't suspend port %d, status %d\n",
- port, status);
+ port + 1, status);
/* paranoia: "should not happen" */
(void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE,
@@ -1477,16 +1485,14 @@ static int __usb_suspend_device (struct usb_device *udev, int port, u32 state)
{
int status;
+ /* caller owns the udev device lock */
if (port < 0)
return port;
- /* NOTE: udev->serialize released on all real returns! */
-
if (state <= udev->dev.power.power_state
|| state < PM_SUSPEND_MEM
|| udev->state == USB_STATE_SUSPENDED
|| udev->state == USB_STATE_NOTATTACHED) {
- up(&udev->serialize);
return 0;
}
@@ -1562,11 +1568,10 @@ static int __usb_suspend_device (struct usb_device *udev, int port, u32 state)
else
status = -EOPNOTSUPP;
} else
- status = hub_port_suspend(udev->parent, port + 1);
+ status = hub_port_suspend(udev->parent, port);
if (status == 0)
udev->dev.power.power_state = state;
- up(&udev->serialize);
return status;
}
@@ -1590,7 +1595,15 @@ static int __usb_suspend_device (struct usb_device *udev, int port, u32 state)
*/
int usb_suspend_device(struct usb_device *udev, u32 state)
{
- return __usb_suspend_device(udev, locktree(udev), state);
+ int port, status;
+
+ port = locktree(udev);
+ if (port < 0)
+ return port;
+
+ status = __usb_suspend_device(udev, port, state);
+ usb_unlock_device(udev);
+ return status;
}
/*
@@ -1603,7 +1616,7 @@ static int finish_port_resume(struct usb_device *udev)
int status;
u16 devstatus;
- /* caller owns udev->serialize */
+ /* caller owns the udev device lock */
dev_dbg(&udev->dev, "usb resume\n");
udev->dev.power.power_state = PM_SUSPEND_ON;
@@ -1687,15 +1700,15 @@ hub_port_resume(struct usb_device *hdev, int port)
int status;
struct usb_device *udev;
- udev = hdev->children[port - 1];
- // dev_dbg(hubdev(hdev), "resume port %d\n", port);
+ udev = hdev->children[port];
+ // dev_dbg(hubdev(hdev), "resume port %d\n", port + 1);
/* see 7.1.7.7; affects power usage, but not budgeting */
- status = clear_port_feature(hdev, port, USB_PORT_FEAT_SUSPEND);
+ status = clear_port_feature(hdev, port + 1, USB_PORT_FEAT_SUSPEND);
if (status) {
dev_dbg(&hdev->actconfig->interface[0]->dev,
"can't resume port %d, status %d\n",
- port, status);
+ port + 1, status);
} else {
u16 devstatus;
u16 portchange;
@@ -1713,7 +1726,7 @@ hub_port_resume(struct usb_device *hdev, int port)
* sequence.
*/
devstatus = portchange = 0;
- status = hub_port_status(hdev, port - 1,
+ status = hub_port_status(hdev, port,
&devstatus, &portchange);
if (status < 0
|| (devstatus & LIVE_FLAGS) != LIVE_FLAGS
@@ -1721,7 +1734,7 @@ hub_port_resume(struct usb_device *hdev, int port)
) {
dev_dbg(&hdev->actconfig->interface[0]->dev,
"port %d status %04x.%04x after resume, %d\n",
- port, portchange, devstatus, status);
+ port + 1, portchange, devstatus, status);
} else {
/* TRSMRCY = 10 msec */
msleep(10);
@@ -1729,7 +1742,7 @@ hub_port_resume(struct usb_device *hdev, int port)
}
}
if (status < 0)
- status = hub_port_disable(hdev, port);
+ hub_port_logical_disconnect(hdev, port);
return status;
}
@@ -1773,7 +1786,7 @@ int usb_resume_device(struct usb_device *udev)
->actconfig->interface[0]);
}
} else if (udev->state == USB_STATE_SUSPENDED) {
- status = hub_port_resume(udev->parent, port + 1);
+ status = hub_port_resume(udev->parent, port);
} else {
status = 0;
udev->dev.power.power_state = PM_SUSPEND_ON;
@@ -1783,10 +1796,12 @@ int usb_resume_device(struct usb_device *udev)
status);
}
- up(&udev->serialize);
+ usb_unlock_device(udev);
/* rebind drivers that had no suspend() */
+ usb_lock_all_devices();
bus_rescan_devices(&usb_bus_type);
+ usb_unlock_all_devices();
return status;
}
@@ -1828,6 +1843,7 @@ static int hub_suspend(struct usb_interface *intf, u32 state)
continue;
down(&udev->serialize);
status = __usb_suspend_device(udev, port, state);
+ up(&udev->serialize);
if (status < 0)
dev_dbg(&intf->dev, "suspend port %d --> %d\n",
port, status);
@@ -1866,20 +1882,20 @@ static int hub_resume(struct usb_interface *intf)
continue;
down (&udev->serialize);
if (portstat & USB_PORT_STAT_SUSPEND)
- status = hub_port_resume(hdev, port + 1);
+ status = hub_port_resume(hdev, port);
else {
status = finish_port_resume(udev);
- if (status < 0)
- status = hub_port_disable(hdev, port);
- if (status < 0)
+ if (status < 0) {
dev_dbg(&intf->dev, "resume port %d --> %d\n",
- port, status);
+ port + 1, status);
+ hub_port_logical_disconnect(hdev, port);
+ }
}
up(&udev->serialize);
}
intf->dev.power.power_state = PM_SUSPEND_ON;
- hub_reactivate(hub);
+ hub_activate(hub);
return 0;
}
@@ -2011,7 +2027,7 @@ hub_port_init (struct usb_device *hdev, struct usb_device *udev, int port)
hdev->bus->b_hnp_enable = 0;
}
- retval = clear_port_feature(hdev, port, USB_PORT_FEAT_SUSPEND);
+ retval = clear_port_feature(hdev, port + 1, USB_PORT_FEAT_SUSPEND);
if (retval < 0 && retval != -EPIPE)
dev_dbg(&udev->dev, "can't clear suspend; %d\n", retval);
@@ -2443,10 +2459,10 @@ static void hub_events(void)
dev_dbg (hub_dev, "resetting for error %d\n",
hub->error);
- if (hub_reset(hub)) {
+ ret = usb_reset_device(hdev);
+ if (ret) {
dev_dbg (hub_dev,
- "can't reset; disconnecting\n");
- hub_start_disconnect(hdev);
+ "error resetting hub: %d\n", ret);
goto loop;
}
@@ -2502,15 +2518,17 @@ static void hub_events(void)
if (portchange & USB_PORT_STAT_C_SUSPEND) {
clear_port_feature(hdev, i + 1,
USB_PORT_FEAT_C_SUSPEND);
- if (hdev->children[i])
+ if (hdev->children[i]) {
ret = remote_wakeup(hdev->children[i]);
- else
+ if (ret < 0)
+ connect_change = 1;
+ } else {
ret = -ENODEV;
+ hub_port_disable(hdev, i);
+ }
dev_dbg (hub_dev,
"resume on port %d, status %d\n",
i + 1, ret);
- if (ret < 0)
- ret = hub_port_disable(hdev, i);
}
if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
@@ -2554,7 +2572,7 @@ static void hub_events(void)
}
loop:
- up(&hdev->serialize);
+ usb_unlock_device(hdev);
usb_put_dev(hdev);
} /* end while (1) */
@@ -2707,13 +2725,15 @@ static int config_descriptors_changed(struct usb_device *udev)
*
* The caller must own the device lock. For example, it's safe to use
* this from a driver probe() routine after downloading new firmware.
+ * For calls that might not occur during probe(), drivers should lock
+ * the device using usb_lock_device_for_reset().
*/
-int __usb_reset_device(struct usb_device *udev)
+int usb_reset_device(struct usb_device *udev)
{
struct usb_device *parent = udev->parent;
struct usb_device_descriptor descriptor = udev->descriptor;
int i, ret, port = -1;
- struct usb_hub *hub;
+ int udev_is_a_hub = 0;
if (udev->state == USB_STATE_NOTATTACHED ||
udev->state == USB_STATE_SUSPENDED) {
@@ -2722,13 +2742,9 @@ int __usb_reset_device(struct usb_device *udev)
return -EINVAL;
}
- /* FIXME: This should be legal for regular hubs. Root hubs may
- * have special requirements. */
- if (udev->maxchild) {
- /* this requires hub- or hcd-specific logic;
- * see hub_reset() and OHCI hc_restart()
- */
- dev_dbg(&udev->dev, "%s for hub!\n", __FUNCTION__);
+ if (!parent) {
+ /* this requires hcd-specific logic; see OHCI hc_restart() */
+ dev_dbg(&udev->dev, "%s for root hub!\n", __FUNCTION__);
return -EISDIR;
}
@@ -2744,6 +2760,19 @@ int __usb_reset_device(struct usb_device *udev)
return -ENOENT;
}
+ /* If we're resetting an active hub, take some special actions */
+ if (udev->actconfig &&
+ udev->actconfig->interface[0]->dev.driver ==
+ &hub_driver.driver) {
+ udev_is_a_hub = 1;
+ hub_pre_reset(udev);
+ }
+
+ /* ep0 maxpacket size may change; let the HCD know about it.
+ * Other endpoints will be handled by re-enumeration. */
+ usb_disable_endpoint(udev, 0);
+ usb_disable_endpoint(udev, 0 + USB_DIR_IN);
+
ret = hub_port_init(parent, udev, port);
if (ret < 0)
goto re_enumerate;
@@ -2757,7 +2786,7 @@ int __usb_reset_device(struct usb_device *udev)
}
if (!udev->actconfig)
- return 0;
+ goto done;
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
USB_REQ_SET_CONFIGURATION, 0,
@@ -2791,32 +2820,12 @@ int __usb_reset_device(struct usb_device *udev)
}
}
+done:
+ if (udev_is_a_hub)
+ hub_post_reset(udev);
return 0;
re_enumerate:
- hub_port_disable(parent, port);
-
- hub = usb_get_intfdata(parent->actconfig->interface[0]);
- set_bit(port, hub->change_bits);
-
- spin_lock_irq(&hub_event_lock);
- if (list_empty(&hub->event_list)) {
- list_add_tail(&hub->event_list, &hub_event_list);
- wake_up(&khubd_wait);
- }
- spin_unlock_irq(&hub_event_lock);
-
+ hub_port_logical_disconnect(parent, port);
return -ENODEV;
}
-EXPORT_SYMBOL(__usb_reset_device);
-
-int usb_reset_device(struct usb_device *udev)
-{
- int r;
-
- down(&udev->serialize);
- r = __usb_reset_device(udev);
- up(&udev->serialize);
-
- return r;
-}
diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c
index c9f57e334a4f..24452d66d576 100644
--- a/drivers/usb/core/inode.c
+++ b/drivers/usb/core/inode.c
@@ -4,7 +4,7 @@
* inode.c -- Inode/Dentry functions for the USB device file system.
*
* Copyright (C) 2000 Thomas Sailer (sailer@ife.ee.ethz.ch)
- * Copyright (C) 2001,2002 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001,2002,2004 Greg Kroah-Hartman (greg@kroah.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
@@ -40,17 +40,15 @@
#include <linux/smp_lock.h>
#include <linux/parser.h>
#include <asm/byteorder.h>
+#include "usb.h"
static struct super_operations usbfs_ops;
static struct file_operations default_file_operations;
static struct inode_operations usbfs_dir_inode_operations;
-static struct vfsmount *usbdevfs_mount;
static struct vfsmount *usbfs_mount;
-static int usbdevfs_mount_count; /* = 0 */
static int usbfs_mount_count; /* = 0 */
static int ignore_mount = 0;
-static struct dentry *devices_usbdevfs_dentry;
static struct dentry *devices_usbfs_dentry;
static int num_buses; /* = 0 */
@@ -240,9 +238,6 @@ static int remount(struct super_block *sb, int *flags, char *data)
if (usbfs_mount && usbfs_mount->mnt_sb)
update_sb(usbfs_mount->mnt_sb);
- if (usbdevfs_mount && usbdevfs_mount->mnt_sb)
- update_sb(usbdevfs_mount->mnt_sb);
-
return 0;
}
@@ -561,28 +556,12 @@ static void fs_remove_file (struct dentry *dentry)
/* --------------------------------------------------------------------- */
-
-
-/*
- * The usbdevfs name is now deprecated (as of 2.5.1).
- * It will be removed when the 2.7.x development cycle is started.
- * You have been warned :)
- */
-static struct file_system_type usbdevice_fs_type;
-
static struct super_block *usb_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
return get_sb_single(fs_type, flags, data, usbfs_fill_super);
}
-static struct file_system_type usbdevice_fs_type = {
- .owner = THIS_MODULE,
- .name = "usbdevfs",
- .get_sb = usb_get_sb,
- .kill_sb = kill_litter_super,
-};
-
static struct file_system_type usb_fs_type = {
.owner = THIS_MODULE,
.name = "usbfs",
@@ -603,16 +582,10 @@ static int create_special_files (void)
ignore_mount = 1;
/* create the devices special file */
- retval = simple_pin_fs("usbdevfs", &usbdevfs_mount, &usbdevfs_mount_count);
- if (retval) {
- err ("Unable to get usbdevfs mount");
- goto exit;
- }
-
retval = simple_pin_fs("usbfs", &usbfs_mount, &usbfs_mount_count);
if (retval) {
err ("Unable to get usbfs mount");
- goto error_clean_usbdevfs_mount;
+ goto exit;
}
ignore_mount = 0;
@@ -620,7 +593,7 @@ static int create_special_files (void)
parent = usbfs_mount->mnt_sb->s_root;
devices_usbfs_dentry = fs_create_file ("devices",
listmode | S_IFREG, parent,
- NULL, &usbdevfs_devices_fops,
+ NULL, &usbfs_devices_fops,
listuid, listgid);
if (devices_usbfs_dentry == NULL) {
err ("Unable to create devices usbfs file");
@@ -628,42 +601,19 @@ static int create_special_files (void)
goto error_clean_mounts;
}
- parent = usbdevfs_mount->mnt_sb->s_root;
- devices_usbdevfs_dentry = fs_create_file ("devices",
- listmode | S_IFREG, parent,
- NULL, &usbdevfs_devices_fops,
- listuid, listgid);
- if (devices_usbdevfs_dentry == NULL) {
- err ("Unable to create devices usbfs file");
- retval = -ENODEV;
- goto error_remove_file;
- }
-
goto exit;
-error_remove_file:
- fs_remove_file (devices_usbfs_dentry);
- devices_usbfs_dentry = NULL;
-
error_clean_mounts:
simple_release_fs(&usbfs_mount, &usbfs_mount_count);
-
-error_clean_usbdevfs_mount:
- simple_release_fs(&usbdevfs_mount, &usbdevfs_mount_count);
-
exit:
return retval;
}
static void remove_special_files (void)
{
- if (devices_usbdevfs_dentry)
- fs_remove_file (devices_usbdevfs_dentry);
if (devices_usbfs_dentry)
fs_remove_file (devices_usbfs_dentry);
- devices_usbdevfs_dentry = NULL;
devices_usbfs_dentry = NULL;
- simple_release_fs(&usbdevfs_mount, &usbdevfs_mount_count);
simple_release_fs(&usbfs_mount, &usbfs_mount_count);
}
@@ -671,11 +621,6 @@ void usbfs_update_special (void)
{
struct inode *inode;
- if (devices_usbdevfs_dentry) {
- inode = devices_usbdevfs_dentry->d_inode;
- if (inode)
- inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
- }
if (devices_usbfs_dentry) {
inode = devices_usbfs_dentry->d_inode;
if (inode)
@@ -707,29 +652,16 @@ void usbfs_add_bus(struct usb_bus *bus)
return;
}
- parent = usbdevfs_mount->mnt_sb->s_root;
- bus->usbdevfs_dentry = fs_create_file (name, busmode | S_IFDIR, parent,
- bus, NULL, busuid, busgid);
- if (bus->usbdevfs_dentry == NULL) {
- err ("error creating usbdevfs bus entry");
- return;
- }
-
usbfs_update_special();
- usbdevfs_conn_disc_event();
+ usbfs_conn_disc_event();
}
-
void usbfs_remove_bus(struct usb_bus *bus)
{
if (bus->usbfs_dentry) {
fs_remove_file (bus->usbfs_dentry);
bus->usbfs_dentry = NULL;
}
- if (bus->usbdevfs_dentry) {
- fs_remove_file (bus->usbdevfs_dentry);
- bus->usbdevfs_dentry = NULL;
- }
--num_buses;
if (num_buses <= 0) {
@@ -738,7 +670,7 @@ void usbfs_remove_bus(struct usb_bus *bus)
}
usbfs_update_special();
- usbdevfs_conn_disc_event();
+ usbfs_conn_disc_event();
}
void usbfs_add_device(struct usb_device *dev)
@@ -750,20 +682,12 @@ void usbfs_add_device(struct usb_device *dev)
sprintf (name, "%03d", dev->devnum);
dev->usbfs_dentry = fs_create_file (name, devmode | S_IFREG,
dev->bus->usbfs_dentry, dev,
- &usbdevfs_device_file_operations,
+ &usbfs_device_file_operations,
devuid, devgid);
if (dev->usbfs_dentry == NULL) {
err ("error creating usbfs device entry");
return;
}
- dev->usbdevfs_dentry = fs_create_file (name, devmode | S_IFREG,
- dev->bus->usbdevfs_dentry, dev,
- &usbdevfs_device_file_operations,
- devuid, devgid);
- if (dev->usbdevfs_dentry == NULL) {
- err ("error creating usbdevfs device entry");
- return;
- }
/* Set the size of the device's file to be
* equal to the size of the device descriptors. */
@@ -775,11 +699,9 @@ void usbfs_add_device(struct usb_device *dev)
}
if (dev->usbfs_dentry->d_inode)
dev->usbfs_dentry->d_inode->i_size = i_size;
- if (dev->usbdevfs_dentry->d_inode)
- dev->usbdevfs_dentry->d_inode->i_size = i_size;
usbfs_update_special();
- usbdevfs_conn_disc_event();
+ usbfs_conn_disc_event();
}
void usbfs_remove_device(struct usb_device *dev)
@@ -791,10 +713,6 @@ void usbfs_remove_device(struct usb_device *dev)
fs_remove_file (dev->usbfs_dentry);
dev->usbfs_dentry = NULL;
}
- if (dev->usbdevfs_dentry) {
- fs_remove_file (dev->usbdevfs_dentry);
- dev->usbdevfs_dentry = NULL;
- }
while (!list_empty(&dev->filelist)) {
ds = list_entry(dev->filelist.next, struct dev_state, list);
list_del_init(&ds->list);
@@ -807,51 +725,38 @@ void usbfs_remove_device(struct usb_device *dev)
}
}
usbfs_update_special();
- usbdevfs_conn_disc_event();
+ usbfs_conn_disc_event();
}
/* --------------------------------------------------------------------- */
-#ifdef CONFIG_PROC_FS
static struct proc_dir_entry *usbdir = NULL;
-#endif
int __init usbfs_init(void)
{
int retval;
- retval = usb_register(&usbdevfs_driver);
+ retval = usb_register(&usbfs_driver);
if (retval)
return retval;
retval = register_filesystem(&usb_fs_type);
if (retval) {
- usb_deregister(&usbdevfs_driver);
- return retval;
- }
- retval = register_filesystem(&usbdevice_fs_type);
- if (retval) {
- unregister_filesystem(&usb_fs_type);
- usb_deregister(&usbdevfs_driver);
+ usb_deregister(&usbfs_driver);
return retval;
}
-#ifdef CONFIG_PROC_FS
- /* create mount point for usbdevfs */
+ /* create mount point for usbfs */
usbdir = proc_mkdir("usb", proc_bus);
-#endif
return 0;
}
void usbfs_cleanup(void)
{
- usb_deregister(&usbdevfs_driver);
+ usb_deregister(&usbfs_driver);
unregister_filesystem(&usb_fs_type);
- unregister_filesystem(&usbdevice_fs_type);
-#ifdef CONFIG_PROC_FS
if (usbdir)
remove_proc_entry("usb", proc_bus);
-#endif
}
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 98695c68d687..f2cd4770eb4f 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -17,6 +17,8 @@
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/timer.h>
+#include <linux/ctype.h>
+#include <linux/device.h>
#include <asm/byteorder.h>
#include "hcd.h" /* for usbcore internals */
@@ -623,6 +625,20 @@ int usb_get_string(struct usb_device *dev, unsigned short langid,
return result;
}
+static void usb_try_string_workarounds(unsigned char *buf, int *length)
+{
+ int newlength, oldlength = *length;
+
+ for (newlength = 2; newlength + 1 < oldlength; newlength += 2)
+ if (!isprint(buf[newlength]) || buf[newlength + 1])
+ break;
+
+ if (newlength > 2) {
+ buf[0] = newlength;
+ *length = newlength;
+ }
+}
+
static int usb_string_sub(struct usb_device *dev, unsigned int langid,
unsigned int index, unsigned char *buf)
{
@@ -634,19 +650,26 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid,
/* If that failed try to read the descriptor length, then
* ask for just that many bytes */
- if (rc < 0) {
+ if (rc < 2) {
rc = usb_get_string(dev, langid, index, buf, 2);
if (rc == 2)
rc = usb_get_string(dev, langid, index, buf, buf[0]);
}
- if (rc >= 0) {
+ if (rc >= 2) {
+ if (!buf[0] && !buf[1])
+ usb_try_string_workarounds(buf, &rc);
+
/* There might be extra junk at the end of the descriptor */
if (buf[0] < rc)
rc = buf[0];
- if (rc < 2)
- rc = -EINVAL;
+
+ rc = rc - (rc & 1); /* force a multiple of two */
}
+
+ if (rc < 2)
+ rc = (rc < 0 ? rc : -EINVAL);
+
return rc;
}
@@ -724,6 +747,9 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
buf[idx] = 0;
err = idx;
+ if (tbuf[1] != USB_DT_STRING)
+ dev_dbg(&dev->dev, "wrong descriptor type %02x for string %d (\"%s\")\n", tbuf[1], index, buf);
+
errout:
kfree(tbuf);
return err;
@@ -1132,6 +1158,8 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
* use usb_set_interface() on the interfaces it claims. Resetting the whole
* configuration would affect other drivers' interfaces.
*
+ * The caller must own the device lock.
+ *
* Returns zero on success, else a negative error code.
*/
int usb_reset_configuration(struct usb_device *dev)
@@ -1142,9 +1170,9 @@ int usb_reset_configuration(struct usb_device *dev)
if (dev->state == USB_STATE_SUSPENDED)
return -EHOSTUNREACH;
- /* caller must own dev->serialize (config won't change)
- * and the usb bus readlock (so driver bindings are stable);
- * so calls during probe() are fine
+ /* caller must have locked the device and must own
+ * the usb bus readlock (so driver bindings are stable);
+ * calls during probe() are fine
*/
for (i = 1; i < 16; ++i) {
@@ -1199,7 +1227,7 @@ static void release_interface(struct device *dev)
* usb_set_configuration - Makes a particular device setting be current
* @dev: the device whose configuration is being updated
* @configuration: the configuration being chosen.
- * Context: !in_interrupt(), caller holds dev->serialize
+ * Context: !in_interrupt(), caller owns the device lock
*
* This is used to enable non-default device modes. Not all devices
* use this kind of configurability; many devices only have one
@@ -1220,8 +1248,8 @@ static void release_interface(struct device *dev)
* usb_set_interface().
*
* This call is synchronous. The calling context must be able to sleep,
- * and must not hold the driver model lock for USB; usb device driver
- * probe() methods may not use this routine.
+ * must own the device lock, and must not hold the driver model's USB
+ * bus rwsem; usb device driver probe() methods cannot use this routine.
*
* Returns zero on success, or else the status code returned by the
* underlying call that failed. On succesful completion, each interface
@@ -1236,8 +1264,6 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
struct usb_interface **new_interfaces = NULL;
int n, nintf;
- /* dev->serialize guards all config changes */
-
for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
if (dev->config[i].desc.bConfigurationValue == configuration) {
cp = &dev->config[i];
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 78c5ca2f1051..bae974d587ae 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -27,11 +27,13 @@
static ssize_t show_##field (struct device *dev, char *buf) \
{ \
struct usb_device *udev; \
+ struct usb_host_config *actconfig; \
\
udev = to_usb_device (dev); \
- if (udev->actconfig) \
+ actconfig = udev->actconfig; \
+ if (actconfig) \
return sprintf (buf, format_string, \
- udev->actconfig->desc.field * multiplier); \
+ actconfig->desc.field * multiplier); \
else \
return 0; \
} \
@@ -44,6 +46,28 @@ usb_actconfig_attr (bNumInterfaces, 1, "%2d\n")
usb_actconfig_attr (bmAttributes, 1, "%2x\n")
usb_actconfig_attr (bMaxPower, 2, "%3dmA\n")
+#define usb_actconfig_str(name, field) \
+static ssize_t show_##name(struct device *dev, char *buf) \
+{ \
+ struct usb_device *udev; \
+ struct usb_host_config *actconfig; \
+ int len; \
+ \
+ udev = to_usb_device (dev); \
+ actconfig = udev->actconfig; \
+ if (!actconfig) \
+ return 0; \
+ len = usb_string(udev, actconfig->desc.field, buf, PAGE_SIZE); \
+ if (len < 0) \
+ return 0; \
+ buf[len] = '\n'; \
+ buf[len+1] = 0; \
+ return len+1; \
+} \
+static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL);
+
+usb_actconfig_str (configuration, iConfiguration)
+
/* configuration value is always present, and r/w */
usb_actconfig_show(bConfigurationValue, 1, "%u\n");
@@ -55,9 +79,9 @@ set_bConfigurationValue (struct device *dev, const char *buf, size_t count)
if (sscanf (buf, "%u", &config) != 1 || config > 255)
return -EINVAL;
- down(&udev->serialize);
+ usb_lock_device(udev);
value = usb_set_configuration (udev, config);
- up(&udev->serialize);
+ usb_unlock_device(udev);
return (value < 0) ? value : count;
}
@@ -198,6 +222,7 @@ void usb_create_sysfs_dev_files (struct usb_device *udev)
device_create_file (dev, &dev_attr_product);
if (udev->descriptor.iSerialNumber)
device_create_file (dev, &dev_attr_serial);
+ device_create_file (dev, &dev_attr_configuration);
}
void usb_remove_sysfs_dev_files (struct usb_device *udev)
@@ -212,6 +237,7 @@ void usb_remove_sysfs_dev_files (struct usb_device *udev)
device_remove_file(dev, &dev_attr_product);
if (udev->descriptor.iSerialNumber)
device_remove_file(dev, &dev_attr_serial);
+ device_remove_file (dev, &dev_attr_configuration);
}
/* Interface fields */
@@ -231,7 +257,26 @@ usb_intf_attr (bNumEndpoints, "%02x\n")
usb_intf_attr (bInterfaceClass, "%02x\n")
usb_intf_attr (bInterfaceSubClass, "%02x\n")
usb_intf_attr (bInterfaceProtocol, "%02x\n")
-usb_intf_attr (iInterface, "%02x\n")
+
+#define usb_intf_str(name, field) \
+static ssize_t show_##name(struct device *dev, char *buf) \
+{ \
+ struct usb_interface *intf; \
+ struct usb_device *udev; \
+ int len; \
+ \
+ intf = to_usb_interface (dev); \
+ udev = interface_to_usbdev (intf); \
+ len = usb_string(udev, intf->cur_altsetting->desc.field, buf, PAGE_SIZE);\
+ if (len < 0) \
+ return 0; \
+ buf[len] = '\n'; \
+ buf[len+1] = 0; \
+ return len+1; \
+} \
+static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL);
+
+usb_intf_str (interface, iInterface);
static struct attribute *intf_attrs[] = {
&dev_attr_bInterfaceNumber.attr,
@@ -240,7 +285,6 @@ static struct attribute *intf_attrs[] = {
&dev_attr_bInterfaceClass.attr,
&dev_attr_bInterfaceSubClass.attr,
&dev_attr_bInterfaceProtocol.attr,
- &dev_attr_iInterface.attr,
NULL,
};
static struct attribute_group intf_attr_grp = {
@@ -250,9 +294,17 @@ static struct attribute_group intf_attr_grp = {
void usb_create_sysfs_intf_files (struct usb_interface *intf)
{
sysfs_create_group(&intf->dev.kobj, &intf_attr_grp);
+
+ if (intf->cur_altsetting->desc.iInterface)
+ device_create_file(&intf->dev, &dev_attr_interface);
+
}
void usb_remove_sysfs_intf_files (struct usb_interface *intf)
{
sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp);
+
+ if (intf->cur_altsetting->desc.iInterface)
+ device_remove_file(&intf->dev, &dev_attr_interface);
+
}
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index 3c14361bbeb3..f57de4dca2bf 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -451,6 +451,11 @@ int usb_unlink_urb(struct urb *urb)
if (!urb)
return -EINVAL;
if (!(urb->transfer_flags & URB_ASYNC_UNLINK)) {
+#ifdef CONFIG_DEBUG_KERNEL
+ printk(KERN_NOTICE "usb_unlink_urb() is deprecated for "
+ "synchronous unlinks. Use usb_kill_urb() instead.\n");
+ WARN_ON(1);
+#endif
usb_kill_urb(urb);
return 0;
}
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 138541225604..ca0b29d0ac1f 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -39,6 +39,7 @@
#include <linux/spinlock.h>
#include <linux/errno.h>
#include <linux/smp_lock.h>
+#include <linux/rwsem.h>
#include <linux/usb.h>
#include <asm/io.h>
@@ -62,6 +63,8 @@ const char *usbcore_name = "usbcore";
int nousb; /* Disable USB when built into kernel image */
/* Not honored on modular build */
+static DECLARE_RWSEM(usb_all_devices_rwsem);
+
static int generic_probe (struct device *dev)
{
@@ -100,7 +103,10 @@ int usb_probe_interface(struct device *dev)
id = usb_match_id (intf, driver->id_table);
if (id) {
dev_dbg (dev, "%s - got id\n", __FUNCTION__);
+ intf->condition = USB_INTERFACE_BINDING;
error = driver->probe (intf, id);
+ intf->condition = error ? USB_INTERFACE_UNBOUND :
+ USB_INTERFACE_BOUND;
}
return error;
@@ -112,6 +118,8 @@ int usb_unbind_interface(struct device *dev)
struct usb_interface *intf = to_usb_interface(dev);
struct usb_driver *driver = to_usb_driver(intf->dev.driver);
+ intf->condition = USB_INTERFACE_UNBINDING;
+
/* release all urbs for this interface */
usb_disable_interface(interface_to_usbdev(intf), intf);
@@ -123,6 +131,7 @@ int usb_unbind_interface(struct device *dev)
intf->altsetting[0].desc.bInterfaceNumber,
0);
usb_set_intfdata(intf, NULL);
+ intf->condition = USB_INTERFACE_UNBOUND;
return 0;
}
@@ -153,7 +162,9 @@ int usb_register(struct usb_driver *new_driver)
new_driver->driver.remove = usb_unbind_interface;
new_driver->driver.owner = new_driver->owner;
+ usb_lock_all_devices();
retval = driver_register(&new_driver->driver);
+ usb_unlock_all_devices();
if (!retval) {
pr_info("%s: registered new driver %s\n",
@@ -182,7 +193,9 @@ void usb_deregister(struct usb_driver *driver)
{
pr_info("%s: deregistering driver %s\n", usbcore_name, driver->name);
+ usb_lock_all_devices();
driver_unregister (&driver->driver);
+ usb_unlock_all_devices();
usbfs_update_special();
}
@@ -204,7 +217,7 @@ void usb_deregister(struct usb_driver *driver)
* alternate settings available for this interfaces.
*
* Don't call this function unless you are bound to one of the interfaces
- * on this device or you own the dev->serialize semaphore!
+ * on this device or you have locked the device!
*/
struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum)
{
@@ -237,7 +250,7 @@ struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum)
* drivers avoid such mistakes.
*
* Don't call this function unless you are bound to the intf interface
- * or you own the device's ->serialize semaphore!
+ * or you have locked the device!
*/
struct usb_host_interface *usb_altnum_to_altsetting(struct usb_interface *intf,
unsigned int altnum)
@@ -305,11 +318,12 @@ usb_epnum_to_ep_desc(struct usb_device *dev, unsigned epnum)
* way to bind to an interface is to return the private data from
* the driver's probe() method.
*
- * Callers must own the driver model's usb bus writelock. So driver
- * probe() entries don't need extra locking, but other call contexts
- * may need to explicitly claim that lock.
+ * Callers must own the device lock and the driver model's usb_bus_type.subsys
+ * writelock. So driver probe() entries don't need extra locking,
+ * but other call contexts may need to explicitly claim those locks.
*/
-int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv)
+int usb_driver_claim_interface(struct usb_driver *driver,
+ struct usb_interface *iface, void* priv)
{
struct device *dev = &iface->dev;
@@ -318,6 +332,7 @@ int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *
dev->driver = &driver->driver;
usb_set_intfdata(iface, priv);
+ iface->condition = USB_INTERFACE_BOUND;
/* if interface was already added, bind now; else let
* the future device_add() bind it, bypassing probe()
@@ -338,8 +353,8 @@ int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *
* also causes the driver disconnect() method to be called.
*
* This call is synchronous, and may not be used in an interrupt context.
- * Callers must own the usb_device serialize semaphore and the driver model's
- * usb bus writelock. So driver disconnect() entries don't need extra locking,
+ * Callers must own the device lock and the driver model's usb_bus_type.subsys
+ * writelock. So driver disconnect() entries don't need extra locking,
* but other call contexts may need to explicitly claim those locks.
*/
void usb_driver_release_interface(struct usb_driver *driver,
@@ -357,6 +372,7 @@ void usb_driver_release_interface(struct usb_driver *driver,
dev->driver = NULL;
usb_set_intfdata(iface, NULL);
+ iface->condition = USB_INTERFACE_UNBOUND;
}
/**
@@ -748,7 +764,10 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port)
init_MUTEX(&dev->serialize);
if (dev->bus->op->allocate)
- dev->bus->op->allocate(dev);
+ if (dev->bus->op->allocate(dev)) {
+ kfree(dev);
+ return NULL;
+ }
return dev;
}
@@ -819,6 +838,160 @@ void usb_put_intf(struct usb_interface *intf)
put_device(&intf->dev);
}
+
+/* USB device locking
+ *
+ * Although locking USB devices should be straightforward, it is
+ * complicated by the way the driver-model core works. When a new USB
+ * driver is registered or unregistered, the core will automatically
+ * probe or disconnect all matching interfaces on all USB devices while
+ * holding the USB subsystem writelock. There's no good way for us to
+ * tell which devices will be used or to lock them beforehand; our only
+ * option is to effectively lock all the USB devices.
+ *
+ * We do that by using a private rw-semaphore, usb_all_devices_rwsem.
+ * When locking an individual device you must first acquire the rwsem's
+ * readlock. When a driver is registered or unregistered the writelock
+ * must be held. These actions are encapsulated in the subroutines
+ * below, so all a driver needs to do is call usb_lock_device() and
+ * usb_unlock_device().
+ *
+ * Complications arise when several devices are to be locked at the same
+ * time. Only hub-aware drivers that are part of usbcore ever have to
+ * do this; nobody else needs to worry about it. The problem is that
+ * usb_lock_device() must not be called to lock a second device since it
+ * would acquire the rwsem's readlock reentrantly, leading to deadlock if
+ * another thread was waiting for the writelock. The solution is simple:
+ *
+ * When locking more than one device, call usb_lock_device()
+ * to lock the first one. Lock the others by calling
+ * down(&udev->serialize) directly.
+ *
+ * When unlocking multiple devices, use up(&udev->serialize)
+ * to unlock all but the last one. Unlock the last one by
+ * calling usb_unlock_device().
+ *
+ * When locking both a device and its parent, always lock the
+ * the parent first.
+ */
+
+/**
+ * usb_lock_device - acquire the lock for a usb device structure
+ * @udev: device that's being locked
+ *
+ * Use this routine when you don't hold any other device locks;
+ * to acquire nested inner locks call down(&udev->serialize) directly.
+ * This is necessary for proper interaction with usb_lock_all_devices().
+ */
+void usb_lock_device(struct usb_device *udev)
+{
+ down_read(&usb_all_devices_rwsem);
+ down(&udev->serialize);
+}
+
+/**
+ * usb_trylock_device - attempt to acquire the lock for a usb device structure
+ * @udev: device that's being locked
+ *
+ * Don't use this routine if you already hold a device lock;
+ * use down_trylock(&udev->serialize) instead.
+ * This is necessary for proper interaction with usb_lock_all_devices().
+ *
+ * Returns 1 if successful, 0 if contention.
+ */
+int usb_trylock_device(struct usb_device *udev)
+{
+ if (!down_read_trylock(&usb_all_devices_rwsem))
+ return 0;
+ if (down_trylock(&udev->serialize)) {
+ up_read(&usb_all_devices_rwsem);
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * usb_lock_device_for_reset - cautiously acquire the lock for a
+ * usb device structure
+ * @udev: device that's being locked
+ * @iface: interface bound to the driver making the request (optional)
+ *
+ * Attempts to acquire the device lock, but fails if the device is
+ * NOTATTACHED or SUSPENDED, or if iface is specified and the interface
+ * is neither BINDING nor BOUND. Rather than sleeping to wait for the
+ * lock, the routine polls repeatedly. This is to prevent deadlock with
+ * disconnect; in some drivers (such as usb-storage) the disconnect()
+ * callback will block waiting for a device reset to complete.
+ *
+ * Returns a negative error code for failure, otherwise 1 or 0 to indicate
+ * that the device will or will not have to be unlocked. (0 can be
+ * returned when an interface is given and is BINDING, because in that
+ * case the driver already owns the device lock.)
+ */
+int usb_lock_device_for_reset(struct usb_device *udev,
+ struct usb_interface *iface)
+{
+ if (udev->state == USB_STATE_NOTATTACHED)
+ return -ENODEV;
+ if (udev->state == USB_STATE_SUSPENDED)
+ return -EHOSTUNREACH;
+ if (iface) {
+ switch (iface->condition) {
+ case USB_INTERFACE_BINDING:
+ return 0;
+ case USB_INTERFACE_BOUND:
+ break;
+ default:
+ return -EINTR;
+ }
+ }
+
+ while (!usb_trylock_device(udev)) {
+ msleep(15);
+ if (udev->state == USB_STATE_NOTATTACHED)
+ return -ENODEV;
+ if (udev->state == USB_STATE_SUSPENDED)
+ return -EHOSTUNREACH;
+ if (iface && iface->condition != USB_INTERFACE_BOUND)
+ return -EINTR;
+ }
+ return 1;
+}
+
+/**
+ * usb_unlock_device - release the lock for a usb device structure
+ * @udev: device that's being unlocked
+ *
+ * Use this routine when releasing the only device lock you hold;
+ * to release inner nested locks call up(&udev->serialize) directly.
+ * This is necessary for proper interaction with usb_lock_all_devices().
+ */
+void usb_unlock_device(struct usb_device *udev)
+{
+ up(&udev->serialize);
+ up_read(&usb_all_devices_rwsem);
+}
+
+/**
+ * usb_lock_all_devices - acquire the lock for all usb device structures
+ *
+ * This is necessary when registering a new driver or probing a bus,
+ * since the driver-model core may try to use any usb_device.
+ */
+void usb_lock_all_devices(void)
+{
+ down_write(&usb_all_devices_rwsem);
+}
+
+/**
+ * usb_unlock_all_devices - release the lock for all usb device structures
+ */
+void usb_unlock_all_devices(void)
+{
+ up_write(&usb_all_devices_rwsem);
+}
+
+
static struct usb_device *match_device(struct usb_device *dev,
u16 vendor_id, u16 product_id)
{
@@ -840,8 +1013,10 @@ static struct usb_device *match_device(struct usb_device *dev,
/* look through all of the children of this device */
for (child = 0; child < dev->maxchild; ++child) {
if (dev->children[child]) {
+ down(&dev->children[child]->serialize);
ret_dev = match_device(dev->children[child],
vendor_id, product_id);
+ up(&dev->children[child]->serialize);
if (ret_dev)
goto exit;
}
@@ -876,7 +1051,9 @@ struct usb_device *usb_find_device(u16 vendor_id, u16 product_id)
bus = container_of(buslist, struct usb_bus, bus_list);
if (!bus->root_hub)
continue;
+ usb_lock_device(bus->root_hub);
dev = match_device(bus->root_hub, vendor_id, product_id);
+ usb_unlock_device(bus->root_hub);
if (dev)
goto exit;
}
@@ -1362,6 +1539,11 @@ EXPORT_SYMBOL(usb_put_dev);
EXPORT_SYMBOL(usb_get_dev);
EXPORT_SYMBOL(usb_hub_tt_clear_buffer);
+EXPORT_SYMBOL(usb_lock_device);
+EXPORT_SYMBOL(usb_trylock_device);
+EXPORT_SYMBOL(usb_lock_device_for_reset);
+EXPORT_SYMBOL(usb_unlock_device);
+
EXPORT_SYMBOL(usb_driver_claim_interface);
EXPORT_SYMBOL(usb_driver_release_interface);
EXPORT_SYMBOL(usb_match_id);
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index f1dff4f4d5d6..30d2cf7dd762 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -22,8 +22,14 @@ extern int usb_get_device_descriptor(struct usb_device *dev,
unsigned int size);
extern int usb_set_configuration(struct usb_device *dev, int configuration);
-extern void usb_set_device_state(struct usb_device *udev,
- enum usb_device_state new_state);
+extern void usb_lock_all_devices(void);
+extern void usb_unlock_all_devices(void);
/* for labeling diagnostics */
extern const char *usbcore_name;
+
+/* usbfs stuff */
+extern struct usb_driver usbfs_driver;
+extern struct file_operations usbfs_devices_fops;
+extern struct file_operations usbfs_device_file_operations;
+extern void usbfs_conn_disc_event(void);
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index decbca335389..c6e0693ed1d6 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -39,6 +39,17 @@ config USB_GADGET
If in doubt, say "N" and don't enable these drivers; most people
don't have this kind of hardware (except maybe inside Linux PDAs).
+config USB_GADGET_DEBUG_FILES
+ boolean "Debugging information files"
+ depends on USB_GADGET && PROC_FS
+ help
+ Some of the drivers in the "gadget" framework can expose
+ debugging information in files such as /proc/driver/udc
+ (for a peripheral controller). The information in these
+ files may help when you're troubleshooting or bringing up a
+ driver on a new board. Enable these files by choosing "Y"
+ here. If in doubt, or to conserve kernel memory, say "N".
+
#
# USB Peripheral Controller Support
#
@@ -206,10 +217,6 @@ config USB_OTG
Select this only if your OMAP board has a Mini-AB connector.
-config USB_OMAP_PROC
- boolean "/proc/driver/udc file"
- depends on USB_GADGET_OMAP
-
endchoice
config USB_GADGET_DUALSPEED
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
index 91e878642b9f..ed532f7176f9 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/dummy_hcd.c
@@ -770,7 +770,8 @@ usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
spin_lock_irqsave (&dum->lock, flags);
stop_activity (dum, driver);
- dum->port_status &= ~USB_PORT_STAT_CONNECTION;
+ dum->port_status &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE |
+ USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED);
dum->port_status |= (1 << USB_PORT_FEAT_C_CONNECTION);
spin_unlock_irqrestore (&dum->lock, flags);
@@ -815,8 +816,8 @@ static int dummy_urb_enqueue (
struct dummy *dum;
unsigned long flags;
- /* patch to usb_sg_init() is in 2.5.60 */
- BUG_ON (!urb->transfer_buffer && urb->transfer_buffer_length);
+ if (!urb->transfer_buffer && urb->transfer_buffer_length)
+ return -EINVAL;
dum = container_of (hcd, struct dummy, hcd);
spin_lock_irqsave (&dum->lock, flags);
@@ -1102,10 +1103,10 @@ restart:
ep = find_endpoint(dum, address);
if (!ep) {
/* set_configuration() disagreement */
- dev_err (hardware,
+ dev_dbg (hardware,
"no ep configured for urb %p\n",
urb);
- maybe_set_status (urb, -ETIMEDOUT);
+ maybe_set_status (urb, -EPROTO);
goto return_urb;
}
@@ -1409,9 +1410,12 @@ static int dummy_hub_control (
case ClearPortFeature:
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
- /* 20msec resume signaling */
- dum->resuming = 1;
- dum->re_timeout = jiffies + ((HZ * 20)/1000);
+ if (dum->port_status & (1 << USB_PORT_FEAT_SUSPEND)) {
+ /* 20msec resume signaling */
+ dum->resuming = 1;
+ dum->re_timeout = jiffies +
+ msecs_to_jiffies(20);
+ }
break;
case USB_PORT_FEAT_POWER:
dum->port_status = 0;
@@ -1440,7 +1444,7 @@ static int dummy_hub_control (
dum->port_status &= ~(1 << USB_PORT_FEAT_SUSPEND);
dum->resuming = 0;
dum->re_timeout = 0;
- if (dum->driver->resume) {
+ if (dum->driver && dum->driver->resume) {
spin_unlock (&dum->lock);
dum->driver->resume (&dum->gadget);
spin_lock (&dum->lock);
@@ -1481,11 +1485,15 @@ static int dummy_hub_control (
case SetPortFeature:
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
- dum->port_status |= (1 << USB_PORT_FEAT_SUSPEND);
- if (dum->driver->suspend) {
- spin_unlock (&dum->lock);
- dum->driver->suspend (&dum->gadget);
- spin_lock (&dum->lock);
+ if ((dum->port_status & (1 << USB_PORT_FEAT_SUSPEND))
+ == 0) {
+ dum->port_status |=
+ (1 << USB_PORT_FEAT_SUSPEND);
+ if (dum->driver && dum->driver->suspend) {
+ spin_unlock (&dum->lock);
+ dum->driver->suspend (&dum->gadget);
+ spin_lock (&dum->lock);
+ }
}
break;
case USB_PORT_FEAT_RESET:
@@ -1502,7 +1510,7 @@ static int dummy_hub_control (
/* FIXME test that code path! */
}
/* 50msec reset signaling */
- dum->re_timeout = jiffies + ((HZ * 50)/1000);
+ dum->re_timeout = jiffies + msecs_to_jiffies(50);
/* FALLTHROUGH */
default:
dum->port_status |= (1 << wValue);
@@ -1790,4 +1798,3 @@ static void __exit cleanup (void)
the_controller = 0;
}
module_exit (cleanup);
-
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index 763d0552146a..e3fec4a5a7f8 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -84,7 +84,7 @@
*/
#define DRIVER_DESC "Ethernet Gadget"
-#define DRIVER_VERSION "St Patrick's Day 2004"
+#define DRIVER_VERSION "Equinox 2004"
static const char shortname [] = "ether";
static const char driver_desc [] = DRIVER_DESC;
@@ -231,6 +231,10 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
#define DEV_CONFIG_CDC
#endif
+#ifdef CONFIG_USB_GADGET_N9604
+#define DEV_CONFIG_CDC
+#endif
+
/* For CDC-incapable hardware, choose the simple cdc subset.
* Anything that talks bulk (without notable bugs) can do this.
@@ -387,7 +391,7 @@ eth_config = {
.bConfigurationValue = DEV_CONFIG_VALUE,
.iConfiguration = STRING_CDC,
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
- .bMaxPower = 1,
+ .bMaxPower = 50,
};
#ifdef CONFIG_USB_ETH_RNDIS
@@ -401,7 +405,7 @@ rndis_config = {
.bConfigurationValue = DEV_RNDIS_CONFIG_VALUE,
.iConfiguration = STRING_RNDIS,
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
- .bMaxPower = 1,
+ .bMaxPower = 50,
};
#endif
@@ -1198,13 +1202,20 @@ eth_set_config (struct eth_dev *dev, unsigned number, int gfp_flags)
result = -EINVAL;
/* FALL THROUGH */
case 0:
- return result;
+ break;
}
- if (result)
- eth_reset_config (dev);
- else {
+ if (result) {
+ if (number)
+ eth_reset_config (dev);
+ usb_gadget_vbus_draw(dev->gadget,
+ dev->gadget->is_otg ? 8 : 100);
+ } else {
char *speed;
+ unsigned power;
+
+ power = 2 * eth_config.bMaxPower;
+ usb_gadget_vbus_draw(dev->gadget, power);
switch (gadget->speed) {
case USB_SPEED_FULL: speed = "full"; break;
@@ -1215,8 +1226,8 @@ eth_set_config (struct eth_dev *dev, unsigned number, int gfp_flags)
}
dev->config = number;
- INFO (dev, "%s speed config #%d: %s, using %s\n",
- speed, number, driver_desc,
+ INFO (dev, "%s speed config #%d: %d mA, %s, using %s\n",
+ speed, number, power, driver_desc,
dev->rndis
? "RNDIS"
: (dev->cdc
@@ -1375,8 +1386,9 @@ static void eth_setup_complete (struct usb_ep *ep, struct usb_request *req)
static void rndis_response_complete (struct usb_ep *ep, struct usb_request *req)
{
if (req->status || req->actual != req->length)
- DEBUG (dev, "rndis response complete --> %d, %d/%d\n",
- req->status, req->actual, req->length);
+ DEBUG ((struct eth_dev *) ep->driver_data,
+ "rndis response complete --> %d, %d/%d\n",
+ req->status, req->actual, req->length);
/* done sending after CDC_GET_ENCAPSULATED_RESPONSE */
}
@@ -2098,11 +2110,13 @@ static void rndis_send_media_state (struct eth_dev *dev, int connect)
}
}
-static void rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req)
+static void
+rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req)
{
if (req->status || req->actual != req->length)
- DEBUG (dev, "rndis control ack complete --> %d, %d/%d\n",
- req->status, req->actual, req->length);
+ DEBUG ((struct eth_dev *) ep->driver_data,
+ "rndis control ack complete --> %d, %d/%d\n",
+ req->status, req->actual, req->length);
usb_ep_free_buffer(ep, req->buf, req->dma, 8);
usb_ep_free_request(ep, req);
@@ -2334,6 +2348,8 @@ eth_bind (struct usb_gadget *gadget)
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0208);
} else if (gadget_is_lh7a40x(gadget)) {
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0209);
+ } else if (gadget_is_n9604(gadget)) {
+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x020a);
} else {
/* can't assume CDC works. don't want to default to
* anything less functional on CDC-capable hardware,
@@ -2466,8 +2482,10 @@ autoconf_fail:
if (gadget->is_otg) {
otg_descriptor.bmAttributes |= USB_OTG_HNP,
eth_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ eth_config.bMaxPower = 4;
#ifdef CONFIG_USB_ETH_RNDIS
rndis_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ rndis_config.bMaxPower = 4;
#endif
}
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
index 6e8008fa43cb..d8c8ab7750ec 100644
--- a/drivers/usb/gadget/file_storage.c
+++ b/drivers/usb/gadget/file_storage.c
@@ -217,6 +217,7 @@
#include <linux/compiler.h>
#include <linux/completion.h>
#include <linux/dcache.h>
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/fcntl.h>
#include <linux/file.h>
@@ -234,6 +235,7 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string.h>
+#include <linux/suspend.h>
#include <linux/uts.h>
#include <linux/version.h>
#include <linux/wait.h>
@@ -248,7 +250,7 @@
#define DRIVER_DESC "File-backed Storage Gadget"
#define DRIVER_NAME "g_file_storage"
-#define DRIVER_VERSION "28 July 2004"
+#define DRIVER_VERSION "31 August 2004"
static const char longname[] = DRIVER_DESC;
static const char shortname[] = DRIVER_NAME;
@@ -866,6 +868,14 @@ config_desc = {
.bMaxPower = 1, // self-powered
};
+static struct usb_otg_descriptor
+otg_desc = {
+ .bLength = sizeof(otg_desc),
+ .bDescriptorType = USB_DT_OTG,
+
+ .bmAttributes = USB_OTG_SRP,
+};
+
/* There is only one interface. */
static struct usb_interface_descriptor
@@ -914,12 +924,14 @@ fs_intr_in_desc = {
};
static const struct usb_descriptor_header *fs_function[] = {
+ (struct usb_descriptor_header *) &otg_desc,
(struct usb_descriptor_header *) &intf_desc,
(struct usb_descriptor_header *) &fs_bulk_in_desc,
(struct usb_descriptor_header *) &fs_bulk_out_desc,
(struct usb_descriptor_header *) &fs_intr_in_desc,
NULL,
};
+#define FS_FUNCTION_PRE_EP_ENTRIES 2
#ifdef CONFIG_USB_GADGET_DUALSPEED
@@ -976,12 +988,14 @@ hs_intr_in_desc = {
};
static const struct usb_descriptor_header *hs_function[] = {
+ (struct usb_descriptor_header *) &otg_desc,
(struct usb_descriptor_header *) &intf_desc,
(struct usb_descriptor_header *) &hs_bulk_in_desc,
(struct usb_descriptor_header *) &hs_bulk_out_desc,
(struct usb_descriptor_header *) &hs_intr_in_desc,
NULL,
};
+#define HS_FUNCTION_PRE_EP_ENTRIES 2
/* Maxpacket and other transfer characteristics vary by speed. */
#define ep_desc(g,fs,hs) (((g)->speed==USB_SPEED_HIGH) ? (hs) : (fs))
@@ -1018,9 +1032,10 @@ static struct usb_gadget_strings stringtab = {
* and with code managing interfaces and their altsettings. They must
* also handle different speeds and other-speed requests.
*/
-static int populate_config_buf(enum usb_device_speed speed,
+static int populate_config_buf(struct usb_gadget *gadget,
u8 *buf, u8 type, unsigned index)
{
+ enum usb_device_speed speed = gadget->speed;
int len;
const struct usb_descriptor_header **function;
@@ -1036,6 +1051,10 @@ static int populate_config_buf(enum usb_device_speed speed,
#endif
function = fs_function;
+ /* for now, don't advertise srp-only devices */
+ if (!gadget->is_otg)
+ function++;
+
len = usb_gadget_config_buf(&config_desc, buf, EP0_BUFSIZE, function);
((struct usb_config_descriptor *) buf)->bDescriptorType = type;
return len;
@@ -1366,7 +1385,7 @@ static int standard_setup_req(struct fsg_dev *fsg,
#ifdef CONFIG_USB_GADGET_DUALSPEED
get_config:
#endif
- value = populate_config_buf(fsg->gadget->speed,
+ value = populate_config_buf(fsg->gadget,
req->buf,
ctrl->wValue >> 8,
ctrl->wValue & 0xff);
@@ -1523,6 +1542,8 @@ static int sleep_thread(struct fsg_dev *fsg)
rc = wait_event_interruptible(fsg->thread_wqh,
fsg->thread_wakeup_needed);
fsg->thread_wakeup_needed = 0;
+ if (current->flags & PF_FREEZE)
+ refrigerator(PF_FREEZE);
return (rc ? -EINTR : 0);
}
@@ -2280,8 +2301,7 @@ static int halt_bulk_in_endpoint(struct fsg_dev *fsg)
}
/* Wait for a short time and then try again */
- set_current_state(TASK_INTERRUPTIBLE);
- if (schedule_timeout(HZ / 10) != 0)
+ if (msleep_interruptible(100) != 0)
return -EINTR;
rc = usb_ep_set_halt(fsg->bulk_in);
}
@@ -3713,8 +3733,10 @@ static int __init check_parameters(struct fsg_dev *fsg)
mod_data.release = __constant_cpu_to_le16(0x0307);
else if (gadget_is_omap(fsg->gadget))
mod_data.release = __constant_cpu_to_le16(0x0308);
- else if (gadget_is_lh7a40x(gadget))
+ else if (gadget_is_lh7a40x(fsg->gadget))
mod_data.release = __constant_cpu_to_le16 (0x0309);
+ else if (gadget_is_n9604(fsg->gadget))
+ mod_data.release = __constant_cpu_to_le16 (0x030a);
else {
WARN(fsg, "controller '%s' not recognized\n",
fsg->gadget->name);
@@ -3882,10 +3904,10 @@ static int __init fsg_bind(struct usb_gadget *gadget)
intf_desc.bNumEndpoints = i;
intf_desc.bInterfaceSubClass = mod_data.protocol_type;
intf_desc.bInterfaceProtocol = mod_data.transport_type;
- fs_function[i+1] = NULL;
+ fs_function[i + FS_FUNCTION_PRE_EP_ENTRIES] = NULL;
#ifdef CONFIG_USB_GADGET_DUALSPEED
- hs_function[i+1] = NULL;
+ hs_function[i + HS_FUNCTION_PRE_EP_ENTRIES] = NULL;
/* Assume ep0 uses the same maxpacket value for both speeds */
dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket;
@@ -3896,6 +3918,11 @@ static int __init fsg_bind(struct usb_gadget *gadget)
hs_intr_in_desc.bEndpointAddress = fs_intr_in_desc.bEndpointAddress;
#endif
+ if (gadget->is_otg) {
+ otg_desc.bmAttributes |= USB_OTG_HNP,
+ config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
rc = -ENOMEM;
/* Allocate the request and buffer for endpoint 0 */
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index e24e2f9ad28e..f6273701fec6 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -62,6 +62,12 @@
#define gadget_is_omap(g) 0
#endif
+#ifdef CONFIG_USB_GADGET_N9604
+#define gadget_is_n9604(g) !strcmp("n9604_udc", (g)->name)
+#else
+#define gadget_is_n9604(g) 0
+#endif
+
// CONFIG_USB_GADGET_AT91RM9200
// CONFIG_USB_GADGET_SX2
// CONFIG_USB_GADGET_AU1X00
diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c
index 61ce5b2eea41..f9cdb23da15b 100644
--- a/drivers/usb/gadget/goku_udc.c
+++ b/drivers/usb/gadget/goku_udc.c
@@ -1092,13 +1092,7 @@ static inline char *dmastr(void)
return "(dma IN)";
}
-/* if we're trying to save space, don't bother with this proc file */
-
-#if defined(CONFIG_PROC_FS) && !defined(CONFIG_EMBEDDED)
-# define UDC_PROC_FILE
-#endif
-
-#ifdef UDC_PROC_FILE
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
static const char proc_node_name [] = "driver/udc";
@@ -1312,7 +1306,7 @@ done:
return count - size;
}
-#endif /* UDC_PROC_FILE */
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
/*-------------------------------------------------------------------------*/
@@ -1815,7 +1809,7 @@ static void goku_remove(struct pci_dev *pdev)
usb_gadget_unregister_driver(dev->driver);
}
-#ifdef UDC_PROC_FILE
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
remove_proc_entry(proc_node_name, NULL);
#endif
if (dev->regs)
@@ -1933,7 +1927,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id)
pci_set_master(pdev);
-#ifdef UDC_PROC_FILE
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev);
#endif
diff --git a/drivers/usb/gadget/lh7a40x_udc.c b/drivers/usb/gadget/lh7a40x_udc.c
index 772627e97a00..0def9f70e889 100644
--- a/drivers/usb/gadget/lh7a40x_udc.c
+++ b/drivers/usb/gadget/lh7a40x_udc.c
@@ -54,7 +54,6 @@ static const char ep0name[] = "ep0-control";
/*
Local definintions.
*/
-#define UDC_PROC_FILE
#ifndef NO_STATES
static char *state_names[] = {
@@ -192,7 +191,7 @@ static __inline__ void usb_clear(u32 val, u32 port)
*/
#define is_usb_connected() get_portc_pdr(2)
-#ifdef UDC_PROC_FILE
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
static const char proc_node_name[] = "driver/udc";
@@ -248,12 +247,12 @@ udc_proc_read(char *page, char **start, off_t off, int count,
#define create_proc_files() create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev)
#define remove_proc_files() remove_proc_entry(proc_node_name, NULL)
-#else /* !UDC_PROC_FILE */
+#else /* !CONFIG_USB_GADGET_DEBUG_FILES */
#define create_proc_files() do {} while (0)
#define remove_proc_files() do {} while (0)
-#endif /* UDC_PROC_FILE */
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
/*
* udc_disable - disable USB device controller
diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c
index d28de0b8ceba..ed6711d54c32 100644
--- a/drivers/usb/gadget/net2280.c
+++ b/drivers/usb/gadget/net2280.c
@@ -76,7 +76,6 @@
#define EP_DONTUSE 13 /* nonzero */
#define USE_RDK_LEDS /* GPIO pins control three LEDs */
-#define USE_SYSFS_DEBUG_FILES
static const char driver_name [] = "net2280";
@@ -117,7 +116,7 @@ module_param (fifo_mode, ushort, 0644);
#define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out")
-#if defined(USE_SYSFS_DEBUG_FILES) || defined (DEBUG)
+#if defined(CONFIG_USB_GADGET_DEBUG_FILES) || defined (DEBUG)
static char *type_string (u8 bmAttributes)
{
switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) {
@@ -1450,7 +1449,12 @@ static const struct usb_gadget_ops net2280_ops = {
/*-------------------------------------------------------------------------*/
-#ifdef USE_SYSFS_DEBUG_FILES
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+/* FIXME move these into procfs, and use seq_file.
+ * Sysfs _still_ doesn't behave for arbitrarily sized files,
+ * and also doesn't help products using this with 2.4 kernels.
+ */
/* "function" sysfs attribute */
static ssize_t
diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c
index e40089d79365..c321c542f0df 100644
--- a/drivers/usb/gadget/omap_udc.c
+++ b/drivers/usb/gadget/omap_udc.c
@@ -1200,7 +1200,8 @@ static void pullup_enable(struct omap_udc *udc)
{
UDC_SYSCON1_REG |= UDC_PULLUP_EN;
#ifndef CONFIG_USB_OTG
- OTG_CTRL_REG |= OTG_BSESSVLD;
+ if (!cpu_is_omap15xx())
+ OTG_CTRL_REG |= OTG_BSESSVLD;
#endif
UDC_IRQ_EN_REG = UDC_DS_CHG_IE;
}
@@ -1208,7 +1209,8 @@ static void pullup_enable(struct omap_udc *udc)
static void pullup_disable(struct omap_udc *udc)
{
#ifndef CONFIG_USB_OTG
- OTG_CTRL_REG &= ~OTG_BSESSVLD;
+ if (!cpu_is_omap15xx())
+ OTG_CTRL_REG &= ~OTG_BSESSVLD;
#endif
UDC_IRQ_EN_REG = UDC_DS_CHG_IE;
UDC_SYSCON1_REG &= ~UDC_PULLUP_EN;
@@ -1688,7 +1690,7 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src)
}
change &= ~UDC_SUS;
}
- if (change & OTG_FLAGS) {
+ if (!cpu_is_omap15xx() && (change & OTG_FLAGS)) {
update_otg(udc);
change &= ~OTG_FLAGS;
}
@@ -1974,7 +1976,7 @@ EXPORT_SYMBOL(usb_gadget_unregister_driver);
/*-------------------------------------------------------------------------*/
-#ifdef CONFIG_USB_OMAP_PROC
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
#include <linux/seq_file.h>
@@ -2036,34 +2038,14 @@ static char *trx_mode(unsigned m)
}
}
-static int proc_udc_show(struct seq_file *s, void *_)
+static int proc_otg_show(struct seq_file *s)
{
u32 tmp;
- struct omap_ep *ep;
- unsigned long flags;
-
- spin_lock_irqsave(&udc->lock, flags);
- seq_printf(s, "%s, version: " DRIVER_VERSION
-#ifdef USE_ISO
- " (iso)"
-#endif
- "%s\n",
- driver_desc,
- use_dma ? " (dma)" : "");
-
- tmp = UDC_REV_REG & 0xff;
- seq_printf(s,
- "UDC rev %d.%d, OTG rev %d.%d, fifo mode %d, gadget %s\n"
- "hmc %d, transceiver %08x %s\n",
+ tmp = OTG_REV_REG;
+ seq_printf(s, "OTG rev %d.%d, transceiver_ctrl %08x\n",
tmp >> 4, tmp & 0xf,
- OTG_REV_REG >> 4, OTG_REV_REG & 0xf,
- fifo_mode,
- udc->driver ? udc->driver->driver.name : "(none)",
- HMC, USB_TRANSCEIVER_CTRL_REG,
- udc->transceiver ? udc->transceiver->label : "");
-
- /* OTG controller registers */
+ USB_TRANSCEIVER_CTRL_REG);
tmp = OTG_SYSCON_1_REG;
seq_printf(s, "otg_syscon1 %08x usb2 %s, usb1 %s, usb0 %s,"
FOURBITS "\n", tmp,
@@ -2117,6 +2099,37 @@ static int proc_udc_show(struct seq_file *s, void *_)
seq_printf(s, "otg_outctrl %04x" "\n", tmp);
tmp = OTG_TEST_REG;
seq_printf(s, "otg_test %04x" "\n", tmp);
+}
+
+static int proc_udc_show(struct seq_file *s, void *_)
+{
+ u32 tmp;
+ struct omap_ep *ep;
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ seq_printf(s, "%s, version: " DRIVER_VERSION
+#ifdef USE_ISO
+ " (iso)"
+#endif
+ "%s\n",
+ driver_desc,
+ use_dma ? " (dma)" : "");
+
+ tmp = UDC_REV_REG & 0xff;
+ seq_printf(s,
+ "UDC rev %d.%d, fifo mode %d, gadget %s\n"
+ "hmc %d, transceiver %s\n",
+ tmp >> 4, tmp & 0xf,
+ fifo_mode,
+ udc->driver ? udc->driver->driver.name : "(none)",
+ HMC,
+ udc->transceiver ? udc->transceiver->label : "");
+
+ /* OTG controller registers */
+ if (!cpu_is_omap15xx())
+ proc_otg_show(s);
tmp = UDC_SYSCON1_REG;
seq_printf(s, "\nsyscon1 %04x" EIGHTBITS "\n", tmp,
@@ -2496,41 +2509,51 @@ static int __init omap_udc_probe(struct device *dev)
return -EBUSY;
}
- INFO("OMAP UDC rev %d.%d, OTG rev %d.%d, %s receptacle\n",
+ INFO("OMAP UDC rev %d.%d, %s receptacle\n",
UDC_REV_REG >> 4, UDC_REV_REG & 0xf,
- OTG_REV_REG >> 4, OTG_REV_REG & 0xf,
config->otg ? "Mini-AB" : "B/Mini-B");
/* use the mode given to us by board init code */
- hmc = HMC;
- switch (hmc) {
- case 3:
- case 11:
- case 19:
- case 25:
- xceiv = otg_get_transceiver();
- if (!xceiv) {
- DBG("external transceiver not registered!\n");
- goto cleanup0;
- }
- type = xceiv->label;
- break;
- case 0: /* POWERUP DEFAULT == 0 */
- case 4:
- case 12:
- case 20:
- type = "INTEGRATED";
- break;
- case 21: /* internal loopback */
- type = "(loopback)";
- break;
- case 14: /* transceiverless */
- type = "(none)";
- break;
+ if (cpu_is_omap15xx()) {
+ hmc = HMC_1510;
+ type = "(unknown)";
- default:
- ERR("unrecognized UDC HMC mode %d\n", hmc);
- return -ENODEV;
+ /* FIXME may need a GPIO-0 handler to call
+ * usb_gadget_vbus_{dis,}connect() on us...
+ */
+ } else {
+ hmc = HMC_1610;
+ switch (hmc) {
+ case 3:
+ case 11:
+ case 19:
+ case 25:
+ xceiv = otg_get_transceiver();
+ if (!xceiv) {
+ DBG("external transceiver not registered!\n");
+ if (config->otg)
+ goto cleanup0;
+ type = "(unknown external)";
+ } else
+ type = xceiv->label;
+ break;
+ case 0: /* POWERUP DEFAULT == 0 */
+ case 4:
+ case 12:
+ case 20:
+ type = "INTEGRATED";
+ break;
+ case 21: /* internal loopback */
+ type = "(loopback)";
+ break;
+ case 14: /* transceiverless */
+ type = "(none)";
+ break;
+
+ default:
+ ERR("unrecognized UDC HMC mode %d\n", hmc);
+ return -ENODEV;
+ }
}
INFO("hmc mode %d, transceiver %s\n", hmc, type);
@@ -2671,13 +2694,6 @@ static struct device_driver udc_driver = {
static int __init udc_init(void)
{
- /* should work on many OMAP systems with at most minor changes,
- * but the 1510 doesn't have an OTG controller.
- */
- if (cpu_is_omap1510()) {
- DBG("no OMAP1510 support yet\n");
- return -ENODEV;
- }
INFO("%s, version: " DRIVER_VERSION "%s\n", driver_desc,
use_dma ? " (dma)" : "");
return driver_register(&udc_driver);
diff --git a/drivers/usb/gadget/omap_udc.h b/drivers/usb/gadget/omap_udc.h
index bd5420cd0b05..ca8572314f95 100644
--- a/drivers/usb/gadget/omap_udc.h
+++ b/drivers/usb/gadget/omap_udc.h
@@ -193,7 +193,14 @@ struct omap_udc {
/*-------------------------------------------------------------------------*/
-// #define HMC_1510 ((MOD_CONF_CTRL_0_REG >> 1) & 0x3f)
+#define MOD_CONF_CTRL_0_REG __REG32(MOD_CONF_CTRL_0)
+#define VBUS_W2FC_1510 (1 << 17) /* 0 gpio0, 1 dvdd2 pin */
+
+#define FUNC_MUX_CTRL_0_REG __REG32(FUNC_MUX_CTRL_0)
+#define VBUS_CTRL_1510 (1 << 19) /* 1 connected (software) */
+#define VBUS_MODE_1510 (1 << 18) /* 0 hardware, 1 software */
+
+#define HMC_1510 ((MOD_CONF_CTRL_0_REG >> 1) & 0x3f)
#define HMC_1610 (OTG_SYSCON_2_REG & 0x3f)
-#define HMC HMC_1610
+#define HMC (cpu_is_omap15xx() ? HMC_1510 : HMC_1610)
diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c
index c1139b51db46..710f7a435f9e 100644
--- a/drivers/usb/gadget/pxa2xx_udc.c
+++ b/drivers/usb/gadget/pxa2xx_udc.c
@@ -92,10 +92,6 @@ static const char ep0name [] = "ep0";
// #define USE_OUT_DMA
// #define DISABLE_TEST_MODE
-#ifdef CONFIG_PROC_FS
-#define UDC_PROC_FILE
-#endif
-
#ifdef CONFIG_ARCH_IXP4XX
#undef USE_DMA
@@ -109,12 +105,6 @@ static const char ep0name [] = "ep0";
#include "pxa2xx_udc.h"
-#ifdef CONFIG_EMBEDDED
-/* few strings, and little code to use them */
-#undef DEBUG
-#undef UDC_PROC_FILE
-#endif
-
#ifdef USE_DMA
static int use_dma = 1;
module_param(use_dma, bool, 0);
@@ -1212,7 +1202,7 @@ static const struct usb_gadget_ops pxa2xx_udc_ops = {
/*-------------------------------------------------------------------------*/
-#ifdef UDC_PROC_FILE
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
static const char proc_node_name [] = "driver/udc";
@@ -1368,11 +1358,12 @@ done:
#define remove_proc_files() \
remove_proc_entry(proc_node_name, NULL)
-#else /* !UDC_PROC_FILE */
+#else /* !CONFIG_USB_GADGET_DEBUG_FILES */
+
#define create_proc_files() do {} while (0)
#define remove_proc_files() do {} while (0)
-#endif /* UDC_PROC_FILE */
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
/* "function" sysfs attribute */
static ssize_t
diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c
index 1cd445a01d13..561ca545e4e5 100644
--- a/drivers/usb/gadget/rndis.c
+++ b/drivers/usb/gadget/rndis.c
@@ -70,8 +70,6 @@ MODULE_PARM_DESC (rndis_debug, "enable debugging");
#define RNDIS_MAX_CONFIGS 1
-static struct proc_dir_entry *rndis_connect_dir;
-static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS];
static rndis_params rndis_per_dev_params [RNDIS_MAX_CONFIGS];
@@ -1275,6 +1273,8 @@ int rndis_rm_hdr (u8 *buf, u32 *length)
return 0;
}
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
int rndis_proc_read (char *page, char **start, off_t off, int count, int *eof,
void *data)
{
@@ -1365,43 +1365,40 @@ int rndis_proc_write (struct file *file, const char __user *buffer,
return count;
}
+#define NAME_TEMPLATE "driver/rndis-%03d"
+
+static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS];
+
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
+
+
int __init rndis_init (void)
{
u8 i;
- char name [4];
- /* FIXME this should probably be /proc/driver/rndis,
- * and only if debugging is enabled
- */
-
- if (!(rndis_connect_dir = proc_mkdir ("rndis", NULL))) {
- printk (KERN_ERR "%s: couldn't create /proc/rndis entry",
- __FUNCTION__);
- return -EIO;
- }
-
for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
- sprintf (name, "%03d", i);
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+ char name [20];
+
+ sprintf (name, NAME_TEMPLATE, i);
if (!(rndis_connect_state [i]
- = create_proc_entry (name, 0660,
- rndis_connect_dir)))
+ = create_proc_entry (name, 0660, NULL)))
{
DEBUG ("%s :remove entries", __FUNCTION__);
- for (i--; i > 0; i--) {
- sprintf (name, "%03d", i);
- remove_proc_entry (name, rndis_connect_dir);
+ while (i) {
+ sprintf (name, NAME_TEMPLATE, --i);
+ remove_proc_entry (name, NULL);
}
DEBUG ("\n");
-
- remove_proc_entry ("000", rndis_connect_dir);
- remove_proc_entry ("rndis", NULL);
return -EIO;
}
+
rndis_connect_state [i]->nlink = 1;
rndis_connect_state [i]->write_proc = rndis_proc_write;
rndis_connect_state [i]->read_proc = rndis_proc_read;
rndis_connect_state [i]->data = (void *)
(rndis_per_dev_params + i);
+#endif
rndis_per_dev_params [i].confignr = i;
rndis_per_dev_params [i].used = 0;
rndis_per_dev_params [i].state = RNDIS_UNINITIALIZED;
@@ -1415,14 +1412,14 @@ int __init rndis_init (void)
void rndis_exit (void)
{
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
u8 i;
- char name [4];
+ char name [20];
for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
- sprintf (name, "%03d", i);
- remove_proc_entry (name, rndis_connect_dir);
+ sprintf (name, NAME_TEMPLATE, i);
+ remove_proc_entry (name, NULL);
}
- remove_proc_entry ("rndis", NULL);
- return;
+#endif
}
diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c
index a415a33ed117..69962c30a3a9 100644
--- a/drivers/usb/gadget/zero.c
+++ b/drivers/usb/gadget/zero.c
@@ -1188,6 +1188,8 @@ autoconf_fail:
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0208);
} else if (gadget_is_lh7a40x(gadget)) {
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0209);
+ } else if (gadget_is_n9604(gadget)) {
+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x020a);
} else {
/* gadget zero is so simple (for now, no altsettings) that
* it SHOULD NOT have problems with bulk-capable hardware.
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index e25adf501967..03d427a672a4 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -155,7 +155,7 @@ MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes");
* before driver shutdown. But it also seems to be caused by bugs in cardbus
* bridge shutdown: shutting down the bridge before the devices using it.
*/
-static int handshake (u32 __iomem *ptr, u32 mask, u32 done, int usec)
+static int handshake (void __iomem *ptr, u32 mask, u32 done, int usec)
{
u32 result;
@@ -341,8 +341,7 @@ static int ehci_hc_reset (struct usb_hcd *hcd)
spin_lock_init (&ehci->lock);
ehci->caps = hcd->regs;
- ehci->regs = (hcd->regs +
- HC_LENGTH (readl (&ehci->caps->hc_capbase)));
+ ehci->regs = hcd->regs + HC_LENGTH (readl (&ehci->caps->hc_capbase));
dbg_hcs_params (ehci, "reset");
dbg_hcc_params (ehci, "reset");
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 4ff7f868b157..86872c8877cb 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -81,7 +81,7 @@ static int ehci_hub_suspend (struct usb_hcd *hcd)
}
-/* caller owns root->serialize, and should reset/reinit on error */
+/* caller has locked the root hub, and should reset/reinit on error */
static int ehci_hub_resume (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
diff --git a/drivers/usb/host/hc_sl811.c b/drivers/usb/host/hc_sl811.c
index b57f1fe8258d..baf0e8086352 100644
--- a/drivers/usb/host/hc_sl811.c
+++ b/drivers/usb/host/hc_sl811.c
@@ -1343,15 +1343,11 @@ static int __init hci_hcd_init (void)
*****************************************************************/
static void __exit hci_hcd_cleanup (void)
{
- struct list_head *hci_l;
- hci_t *hci;
+ hci_t *hci, *tmp;
DBGFUNC ("Enter hci_hcd_cleanup\n");
- for (hci_l = hci_hcd_list.next; hci_l != &hci_hcd_list;) {
- hci = list_entry (hci_l, hci_t, hci_hcd_list);
- hci_l = hci_l->next;
+ list_for_each_entry_safe(hci, tmp, &hci_hcd_list, hci_hcd_list)
hc_release_hci (hci);
- }
}
module_init (hci_hcd_init);
diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c
index 947bf4a5ea03..ff1f80fd59c8 100644
--- a/drivers/usb/host/ohci-dbg.c
+++ b/drivers/usb/host/ohci-dbg.c
@@ -640,14 +640,14 @@ show_registers (struct class_device *class_dev, char *buf)
rdata = ohci_readl (&regs->fminterval);
temp = scnprintf (next, size,
"fmintvl 0x%08x %sFSMPS=0x%04x FI=0x%04x\n",
- rdata, (rdata >> 31) ? " FIT" : "",
+ rdata, (rdata >> 31) ? "FIT " : "",
(rdata >> 16) & 0xefff, rdata & 0xffff);
size -= temp;
next += temp;
rdata = ohci_readl (&regs->fmremaining);
temp = scnprintf (next, size, "fmremaining 0x%08x %sFR=0x%04x\n",
- rdata, (rdata >> 31) ? " FRT" : "",
+ rdata, (rdata >> 31) ? "FRT " : "",
rdata & 0x3fff);
size -= temp;
next += temp;
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 0609e12efeb2..09f37cce2c75 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -2,7 +2,7 @@
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
- * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ * (C) Copyright 2000-2004 David Brownell <dbrownell@users.sourceforge.net>
*
* [ Initialisation is based on Linus' ]
* [ uhci code and gregs ohci fragments ]
@@ -122,12 +122,27 @@
#define OHCI_INTR_INIT \
(OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_WDH)
+#ifdef __hppa__
+/* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */
+#define IR_DISABLE
+#endif
+
+#ifdef CONFIG_ARCH_OMAP
+/* OMAP doesn't support IR (no SMM; not needed) */
+#define IR_DISABLE
+#endif
+
/*-------------------------------------------------------------------------*/
static const char hcd_name [] = "ohci_hcd";
#include "ohci.h"
+static void ohci_dump (struct ohci_hcd *ohci, int verbose);
+static int ohci_init (struct ohci_hcd *ohci);
+static int ohci_restart (struct ohci_hcd *ohci);
+static void ohci_stop (struct usb_hcd *hcd);
+
#include "ohci-hub.c"
#include "ohci-dbg.c"
#include "ohci-mem.c"
@@ -387,30 +402,30 @@ static int ohci_get_frame (struct usb_hcd *hcd)
return OHCI_FRAME_NO(ohci->hcca);
}
+static void ohci_usb_reset (struct ohci_hcd *ohci)
+{
+ ohci->hc_control = ohci_readl (&ohci->regs->control);
+ ohci->hc_control &= OHCI_CTRL_RWC;
+ writel (ohci->hc_control, &ohci->regs->control);
+}
+
/*-------------------------------------------------------------------------*
* HC functions
*-------------------------------------------------------------------------*/
-/* reset the HC and BUS */
+/* init memory, and kick BIOS/SMM off */
-static int hc_reset (struct ohci_hcd *ohci)
+static int ohci_init (struct ohci_hcd *ohci)
{
u32 temp;
+ int ret;
- /* boot firmware should have set this up (5.1.1.3.1) */
- if (!ohci->fminterval) {
- temp = ohci_readl (&ohci->regs->fminterval);
- if (temp & 0x3fff0000)
- ohci->fminterval = temp;
- else
- ohci->fminterval = DEFAULT_FMINTERVAL;
- /* also: power/overcurrent flags in roothub.a */
- }
+ disable (ohci);
+ ohci->regs = ohci->hcd.regs;
+ ohci->next_statechange = jiffies;
- /* SMM owns the HC? not for long!
- * On PA-RISC, PDC can leave IR set incorrectly; ignore it there.
- */
-#ifndef __hppa__
+#ifndef IR_DISABLE
+ /* SMM owns the HC? not for long! */
if (ohci_readl (&ohci->regs->control) & OHCI_CTRL_IR) {
ohci_dbg (ohci, "USB HC TakeOver from BIOS/SMM\n");
@@ -426,27 +441,95 @@ static int hc_reset (struct ohci_hcd *ohci)
msleep (10);
if (--temp == 0) {
ohci_err (ohci, "USB HC TakeOver failed!\n");
- return -1;
+ return -EBUSY;
}
}
+ ohci_usb_reset (ohci);
}
#endif
/* Disable HC interrupts */
writel (OHCI_INTR_MIE, &ohci->regs->intrdisable);
+ // flush the writes
+ (void) ohci_readl (&ohci->regs->control);
+
+ if (ohci->hcca)
+ return 0;
+
+ ohci->hcca = dma_alloc_coherent (ohci->hcd.self.controller,
+ sizeof *ohci->hcca, &ohci->hcca_dma, 0);
+ if (!ohci->hcca)
+ return -ENOMEM;
+
+ if ((ret = ohci_mem_init (ohci)) < 0)
+ ohci_stop (&ohci->hcd);
- ohci_dbg (ohci, "reset, control = 0x%x\n",
- ohci_readl (&ohci->regs->control));
+ return ret;
- /* Reset USB (needed by some controllers); RemoteWakeupConnected
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Start an OHCI controller, set the BUS operational
+ * resets USB and controller
+ * enable interrupts
+ * connect the virtual root hub
+ */
+static int ohci_run (struct ohci_hcd *ohci)
+{
+ u32 mask, temp;
+ struct usb_device *udev;
+ struct usb_bus *bus;
+ int first = ohci->fminterval == 0;
+
+ disable (ohci);
+
+ /* boot firmware should have set this up (5.1.1.3.1) */
+ if (first) {
+
+ temp = ohci_readl (&ohci->regs->fminterval);
+ ohci->fminterval = temp & 0x3fff;
+ if (ohci->fminterval != FI)
+ ohci_dbg (ohci, "fminterval delta %d\n",
+ ohci->fminterval - FI);
+ ohci->fminterval |= FSMP (ohci->fminterval) << 16;
+ /* also: power/overcurrent flags in roothub.a */
+ }
+
+ /* Reset USB nearly "by the book". RemoteWakeupConnected
* saved if boot firmware (BIOS/SMM/...) told us it's connected
* (for OHCI integrated on mainboard, it normally is)
*/
ohci->hc_control = ohci_readl (&ohci->regs->control);
- ohci->hc_control &= OHCI_CTRL_RWC; /* hcfs 0 = RESET */
- if (ohci->hc_control)
+ ohci_dbg (ohci, "resetting from state '%s', control = 0x%x\n",
+ hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
+ ohci->hc_control);
+
+ if (ohci->hc_control & OHCI_CTRL_RWC
+ && !(ohci->flags & OHCI_QUIRK_AMD756))
ohci->hcd.can_wakeup = 1;
+
+ switch (ohci->hc_control & OHCI_CTRL_HCFS) {
+ case OHCI_USB_OPER:
+ temp = 0;
+ break;
+ case OHCI_USB_SUSPEND:
+ case OHCI_USB_RESUME:
+ ohci->hc_control &= OHCI_CTRL_RWC;
+ ohci->hc_control |= OHCI_USB_RESUME;
+ temp = 10 /* msec wait */;
+ break;
+ // case OHCI_USB_RESET:
+ default:
+ ohci->hc_control &= OHCI_CTRL_RWC;
+ ohci->hc_control |= OHCI_USB_RESET;
+ temp = 50 /* msec wait */;
+ break;
+ }
writel (ohci->hc_control, &ohci->regs->control);
+ // flush the writes
+ (void) ohci_readl (&ohci->regs->control);
+ msleep(temp);
if (power_switching) {
unsigned ports = roothub_a (ohci) & RH_A_NDP;
@@ -455,15 +538,20 @@ static int hc_reset (struct ohci_hcd *ohci)
writel (RH_PS_LSDA,
&ohci->regs->roothub.portstatus [temp]);
}
- // flush those pci writes
+ // flush those writes
(void) ohci_readl (&ohci->regs->control);
- msleep (50);
+ memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
+
+ /* 2msec timelimit here means no irqs/preempt */
+ spin_lock_irq (&ohci->lock);
+retry:
/* HC Reset requires max 10 us delay */
writel (OHCI_HCR, &ohci->regs->cmdstatus);
temp = 30; /* ... allow extra time */
while ((ohci_readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
if (--temp == 0) {
+ spin_unlock_irq (&ohci->lock);
ohci_err (ohci, "USB HC reset timed out!\n");
return -1;
}
@@ -476,27 +564,15 @@ static int hc_reset (struct ohci_hcd *ohci)
* ... but some hardware won't init fmInterval "by the book"
* (SiS, OPTi ...), so reset again instead. SiS doesn't need
* this if we write fmInterval after we're OPERATIONAL.
+ * Unclear about ALi, ServerWorks, and others ... this could
+ * easily be a longstanding bug in chip init on Linux.
*/
- writel (ohci->hc_control, &ohci->regs->control);
- // flush those pci writes
- (void) ohci_readl (&ohci->regs->control);
-
- return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* Start an OHCI controller, set the BUS operational
- * enable interrupts
- * connect the virtual root hub
- */
-static int hc_start (struct ohci_hcd *ohci)
-{
- u32 mask, tmp;
- struct usb_device *udev;
- struct usb_bus *bus;
-
- disable (ohci);
+ if (ohci->flags & OHCI_QUIRK_INITRESET) {
+ writel (ohci->hc_control, &ohci->regs->control);
+ // flush those writes
+ (void) ohci_readl (&ohci->regs->control);
+ }
+ writel (ohci->fminterval, &ohci->regs->fminterval);
/* Tell the controller where the control and bulk lists are
* The lists are empty now. */
@@ -513,7 +589,15 @@ static int hc_start (struct ohci_hcd *ohci)
*/
if ((ohci_readl (&ohci->regs->fminterval) & 0x3fff0000) == 0
|| !ohci_readl (&ohci->regs->periodicstart)) {
- ohci_err (ohci, "init err\n");
+ if (!(ohci->flags & OHCI_QUIRK_INITRESET)) {
+ ohci->flags |= OHCI_QUIRK_INITRESET;
+ ohci_dbg (ohci, "enabling initreset quirk\n");
+ goto retry;
+ }
+ spin_unlock_irq (&ohci->lock);
+ ohci_err (ohci, "init err (%08x %04x)\n",
+ ohci_readl (&ohci->regs->fminterval),
+ ohci_readl (&ohci->regs->periodicstart));
return -EOVERFLOW;
}
@@ -532,42 +616,48 @@ static int hc_start (struct ohci_hcd *ohci)
writel (mask, &ohci->regs->intrenable);
/* handle root hub init quirks ... */
- tmp = roothub_a (ohci);
- tmp &= ~(RH_A_PSM | RH_A_OCPM);
+ temp = roothub_a (ohci);
+ temp &= ~(RH_A_PSM | RH_A_OCPM);
if (ohci->flags & OHCI_QUIRK_SUPERIO) {
/* NSC 87560 and maybe others */
- tmp |= RH_A_NOCP;
- tmp &= ~(RH_A_POTPGT | RH_A_NPS);
+ temp |= RH_A_NOCP;
+ temp &= ~(RH_A_POTPGT | RH_A_NPS);
} else if (power_switching) {
/* act like most external hubs: use per-port power
* switching and overcurrent reporting.
*/
- tmp &= ~(RH_A_NPS | RH_A_NOCP);
- tmp |= RH_A_PSM | RH_A_OCPM;
+ temp &= ~(RH_A_NPS | RH_A_NOCP);
+ temp |= RH_A_PSM | RH_A_OCPM;
} else {
/* hub power always on; required for AMD-756 and some
* Mac platforms. ganged overcurrent reporting, if any.
*/
- tmp |= RH_A_NPS;
+ temp |= RH_A_NPS;
}
- writel (tmp, &ohci->regs->roothub.a);
+ writel (temp, &ohci->regs->roothub.a);
writel (RH_HS_LPSC, &ohci->regs->roothub.status);
writel (power_switching ? RH_B_PPCM : 0, &ohci->regs->roothub.b);
- // flush those pci writes
+ // flush those writes
(void) ohci_readl (&ohci->regs->control);
+ spin_unlock_irq (&ohci->lock);
+
// POTPGT delay is bits 24-31, in 2 ms units.
mdelay ((roothub_a (ohci) >> 23) & 0x1fe);
bus = hcd_to_bus (&ohci->hcd);
+ ohci->hcd.state = USB_STATE_RUNNING;
+
+ ohci_dump (ohci, 1);
- if (bus->root_hub) {
- ohci->hcd.state = USB_STATE_RUNNING;
+ udev = hcd_to_bus (&ohci->hcd)->root_hub;
+ if (udev) {
+ udev->dev.power.power_state = 0;
+ usb_set_device_state (udev, USB_STATE_CONFIGURED);
return 0;
}
/* connect the virtual root hub */
udev = usb_alloc_dev (NULL, bus, 0);
- ohci->hcd.state = USB_STATE_RUNNING;
if (!udev) {
disable (ohci);
ohci->hc_control &= ~OHCI_CTRL_HCFS;
@@ -583,7 +673,10 @@ static int hc_start (struct ohci_hcd *ohci)
writel (ohci->hc_control, &ohci->regs->control);
return -ENODEV;
}
+ if (ohci->power_budget)
+ hub_set_power_budget(udev, ohci->power_budget);
+ create_debug_files (ohci);
return 0;
}
@@ -620,7 +713,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
// e.g. due to PCI Master/Target Abort
ohci_dump (ohci, 1);
- hc_reset (ohci);
+ ohci_usb_reset (ohci);
}
if (ints & OHCI_INTR_RD) {
@@ -655,7 +748,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
if (HCD_IS_RUNNING(ohci->hcd.state)) {
writel (ints, &regs->intrstatus);
writel (OHCI_INTR_MIE, &regs->intrenable);
- // flush those pci writes
+ // flush those writes
(void) ohci_readl (&ohci->regs->control);
}
@@ -674,10 +767,9 @@ static void ohci_stop (struct usb_hcd *hcd)
ohci_dump (ohci, 1);
flush_scheduled_work();
- if (HCD_IS_RUNNING(ohci->hcd.state))
- hc_reset (ohci);
- else
- writel (OHCI_INTR_MIE, &ohci->regs->intrdisable);
+
+ ohci_usb_reset (ohci);
+ writel (OHCI_INTR_MIE, &ohci->regs->intrdisable);
remove_debug_files (ohci);
ohci_mem_cleanup (ohci);
@@ -696,19 +788,7 @@ static void ohci_stop (struct usb_hcd *hcd)
#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM)
-static void mark_children_gone (struct usb_device *dev)
-{
- unsigned i;
-
- for (i = 0; i < dev->maxchild; i++) {
- if (dev->children [i] == 0)
- continue;
- dev->children [i]->state = USB_STATE_NOTATTACHED;
- mark_children_gone (dev->children [i]);
- }
-}
-
-static int hc_restart (struct ohci_hcd *ohci)
+static int ohci_restart (struct ohci_hcd *ohci)
{
int temp;
int i;
@@ -721,7 +801,7 @@ static int hc_restart (struct ohci_hcd *ohci)
*/
spin_lock_irq(&ohci->lock);
disable (ohci);
- mark_children_gone (ohci->hcd.self.root_hub);
+ usb_set_device_state (ohci->hcd.self.root_hub, USB_STATE_NOTATTACHED);
if (!list_empty (&ohci->pending))
ohci_dbg(ohci, "abort schedule...\n");
list_for_each_entry (priv, &ohci->pending, pending) {
@@ -765,7 +845,7 @@ static int hc_restart (struct ohci_hcd *ohci)
ohci->ed_controltail = NULL;
ohci->ed_bulktail = NULL;
- if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) {
+ if ((temp = ohci_run (ohci)) < 0) {
ohci_err (ohci, "can't restart, %d\n", temp);
return temp;
} else {
@@ -777,10 +857,7 @@ static int hc_restart (struct ohci_hcd *ohci)
while (i--)
writel (RH_PS_PSS,
&ohci->regs->roothub.portstatus [temp]);
- ohci->hcd.self.root_hub->dev.power.power_state = 0;
- ohci->hcd.state = USB_STATE_RUNNING;
ohci_dbg (ohci, "restart complete\n");
- ohci_dump (ohci, 1);
}
return 0;
}
@@ -810,10 +887,25 @@ MODULE_LICENSE ("GPL");
#include "ohci-lh7a404.c"
#endif
+#ifdef CONFIG_PXA27x
+#include "ohci-pxa27x.c"
+#endif
+
#if !(defined(CONFIG_PCI) \
|| defined(CONFIG_SA1111) \
|| defined(CONFIG_ARCH_OMAP) \
|| defined (CONFIG_ARCH_LH7A404) \
+ || defined (CONFIG_PXA27x) \
)
#error "missing bus glue for ohci-hcd"
#endif
+
+#if !defined(HAVE_HNP) && defined(CONFIG_USB_OTG)
+
+#warning non-OTG configuration, too many HCDs
+
+static void start_hnp(struct ohci_hcd *ohci)
+{
+ /* "can't happen" */
+}
+#endif
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index 424971c4bc27..84b133304edf 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -2,7 +2,7 @@
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
- * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ * (C) Copyright 2000-2004 David Brownell <dbrownell@users.sourceforge.net>
*
* This file is licenced under GPL
*/
@@ -11,34 +11,8 @@
/*
* OHCI Root Hub ... the nonsharable stuff
- *
- * Registers don't need cpu_to_le32, that happens transparently
*/
-/* AMD-756 (D2 rev) reports corrupt register contents in some cases.
- * The erratum (#4) description is incorrect. AMD's workaround waits
- * till some bits (mostly reserved) are clear; ok for all revs.
- */
-#define read_roothub(hc, register, mask) ({ \
- u32 temp = ohci_readl (&hc->regs->roothub.register); \
- if (temp == -1) \
- disable (hc); \
- else if (hc->flags & OHCI_QUIRK_AMD756) \
- while (temp & mask) \
- temp = ohci_readl (&hc->regs->roothub.register); \
- temp; })
-
-static u32 roothub_a (struct ohci_hcd *hc)
- { return read_roothub (hc, a, 0xfc0fe000); }
-static inline u32 roothub_b (struct ohci_hcd *hc)
- { return ohci_readl (&hc->regs->roothub.b); }
-static inline u32 roothub_status (struct ohci_hcd *hc)
- { return ohci_readl (&hc->regs->roothub.status); }
-static u32 roothub_portstatus (struct ohci_hcd *hc, int i)
- { return read_roothub (hc, portstatus [i], 0xffe0fce0); }
-
-/*-------------------------------------------------------------------------*/
-
#define dbg_port(hc,label,num,value) \
ohci_dbg (hc, \
"%s roothub.portstatus [%d] " \
@@ -146,10 +120,11 @@ static int ohci_hub_suspend (struct usb_hcd *hcd)
ohci->next_statechange = jiffies + msecs_to_jiffies (5);
succeed:
- /* it's not USB_STATE_SUSPENDED unless access to this
+ /* it's not HCD_STATE_SUSPENDED unless access to this
* hub from the non-usb side (PCI, SOC, etc) stopped
*/
root->dev.power.power_state = 3;
+ usb_set_device_state (root, USB_STATE_SUSPENDED);
done:
spin_unlock_irq (&ohci->lock);
return status;
@@ -163,9 +138,7 @@ static inline struct ed *find_head (struct ed *ed)
return ed;
}
-static int hc_restart (struct ohci_hcd *ohci);
-
-/* caller owns root->serialize */
+/* caller has locked the root hub */
static int ohci_hub_resume (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
@@ -180,7 +153,12 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
spin_lock_irq (&ohci->lock);
ohci->hc_control = ohci_readl (&ohci->regs->control);
- switch (ohci->hc_control & OHCI_CTRL_HCFS) {
+ if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) {
+ /* this can happen after suspend-to-disk */
+ ohci_dbg (ohci, "BIOS/SMM active, control %03x\n",
+ ohci->hc_control);
+ status = -EBUSY;
+ } else switch (ohci->hc_control & OHCI_CTRL_HCFS) {
case OHCI_USB_SUSPEND:
ohci->hc_control &= ~(OHCI_CTRL_HCFS|OHCI_SCHED_ENABLES);
ohci->hc_control |= OHCI_USB_RESUME;
@@ -202,8 +180,10 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
status = -EBUSY;
}
spin_unlock_irq (&ohci->lock);
- if (status == -EBUSY)
- return hc_restart (ohci);
+ if (status == -EBUSY) {
+ (void) ohci_init (ohci);
+ return ohci_restart (ohci);
+ }
if (status != -EINPROGRESS)
return status;
@@ -260,6 +240,7 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
/* TRSMRCY */
msleep (10);
root->dev.power.power_state = 0;
+ usb_set_device_state (root, USB_STATE_CONFIGURED);
/* keep it alive for ~5x suspend + resume costs */
ohci->next_statechange = jiffies + msecs_to_jiffies (250);
@@ -289,7 +270,7 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
ohci->hc_control |= enables;
writel (ohci->hc_control, &ohci->regs->control);
if (temp)
- writel (status, &ohci->regs->cmdstatus);
+ writel (temp, &ohci->regs->cmdstatus);
(void) ohci_readl (&ohci->regs->control);
}
@@ -301,9 +282,9 @@ static void ohci_rh_resume (void *_hcd)
{
struct usb_hcd *hcd = _hcd;
- down (&hcd->self.root_hub->serialize);
+ usb_lock_device (hcd->self.root_hub);
(void) ohci_hub_resume (hcd);
- up (&hcd->self.root_hub->serialize);
+ usb_unlock_device (hcd->self.root_hub);
}
#else
@@ -381,12 +362,12 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
&& ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES)
& ohci->hc_control)
== OHCI_USB_OPER
- && down_trylock (&hcd->self.root_hub->serialize) == 0
+ && usb_trylock_device (hcd->self.root_hub)
) {
ohci_vdbg (ohci, "autosuspend\n");
(void) ohci_hub_suspend (&ohci->hcd);
ohci->hcd.state = USB_STATE_RUNNING;
- up (&hcd->self.root_hub->serialize);
+ usb_unlock_device (hcd->self.root_hub);
}
#endif
@@ -481,8 +462,8 @@ static void start_hnp(struct ohci_hcd *ohci);
/* this timer value might be vendor-specific ... */
#define PORT_RESET_HW_MSEC 10
-/* wrap-aware logic stolen from <linux/jiffies.h> */
-#define tick_before(t1,t2) ((((s16)(t1))-((s16)(t2))) < 0)
+/* wrap-aware logic morphed from <linux/jiffies.h> */
+#define tick_before(t1,t2) ((s16)(((s16)(t1))-((s16)(t2))) < 0)
/* called from some task, normally khubd */
static inline void root_port_reset (struct ohci_hcd *ohci, unsigned port)
diff --git a/drivers/usb/host/ohci-lh7a404.c b/drivers/usb/host/ohci-lh7a404.c
index 4e11a8eae481..1594d1e635da 100644
--- a/drivers/usb/host/ohci-lh7a404.c
+++ b/drivers/usb/host/ohci-lh7a404.c
@@ -229,38 +229,14 @@ ohci_lh7a404_start (struct usb_hcd *hcd)
int ret;
ohci_dbg (ohci, "ohci_lh7a404_start, ohci:%p", ohci);
-
- ohci->hcca = dma_alloc_coherent (hcd->self.controller,
- sizeof *ohci->hcca, &ohci->hcca_dma, 0);
- if (!ohci->hcca)
- return -ENOMEM;
-
- ohci_dbg (ohci, "ohci_lh7a404_start, ohci->hcca:%p",
- ohci->hcca);
-
- memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
-
- if ((ret = ohci_mem_init (ohci)) < 0) {
- ohci_stop (hcd);
+ if ((ret = ohci_init(ohci)) < 0)
return ret;
- }
- ohci->regs = hcd->regs;
-
- if (hc_reset (ohci) < 0) {
- ohci_stop (hcd);
- return -ENODEV;
- }
- if (hc_start (ohci) < 0) {
+ if ((ret = ohci_run (ohci)) < 0) {
err ("can't start %s", ohci->hcd.self.bus_name);
ohci_stop (hcd);
- return -EBUSY;
+ return ret;
}
- create_debug_files (ohci);
-
-#ifdef DEBUG
- ohci_dump (ohci, 1);
-#endif /*DEBUG*/
return 0;
}
diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c
index d133ff22a4a7..a8e641f595bb 100644
--- a/drivers/usb/host/ohci-omap.c
+++ b/drivers/usb/host/ohci-omap.c
@@ -428,40 +428,18 @@ ohci_omap_start (struct usb_hcd *hcd)
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
- config = hcd->self.controller->platform_data;
- ohci->hcca = dma_alloc_coherent (hcd->self.controller,
- sizeof *ohci->hcca, &ohci->hcca_dma, 0);
- if (!ohci->hcca)
- return -ENOMEM;
-
- memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
- if ((ret = ohci_mem_init (ohci)) < 0) {
- ohci_stop (hcd);
+ if ((ret = ohci_init(ohci)) < 0)
return ret;
- }
- ohci->regs = hcd->regs;
+ config = hcd->self.controller->platform_data;
if (config->otg || config->rwc)
writel(OHCI_CTRL_RWC, &ohci->regs->control);
- if (hc_reset (ohci) < 0) {
- ohci_stop (hcd);
- return -ENODEV;
- }
-
- if (hc_start (ohci) < 0) {
+ if ((ret = ohci_run (ohci)) < 0) {
err ("can't start %s", ohci->hcd.self.bus_name);
ohci_stop (hcd);
- return -EBUSY;
+ return ret;
}
- if (ohci->power_budget)
- hub_set_power_budget(ohci->hcd.self.root_hub,
- ohci->power_budget);
- create_debug_files (ohci);
-
-#ifdef DEBUG
- ohci_dump (ohci, 1);
-#endif
return 0;
}
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index 73d9aee657d5..2211a69e0b9d 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -35,9 +35,7 @@ ohci_pci_reset (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
- ohci->regs = hcd->regs;
- ohci->next_statechange = jiffies;
- return hc_reset (ohci);
+ return ohci_init (ohci);
}
static int __devinit
@@ -46,11 +44,6 @@ ohci_pci_start (struct usb_hcd *hcd)
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
- ohci->hcca = dma_alloc_coherent (hcd->self.controller,
- sizeof *ohci->hcca, &ohci->hcca_dma, 0);
- if (!ohci->hcca)
- return -ENOMEM;
-
if(hcd->self.controller && hcd->self.controller->bus == &pci_bus_type) {
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
@@ -61,6 +54,7 @@ ohci_pci_start (struct usb_hcd *hcd)
&& pdev->device == 0x740c) {
ohci->flags = OHCI_QUIRK_AMD756;
ohci_info (ohci, "AMD756 erratum 4 workaround\n");
+ // also somewhat erratum 10 (suspend/resume issues)
}
/* FIXME for some of the early AMD 760 southbridges, OHCI
@@ -92,25 +86,16 @@ ohci_pci_start (struct usb_hcd *hcd)
ohci_info (ohci, "Using NSC SuperIO setup\n");
}
}
-
- }
-
- memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
- if ((ret = ohci_mem_init (ohci)) < 0) {
- ohci_stop (hcd);
- return ret;
}
- if (hc_start (ohci) < 0) {
+ /* NOTE: there may have already been a first reset, to
+ * keep bios/smm irqs from making trouble
+ */
+ if ((ret = ohci_run (ohci)) < 0) {
ohci_err (ohci, "can't start\n");
ohci_stop (hcd);
- return -EBUSY;
+ return ret;
}
- create_debug_files (ohci);
-
-#ifdef DEBUG
- ohci_dump (ohci, 1);
-#endif
return 0;
}
@@ -127,9 +112,9 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, u32 state)
#ifdef CONFIG_USB_SUSPEND
(void) usb_suspend_device (hcd->self.root_hub, state);
#else
- down (&hcd->self.root_hub->serialize);
+ usb_lock_device (hcd->self.root_hub);
(void) ohci_hub_suspend (hcd);
- up (&hcd->self.root_hub->serialize);
+ usb_unlock_device (hcd->self.root_hub);
#endif
/* let things settle down a bit */
@@ -175,9 +160,9 @@ static int ohci_pci_resume (struct usb_hcd *hcd)
/* get extra cleanup even if remote wakeup isn't in use */
retval = usb_resume_device (hcd->self.root_hub);
#else
- down (&hcd->self.root_hub->serialize);
+ usb_lock_device (hcd->self.root_hub);
retval = ohci_hub_resume (hcd);
- up (&hcd->self.root_hub->serialize);
+ usb_unlock_device (hcd->self.root_hub);
#endif
if (retval == 0) {
diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c
new file mode 100644
index 000000000000..4bfe74123373
--- /dev/null
+++ b/drivers/usb/host/ohci-pxa27x.c
@@ -0,0 +1,460 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ * (C) Copyright 2002 Hewlett-Packard Company
+ *
+ * Bus Glue for pxa27x
+ *
+ * Written by Christopher Hoover <ch@hpl.hp.com>
+ * Based on fragments of previous driver by Russell King et al.
+ *
+ * Modified for LH7A404 from ohci-sa1111.c
+ * by Durgesh Pattamatta <pattamattad@sharpsec.com>
+ *
+ * Modified for pxa27x from ohci-lh7a404.c
+ * by Nick Bane <nick@cecomputing.co.uk> 26-8-2004
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <linux/device.h>
+#include <asm/mach-types.h>
+#include <asm/hardware.h>
+
+
+#define PMM_NPS_MODE 1
+#define PMM_GLOBAL_MODE 2
+#define PMM_PERPORT_MODE 3
+
+#define PXA_UHC_MAX_PORTNUM 3
+
+#define UHCRHPS(x) __REG2( 0x4C000050, (x)<<2 )
+
+static int pxa27x_ohci_pmm_state;
+
+/*
+ PMM_NPS_MODE -- PMM Non-power switching mode
+ Ports are powered continuously.
+
+ PMM_GLOBAL_MODE -- PMM global switching mode
+ All ports are powered at the same time.
+
+ PMM_PERPORT_MODE -- PMM per port switching mode
+ Ports are powered individually.
+ */
+static int pxa27x_ohci_select_pmm( int mode )
+{
+ pxa27x_ohci_pmm_state = mode;
+
+ switch ( mode ) {
+ case PMM_NPS_MODE:
+ UHCRHDA |= RH_A_NPS;
+ break;
+ case PMM_GLOBAL_MODE:
+ UHCRHDA &= ~(RH_A_NPS & RH_A_PSM);
+ break;
+ case PMM_PERPORT_MODE:
+ UHCRHDA &= ~(RH_A_NPS);
+ UHCRHDA |= RH_A_PSM;
+
+ /* Set port power control mask bits, only 3 ports. */
+ UHCRHDB |= (0x7<<17);
+ break;
+ default:
+ printk( KERN_ERR
+ "Invalid mode %d, set to non-power switch mode.\n",
+ mode );
+
+ pxa27x_ohci_pmm_state = PMM_NPS_MODE;
+ UHCRHDA |= RH_A_NPS;
+ }
+
+ return 0;
+}
+
+/*
+ If you select PMM_PERPORT_MODE, you should set the port power
+ */
+static int pxa27x_ohci_set_port_power( int port )
+{
+ if ( (pxa27x_ohci_pmm_state==PMM_PERPORT_MODE)
+ && (port>0) && (port<PXA_UHC_MAX_PORTNUM) ) {
+ UHCRHPS(port) |= 0x100;
+ return 0;
+ }
+ return -1;
+}
+
+/*
+ If you select PMM_PERPORT_MODE, you should set the port power
+ */
+static int pxa27x_ohci_clear_port_power( int port )
+{
+ if ( (pxa27x_ohci_pmm_state==PMM_PERPORT_MODE)
+ && (port>0) && (port<PXA_UHC_MAX_PORTNUM) ) {
+ UHCRHPS(port) |= 0x200;
+ return 0;
+ }
+
+ return -1;
+}
+
+extern int usb_disabled(void);
+
+/*-------------------------------------------------------------------------*/
+
+static void pxa27x_start_hc(struct platform_device *dev)
+{
+ pxa_set_cken(CKEN10_USBHOST, 1);
+
+ UHCHR |= UHCHR_FHR;
+ udelay(11);
+ UHCHR &= ~UHCHR_FHR;
+
+ UHCHR |= UHCHR_FSBIR;
+ while (UHCHR & UHCHR_FSBIR)
+ cpu_relax();
+
+ /* This could be properly abstracted away through the
+ device data the day more machines are supported and
+ their differences can be figured out correctly. */
+ if (machine_is_mainstone()) {
+ /* setup Port1 GPIO pin. */
+ pxa_gpio_mode( 88 | GPIO_ALT_FN_1_IN); /* USBHPWR1 */
+ pxa_gpio_mode( 89 | GPIO_ALT_FN_2_OUT); /* USBHPEN1 */
+
+ /* Set the Power Control Polarity Low and Power Sense
+ Polarity Low to active low. Supply power to USB ports. */
+ UHCHR = (UHCHR | UHCHR_PCPL | UHCHR_PSPL) &
+ ~(UHCHR_SSEP1 | UHCHR_SSEP2 | UHCHR_SSEP3 | UHCHR_SSE);
+ }
+
+ UHCHR &= ~UHCHR_SSE;
+
+ UHCHIE = (UHCHIE_UPRIE | UHCHIE_RWIE);
+}
+
+static void pxa27x_stop_hc(struct platform_device *dev)
+{
+ UHCHR |= UHCHR_FHR;
+ udelay(11);
+ UHCHR &= ~UHCHR_FHR;
+
+ UHCCOMS |= 1;
+ udelay(10);
+
+ pxa_set_cken(CKEN10_USBHOST, 0);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+void usb_hcd_pxa27x_remove (struct usb_hcd *, struct platform_device *);
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+
+/**
+ * usb_hcd_pxa27x_probe - initialize pxa27x-based HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ */
+int usb_hcd_pxa27x_probe (const struct hc_driver *driver,
+ struct usb_hcd **hcd_out,
+ struct platform_device *dev)
+{
+ int retval;
+ struct usb_hcd *hcd = 0;
+
+ unsigned int *addr = NULL;
+
+ if (!request_mem_region(dev->resource[0].start,
+ dev->resource[0].end
+ - dev->resource[0].start + 1, hcd_name)) {
+ pr_debug("request_mem_region failed");
+ return -EBUSY;
+ }
+
+ pxa27x_start_hc(dev);
+
+ /* Select Power Management Mode */
+ pxa27x_ohci_select_pmm( PMM_PERPORT_MODE );
+
+ /* If choosing PMM_PERPORT_MODE, we should set the port power before we use it. */
+ if (pxa27x_ohci_set_port_power(1) < 0)
+ printk(KERN_ERR "Setting port 1 power failed.\n");
+
+ if (pxa27x_ohci_clear_port_power(2) < 0)
+ printk(KERN_ERR "Setting port 2 power failed.\n");
+
+ if (pxa27x_ohci_clear_port_power(3) < 0)
+ printk(KERN_ERR "Setting port 3 power failed.\n");
+
+ addr = ioremap(dev->resource[0].start,
+ dev->resource[0].end - dev->resource[0].start + 1);
+ if (!addr) {
+ pr_debug("ioremap failed");
+ retval = -ENOMEM;
+ goto err1;
+ }
+
+ hcd = driver->hcd_alloc ();
+ if (hcd == NULL){
+ pr_debug ("hcd_alloc failed");
+ retval = -ENOMEM;
+ goto err1;
+ }
+
+ if(dev->resource[1].flags != IORESOURCE_IRQ){
+ pr_debug ("resource[1] is not IORESOURCE_IRQ");
+ retval = -ENOMEM;
+ goto err1;
+ }
+
+ hcd->driver = (struct hc_driver *) driver;
+ hcd->description = driver->description;
+ hcd->irq = dev->resource[1].start;
+ hcd->regs = addr;
+ hcd->self.controller = &dev->dev;
+
+ retval = hcd_buffer_create (hcd);
+ if (retval != 0) {
+ pr_debug ("pool alloc fail");
+ goto err1;
+ }
+
+ retval = request_irq (hcd->irq, usb_hcd_irq, SA_INTERRUPT,
+ hcd->description, hcd);
+ if (retval != 0) {
+ pr_debug("request_irq(%d) failed with retval %d\n",hcd->irq,retval);
+ retval = -EBUSY;
+ goto err2;
+ }
+
+ pr_debug ("%s (pxa27x) at 0x%p, irq %d",
+ hcd->description, hcd->regs, hcd->irq);
+
+ usb_bus_init (&hcd->self);
+ hcd->self.op = &usb_hcd_operations;
+ hcd->self.hcpriv = (void *) hcd;
+ hcd->self.bus_name = "pxa27x";
+ hcd->product_desc = "PXA27x OHCI";
+
+ INIT_LIST_HEAD (&hcd->dev_list);
+
+ usb_register_bus (&hcd->self);
+
+ if ((retval = driver->start (hcd)) < 0) {
+ usb_hcd_pxa27x_remove(hcd, dev);
+ return retval;
+ }
+
+ *hcd_out = hcd;
+ return 0;
+
+ err2:
+ hcd_buffer_destroy (hcd);
+ if (hcd)
+ driver->hcd_free(hcd);
+ err1:
+ pxa27x_stop_hc(dev);
+ release_mem_region(dev->resource[0].start,
+ dev->resource[0].end
+ - dev->resource[0].start + 1);
+ return retval;
+}
+
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_pxa27x_remove - shutdown processing for pxa27x-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_pxa27x_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ */
+void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *dev)
+{
+ void *base;
+
+ pr_debug ("remove: %s, state %x", hcd->self.bus_name, hcd->state);
+
+ if (in_interrupt ())
+ BUG ();
+
+ hcd->state = USB_STATE_QUIESCING;
+
+ pr_debug ("%s: roothub graceful disconnect", hcd->self.bus_name);
+ usb_disconnect (&hcd->self.root_hub);
+
+ hcd->driver->stop (hcd);
+ hcd->state = USB_STATE_HALT;
+
+ free_irq (hcd->irq, hcd);
+ hcd_buffer_destroy (hcd);
+
+ usb_deregister_bus (&hcd->self);
+
+ base = hcd->regs;
+ hcd->driver->hcd_free (hcd);
+
+ pxa27x_stop_hc(dev);
+ release_mem_region(dev->resource[0].start,
+ dev->resource[0].end - dev->resource[0].start + 1);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int __devinit
+ohci_pxa27x_start (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int ret;
+
+ ohci_dbg (ohci, "ohci_pxa27x_start, ohci:%p", ohci);
+
+ if ((ret = ohci_init(ohci)) < 0)
+ return ret;
+
+ if ((ret = ohci_run (ohci)) < 0) {
+ err ("can't start %s", ohci->hcd.self.bus_name);
+ ohci_stop (hcd);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const struct hc_driver ohci_pxa27x_hc_driver = {
+ .description = hcd_name,
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ohci_irq,
+ .flags = HCD_USB11,
+
+ /*
+ * basic lifecycle operations
+ */
+ .start = ohci_pxa27x_start,
+ .stop = ohci_stop,
+
+ /*
+ * memory lifecycle (except per-request)
+ */
+ .hcd_alloc = ohci_hcd_alloc,
+ .hcd_free = ohci_hcd_free,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ohci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
+#ifdef CONFIG_USB_SUSPEND
+ .hub_suspend = ohci_hub_suspend,
+ .hub_resume = ohci_hub_resume,
+#endif
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int ohci_hcd_pxa27x_drv_probe(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct usb_hcd *hcd = NULL;
+ int ret;
+
+ pr_debug ("In ohci_hcd_pxa27x_drv_probe");
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ ret = usb_hcd_pxa27x_probe(&ohci_pxa27x_hc_driver, &hcd, pdev);
+
+ if (ret == 0)
+ dev_set_drvdata(dev, hcd);
+
+ return ret;
+}
+
+static int ohci_hcd_pxa27x_drv_remove(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+ usb_hcd_pxa27x_remove(hcd, pdev);
+ dev_set_drvdata(dev, NULL);
+ return 0;
+}
+
+static int ohci_hcd_pxa27x_drv_suspend(struct device *dev, u32 state, u32 level)
+{
+// struct platform_device *pdev = to_platform_device(dev);
+// struct usb_hcd *hcd = dev_get_drvdata(dev);
+ printk("%s: not implemented yet\n", __FUNCTION__);
+
+ return 0;
+}
+
+static int ohci_hcd_pxa27x_drv_resume(struct device *dev, u32 state)
+{
+// struct platform_device *pdev = to_platform_device(dev);
+// struct usb_hcd *hcd = dev_get_drvdata(dev);
+ printk("%s: not implemented yet\n", __FUNCTION__);
+
+ return 0;
+}
+
+
+static struct device_driver ohci_hcd_pxa27x_driver = {
+ .name = "pxa27x-ohci",
+ .bus = &platform_bus_type,
+ .probe = ohci_hcd_pxa27x_drv_probe,
+ .remove = ohci_hcd_pxa27x_drv_remove,
+ .suspend = ohci_hcd_pxa27x_drv_suspend,
+ .resume = ohci_hcd_pxa27x_drv_resume,
+};
+
+static int __init ohci_hcd_pxa27x_init (void)
+{
+ pr_debug (DRIVER_INFO " (pxa27x)");
+ pr_debug ("block sizes: ed %d td %d\n",
+ sizeof (struct ed), sizeof (struct td));
+
+ return driver_register(&ohci_hcd_pxa27x_driver);
+}
+
+static void __exit ohci_hcd_pxa27x_cleanup (void)
+{
+ driver_unregister(&ohci_hcd_pxa27x_driver);
+}
+
+module_init (ohci_hcd_pxa27x_init);
+module_exit (ohci_hcd_pxa27x_cleanup);
diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c
index c3cd76aba163..35d71aceff63 100644
--- a/drivers/usb/host/ohci-sa1111.c
+++ b/drivers/usb/host/ohci-sa1111.c
@@ -272,33 +272,14 @@ ohci_sa1111_start (struct usb_hcd *hcd)
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
- ohci->hcca = dma_alloc_coherent (hcd->self.controller,
- sizeof *ohci->hcca, &ohci->hcca_dma, 0);
- if (!ohci->hcca)
- return -ENOMEM;
-
- memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
- if ((ret = ohci_mem_init (ohci)) < 0) {
- ohci_stop (hcd);
+ if ((ret = ohci_init(ohci)) < 0)
return ret;
- }
- ohci->regs = hcd->regs;
-
- if (hc_reset (ohci) < 0) {
- ohci_stop (hcd);
- return -ENODEV;
- }
- if (hc_start (ohci) < 0) {
+ if ((ret = ohci_run (ohci)) < 0) {
err ("can't start %s", ohci->hcd.self.bus_name);
ohci_stop (hcd);
- return -EBUSY;
+ return ret;
}
- create_debug_files (ohci);
-
-#ifdef DEBUG
- ohci_dump (ohci, 1);
-#endif
return 0;
}
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h
index 19436cdd7a11..b2756dee9507 100644
--- a/drivers/usb/host/ohci.h
+++ b/drivers/usb/host/ohci.h
@@ -42,7 +42,6 @@ struct ed {
/* create --> IDLE --> OPER --> ... --> IDLE --> destroy
* usually: OPER --> UNLINK --> (IDLE | OPER) --> ...
- * some special cases : OPER --> IDLE ...
*/
u8 state; /* ED_{IDLE,UNLINK,OPER} */
#define ED_IDLE 0x00 /* NOT linked to HC */
@@ -387,6 +386,7 @@ struct ohci_hcd {
unsigned long flags; /* for HC bugs */
#define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */
#define OHCI_QUIRK_SUPERIO 0x02 /* natsemi */
+#define OHCI_QUIRK_INITRESET 0x04 /* SiS, OPTi, ... */
// there are also chip quirks/bugs in init logic
/*
@@ -405,14 +405,14 @@ static inline void disable (struct ohci_hcd *ohci)
}
#define FI 0x2edf /* 12000 bits per frame (-1) */
-#define DEFAULT_FMINTERVAL ((((6 * (FI - 210)) / 7) << 16) | FI)
+#define FSMP(fi) (0x7fff & ((6 * ((fi) - 210)) / 7))
#define LSTHRESH 0x628 /* lowspeed bit threshold */
static inline void periodic_reinit (struct ohci_hcd *ohci)
{
- writel (ohci->fminterval, &ohci->regs->fminterval);
- writel (((9 * FI) / 10) & 0x3fff, &ohci->regs->periodicstart);
- writel (LSTHRESH, &ohci->regs->lsthresh);
+ u32 fi = ohci->fminterval & 0x0ffff;
+
+ writel (((9 * fi) / 10) & 0x3fff, &ohci->regs->periodicstart);
}
/*-------------------------------------------------------------------------*/
@@ -436,6 +436,8 @@ static inline void periodic_reinit (struct ohci_hcd *ohci)
# define ohci_vdbg(ohci, fmt, args...) do { } while (0)
#endif
+/*-------------------------------------------------------------------------*/
+
#ifdef CONFIG_ARCH_LH7A404
/* Marc Singer: at the time this code was written, the LH7A404
* had a problem reading the USB host registers. This
@@ -455,3 +457,25 @@ static inline unsigned int ohci_readl (void __iomem * regs)
return readl (regs);
}
#endif
+
+/* AMD-756 (D2 rev) reports corrupt register contents in some cases.
+ * The erratum (#4) description is incorrect. AMD's workaround waits
+ * till some bits (mostly reserved) are clear; ok for all revs.
+ */
+#define read_roothub(hc, register, mask) ({ \
+ u32 temp = ohci_readl (&hc->regs->roothub.register); \
+ if (temp == -1) \
+ disable (hc); \
+ else if (hc->flags & OHCI_QUIRK_AMD756) \
+ while (temp & mask) \
+ temp = ohci_readl (&hc->regs->roothub.register); \
+ temp; })
+
+static u32 roothub_a (struct ohci_hcd *hc)
+ { return read_roothub (hc, a, 0xfc0fe000); }
+static inline u32 roothub_b (struct ohci_hcd *hc)
+ { return ohci_readl (&hc->regs->roothub.b); }
+static inline u32 roothub_status (struct ohci_hcd *hc)
+ { return ohci_readl (&hc->regs->roothub.status); }
+static u32 roothub_portstatus (struct ohci_hcd *hc, int i)
+ { return read_roothub (hc, portstatus [i], 0xffe0fce0); }
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index 20a42f914684..99b706b64052 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -230,42 +230,22 @@ static void uhci_remove_td(struct uhci_hcd *uhci, struct uhci_td *td)
}
/*
- * Inserts a td into qh list at the top.
+ * Inserts a td list into qh.
*/
static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, __le32 breadth)
{
- struct list_head *tmp, *head;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- struct uhci_td *td, *ptd;
-
- if (list_empty(&urbp->td_list))
- return;
-
- head = &urbp->td_list;
- tmp = head->next;
+ struct uhci_td *td;
+ u32 *plink;
/* Ordering isn't important here yet since the QH hasn't been */
- /* inserted into the schedule yet */
- td = list_entry(tmp, struct uhci_td, list);
-
- /* Add the first TD to the QH element pointer */
- qh->element = cpu_to_le32(td->dma_handle) | breadth;
-
- ptd = td;
-
- /* Then link the rest of the TD's */
- tmp = tmp->next;
- while (tmp != head) {
- td = list_entry(tmp, struct uhci_td, list);
-
- tmp = tmp->next;
-
- ptd->link = cpu_to_le32(td->dma_handle) | breadth;
-
- ptd = td;
+ /* inserted into the schedule yet */
+ plink = &qh->element;
+ list_for_each_entry(td, &urbp->td_list, list) {
+ *plink = cpu_to_le32(td->dma_handle) | breadth;
+ plink = &td->link;
}
-
- ptd->link = UHCI_PTR_TERM;
+ *plink = UHCI_PTR_TERM;
}
static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td)
@@ -330,7 +310,7 @@ static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
static void uhci_insert_qh(struct uhci_hcd *uhci, struct uhci_qh *skelqh, struct urb *urb)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- struct list_head *tmp;
+ struct urb_priv *turbp;
struct uhci_qh *lqh;
/* Grab the last QH */
@@ -358,12 +338,8 @@ static void uhci_insert_qh(struct uhci_hcd *uhci, struct uhci_qh *skelqh, struct
*/
lqh->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH;
if (lqh->urbp) {
- list_for_each (tmp, &lqh->urbp->queue_list) {
- struct urb_priv *turbp =
- list_entry(tmp, struct urb_priv, queue_list);
-
+ list_for_each_entry(turbp, &lqh->urbp->queue_list, queue_list)
turbp->qh->link = lqh->link;
- }
}
list_add_tail(&urbp->qh->list, &skelqh->list);
@@ -405,18 +381,11 @@ static void uhci_remove_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
pqh = list_entry(qh->list.prev, struct uhci_qh, list);
pqh->link = newlink;
if (pqh->urbp) {
- struct list_head *head, *tmp;
-
- head = &pqh->urbp->queue_list;
- tmp = head->next;
- while (head != tmp) {
- struct urb_priv *turbp =
- list_entry(tmp, struct urb_priv, queue_list);
-
- tmp = tmp->next;
+ struct urb_priv *turbp;
+ list_for_each_entry(turbp, &pqh->urbp->queue_list,
+ queue_list)
turbp->qh->link = newlink;
- }
}
wmb();
@@ -447,21 +416,14 @@ static void uhci_remove_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- struct list_head *head, *tmp;
-
- head = &urbp->td_list;
- tmp = head->next;
- while (head != tmp) {
- struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
-
- tmp = tmp->next;
+ struct uhci_td *td;
+ list_for_each_entry(td, &urbp->td_list, list) {
if (toggle)
td->token |= cpu_to_le32(TD_TOKEN_TOGGLE);
else
td->token &= ~cpu_to_le32(TD_TOKEN_TOGGLE);
-
toggle ^= 1;
}
@@ -473,30 +435,19 @@ static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle)
static void uhci_append_queued_urb(struct uhci_hcd *uhci, struct urb *eurb, struct urb *urb)
{
struct urb_priv *eurbp, *urbp, *furbp, *lurbp;
- struct list_head *tmp;
struct uhci_td *lltd;
eurbp = eurb->hcpriv;
urbp = urb->hcpriv;
/* Find the first URB in the queue */
+ furbp = eurbp;
if (eurbp->queued) {
- struct list_head *head = &eurbp->queue_list;
-
- tmp = head->next;
- while (tmp != head) {
- struct urb_priv *turbp =
- list_entry(tmp, struct urb_priv, queue_list);
-
- if (!turbp->queued)
+ list_for_each_entry(furbp, &eurbp->queue_list, queue_list)
+ if (!furbp->queued)
break;
+ }
- tmp = tmp->next;
- }
- } else
- tmp = &eurbp->queue_list;
-
- furbp = list_entry(tmp, struct urb_priv, queue_list);
lurbp = list_entry(furbp->queue_list.prev, struct urb_priv, queue_list);
lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list);
@@ -522,9 +473,7 @@ static void uhci_append_queued_urb(struct uhci_hcd *uhci, struct urb *eurb, stru
static void uhci_delete_queued_urb(struct uhci_hcd *uhci, struct urb *urb)
{
- struct urb_priv *urbp, *nurbp;
- struct list_head *head, *tmp;
- struct urb_priv *purbp;
+ struct urb_priv *urbp, *nurbp, *purbp, *turbp;
struct uhci_td *pltd;
unsigned int toggle;
@@ -556,14 +505,7 @@ static void uhci_delete_queued_urb(struct uhci_hcd *uhci, struct urb *urb)
toggle = uhci_toggle(td_token(pltd)) ^ 1;
}
- head = &urbp->queue_list;
- tmp = head->next;
- while (head != tmp) {
- struct urb_priv *turbp;
-
- turbp = list_entry(tmp, struct urb_priv, queue_list);
- tmp = tmp->next;
-
+ list_for_each_entry(turbp, &urbp->queue_list, queue_list) {
if (!turbp->queued)
break;
toggle = uhci_fixup_toggle(turbp->urb, toggle);
@@ -637,7 +579,7 @@ static void uhci_remove_td_from_urb(struct uhci_td *td)
static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
{
- struct list_head *head, *tmp;
+ struct uhci_td *td, *tmp;
struct urb_priv *urbp;
unsigned int age;
@@ -660,13 +602,7 @@ static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
if (list_empty(&uhci->td_remove_list))
uhci_set_next_interrupt(uhci);
- head = &urbp->td_list;
- tmp = head->next;
- while (tmp != head) {
- struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
-
- tmp = tmp->next;
-
+ list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
uhci_remove_td_from_urb(td);
uhci_remove_td(uhci, td);
list_add(&td->remove_list, &uhci->td_remove_list);
@@ -1083,7 +1019,6 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb
*/
static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
{
- struct list_head *tmp, *head;
struct urb_priv *urbp = urb->hcpriv;
struct uhci_td *td;
unsigned int status = 0;
@@ -1091,13 +1026,7 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
urb->actual_length = 0;
- head = &urbp->td_list;
- tmp = head->next;
- while (tmp != head) {
- td = list_entry(tmp, struct uhci_td, list);
-
- tmp = tmp->next;
-
+ list_for_each_entry(td, &urbp->td_list, list) {
status = uhci_status_bits(td_status(td));
if (status & TD_CTRL_ACTIVE)
return -EINPROGRESS;
@@ -1176,17 +1105,12 @@ static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb,
static int isochronous_find_limits(struct uhci_hcd *uhci, struct urb *urb, unsigned int *start, unsigned int *end)
{
struct urb *last_urb = NULL;
- struct list_head *tmp, *head;
+ struct urb_priv *up;
int ret = 0;
- head = &uhci->urb_list;
- tmp = head->next;
- while (tmp != head) {
- struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list);
+ list_for_each_entry(up, &uhci->urb_list, urb_list) {
struct urb *u = up->urb;
- tmp = tmp->next;
-
/* look for pending URB's with identical pipe handle */
if ((urb->pipe == u->pipe) && (urb->dev == u->dev) &&
(u->status == -EINPROGRESS) && (u != urb)) {
@@ -1272,7 +1196,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb)
static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
{
- struct list_head *tmp, *head;
+ struct uhci_td *td;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
int status;
int i, ret = 0;
@@ -1280,14 +1204,9 @@ static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
urb->actual_length = 0;
i = 0;
- head = &urbp->td_list;
- tmp = head->next;
- while (tmp != head) {
- struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
+ list_for_each_entry(td, &urbp->td_list, list) {
int actlength;
- tmp = tmp->next;
-
if (td_status(td) & TD_CTRL_ACTIVE)
return -EINPROGRESS;
@@ -1311,20 +1230,15 @@ static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
static struct urb *uhci_find_urb_ep(struct uhci_hcd *uhci, struct urb *urb)
{
- struct list_head *tmp, *head;
+ struct urb_priv *up;
/* We don't match Isoc transfers since they are special */
if (usb_pipeisoc(urb->pipe))
return NULL;
- head = &uhci->urb_list;
- tmp = head->next;
- while (tmp != head) {
- struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list);
+ list_for_each_entry(up, &uhci->urb_list, urb_list) {
struct urb *u = up->urb;
- tmp = tmp->next;
-
if (u->dev == urb->dev && u->status == -EINPROGRESS) {
/* For control, ignore the direction */
if (usb_pipecontrol(urb->pipe) &&
@@ -1475,9 +1389,10 @@ out:
static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb)
{
- struct list_head *head, *tmp;
+ struct list_head *head;
+ struct uhci_td *td;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- int prevactive = 1;
+ int prevactive = 0;
uhci_dec_fsbr(uhci, urb); /* Safe since it checks */
@@ -1485,25 +1400,28 @@ static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb)
* Now we need to find out what the last successful toggle was
* so we can update the local data toggle for the next transfer
*
- * There's 3 way's the last successful completed TD is found:
+ * There are 2 ways the last successful completed TD is found:
*
* 1) The TD is NOT active and the actual length < expected length
* 2) The TD is NOT active and it's the last TD in the chain
+ *
+ * and a third way the first uncompleted TD is found:
+ *
* 3) The TD is active and the previous TD is NOT active
*
* Control and Isochronous ignore the toggle, so this is safe
* for all types
+ *
+ * FIXME: The toggle fixups won't be 100% reliable until we
+ * change over to using a single queue for each endpoint and
+ * stop the queue before unlinking.
*/
head = &urbp->td_list;
- tmp = head->next;
- while (tmp != head) {
- struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
-
- tmp = tmp->next;
-
+ list_for_each_entry(td, head, list) {
if (!(td_status(td) & TD_CTRL_ACTIVE) &&
- (uhci_actual_length(td_status(td)) < uhci_expected_length(td_token(td)) ||
- tmp == head))
+ (uhci_actual_length(td_status(td)) <
+ uhci_expected_length(td_token(td)) ||
+ td->list.next == head))
usb_settoggle(urb->dev, uhci_endpoint(td_token(td)),
uhci_packetout(td_token(td)),
uhci_toggle(td_token(td)) ^ 1);
@@ -1556,7 +1474,8 @@ done:
static int uhci_fsbr_timeout(struct uhci_hcd *uhci, struct urb *urb)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- struct list_head *head, *tmp;
+ struct list_head *head;
+ struct uhci_td *td;
int count = 0;
uhci_dec_fsbr(uhci, urb);
@@ -1570,18 +1489,14 @@ static int uhci_fsbr_timeout(struct uhci_hcd *uhci, struct urb *urb)
*/
head = &urbp->td_list;
- tmp = head->next;
- while (tmp != head) {
- struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
-
- tmp = tmp->next;
-
+ list_for_each_entry(td, head, list) {
/*
* Make sure we don't do the last one (since it'll have the
* TERM bit set) as well as we skip every so many TD's to
* make sure it doesn't hog the bandwidth
*/
- if (tmp != head && (count % DEPTH_INTERVAL) == (DEPTH_INTERVAL - 1))
+ if (td->list.next != head && (count % DEPTH_INTERVAL) ==
+ (DEPTH_INTERVAL - 1))
td->link |= UHCI_PTR_DEPTH;
count++;
@@ -1606,12 +1521,10 @@ static void stall_callback(unsigned long ptr)
{
struct usb_hcd *hcd = (struct usb_hcd *)ptr;
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
- struct list_head list, *tmp, *head;
+ struct urb_priv *up;
unsigned long flags;
int called_uhci_finish_completion = 0;
- INIT_LIST_HEAD(&list);
-
spin_lock_irqsave(&uhci->schedule_lock, flags);
if (!list_empty(&uhci->urb_remove_list) &&
uhci_get_current_frame_number(uhci) != uhci->urb_remove_age) {
@@ -1620,14 +1533,9 @@ static void stall_callback(unsigned long ptr)
called_uhci_finish_completion = 1;
}
- head = &uhci->urb_list;
- tmp = head->next;
- while (tmp != head) {
- struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list);
+ list_for_each_entry(up, &uhci->urb_list, urb_list) {
struct urb *u = up->urb;
- tmp = tmp->next;
-
spin_lock(&u->lock);
/* Check if the FSBR timed out */
@@ -1642,17 +1550,6 @@ static void stall_callback(unsigned long ptr)
if (called_uhci_finish_completion)
wake_up_all(&uhci->waitqh);
- head = &list;
- tmp = head->next;
- while (tmp != head) {
- struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list);
- struct urb *u = up->urb;
-
- tmp = tmp->next;
-
- uhci_urb_dequeue(hcd, u);
- }
-
/* Really disable FSBR */
if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) {
uhci->fsbrtimeout = 0;
@@ -1661,6 +1558,8 @@ static void stall_callback(unsigned long ptr)
/* Poll for and perform state transitions */
hc_state_transitions(uhci);
+ if (unlikely(uhci->suspended_ports && uhci->state != UHCI_SUSPENDED))
+ uhci_check_resume(uhci);
init_stall_timer(hcd);
}
@@ -1680,15 +1579,9 @@ static int init_stall_timer(struct usb_hcd *hcd)
static void uhci_free_pending_qhs(struct uhci_hcd *uhci)
{
- struct list_head *tmp, *head;
-
- head = &uhci->qh_remove_list;
- tmp = head->next;
- while (tmp != head) {
- struct uhci_qh *qh = list_entry(tmp, struct uhci_qh, remove_list);
-
- tmp = tmp->next;
+ struct uhci_qh *qh, *tmp;
+ list_for_each_entry_safe(qh, tmp, &uhci->qh_remove_list, remove_list) {
list_del_init(&qh->remove_list);
uhci_free_qh(uhci, qh);
@@ -1697,15 +1590,9 @@ static void uhci_free_pending_qhs(struct uhci_hcd *uhci)
static void uhci_free_pending_tds(struct uhci_hcd *uhci)
{
- struct list_head *tmp, *head;
-
- head = &uhci->td_remove_list;
- tmp = head->next;
- while (tmp != head) {
- struct uhci_td *td = list_entry(tmp, struct uhci_td, remove_list);
-
- tmp = tmp->next;
+ struct uhci_td *td, *tmp;
+ list_for_each_entry_safe(td, tmp, &uhci->td_remove_list, remove_list) {
list_del_init(&td->remove_list);
uhci_free_td(uhci, td);
@@ -1726,19 +1613,13 @@ static void uhci_finish_urb(struct usb_hcd *hcd, struct urb *urb, struct pt_regs
static void uhci_finish_completion(struct usb_hcd *hcd, struct pt_regs *regs)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
- struct list_head *tmp, *head;
+ struct urb_priv *urbp, *tmp;
- head = &uhci->complete_list;
- tmp = head->next;
- while (tmp != head) {
- struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
+ list_for_each_entry_safe(urbp, tmp, &uhci->complete_list, urb_list) {
struct urb *urb = urbp->urb;
list_del_init(&urbp->urb_list);
uhci_finish_urb(hcd, urb, regs);
-
- head = &uhci->complete_list;
- tmp = head->next;
}
}
@@ -1754,7 +1635,7 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs)
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
unsigned long io_addr = uhci->io_addr;
unsigned short status;
- struct list_head *tmp, *head;
+ struct urb_priv *urbp, *tmp;
unsigned int age;
/*
@@ -1801,15 +1682,11 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs)
else
uhci_set_next_interrupt(uhci);
- /* Walk the list of pending URB's to see which ones completed */
- head = &uhci->urb_list;
- tmp = head->next;
- while (tmp != head) {
- struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
+ /* Walk the list of pending URBs to see which ones completed
+ * (must be _safe because uhci_transfer_result() dequeues URBs) */
+ list_for_each_entry_safe(urbp, tmp, &uhci->urb_list, urb_list) {
struct urb *urb = urbp->urb;
- tmp = tmp->next;
-
/* Checks the status and does all of the magic necessary */
uhci_transfer_result(uhci, urb);
}
diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h
index 785c0c2cca12..ea9bd98b5edc 100644
--- a/drivers/usb/host/uhci-hcd.h
+++ b/drivers/usb/host/uhci-hcd.h
@@ -352,6 +352,12 @@ struct uhci_hcd {
int resume_detect; /* Need a Global Resume */
unsigned int saved_framenumber; /* Save during PM suspend */
+ /* Support for port suspend/resume */
+ unsigned long port_c_suspend; /* Bit-arrays of ports */
+ unsigned long suspended_ports;
+ unsigned long resuming_ports;
+ unsigned long resume_timeout; /* Time to stop signalling */
+
/* Main list of URB's currently controlled by this HC */
struct list_head urb_list; /* P: uhci->schedule_lock */
@@ -385,12 +391,12 @@ struct urb_priv {
struct uhci_qh *qh; /* QH for this URB */
struct list_head td_list; /* P: urb->lock */
- int fsbr : 1; /* URB turned on FSBR */
- int fsbr_timeout : 1; /* URB timed out on FSBR */
- int queued : 1; /* QH was queued (not linked in) */
- int short_control_packet : 1; /* If we get a short packet during */
- /* a control transfer, retrigger */
- /* the status phase */
+ unsigned fsbr : 1; /* URB turned on FSBR */
+ unsigned fsbr_timeout : 1; /* URB timed out on FSBR */
+ unsigned queued : 1; /* QH was queued (not linked in) */
+ unsigned short_control_packet : 1; /* If we get a short packet during */
+ /* a control transfer, retrigger */
+ /* the status phase */
unsigned long inserttime; /* In jiffies */
unsigned long fsbrtime; /* In jiffies */
diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c
index 22bbd70063ce..f44d90e23df2 100644
--- a/drivers/usb/host/uhci-hub.c
+++ b/drivers/usb/host/uhci-hub.c
@@ -36,13 +36,13 @@ static __u8 root_hub_hub_des[] =
static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
- unsigned long io_addr = uhci->io_addr;
- int i;
+ int port;
*buf = 0;
- for (i = 0; i < uhci->rh_numports; i++) {
- if (inw(io_addr + USBPORTSC1 + i * 2) & RWC_BITS)
- *buf |= (1 << (i + 1));
+ for (port = 0; port < uhci->rh_numports; ++port) {
+ if ((inw(uhci->io_addr + USBPORTSC1 + port * 2) & RWC_BITS) ||
+ test_bit(port, &uhci->port_c_suspend))
+ *buf |= (1 << (port + 1));
}
return !!*buf;
}
@@ -62,31 +62,67 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
status &= ~(RWC_BITS|WZ_BITS); \
outw(status, port_addr)
+/* UHCI controllers don't automatically stop resume signalling after 20 msec,
+ * so we have to poll and check timeouts in order to take care of it.
+ * FIXME: Synchronize access to these fields by a spinlock.
+ */
+static void uhci_finish_suspend(struct uhci_hcd *uhci, int port,
+ unsigned int port_addr)
+{
+ int status;
+
+ if (test_bit(port, &uhci->suspended_ports)) {
+ CLR_RH_PORTSTAT(USBPORTSC_SUSP | USBPORTSC_RD);
+ clear_bit(port, &uhci->suspended_ports);
+ clear_bit(port, &uhci->resuming_ports);
+ set_bit(port, &uhci->port_c_suspend);
+ }
+}
+
+static void uhci_check_resume(struct uhci_hcd *uhci)
+{
+ unsigned int port;
+ unsigned int port_addr;
+
+ for (port = 0; port < uhci->rh_numports; ++port) {
+ port_addr = uhci->io_addr + USBPORTSC1 + 2 * port;
+ if (unlikely(inw(port_addr) & USBPORTSC_RD)) {
+ if (!test_bit(port, &uhci->resuming_ports)) {
+
+ /* Port received a wakeup request */
+ set_bit(port, &uhci->resuming_ports);
+ uhci->resume_timeout = jiffies +
+ msecs_to_jiffies(20);
+ } else if (time_after_eq(jiffies,
+ uhci->resume_timeout)) {
+ uhci_finish_suspend(uhci, port, port_addr);
+ }
+ }
+ }
+}
/* size of returned buffer is part of USB spec */
static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
u16 wIndex, char *buf, u16 wLength)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
- int status, retval = 0, len = 0;
- unsigned long port_addr = uhci->io_addr + USBPORTSC1 + 2 * (wIndex-1);
- __u16 wPortChange, wPortStatus;
+ int status, lstatus, retval = 0, len = 0;
+ unsigned int port = wIndex - 1;
+ unsigned long port_addr = uhci->io_addr + USBPORTSC1 + 2 * port;
+ u16 wPortChange, wPortStatus;
switch (typeReq) {
- /* Request Destination:
- without flags: Device,
- RH_INTERFACE: interface,
- RH_ENDPOINT: endpoint,
- RH_CLASS means HUB here,
- RH_OTHER | RH_CLASS almost ever means HUB_PORT here
- */
case GetHubStatus:
*(__le32 *)buf = cpu_to_le32(0);
OK(4); /* hub power */
case GetPortStatus:
- if (!wIndex || wIndex > uhci->rh_numports)
+ if (port >= uhci->rh_numports)
goto err;
+
+ if (uhci->resuming_ports)
+ uhci_check_resume(uhci);
+
status = inw(port_addr);
/* Intel controllers report the OverCurrent bit active on.
@@ -97,34 +133,43 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
PCI_VENDOR_ID_VIA)
status ^= USBPORTSC_OC;
- /* UHCI doesn't support C_SUSPEND and C_RESET (always false) */
- wPortChange = 0;
+ /* UHCI doesn't support C_RESET (always false) */
+ wPortChange = lstatus = 0;
if (status & USBPORTSC_CSC)
- wPortChange |= 1 << (USB_PORT_FEAT_C_CONNECTION - 16);
+ wPortChange |= USB_PORT_STAT_C_CONNECTION;
if (status & USBPORTSC_PEC)
- wPortChange |= 1 << (USB_PORT_FEAT_C_ENABLE - 16);
+ wPortChange |= USB_PORT_STAT_C_ENABLE;
if (status & USBPORTSC_OCC)
- wPortChange |= 1 << (USB_PORT_FEAT_C_OVER_CURRENT - 16);
+ wPortChange |= USB_PORT_STAT_C_OVERCURRENT;
+
+ if (test_bit(port, &uhci->port_c_suspend)) {
+ wPortChange |= USB_PORT_STAT_C_SUSPEND;
+ lstatus |= 1;
+ }
+ if (test_bit(port, &uhci->suspended_ports))
+ lstatus |= 2;
+ if (test_bit(port, &uhci->resuming_ports))
+ lstatus |= 4;
/* UHCI has no power switching (always on) */
- wPortStatus = 1 << USB_PORT_FEAT_POWER;
+ wPortStatus = USB_PORT_STAT_POWER;
if (status & USBPORTSC_CCS)
- wPortStatus |= 1 << USB_PORT_FEAT_CONNECTION;
+ wPortStatus |= USB_PORT_STAT_CONNECTION;
if (status & USBPORTSC_PE) {
- wPortStatus |= 1 << USB_PORT_FEAT_ENABLE;
+ wPortStatus |= USB_PORT_STAT_ENABLE;
if (status & (USBPORTSC_SUSP | USBPORTSC_RD))
- wPortStatus |= 1 << USB_PORT_FEAT_SUSPEND;
+ wPortStatus |= USB_PORT_STAT_SUSPEND;
}
if (status & USBPORTSC_OC)
- wPortStatus |= 1 << USB_PORT_FEAT_OVER_CURRENT;
+ wPortStatus |= USB_PORT_STAT_OVERCURRENT;
if (status & USBPORTSC_PR)
- wPortStatus |= 1 << USB_PORT_FEAT_RESET;
+ wPortStatus |= USB_PORT_STAT_RESET;
if (status & USBPORTSC_LSDA)
- wPortStatus |= 1 << USB_PORT_FEAT_LOWSPEED;
+ wPortStatus |= USB_PORT_STAT_LOW_SPEED;
if (wPortChange)
- dev_dbg(uhci_dev(uhci), "port %d portsc %04x\n",
- wIndex, status);
+ dev_dbg(uhci_dev(uhci), "port %d portsc %04x,%02x\n",
+ wIndex, status, lstatus);
*(__le16 *)buf = cpu_to_le16(wPortStatus);
*(__le16 *)(buf + 2) = cpu_to_le16(wPortChange);
@@ -140,11 +185,12 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
}
break;
case SetPortFeature:
- if (!wIndex || wIndex > uhci->rh_numports)
+ if (port >= uhci->rh_numports)
goto err;
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
+ set_bit(port, &uhci->suspended_ports);
SET_RH_PORTSTAT(USBPORTSC_SUSP);
OK(0);
case USB_PORT_FEAT_RESET:
@@ -152,6 +198,9 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
mdelay(50); /* USB v1.1 7.1.7.3 */
CLR_RH_PORTSTAT(USBPORTSC_PR);
udelay(10);
+
+ /* Reset terminates Resume signalling */
+ uhci_finish_suspend(uhci, port, port_addr);
SET_RH_PORTSTAT(USBPORTSC_PE);
mdelay(10);
CLR_RH_PORTSTAT(USBPORTSC_PEC|USBPORTSC_CSC);
@@ -164,21 +213,38 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
}
break;
case ClearPortFeature:
- if (!wIndex || wIndex > uhci->rh_numports)
+ if (port >= uhci->rh_numports)
goto err;
switch (wValue) {
case USB_PORT_FEAT_ENABLE:
CLR_RH_PORTSTAT(USBPORTSC_PE);
+
+ /* Disable terminates Resume signalling */
+ uhci_finish_suspend(uhci, port, port_addr);
OK(0);
case USB_PORT_FEAT_C_ENABLE:
CLR_RH_PORTSTAT(USBPORTSC_PEC);
OK(0);
case USB_PORT_FEAT_SUSPEND:
- CLR_RH_PORTSTAT(USBPORTSC_SUSP);
+ if (test_bit(port, &uhci->suspended_ports) &&
+ !test_and_set_bit(port,
+ &uhci->resuming_ports)) {
+ uhci->resume_timeout = jiffies +
+ msecs_to_jiffies(20);
+ SET_RH_PORTSTAT(USBPORTSC_RD);
+
+ /* The controller won't allow RD to be set
+ * if the port is disabled. When this happens
+ * just skip the Resume signalling.
+ */
+ if (!(inw(port_addr) & USBPORTSC_RD))
+ uhci_finish_suspend(uhci, port,
+ port_addr);
+ }
OK(0);
case USB_PORT_FEAT_C_SUSPEND:
- /* this driver won't report these */
+ clear_bit(port, &uhci->port_c_suspend);
OK(0);
case USB_PORT_FEAT_POWER:
/* UHCI has no power switching */
diff --git a/drivers/usb/image/Kconfig b/drivers/usb/image/Kconfig
index 0b0f80e4f9a7..b541b67d2eb6 100644
--- a/drivers/usb/image/Kconfig
+++ b/drivers/usb/image/Kconfig
@@ -30,11 +30,12 @@ config USB_MICROTEK
This driver can be compiled as a module, called microtek.
config USB_HPUSBSCSI
- tristate "HP53xx USB scanner support (EXPERIMENTAL)"
- depends on USB && SCSI && EXPERIMENTAL
+ tristate "HP53xx USB scanner support"
+ depends on USB && SCSI
help
Say Y here if you want support for the HP 53xx series of scanners
- and the Minolta Scan Dual. This driver is experimental.
+ and the Minolta Scan Dual.
The scanner will be accessible as a SCSI device.
+ Please note that recent versions of SANE use usbfs, not this driver.
This can be compiled as a module, called hpusbscsi.
diff --git a/drivers/usb/image/hpusbscsi.c b/drivers/usb/image/hpusbscsi.c
index dabc3666aee7..47a864b29f3d 100644
--- a/drivers/usb/image/hpusbscsi.c
+++ b/drivers/usb/image/hpusbscsi.c
@@ -106,7 +106,7 @@ hpusbscsi_usb_probe(struct usb_interface *intf,
/* In host->hostdata we store a pointer to desc */
new->host = scsi_host_alloc(&hpusbscsi_scsi_host_template, sizeof(new));
if (!new->host)
- goto out_unlink_controlurb;
+ goto out_kill_controlurb;
new->host->hostdata[0] = (unsigned long)new;
scsi_add_host(new->host, &intf->dev); /* XXX handle failure */
@@ -118,8 +118,8 @@ hpusbscsi_usb_probe(struct usb_interface *intf,
usb_set_intfdata(intf, new);
return 0;
- out_unlink_controlurb:
- usb_unlink_urb(new->controlurb);
+ out_kill_controlurb:
+ usb_kill_urb(new->controlurb);
out_free_controlurb:
usb_free_urb(new->controlurb);
out_free_dataurb:
@@ -137,7 +137,7 @@ hpusbscsi_usb_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
scsi_remove_host(desc->host);
- usb_unlink_urb(desc->controlurb);
+ usb_kill_urb(desc->controlurb);
scsi_host_put(desc->host);
usb_free_urb(desc->controlurb);
@@ -280,8 +280,8 @@ static int hpusbscsi_scsi_abort (Scsi_Cmnd *srb)
struct hpusbscsi* hpusbscsi = (struct hpusbscsi*)(srb->device->host->hostdata[0]);
printk(KERN_DEBUG"Requested is canceled.\n");
- usb_unlink_urb(hpusbscsi->dataurb);
- usb_unlink_urb(hpusbscsi->controlurb);
+ usb_kill_urb(hpusbscsi->dataurb);
+ usb_kill_urb(hpusbscsi->controlurb);
hpusbscsi->state = HP_STATE_FREE;
return SCSI_ABORT_PENDING;
diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c
index f401557f0356..66bb13307df5 100644
--- a/drivers/usb/image/mdc800.c
+++ b/drivers/usb/image/mdc800.c
@@ -317,7 +317,6 @@ static int mdc800_usb_waitForIRQ (int mode, int msec)
mdc800->camera_request_ready=1+mode;
add_wait_queue(&mdc800->irq_wait, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
timeout = msec*HZ/1000;
while (!mdc800->irq_woken && timeout)
{
@@ -325,7 +324,6 @@ static int mdc800_usb_waitForIRQ (int mode, int msec)
timeout = schedule_timeout (timeout);
}
remove_wait_queue(&mdc800->irq_wait, &wait);
- set_current_state(TASK_RUNNING);
mdc800->irq_woken = 0;
if (mdc800->camera_request_ready>0)
@@ -543,9 +541,9 @@ static void mdc800_usb_disconnect (struct usb_interface *intf)
mdc800->state=NOT_CONNECTED;
- usb_unlink_urb (mdc800->irq_urb);
- usb_unlink_urb (mdc800->write_urb);
- usb_unlink_urb (mdc800->download_urb);
+ usb_kill_urb(mdc800->irq_urb);
+ usb_kill_urb(mdc800->write_urb);
+ usb_kill_urb(mdc800->download_urb);
mdc800->dev = NULL;
usb_set_intfdata(intf, NULL);
@@ -649,9 +647,9 @@ static int mdc800_device_release (struct inode* inode, struct file *file)
down (&mdc800->io_lock);
if (mdc800->open && (mdc800->state != NOT_CONNECTED))
{
- usb_unlink_urb (mdc800->irq_urb);
- usb_unlink_urb (mdc800->write_urb);
- usb_unlink_urb (mdc800->download_urb);
+ usb_kill_urb(mdc800->irq_urb);
+ usb_kill_urb(mdc800->write_urb);
+ usb_kill_urb(mdc800->download_urb);
mdc800->open=0;
}
else
@@ -725,7 +723,6 @@ static ssize_t mdc800_device_read (struct file *file, char __user *buf, size_t l
set_current_state(TASK_UNINTERRUPTIBLE);
timeout = schedule_timeout (timeout);
}
- set_current_state(TASK_RUNNING);
remove_wait_queue(&mdc800->download_wait, &wait);
mdc800->downloaded = 0;
if (mdc800->download_urb->status != 0)
@@ -851,12 +848,11 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s
set_current_state(TASK_UNINTERRUPTIBLE);
timeout = schedule_timeout (timeout);
}
- set_current_state(TASK_RUNNING);
remove_wait_queue(&mdc800->write_wait, &wait);
mdc800->written = 0;
if (mdc800->state == WORKING)
{
- usb_unlink_urb (mdc800->write_urb);
+ usb_kill_urb(mdc800->write_urb);
up (&mdc800->io_lock);
return -EIO;
}
diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c
index 9d3aa3075086..2a18c35629eb 100644
--- a/drivers/usb/image/microtek.c
+++ b/drivers/usb/image/microtek.c
@@ -324,7 +324,7 @@ static inline void mts_urb_abort(struct mts_desc* desc) {
MTS_DEBUG_GOT_HERE();
mts_debug_dump(desc);
- usb_unlink_urb( desc->urb );
+ usb_kill_urb( desc->urb );
}
static int mts_scsi_abort (Scsi_Cmnd *srb)
@@ -341,12 +341,18 @@ static int mts_scsi_abort (Scsi_Cmnd *srb)
static int mts_scsi_host_reset (Scsi_Cmnd *srb)
{
struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]);
+ int result, rc;
MTS_DEBUG_GOT_HERE();
mts_debug_dump(desc);
- usb_reset_device(desc->usb_dev); /*FIXME: untested on new reset code */
- return 0; /* RANT why here 0 and not SUCCESS */
+ rc = usb_lock_device_for_reset(desc->usb_dev, desc->usb_intf);
+ if (rc < 0)
+ return FAILED;
+ result = usb_reset_device(desc->usb_dev);;
+ if (rc)
+ usb_unlock_device(desc->usb_dev);
+ return result ? FAILED : SUCCESS;
}
static
@@ -777,6 +783,7 @@ static int mts_usb_probe(struct usb_interface *intf,
goto out_kfree;
new_desc->usb_dev = dev;
+ new_desc->usb_intf = intf;
init_MUTEX(&new_desc->lock);
/* endpoints */
@@ -822,10 +829,10 @@ static void mts_usb_disconnect (struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
+ usb_kill_urb(desc->urb);
scsi_remove_host(desc->host);
- usb_unlink_urb(desc->urb);
- scsi_host_put(desc->host);
+ scsi_host_put(desc->host);
usb_free_urb(desc->urb);
kfree(desc);
}
diff --git a/drivers/usb/image/microtek.h b/drivers/usb/image/microtek.h
index 206994ddcedf..3271deb8c001 100644
--- a/drivers/usb/image/microtek.h
+++ b/drivers/usb/image/microtek.h
@@ -31,6 +31,7 @@ struct mts_desc {
struct mts_desc *prev;
struct usb_device *usb_dev;
+ struct usb_interface *usb_intf;
/* Endpoint addresses */
u8 ep_out;
diff --git a/drivers/usb/input/aiptek.c b/drivers/usb/input/aiptek.c
index 44b8faee6536..67a5b70a6daf 100644
--- a/drivers/usb/input/aiptek.c
+++ b/drivers/usb/input/aiptek.c
@@ -837,7 +837,7 @@ static void aiptek_close(struct input_dev *inputdev)
struct aiptek *aiptek = inputdev->private;
if (--aiptek->openCount == 0) {
- usb_unlink_urb(aiptek->urb);
+ usb_kill_urb(aiptek->urb);
}
}
@@ -2258,7 +2258,7 @@ static void aiptek_disconnect(struct usb_interface *intf)
if (aiptek != NULL) {
/* Free & unhook everything from the system.
*/
- usb_unlink_urb(aiptek->urb);
+ usb_kill_urb(aiptek->urb);
input_unregister_device(&aiptek->inputdev);
aiptek_delete_files(&intf->dev);
usb_free_urb(aiptek->urb);
diff --git a/drivers/usb/input/ati_remote.c b/drivers/usb/input/ati_remote.c
index 61a42bdae1d8..91f7f434dd48 100644
--- a/drivers/usb/input/ati_remote.c
+++ b/drivers/usb/input/ati_remote.c
@@ -418,13 +418,14 @@ static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigne
while (timeout && (ati_remote->out_urb->status == -EINPROGRESS)
&& !(ati_remote->send_flags & SEND_FLAG_COMPLETE)) {
+ set_current_state(TASK_INTERRUPTIBLE);
timeout = schedule_timeout(timeout);
rmb();
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&ati_remote->wait, &wait);
- usb_unlink_urb(ati_remote->out_urb);
+ usb_kill_urb(ati_remote->out_urb);
return retval;
}
@@ -624,10 +625,10 @@ static void ati_remote_delete(struct ati_remote *ati_remote)
if (!ati_remote) return;
if (ati_remote->irq_urb)
- usb_unlink_urb(ati_remote->irq_urb);
+ usb_kill_urb(ati_remote->irq_urb);
if (ati_remote->out_urb)
- usb_unlink_urb(ati_remote->out_urb);
+ usb_kill_urb(ati_remote->out_urb);
input_unregister_device(&ati_remote->idev);
diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c
index 35baacd7706d..c38e7fccf564 100644
--- a/drivers/usb/input/hid-core.c
+++ b/drivers/usb/input/hid-core.c
@@ -1260,8 +1260,10 @@ int hid_wait_io(struct hid_device *hid)
add_wait_queue(&hid->wait, &wait);
while (timeout && (test_bit(HID_CTRL_RUNNING, &hid->iofl) ||
- test_bit(HID_OUT_RUNNING, &hid->iofl)))
+ test_bit(HID_OUT_RUNNING, &hid->iofl))) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
timeout = schedule_timeout(timeout);
+ }
set_current_state(TASK_RUNNING);
remove_wait_queue(&hid->wait, &wait);
@@ -1350,9 +1352,9 @@ void hid_init_reports(struct hid_device *hid)
while (ret) {
err |= ret;
if (test_bit(HID_CTRL_RUNNING, &hid->iofl))
- usb_unlink_urb(hid->urbctrl);
+ usb_kill_urb(hid->urbctrl);
if (test_bit(HID_OUT_RUNNING, &hid->iofl))
- usb_unlink_urb(hid->urbout);
+ usb_kill_urb(hid->urbout);
ret = hid_wait_io(hid);
}
@@ -1455,10 +1457,11 @@ void hid_init_reports(struct hid_device *hid)
#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101
#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104
-#define USB_VENDOR_ID_CODEMERCS 0x07c0
-#define USB_DEVICE_ID_CODEMERCS_IOW40 0x1500
-#define USB_DEVICE_ID_CODEMERCS_IOW24 0x1501
-
+#define USB_VENDOR_ID_CODEMERCS 0x07c0
+#define USB_DEVICE_ID_CODEMERCS_IOW40 0x1500
+#define USB_DEVICE_ID_CODEMERCS_IOW24 0x1501
+#define USB_DEVICE_ID_CODEMERCS_IOW48 0x1502
+#define USB_DEVICE_ID_CODEMERCS_IOW28 0x1503
static struct hid_blacklist {
__u16 idVendor;
@@ -1542,6 +1545,11 @@ static struct hid_blacklist {
{ USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD },
{ USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
+ { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW48, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW28, HID_QUIRK_IGNORE },
+
{ 0, 0 }
};
diff --git a/drivers/usb/input/kbtab.c b/drivers/usb/input/kbtab.c
index 39c0d38aefa4..2e4c0d246030 100644
--- a/drivers/usb/input/kbtab.c
+++ b/drivers/usb/input/kbtab.c
@@ -122,7 +122,7 @@ static void kbtab_close(struct input_dev *dev)
struct kbtab *kbtab = dev->private;
if (!--kbtab->open)
- usb_unlink_urb(kbtab->irq);
+ usb_kill_urb(kbtab->irq);
}
static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *id)
@@ -205,7 +205,7 @@ static void kbtab_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
if (kbtab) {
- usb_unlink_urb(kbtab->irq);
+ usb_kill_urb(kbtab->irq);
input_unregister_device(&kbtab->dev);
usb_free_urb(kbtab->irq);
usb_buffer_free(interface_to_usbdev(intf), 10, kbtab->data, kbtab->data_dma);
diff --git a/drivers/usb/input/mtouchusb.c b/drivers/usb/input/mtouchusb.c
index ae5713876d4c..9dcfd7e2ffbf 100644
--- a/drivers/usb/input/mtouchusb.c
+++ b/drivers/usb/input/mtouchusb.c
@@ -155,7 +155,7 @@ static void mtouchusb_close (struct input_dev *input)
struct mtouch_usb *mtouch = input->private;
if (!--mtouch->open)
- usb_unlink_urb (mtouch->irq);
+ usb_kill_urb (mtouch->irq);
}
static int mtouchusb_alloc_buffers(struct usb_device *udev, struct mtouch_usb *mtouch)
@@ -320,7 +320,7 @@ static void mtouchusb_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
if (mtouch) {
dbg("%s - mtouch is initialized, cleaning up", __FUNCTION__);
- usb_unlink_urb(mtouch->irq);
+ usb_kill_urb(mtouch->irq);
input_unregister_device(&mtouch->input);
usb_free_urb(mtouch->irq);
mtouchusb_free_buffers(interface_to_usbdev(intf), mtouch);
diff --git a/drivers/usb/input/pid.c b/drivers/usb/input/pid.c
index a38b0db8dcea..d1ea1f056599 100644
--- a/drivers/usb/input/pid.c
+++ b/drivers/usb/input/pid.c
@@ -56,7 +56,7 @@ static void hid_pid_exit(struct hid_device* hid)
struct hid_ff_pid *private = hid->ff_private;
if (private->urbffout) {
- usb_unlink_urb(private->urbffout);
+ usb_kill_urb(private->urbffout);
usb_free_urb(private->urbffout);
}
}
diff --git a/drivers/usb/input/powermate.c b/drivers/usb/input/powermate.c
index 0ba1bcbfb431..852ebf8574a8 100644
--- a/drivers/usb/input/powermate.c
+++ b/drivers/usb/input/powermate.c
@@ -417,7 +417,7 @@ static void powermate_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
if (pm) {
pm->requires_update = 0;
- usb_unlink_urb(pm->irq);
+ usb_kill_urb(pm->irq);
input_unregister_device(&pm->input);
usb_free_urb(pm->irq);
usb_free_urb(pm->config);
diff --git a/drivers/usb/input/touchkitusb.c b/drivers/usb/input/touchkitusb.c
index 4917b042ef9a..14443f926ab0 100644
--- a/drivers/usb/input/touchkitusb.c
+++ b/drivers/usb/input/touchkitusb.c
@@ -141,7 +141,7 @@ static void touchkit_close(struct input_dev *input)
struct touchkit_usb *touchkit = input->private;
if (!--touchkit->open)
- usb_unlink_urb(touchkit->irq);
+ usb_kill_urb(touchkit->irq);
}
static int touchkit_alloc_buffers(struct usb_device *udev,
@@ -276,7 +276,7 @@ static void touchkit_disconnect(struct usb_interface *intf)
dbg("%s - touchkit is initialized, cleaning up", __FUNCTION__);
usb_set_intfdata(intf, NULL);
input_unregister_device(&touchkit->input);
- usb_unlink_urb(touchkit->irq);
+ usb_kill_urb(touchkit->irq);
usb_free_urb(touchkit->irq);
touchkit_free_buffers(interface_to_usbdev(intf), touchkit);
kfree(touchkit);
diff --git a/drivers/usb/input/usbkbd.c b/drivers/usb/input/usbkbd.c
index 0c51b1eb84cf..1700f405b00b 100644
--- a/drivers/usb/input/usbkbd.c
+++ b/drivers/usb/input/usbkbd.c
@@ -196,7 +196,7 @@ static void usb_kbd_close(struct input_dev *dev)
struct usb_kbd *kbd = dev->private;
if (!--kbd->open)
- usb_unlink_urb(kbd->irq);
+ usb_kill_urb(kbd->irq);
}
static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd)
@@ -343,7 +343,7 @@ static void usb_kbd_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
if (kbd) {
- usb_unlink_urb(kbd->irq);
+ usb_kill_urb(kbd->irq);
input_unregister_device(&kbd->dev);
usb_kbd_free_mem(interface_to_usbdev(intf), kbd);
kfree(kbd);
diff --git a/drivers/usb/input/usbmouse.c b/drivers/usb/input/usbmouse.c
index 5bf27306ea3c..8c7381b74499 100644
--- a/drivers/usb/input/usbmouse.c
+++ b/drivers/usb/input/usbmouse.c
@@ -118,7 +118,7 @@ static void usb_mouse_close(struct input_dev *dev)
struct usb_mouse *mouse = dev->private;
if (!--mouse->open)
- usb_unlink_urb(mouse->irq);
+ usb_kill_urb(mouse->irq);
}
static int usb_mouse_probe(struct usb_interface * intf, const struct usb_device_id * id)
@@ -223,7 +223,7 @@ static void usb_mouse_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
if (mouse) {
- usb_unlink_urb(mouse->irq);
+ usb_kill_urb(mouse->irq);
input_unregister_device(&mouse->dev);
usb_free_urb(mouse->irq);
usb_buffer_free(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma);
diff --git a/drivers/usb/input/wacom.c b/drivers/usb/input/wacom.c
index 492a5cb12af4..471d1bf68bf0 100644
--- a/drivers/usb/input/wacom.c
+++ b/drivers/usb/input/wacom.c
@@ -608,7 +608,7 @@ static void wacom_close(struct input_dev *dev)
struct wacom *wacom = dev->private;
if (!--wacom->open)
- usb_unlink_urb(wacom->irq);
+ usb_kill_urb(wacom->irq);
}
static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id)
@@ -729,7 +729,7 @@ static void wacom_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
if (wacom) {
- usb_unlink_urb(wacom->irq);
+ usb_kill_urb(wacom->irq);
input_unregister_device(&wacom->dev);
usb_free_urb(wacom->irq);
usb_buffer_free(interface_to_usbdev(intf), 10, wacom->data, wacom->data_dma);
diff --git a/drivers/usb/input/xpad.c b/drivers/usb/input/xpad.c
index 1956eb06b75c..c5fa87edb667 100644
--- a/drivers/usb/input/xpad.c
+++ b/drivers/usb/input/xpad.c
@@ -214,7 +214,7 @@ static void xpad_close (struct input_dev *dev)
struct usb_xpad *xpad = dev->private;
if (!--xpad->open_count)
- usb_unlink_urb(xpad->irq_in);
+ usb_kill_urb(xpad->irq_in);
}
static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id)
@@ -325,7 +325,7 @@ static void xpad_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
if (xpad) {
- usb_unlink_urb(xpad->irq_in);
+ usb_kill_urb(xpad->irq_in);
input_unregister_device(&xpad->dev);
usb_free_urb(xpad->irq_in);
usb_buffer_free(interface_to_usbdev(intf), XPAD_PKT_LEN, xpad->idata, xpad->idata_dma);
diff --git a/drivers/usb/media/Kconfig b/drivers/usb/media/Kconfig
index 4ac7ac4f3b1d..678e5fcbe29a 100644
--- a/drivers/usb/media/Kconfig
+++ b/drivers/usb/media/Kconfig
@@ -123,11 +123,11 @@ config USB_SE401
module will be called se401.
config USB_SN9C102
- tristate "USB SN9C10[12] PC Camera Controller support"
+ tristate "USB SN9C10x PC Camera Controller support"
depends on USB && VIDEO_DEV
---help---
- Say Y here if you want support for cameras based on SONiX SN9C101
- or SN9C102 PC Camera Controllers.
+ Say Y here if you want support for cameras based on SONiX SN9C101,
+ SN9C102 or SN9C103 PC Camera Controllers.
See <file:Documentation/usb/sn9c102.txt> for more informations.
diff --git a/drivers/usb/media/dabusb.c b/drivers/usb/media/dabusb.c
index 0e5425f328d0..0aae6ecc7ad6 100644
--- a/drivers/usb/media/dabusb.c
+++ b/drivers/usb/media/dabusb.c
@@ -109,16 +109,13 @@ static void dump_urb (struct urb *urb)
static int dabusb_cancel_queue (pdabusb_t s, struct list_head *q)
{
unsigned long flags;
- struct list_head *p;
pbuff_t b;
dbg("dabusb_cancel_queue");
spin_lock_irqsave (&s->lock, flags);
- for (p = q->next; p != q; p = p->next) {
- b = list_entry (p, buff_t, buff_list);
-
+ list_for_each_entry(b, q, buff_list) {
#ifdef DEBUG
dump_urb(b->purb);
#endif
@@ -598,6 +595,7 @@ static int dabusb_open (struct inode *inode, struct file *file)
if (file->f_flags & O_NONBLOCK) {
return -EBUSY;
}
+ set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout (HZ / 2);
if (signal_pending (current)) {
diff --git a/drivers/usb/media/konicawc.c b/drivers/usb/media/konicawc.c
index 3376654ca051..aca3093c2b87 100644
--- a/drivers/usb/media/konicawc.c
+++ b/drivers/usb/media/konicawc.c
@@ -474,13 +474,8 @@ static void konicawc_stop_data(struct uvd *uvd)
/* Unschedule all of the iso td's */
for (i=0; i < USBVIDEO_NUMSBUF; i++) {
- j = usb_unlink_urb(uvd->sbuf[i].urb);
- if (j < 0)
- err("usb_unlink_urb() error %d.", j);
-
- j = usb_unlink_urb(cam->sts_urb[i]);
- if (j < 0)
- err("usb_unlink_urb() error %d.", j);
+ usb_kill_urb(uvd->sbuf[i].urb);
+ usb_kill_urb(cam->sts_urb[i]);
}
if (!uvd->remove_pending) {
diff --git a/drivers/usb/media/ov511.c b/drivers/usb/media/ov511.c
index d040c8e30b6b..7de03331289e 100644
--- a/drivers/usb/media/ov511.c
+++ b/drivers/usb/media/ov511.c
@@ -3830,7 +3830,7 @@ ov51x_unlink_isoc(struct usb_ov511 *ov)
/* Unschedule all of the iso td's */
for (n = OV511_NUMSBUF - 1; n >= 0; n--) {
if (ov->sbuf[n].urb) {
- usb_unlink_urb(ov->sbuf[n].urb);
+ usb_kill_urb(ov->sbuf[n].urb);
usb_free_urb(ov->sbuf[n].urb);
ov->sbuf[n].urb = NULL;
}
diff --git a/drivers/usb/media/se401.c b/drivers/usb/media/se401.c
index c694caaac447..37d28640c790 100644
--- a/drivers/usb/media/se401.c
+++ b/drivers/usb/media/se401.c
@@ -514,7 +514,7 @@ static int se401_stop_stream(struct usb_se401 *se401)
se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0);
for (i=0; i<SE401_NUMSBUF; i++) if (se401->urb[i]) {
- usb_unlink_urb(se401->urb[i]);
+ usb_kill_urb(se401->urb[i]);
usb_free_urb(se401->urb[i]);
se401->urb[i]=NULL;
kfree(se401->sbuf[i].data);
@@ -883,7 +883,7 @@ static void usb_se401_remove_disconnected (struct usb_se401 *se401)
se401->dev = NULL;
for (i=0; i<SE401_NUMSBUF; i++) if (se401->urb[i]) {
- usb_unlink_urb(se401->urb[i]);
+ usb_kill_urb(se401->urb[i]);
usb_free_urb(se401->urb[i]);
se401->urb[i] = NULL;
kfree(se401->sbuf[i].data);
@@ -892,7 +892,7 @@ static void usb_se401_remove_disconnected (struct usb_se401 *se401)
kfree(se401->scratch[i].data);
}
if (se401->inturb) {
- usb_unlink_urb(se401->inturb);
+ usb_kill_urb(se401->inturb);
usb_free_urb(se401->inturb);
}
info("%s disconnected", se401->camera_name);
diff --git a/drivers/usb/media/sn9c102.h b/drivers/usb/media/sn9c102.h
index 62ff2144392c..a682dfc2f747 100644
--- a/drivers/usb/media/sn9c102.h
+++ b/drivers/usb/media/sn9c102.h
@@ -1,5 +1,5 @@
/***************************************************************************
- * V4L2 driver for SN9C10[12] PC Camera Controllers *
+ * V4L2 driver for SN9C10x PC Camera Controllers *
* *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
@@ -49,15 +49,21 @@
/*****************************************************************************/
-#define SN9C102_MODULE_NAME "V4L2 driver for SN9C10[12] PC Camera Controllers"
+#define SN9C102_MODULE_NAME "V4L2 driver for SN9C10x PC Camera Controllers"
#define SN9C102_MODULE_AUTHOR "(C) 2004 Luca Risolia"
#define SN9C102_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
#define SN9C102_MODULE_LICENSE "GPL"
-#define SN9C102_MODULE_VERSION "1:1.08"
-#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 8)
+#define SN9C102_MODULE_VERSION "1:1.12"
+#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 12)
-SN9C102_ID_TABLE;
-SN9C102_SENSOR_TABLE;
+enum sn9c102_bridge {
+ BRIDGE_SN9C101 = 0x01,
+ BRIDGE_SN9C102 = 0x02,
+ BRIDGE_SN9C103 = 0x04,
+};
+
+SN9C102_ID_TABLE
+SN9C102_SENSOR_TABLE
enum sn9c102_frame_state {
F_UNUSED,
@@ -105,6 +111,7 @@ struct sn9c102_device {
struct video_device* v4ldev;
+ enum sn9c102_bridge bridge;
struct sn9c102_sensor* sensor;
struct usb_device* usbdev;
diff --git a/drivers/usb/media/sn9c102_core.c b/drivers/usb/media/sn9c102_core.c
index 9f775f74002c..6d428a5b8d03 100644
--- a/drivers/usb/media/sn9c102_core.c
+++ b/drivers/usb/media/sn9c102_core.c
@@ -1,5 +1,5 @@
/***************************************************************************
- * V4L2 driver for SN9C10[12] PC Camera Controllers *
+ * V4L2 driver for SN9C10x PC Camera Controllers *
* *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
@@ -169,15 +169,15 @@ static u32 sn9c102_request_buffers(struct sn9c102_device* cam, u32 count)
cam->nbuffers = count;
while (cam->nbuffers > 0) {
- if ((buff = rvmalloc(cam->nbuffers * imagesize)))
+ if ((buff = rvmalloc(cam->nbuffers * PAGE_ALIGN(imagesize))))
break;
cam->nbuffers--;
}
for (i = 0; i < cam->nbuffers; i++) {
- cam->frame[i].bufmem = buff + i*imagesize;
+ cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize);
cam->frame[i].buf.index = i;
- cam->frame[i].buf.m.offset = i*imagesize;
+ cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize);
cam->frame[i].buf.length = imagesize;
cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
cam->frame[i].buf.sequence = 0;
@@ -388,7 +388,7 @@ sn9c102_i2c_try_raw_write(struct sn9c102_device* cam,
data[4] = data3;
data[5] = data4;
data[6] = data5;
- data[7] = 0x10;
+ data[7] = 0x14;
res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT);
if (res < 0)
@@ -400,7 +400,7 @@ sn9c102_i2c_try_raw_write(struct sn9c102_device* cam,
if (err)
DBG(3, "I2C write failed for %s image sensor", sensor->name)
- PDBGG("I2C write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, "
+ PDBGG("I2C raw write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, "
"data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X",
n, data0, data1, data2, data3, data4, data5)
@@ -634,7 +634,7 @@ static int sn9c102_start_transfer(struct sn9c102_device* cam)
struct usb_device *udev = cam->usbdev;
struct urb* urb;
const unsigned int wMaxPacketSize[] = {0, 128, 256, 384, 512,
- 680, 800, 900, 1023};
+ 680, 800, 900, 1023};
const unsigned int psz = wMaxPacketSize[SN9C102_ALTERNATE_SETTING];
s8 i, j;
int err = 0;
@@ -965,6 +965,11 @@ static ssize_t sn9c102_show_i2c_val(struct class_device* cd, char* buf)
return -ENODEV;
}
+ if (cam->sensor->slave_read_id == SN9C102_I2C_SLAVEID_UNAVAILABLE) {
+ up(&sn9c102_sysfs_lock);
+ return -ENOSYS;
+ }
+
if ((val = sn9c102_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) {
up(&sn9c102_sysfs_lock);
return -EIO;
@@ -1022,15 +1027,79 @@ sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len)
static ssize_t
sn9c102_store_green(struct class_device* cd, const char* buf, size_t len)
{
+ struct sn9c102_device* cam;
+ enum sn9c102_bridge bridge;
ssize_t res = 0;
u8 value;
ssize_t count;
+ if (down_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ up(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ bridge = cam->bridge;
+
+ up(&sn9c102_sysfs_lock);
+
value = sn9c102_strtou8(buf, len, &count);
- if (!count || value > 0x0f)
+ if (!count)
return -EINVAL;
- if ((res = sn9c102_store_reg(cd, "0x11", 4)) >= 0)
+ switch (bridge) {
+ case BRIDGE_SN9C101:
+ case BRIDGE_SN9C102:
+ if (value > 0x0f)
+ return -EINVAL;
+ if ((res = sn9c102_store_reg(cd, "0x11", 4)) >= 0)
+ res = sn9c102_store_val(cd, buf, len);
+ break;
+ case BRIDGE_SN9C103:
+ if (value > 0x7f)
+ return -EINVAL;
+ if ((res = sn9c102_store_reg(cd, "0x04", 4)) >= 0)
+ res = sn9c102_store_val(cd, buf, len);
+ break;
+ }
+
+ return res;
+}
+
+
+static ssize_t
+sn9c102_store_blue(struct class_device* cd, const char* buf, size_t len)
+{
+ ssize_t res = 0;
+ u8 value;
+ ssize_t count;
+
+ value = sn9c102_strtou8(buf, len, &count);
+ if (!count || value > 0x7f)
+ return -EINVAL;
+
+ if ((res = sn9c102_store_reg(cd, "0x06", 4)) >= 0)
+ res = sn9c102_store_val(cd, buf, len);
+
+ return res;
+}
+
+
+static ssize_t
+sn9c102_store_red(struct class_device* cd, const char* buf, size_t len)
+{
+ ssize_t res = 0;
+ u8 value;
+ ssize_t count;
+
+ value = sn9c102_strtou8(buf, len, &count);
+ if (!count || value > 0x7f)
+ return -EINVAL;
+
+ if ((res = sn9c102_store_reg(cd, "0x05", 4)) >= 0)
res = sn9c102_store_val(cd, buf, len);
return res;
@@ -1046,6 +1115,8 @@ static CLASS_DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR,
static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR,
sn9c102_show_i2c_val, sn9c102_store_i2c_val);
static CLASS_DEVICE_ATTR(green, S_IWUGO, NULL, sn9c102_store_green);
+static CLASS_DEVICE_ATTR(blue, S_IWUGO, NULL, sn9c102_store_blue);
+static CLASS_DEVICE_ATTR(red, S_IWUGO, NULL, sn9c102_store_red);
static void sn9c102_create_sysfs(struct sn9c102_device* cam)
@@ -1054,8 +1125,14 @@ static void sn9c102_create_sysfs(struct sn9c102_device* cam)
video_device_create_file(v4ldev, &class_device_attr_reg);
video_device_create_file(v4ldev, &class_device_attr_val);
- video_device_create_file(v4ldev, &class_device_attr_green);
- if (cam->sensor->slave_write_id && cam->sensor->slave_read_id) {
+ if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102)
+ video_device_create_file(v4ldev, &class_device_attr_green);
+ else if (cam->bridge == BRIDGE_SN9C103) {
+ video_device_create_file(v4ldev, &class_device_attr_blue);
+ video_device_create_file(v4ldev, &class_device_attr_red);
+ }
+ if (cam->sensor->slave_write_id != SN9C102_I2C_SLAVEID_UNAVAILABLE ||
+ cam->sensor->slave_read_id != SN9C102_I2C_SLAVEID_UNAVAILABLE) {
video_device_create_file(v4ldev, &class_device_attr_i2c_reg);
video_device_create_file(v4ldev, &class_device_attr_i2c_val);
}
@@ -1092,21 +1169,13 @@ static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect)
u8 h_start = (u8)(rect->left - s->cropcap.bounds.left),
v_start = (u8)(rect->top - s->cropcap.bounds.top),
h_size = (u8)(rect->width / 16),
- v_size = (u8)(rect->height / 16),
- ae_strx = 0x00,
- ae_stry = 0x00,
- ae_endx = h_size / 2,
- ae_endy = v_size / 2;
+ v_size = (u8)(rect->height / 16);
int err = 0;
err += sn9c102_write_reg(cam, h_start, 0x12);
err += sn9c102_write_reg(cam, v_start, 0x13);
err += sn9c102_write_reg(cam, h_size, 0x15);
err += sn9c102_write_reg(cam, v_size, 0x16);
- err += sn9c102_write_reg(cam, ae_strx, 0x1c);
- err += sn9c102_write_reg(cam, ae_stry, 0x1d);
- err += sn9c102_write_reg(cam, ae_endx, 0x1e);
- err += sn9c102_write_reg(cam, ae_endy, 0x1f);
if (err)
return -EIO;
@@ -1636,16 +1705,21 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
return -EFAULT;
- if ((err = s->set_ctrl(cam, &ctrl)))
- return err;
-
n = sizeof(s->qctrl) / sizeof(s->qctrl[0]);
for (i = 0; i < n; i++)
if (ctrl.id == s->qctrl[i].id) {
- s->_qctrl[i].default_value = ctrl.value;
+ if (ctrl.value < s->qctrl[i].minimum ||
+ ctrl.value > s->qctrl[i].maximum)
+ return -ERANGE;
+ ctrl.value -= ctrl.value % s->qctrl[i].step;
break;
}
+ if ((err = s->set_ctrl(cam, &ctrl)))
+ return err;
+
+ s->_qctrl[i].default_value = ctrl.value;
+
return 0;
}
@@ -1776,7 +1850,7 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
DBG(1, "VIDIOC_S_CROP failed because of hardware "
"problems. To use the camera, close and open "
"/dev/video%d again.", cam->v4ldev->minor)
- return err;
+ return -EIO;
}
s->pix_format.width = rect->width/scale;
@@ -1951,7 +2025,7 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
DBG(1, "VIDIOC_S_FMT failed because of hardware "
"problems. To use the camera, close and open "
"/dev/video%d again.", cam->v4ldev->minor)
- return err;
+ return -EIO;
}
memcpy(pfmt, pix, sizeof(*pix));
@@ -2286,16 +2360,28 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
r = sn9c102_read_reg(cam, 0x00);
if (r < 0 || r != 0x10) {
- DBG(1, "Sorry, this is not a SN9C10[12] based camera "
+ DBG(1, "Sorry, this is not a SN9C10x based camera "
"(vid/pid 0x%04X/0x%04X)",
sn9c102_id_table[i].idVendor,sn9c102_id_table[i].idProduct)
err = -ENODEV;
goto fail;
}
- DBG(2, "SN9C10[12] PC Camera Controller detected "
- "(vid/pid 0x%04X/0x%04X)",
- sn9c102_id_table[i].idVendor, sn9c102_id_table[i].idProduct)
+ cam->bridge = (sn9c102_id_table[i].idProduct & 0xffc0) == 0x6080 ?
+ BRIDGE_SN9C103 : BRIDGE_SN9C102;
+ switch (cam->bridge) {
+ case BRIDGE_SN9C101:
+ case BRIDGE_SN9C102:
+ DBG(2, "SN9C10[12] PC Camera Controller detected "
+ "(vid/pid 0x%04X/0x%04X)", sn9c102_id_table[i].idVendor,
+ sn9c102_id_table[i].idProduct)
+ break;
+ case BRIDGE_SN9C103:
+ DBG(2, "SN9C103 PC Camera Controller detected "
+ "(vid/pid 0x%04X/0x%04X)", sn9c102_id_table[i].idVendor,
+ sn9c102_id_table[i].idProduct)
+ break;
+ }
for (i = 0; sn9c102_sensor_table[i]; i++) {
err = sn9c102_sensor_table[i](cam);
@@ -2318,7 +2404,7 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
cam->state |= DEV_MISCONFIGURED;
}
- strcpy(cam->v4ldev->name, "SN9C10[12] PC Camera");
+ strcpy(cam->v4ldev->name, "SN9C10x PC Camera");
cam->v4ldev->owner = THIS_MODULE;
cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES;
cam->v4ldev->hardware = VID_HARDWARE_SN9C102;
diff --git a/drivers/usb/media/sn9c102_pas106b.c b/drivers/usb/media/sn9c102_pas106b.c
index a302a4845893..8453409ea125 100644
--- a/drivers/usb/media/sn9c102_pas106b.c
+++ b/drivers/usb/media/sn9c102_pas106b.c
@@ -1,5 +1,5 @@
/***************************************************************************
- * Driver for PAS106B image sensor connected to the SN9C10[12] PC Camera *
+ * Driver for PAS106B image sensor connected to the SN9C10x PC Camera *
* Controllers *
* *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
@@ -100,26 +100,26 @@ static int pas106b_set_ctrl(struct sn9c102_device* cam,
switch (ctrl->id) {
case V4L2_CID_RED_BALANCE:
- err += sn9c102_i2c_write(cam, 0x0c, ctrl->value & 0x1f);
+ err += sn9c102_i2c_write(cam, 0x0c, ctrl->value);
break;
case V4L2_CID_BLUE_BALANCE:
- err += sn9c102_i2c_write(cam, 0x09, ctrl->value & 0x1f);
+ err += sn9c102_i2c_write(cam, 0x09, ctrl->value);
break;
case V4L2_CID_GAIN:
- err += sn9c102_i2c_write(cam, 0x0e, ctrl->value & 0x1f);
+ err += sn9c102_i2c_write(cam, 0x0e, ctrl->value);
break;
case V4L2_CID_BRIGHTNESS:
- err += sn9c102_i2c_write(cam, 0x0d, 0x1f-(ctrl->value & 0x1f));
+ err += sn9c102_i2c_write(cam, 0x0d, 0x1f - ctrl->value);
break;
case V4L2_CID_CONTRAST:
- err += sn9c102_i2c_write(cam, 0x0f, ctrl->value & 0x03);
+ err += sn9c102_i2c_write(cam, 0x0f, ctrl->value);
break;
default:
return -EINVAL;
}
err += sn9c102_i2c_write(cam, 0x13, 0x01);
- return err;
+ return err ? -EIO : 0;
}
diff --git a/drivers/usb/media/sn9c102_pas202bcb.c b/drivers/usb/media/sn9c102_pas202bcb.c
index 26944eaf85d0..72063e885871 100644
--- a/drivers/usb/media/sn9c102_pas202bcb.c
+++ b/drivers/usb/media/sn9c102_pas202bcb.c
@@ -1,5 +1,5 @@
/***************************************************************************
- * Driver for PAS202BCB image sensor connected to the SN9C10[12] PC Camera *
+ * Driver for PAS202BCB image sensor connected to the SN9C10x PC Camera *
* Controllers *
* *
* Copyright (C) 2004 by Carlos Eduardo Medaglia Dyonisio *
@@ -36,18 +36,19 @@ static int pas202bcb_init(struct sn9c102_device* cam)
err += sn9c102_write_reg(cam, 0x00, 0x11);
err += sn9c102_write_reg(cam, 0x00, 0x14);
err += sn9c102_write_reg(cam, 0x20, 0x17);
- err += sn9c102_write_reg(cam, 0x20, 0x19);
+ err += sn9c102_write_reg(cam, 0x30, 0x19);
err += sn9c102_write_reg(cam, 0x09, 0x18);
- err += sn9c102_i2c_write(cam, 0x02, 0x0c);
+ err += sn9c102_i2c_write(cam, 0x02, 0x14);
err += sn9c102_i2c_write(cam, 0x03, 0x40);
err += sn9c102_i2c_write(cam, 0x04, 0x07);
err += sn9c102_i2c_write(cam, 0x05, 0x25);
err += sn9c102_i2c_write(cam, 0x0d, 0x2c);
err += sn9c102_i2c_write(cam, 0x0e, 0x01);
err += sn9c102_i2c_write(cam, 0x0f, 0xa9);
- err += sn9c102_i2c_write(cam, 0x08, 0x01);
+ err += sn9c102_i2c_write(cam, 0x10, 0x08);
err += sn9c102_i2c_write(cam, 0x0b, 0x01);
+ err += sn9c102_i2c_write(cam, 0x0c, 0x04);
err += sn9c102_i2c_write(cam, 0x13, 0x63);
err += sn9c102_i2c_write(cam, 0x15, 0x70);
err += sn9c102_i2c_write(cam, 0x11, 0x01);
@@ -95,23 +96,23 @@ static int pas202bcb_set_ctrl(struct sn9c102_device* cam,
switch (ctrl->id) {
case V4L2_CID_RED_BALANCE:
- err += sn9c102_i2c_write(cam, 0x09, ctrl->value & 0x0f);
+ err += sn9c102_i2c_write(cam, 0x09, ctrl->value);
break;
case V4L2_CID_BLUE_BALANCE:
- err += sn9c102_i2c_write(cam, 0x07, ctrl->value & 0x0f);
+ err += sn9c102_i2c_write(cam, 0x07, ctrl->value);
break;
case V4L2_CID_GAIN:
- err += sn9c102_i2c_write(cam, 0x10, ctrl->value & 0x1f);
+ err += sn9c102_i2c_write(cam, 0x10, ctrl->value);
break;
case V4L2_CID_BRIGHTNESS:
- err += sn9c102_i2c_write(cam, 0x06, 0x0f-(ctrl->value & 0x0f));
+ err += sn9c102_i2c_write(cam, 0x06, 0x0f - ctrl->value);
break;
default:
return -EINVAL;
}
err += sn9c102_i2c_write(cam, 0x11, 0x01);
- return err;
+ return err ? -EIO : 0;
}
@@ -217,7 +218,7 @@ int sn9c102_probe_pas202bcb(struct sn9c102_device* cam)
* NOTE: do NOT change the values!
*/
err += sn9c102_write_reg(cam, 0x01, 0x01); /* sensor power down */
- err += sn9c102_write_reg(cam, 0x00, 0x01); /* sensor power on */
+ err += sn9c102_write_reg(cam, 0x40, 0x01); /* sensor power on */
err += sn9c102_write_reg(cam, 0x28, 0x17); /* sensor clock at 24 MHz */
if (err)
return -EIO;
diff --git a/drivers/usb/media/sn9c102_sensor.h b/drivers/usb/media/sn9c102_sensor.h
index 3e7e4a2578bc..01bcad14ca6d 100644
--- a/drivers/usb/media/sn9c102_sensor.h
+++ b/drivers/usb/media/sn9c102_sensor.h
@@ -1,5 +1,5 @@
/***************************************************************************
- * API for image sensors connected to the SN9C10[12] PC Camera Controllers *
+ * API for image sensors connected to the SN9C10x PC Camera Controllers *
* *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
@@ -89,17 +89,44 @@ sn9c102_attach_sensor(struct sn9c102_device* cam,
/* Each SN9C10X camera has proper PID/VID identifiers. Add them here in case.*/
#define SN9C102_ID_TABLE \
static const struct usb_device_id sn9c102_id_table[] = { \
- { USB_DEVICE(0xc45, 0x6001), }, /* TAS5110C1B */ \
- { USB_DEVICE(0xc45, 0x6005), }, /* TAS5110C1B */ \
- { USB_DEVICE(0xc45, 0x6009), }, /* PAS106B */ \
- { USB_DEVICE(0xc45, 0x600d), }, /* PAS106B */ \
- { USB_DEVICE(0xc45, 0x6024), }, \
- { USB_DEVICE(0xc45, 0x6025), }, /* TAS5130D1B and TAS5110C1B */ \
- { USB_DEVICE(0xc45, 0x6028), }, /* PAS202BCB */ \
- { USB_DEVICE(0xc45, 0x6029), }, /* PAS106B */ \
- { USB_DEVICE(0xc45, 0x602a), }, /* HV7131[D|E1] */ \
- { USB_DEVICE(0xc45, 0x602c), }, /* OV7620 */ \
- { USB_DEVICE(0xc45, 0x6030), }, /* MI03 */ \
+ { USB_DEVICE(0x0c45, 0x6001), }, /* TAS5110C1B */ \
+ { USB_DEVICE(0x0c45, 0x6005), }, /* TAS5110C1B */ \
+ { USB_DEVICE(0x0c45, 0x6009), }, /* PAS106B */ \
+ { USB_DEVICE(0x0c45, 0x600d), }, /* PAS106B */ \
+ { USB_DEVICE(0x0c45, 0x6024), }, \
+ { USB_DEVICE(0x0c45, 0x6025), }, /* TAS5130D1B and TAS5110C1B */ \
+ { USB_DEVICE(0x0c45, 0x6028), }, /* PAS202BCB */ \
+ { USB_DEVICE(0x0c45, 0x6029), }, /* PAS106B */ \
+ { USB_DEVICE(0x0c45, 0x602a), }, /* HV7131[D|E1] */ \
+ { USB_DEVICE(0x0c45, 0x602b), }, \
+ { USB_DEVICE(0x0c45, 0x602c), }, /* OV7620 */ \
+ { USB_DEVICE(0x0c45, 0x6030), }, /* MI03x */ \
+ { USB_DEVICE(0x0c45, 0x6080), }, \
+ { USB_DEVICE(0x0c45, 0x6082), }, /* MI0343 and MI0360 */ \
+ { USB_DEVICE(0x0c45, 0x6083), }, /* HV7131[D|E1] */ \
+ { USB_DEVICE(0x0c45, 0x6088), }, \
+ { USB_DEVICE(0x0c45, 0x608a), }, \
+ { USB_DEVICE(0x0c45, 0x608b), }, \
+ { USB_DEVICE(0x0c45, 0x608c), }, /* HV7131x */ \
+ { USB_DEVICE(0x0c45, 0x608e), }, /* CIS-VF10 */ \
+ { USB_DEVICE(0x0c45, 0x608f), }, /* OV7630 */ \
+ { USB_DEVICE(0x0c45, 0x60a0), }, \
+ { USB_DEVICE(0x0c45, 0x60a2), }, \
+ { USB_DEVICE(0x0c45, 0x60a3), }, \
+ { USB_DEVICE(0x0c45, 0x60a8), }, /* PAS106B */ \
+ { USB_DEVICE(0x0c45, 0x60aa), }, /* TAS5130D1B */ \
+ { USB_DEVICE(0x0c45, 0x60ab), }, /* TAS5110C1B */ \
+ { USB_DEVICE(0x0c45, 0x60ac), }, \
+ { USB_DEVICE(0x0c45, 0x60ae), }, \
+ { USB_DEVICE(0x0c45, 0x60af), }, /* PAS202BCB */ \
+ { USB_DEVICE(0x0c45, 0x60b0), }, \
+ { USB_DEVICE(0x0c45, 0x60b2), }, \
+ { USB_DEVICE(0x0c45, 0x60b3), }, \
+ { USB_DEVICE(0x0c45, 0x60b8), }, \
+ { USB_DEVICE(0x0c45, 0x60ba), }, \
+ { USB_DEVICE(0x0c45, 0x60bb), }, \
+ { USB_DEVICE(0x0c45, 0x60bc), }, \
+ { USB_DEVICE(0x0c45, 0x60be), }, \
{ } \
};
@@ -159,6 +186,9 @@ enum sn9c102_i2c_interface {
SN9C102_I2C_3WIRES,
};
+#define SN9C102_I2C_SLAVEID_FICTITIOUS 0xff
+#define SN9C102_I2C_SLAVEID_UNAVAILABLE 0x00
+
struct sn9c102_sensor {
char name[32], /* sensor name */
maintainer[64]; /* name of the mantainer <email> */
@@ -173,9 +203,7 @@ struct sn9c102_sensor {
/*
These identifiers must be provided if the image sensor implements
- the standard I2C protocol. TASC sensors don't, although they have a
- serial interface: so this is a case where the "raw" I2C version
- could be helpful.
+ the standard I2C protocol.
*/
u8 slave_read_id, slave_write_id; /* reg. 0x09 */
@@ -214,7 +242,8 @@ struct sn9c102_sensor {
the list above. The returned value must follow the V4L2
specifications for the VIDIOC_G|C_CTRL ioctls. V4L2_CID_H|VCENTER
are not supported by this driver, so do not implement them. Also,
- passed values are NOT checked to see if they are out of bounds.
+ you don't have to check whether the passed values are out of bounds,
+ given that this is done by the core module.
*/
struct v4l2_cropcap cropcap;
diff --git a/drivers/usb/media/sn9c102_tas5110c1b.c b/drivers/usb/media/sn9c102_tas5110c1b.c
index 68e1b2e0ce18..ce8b47b59a75 100644
--- a/drivers/usb/media/sn9c102_tas5110c1b.c
+++ b/drivers/usb/media/sn9c102_tas5110c1b.c
@@ -1,6 +1,6 @@
/***************************************************************************
- * Driver for TAS5110C1B image sensor connected to the SN9C10[12] PC *
- * Camera Controllers *
+ * Driver for TAS5110C1B image sensor connected to the SN9C10x PC Camera *
+ * Controllers *
* *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
@@ -24,6 +24,8 @@
static struct sn9c102_sensor tas5110c1b;
+static struct v4l2_control tas5110c1b_gain;
+
static int tas5110c1b_init(struct sn9c102_device* cam)
{
@@ -38,25 +40,42 @@ static int tas5110c1b_init(struct sn9c102_device* cam)
err += sn9c102_write_reg(cam, 0x06, 0x18);
err += sn9c102_write_reg(cam, 0xfb, 0x19);
- err += sn9c102_i2c_try_raw_write(cam, &tas5110c1b, 4, 0x11, 0x00, 0xc0,
- 0x80, 0, 0);
+ err += sn9c102_i2c_write(cam, 0xc0, 0x80);
return err;
}
+static int tas5110c1b_get_ctrl(struct sn9c102_device* cam,
+ struct v4l2_control* ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ ctrl->value = tas5110c1b_gain.value;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
static int tas5110c1b_set_ctrl(struct sn9c102_device* cam,
const struct v4l2_control* ctrl)
{
+ int err = 0;
+
switch (ctrl->id) {
case V4L2_CID_GAIN:
- return sn9c102_i2c_try_raw_write(cam, &tas5110c1b, 4, 0x11,
- 0x02, 0x20,
- 0xff - (ctrl->value & 0xff),
- 0, 0);
+ if (!(err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value)))
+ tas5110c1b_gain.value = ctrl->value;
+ break;
default:
return -EINVAL;
}
+
+ return err ? -EIO : 0;
}
@@ -85,6 +104,8 @@ static struct sn9c102_sensor tas5110c1b = {
.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
.frequency = SN9C102_I2C_100KHZ,
.interface = SN9C102_I2C_3WIRES,
+ .slave_read_id = SN9C102_I2C_SLAVEID_UNAVAILABLE,
+ .slave_write_id = SN9C102_I2C_SLAVEID_FICTITIOUS,
.init = &tas5110c1b_init,
.qctrl = {
{
@@ -92,9 +113,9 @@ static struct sn9c102_sensor tas5110c1b = {
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "global gain",
.minimum = 0x00,
- .maximum = 0xff,
+ .maximum = 0xf6,
.step = 0x01,
- .default_value = 0x48,
+ .default_value = 0x40,
.flags = 0,
},
},
@@ -113,6 +134,7 @@ static struct sn9c102_sensor tas5110c1b = {
.height = 288,
},
},
+ .get_ctrl = &tas5110c1b_get_ctrl,
.set_crop = &tas5110c1b_set_crop,
.pix_format = {
.width = 352,
@@ -130,7 +152,8 @@ int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam)
/* At the moment, sensor detection is based on USB pid/vid */
if (tas5110c1b.usbdev->descriptor.idProduct != 0x6001 &&
- tas5110c1b.usbdev->descriptor.idProduct != 0x6005)
+ tas5110c1b.usbdev->descriptor.idProduct != 0x6005 &&
+ tas5110c1b.usbdev->descriptor.idProduct != 0x60ab)
return -ENODEV;
return 0;
diff --git a/drivers/usb/media/sn9c102_tas5130d1b.c b/drivers/usb/media/sn9c102_tas5130d1b.c
index 0bab19435399..0048e9c20d80 100644
--- a/drivers/usb/media/sn9c102_tas5130d1b.c
+++ b/drivers/usb/media/sn9c102_tas5130d1b.c
@@ -1,6 +1,6 @@
/***************************************************************************
- * Driver for TAS5130D1B image sensor connected to the SN9C10[12] PC *
- * Camera Controllers *
+ * Driver for TAS5130D1B image sensor connected to the SN9C10x PC Camera *
+ * Controllers *
* *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
@@ -24,6 +24,8 @@
static struct sn9c102_sensor tas5130d1b;
+static struct v4l2_control tas5130d1b_gain, tas5130d1b_exposure;
+
static int tas5130d1b_init(struct sn9c102_device* cam)
{
@@ -38,25 +40,47 @@ static int tas5130d1b_init(struct sn9c102_device* cam)
err += sn9c102_write_reg(cam, 0x60, 0x17);
err += sn9c102_write_reg(cam, 0x07, 0x18);
- err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0x40,
- 0x47, 0, 0);
-
return err;
}
+static int tas5130d1b_get_ctrl(struct sn9c102_device* cam,
+ struct v4l2_control* ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ ctrl->value = tas5130d1b_gain.value;
+ break;
+ case V4L2_CID_EXPOSURE:
+ ctrl->value = tas5130d1b_exposure.value;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
static int tas5130d1b_set_ctrl(struct sn9c102_device* cam,
const struct v4l2_control* ctrl)
{
+ int err = 0;
+
switch (ctrl->id) {
case V4L2_CID_GAIN:
- return sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11,
- 0x02, 0x20,
- 0xff - (ctrl->value & 0xff),
- 0, 0);
+ if (!(err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value)))
+ tas5130d1b_gain.value = ctrl->value;
+ break;
+ case V4L2_CID_EXPOSURE:
+ if (!(err += sn9c102_i2c_write(cam, 0x40, 0x47 - ctrl->value)))
+ tas5130d1b_exposure.value = ctrl->value;
+ break;
default:
return -EINVAL;
}
+
+ return err ? -EIO : 0;
}
@@ -85,6 +109,8 @@ static struct sn9c102_sensor tas5130d1b = {
.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
.frequency = SN9C102_I2C_100KHZ,
.interface = SN9C102_I2C_3WIRES,
+ .slave_read_id = SN9C102_I2C_SLAVEID_UNAVAILABLE,
+ .slave_write_id = SN9C102_I2C_SLAVEID_FICTITIOUS,
.init = &tas5130d1b_init,
.qctrl = {
{
@@ -92,12 +118,23 @@ static struct sn9c102_sensor tas5130d1b = {
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "global gain",
.minimum = 0x00,
- .maximum = 0xff,
+ .maximum = 0xf6,
+ .step = 0x02,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x00,
+ .maximum = 0x47,
.step = 0x01,
.default_value = 0x00,
.flags = 0,
},
},
+ .get_ctrl = &tas5130d1b_get_ctrl,
.set_ctrl = &tas5130d1b_set_ctrl,
.cropcap = {
.bounds = {
@@ -129,7 +166,8 @@ int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam)
sn9c102_attach_sensor(cam, &tas5130d1b);
/* At the moment, sensor detection is based on USB pid/vid */
- if (tas5130d1b.usbdev->descriptor.idProduct != 0x6025)
+ if (tas5130d1b.usbdev->descriptor.idProduct != 0x6025 &&
+ tas5130d1b.usbdev->descriptor.idProduct != 0x60aa)
return -ENODEV;
return 0;
diff --git a/drivers/usb/media/stv680.c b/drivers/usb/media/stv680.c
index cd73a45d0dd0..a91870f0b412 100644
--- a/drivers/usb/media/stv680.c
+++ b/drivers/usb/media/stv680.c
@@ -725,7 +725,7 @@ static int stv680_stop_stream (struct usb_stv *stv680)
for (i = 0; i < STV680_NUMSBUF; i++)
if (stv680->urb[i]) {
- usb_unlink_urb (stv680->urb[i]);
+ usb_kill_urb (stv680->urb[i]);
usb_free_urb (stv680->urb[i]);
stv680->urb[i] = NULL;
kfree (stv680->sbuf[i].data);
@@ -1457,7 +1457,7 @@ static inline void usb_stv680_remove_disconnected (struct usb_stv *stv680)
for (i = 0; i < STV680_NUMSBUF; i++)
if (stv680->urb[i]) {
- usb_unlink_urb (stv680->urb[i]);
+ usb_kill_urb (stv680->urb[i]);
usb_free_urb (stv680->urb[i]);
stv680->urb[i] = NULL;
kfree (stv680->sbuf[i].data);
diff --git a/drivers/usb/media/usbvideo.c b/drivers/usb/media/usbvideo.c
index 1107e398d6f3..122b7accf1e1 100644
--- a/drivers/usb/media/usbvideo.c
+++ b/drivers/usb/media/usbvideo.c
@@ -1910,9 +1910,7 @@ static void usbvideo_StopDataPump(struct uvd *uvd)
/* Unschedule all of the iso td's */
for (i=0; i < USBVIDEO_NUMSBUF; i++) {
- j = usb_unlink_urb(uvd->sbuf[i].urb);
- if (j < 0)
- err("%s: usb_unlink_urb() error %d.", __FUNCTION__, j);
+ usb_kill_urb(uvd->sbuf[i].urb);
}
if (uvd->debug > 1)
info("%s: streaming=0", __FUNCTION__);
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index 8c958af4a1d3..d6401c0a80d5 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -121,18 +121,6 @@ config USB_CYTHERM
To compile this driver as a module, choose M here: the
module will be called cytherm.
-config USB_SPEEDTOUCH
- tristate "Alcatel Speedtouch USB support"
- depends on USB && ATM
- select CRC32
- help
- Say Y here if you have an Alcatel SpeedTouch USB or SpeedTouch 330
- modem. In order to use your modem you will need to install some user
- space tools, see <http://www.linux-usb.org/SpeedTouch/> for details.
-
- To compile this driver as a module, choose M here: the
- module will be called speedtch.
-
config USB_PHIDGETSERVO
tristate "USB PhidgetServo support"
depends on USB
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index d641a470e8d7..074a1d66250b 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -11,7 +11,6 @@ obj-$(CONFIG_USB_LCD) += usblcd.o
obj-$(CONFIG_USB_LED) += usbled.o
obj-$(CONFIG_USB_LEGOTOWER) += legousbtower.o
obj-$(CONFIG_USB_RIO500) += rio500.o
-obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o
obj-$(CONFIG_USB_TEST) += usbtest.o
obj-$(CONFIG_USB_TIGL) += tiglusb.o
obj-$(CONFIG_USB_USS720) += uss720.o
diff --git a/drivers/usb/misc/auerswald.c b/drivers/usb/misc/auerswald.c
index c4bebdacda5c..94842c34a4c4 100644
--- a/drivers/usb/misc/auerswald.c
+++ b/drivers/usb/misc/auerswald.c
@@ -516,7 +516,7 @@ static void auerchain_unlink_all (pauerchain_t acp)
urbp = acep->urbp;
urbp->transfer_flags &= ~URB_ASYNC_UNLINK;
dbg ("unlink active urb");
- usb_unlink_urb (urbp);
+ usb_kill_urb (urbp);
}
}
@@ -1171,22 +1171,16 @@ intoend:
endpoint. This function returns 0 if successful or an error code.
NOTE: no mutex please!
*/
-static int auerswald_int_release (pauerswald_t cp)
+static void auerswald_int_release (pauerswald_t cp)
{
- int ret = 0;
dbg ("auerswald_int_release");
/* stop the int endpoint */
- if (cp->inturbp) {
- ret = usb_unlink_urb (cp->inturbp);
- if (ret)
- dbg ("nonzero int unlink result received: %d", ret);
- }
+ if (cp->inturbp)
+ usb_kill_urb (cp->inturbp);
/* deallocate memory */
auerswald_int_free (cp);
-
- return ret;
}
/* --------------------------------------------------------------------- */
diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c
index 06c4bfa2f4b9..e83e91148970 100644
--- a/drivers/usb/misc/legousbtower.c
+++ b/drivers/usb/misc/legousbtower.c
@@ -505,12 +505,12 @@ static void tower_abort_transfers (struct lego_usb_tower *dev)
dev->interrupt_in_running = 0;
mb();
if (dev->interrupt_in_urb != NULL && dev->udev) {
- usb_unlink_urb (dev->interrupt_in_urb);
+ usb_kill_urb (dev->interrupt_in_urb);
}
}
if (dev->interrupt_out_busy) {
if (dev->interrupt_out_urb != NULL && dev->udev) {
- usb_unlink_urb (dev->interrupt_out_urb);
+ usb_kill_urb (dev->interrupt_out_urb);
}
}
diff --git a/drivers/usb/misc/speedtch.c b/drivers/usb/misc/speedtch.c
deleted file mode 100644
index 667e2d1b227c..000000000000
--- a/drivers/usb/misc/speedtch.c
+++ /dev/null
@@ -1,1373 +0,0 @@
-/******************************************************************************
- * speedtouch.c - Alcatel SpeedTouch USB xDSL modem driver
- *
- * Copyright (C) 2001, Alcatel
- * Copyright (C) 2003, Duncan Sands
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 59
- * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- ******************************************************************************/
-
-/*
- * Written by Johan Verrept, maintained by Duncan Sands (duncan.sands@free.fr)
- *
- * 1.7+: - See the check-in logs
- *
- * 1.6: - No longer opens a connection if the firmware is not loaded
- * - Added support for the speedtouch 330
- * - Removed the limit on the number of devices
- * - Module now autoloads on device plugin
- * - Merged relevant parts of sarlib
- * - Replaced the kernel thread with a tasklet
- * - New packet transmission code
- * - Changed proc file contents
- * - Fixed all known SMP races
- * - Many fixes and cleanups
- * - Various fixes by Oliver Neukum (oliver@neukum.name)
- *
- * 1.5A: - Version for inclusion in 2.5 series kernel
- * - Modifications by Richard Purdie (rpurdie@rpsys.net)
- * - made compatible with kernel 2.5.6 onwards by changing
- * udsl_usb_send_data_context->urb to a pointer and adding code
- * to alloc and free it
- * - remove_wait_queue() added to udsl_atm_processqueue_thread()
- *
- * 1.5: - fixed memory leak when atmsar_decode_aal5 returned NULL.
- * (reported by stephen.robinson@zen.co.uk)
- *
- * 1.4: - changed the spin_lock() under interrupt to spin_lock_irqsave()
- * - unlink all active send urbs of a vcc that is being closed.
- *
- * 1.3.1: - added the version number
- *
- * 1.3: - Added multiple send urb support
- * - fixed memory leak and vcc->tx_inuse starvation bug
- * when not enough memory left in vcc.
- *
- * 1.2: - Fixed race condition in udsl_usb_send_data()
- * 1.1: - Turned off packet debugging
- *
- */
-
-#include <asm/semaphore.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/errno.h>
-#include <linux/proc_fs.h>
-#include <linux/slab.h>
-#include <linux/list.h>
-#include <asm/uaccess.h>
-#include <linux/smp_lock.h>
-#include <linux/interrupt.h>
-#include <linux/atm.h>
-#include <linux/atmdev.h>
-#include <linux/crc32.h>
-#include <linux/init.h>
-
-/*
-#define DEBUG
-#define VERBOSE_DEBUG
-*/
-
-#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG)
-# define DEBUG
-#endif
-
-#include <linux/usb.h>
-
-#ifdef DEBUG
-#define DEBUG_ON(x) BUG_ON(x)
-#else
-#define DEBUG_ON(x) do { if (x); } while (0)
-#endif
-
-#ifdef VERBOSE_DEBUG
-static int udsl_print_packet (const unsigned char *data, int len);
-#define PACKETDEBUG(arg...) udsl_print_packet (arg)
-#define vdbg(arg...) dbg (arg)
-#else
-#define PACKETDEBUG(arg...)
-#define vdbg(arg...)
-#endif
-
-#define DRIVER_AUTHOR "Johan Verrept, Duncan Sands <duncan.sands@free.fr>"
-#define DRIVER_VERSION "1.8"
-#define DRIVER_DESC "Alcatel SpeedTouch USB driver version " DRIVER_VERSION
-
-static const char udsl_driver_name [] = "speedtch";
-
-#define SPEEDTOUCH_VENDORID 0x06b9
-#define SPEEDTOUCH_PRODUCTID 0x4061
-
-#define UDSL_MAX_RCV_URBS 4
-#define UDSL_MAX_SND_URBS 4
-#define UDSL_MAX_RCV_BUFS 8
-#define UDSL_MAX_SND_BUFS 8
-#define UDSL_MAX_RCV_BUF_SIZE 1024 /* ATM cells */
-#define UDSL_MAX_SND_BUF_SIZE 1024 /* ATM cells */
-#define UDSL_DEFAULT_RCV_URBS 2
-#define UDSL_DEFAULT_SND_URBS 2
-#define UDSL_DEFAULT_RCV_BUFS 4
-#define UDSL_DEFAULT_SND_BUFS 4
-#define UDSL_DEFAULT_RCV_BUF_SIZE 64 /* ATM cells */
-#define UDSL_DEFAULT_SND_BUF_SIZE 64 /* ATM cells */
-
-static unsigned int num_rcv_urbs = UDSL_DEFAULT_RCV_URBS;
-static unsigned int num_snd_urbs = UDSL_DEFAULT_SND_URBS;
-static unsigned int num_rcv_bufs = UDSL_DEFAULT_RCV_BUFS;
-static unsigned int num_snd_bufs = UDSL_DEFAULT_SND_BUFS;
-static unsigned int rcv_buf_size = UDSL_DEFAULT_RCV_BUF_SIZE;
-static unsigned int snd_buf_size = UDSL_DEFAULT_SND_BUF_SIZE;
-
-module_param (num_rcv_urbs, uint, 0444);
-MODULE_PARM_DESC (num_rcv_urbs, "Number of urbs used for reception (range: 0-" __MODULE_STRING (UDSL_MAX_RCV_URBS) ", default: " __MODULE_STRING (UDSL_DEFAULT_RCV_URBS) ")");
-
-module_param (num_snd_urbs, uint, 0444);
-MODULE_PARM_DESC (num_snd_urbs, "Number of urbs used for transmission (range: 0-" __MODULE_STRING (UDSL_MAX_SND_URBS) ", default: " __MODULE_STRING (UDSL_DEFAULT_SND_URBS) ")");
-
-module_param (num_rcv_bufs, uint, 0444);
-MODULE_PARM_DESC (num_rcv_bufs, "Number of buffers used for reception (range: 0-" __MODULE_STRING (UDSL_MAX_RCV_BUFS) ", default: " __MODULE_STRING (UDSL_DEFAULT_RCV_BUFS) ")");
-
-module_param (num_snd_bufs, uint, 0444);
-MODULE_PARM_DESC (num_snd_bufs, "Number of buffers used for transmission (range: 0-" __MODULE_STRING (UDSL_MAX_SND_BUFS) ", default: " __MODULE_STRING (UDSL_DEFAULT_SND_BUFS) ")");
-
-module_param (rcv_buf_size, uint, 0444);
-MODULE_PARM_DESC (rcv_buf_size, "Size of the buffers used for reception (range: 0-" __MODULE_STRING (UDSL_MAX_RCV_BUF_SIZE) ", default: " __MODULE_STRING (UDSL_DEFAULT_RCV_BUF_SIZE) ")");
-
-module_param (snd_buf_size, uint, 0444);
-MODULE_PARM_DESC (snd_buf_size, "Size of the buffers used for transmission (range: 0-" __MODULE_STRING (UDSL_MAX_SND_BUF_SIZE) ", default: " __MODULE_STRING (UDSL_DEFAULT_SND_BUF_SIZE) ")");
-
-#define UDSL_IOCTL_LINE_UP 1
-#define UDSL_IOCTL_LINE_DOWN 2
-
-#define UDSL_ENDPOINT_DATA_OUT 0x07
-#define UDSL_ENDPOINT_DATA_IN 0x87
-
-#define ATM_CELL_HEADER (ATM_CELL_SIZE - ATM_CELL_PAYLOAD)
-#define UDSL_NUM_CELLS(x) (((x) + ATM_AAL5_TRAILER + ATM_CELL_PAYLOAD - 1) / ATM_CELL_PAYLOAD)
-
-#define hex2int(c) ( (c >= '0') && (c <= '9') ? (c - '0') : ((c & 0xf) + 9) )
-
-static struct usb_device_id udsl_usb_ids [] = {
- { USB_DEVICE (SPEEDTOUCH_VENDORID, SPEEDTOUCH_PRODUCTID) },
- { }
-};
-
-MODULE_DEVICE_TABLE (usb, udsl_usb_ids);
-
-/* receive */
-
-struct udsl_receive_buffer {
- struct list_head list;
- unsigned char *base;
- unsigned int filled_cells;
-};
-
-struct udsl_receiver {
- struct list_head list;
- struct udsl_receive_buffer *buffer;
- struct urb *urb;
- struct udsl_instance_data *instance;
-};
-
-struct udsl_vcc_data {
- /* vpi/vci lookup */
- struct list_head list;
- short vpi;
- int vci;
- struct atm_vcc *vcc;
-
- /* raw cell reassembly */
- struct sk_buff *sarb;
-};
-
-/* send */
-
-struct udsl_send_buffer {
- struct list_head list;
- unsigned char *base;
- unsigned char *free_start;
- unsigned int free_cells;
-};
-
-struct udsl_sender {
- struct list_head list;
- struct udsl_send_buffer *buffer;
- struct urb *urb;
- struct udsl_instance_data *instance;
-};
-
-struct udsl_control {
- struct atm_skb_data atm_data;
- unsigned int num_cells;
- unsigned int num_entire;
- unsigned int pdu_padding;
- unsigned char cell_header [ATM_CELL_HEADER];
- unsigned char aal5_trailer [ATM_AAL5_TRAILER];
-};
-
-#define UDSL_SKB(x) ((struct udsl_control *)(x)->cb)
-
-/* main driver data */
-
-struct udsl_instance_data {
- struct semaphore serialize;
-
- /* USB device part */
- struct usb_device *usb_dev;
- char description [64];
- int firmware_loaded;
-
- /* ATM device part */
- struct atm_dev *atm_dev;
- struct list_head vcc_list;
-
- /* receive */
- struct udsl_receiver receivers [UDSL_MAX_RCV_URBS];
- struct udsl_receive_buffer receive_buffers [UDSL_MAX_RCV_BUFS];
-
- spinlock_t receive_lock;
- struct list_head spare_receivers;
- struct list_head filled_receive_buffers;
-
- struct tasklet_struct receive_tasklet;
- struct list_head spare_receive_buffers;
-
- /* send */
- struct udsl_sender senders [UDSL_MAX_SND_URBS];
- struct udsl_send_buffer send_buffers [UDSL_MAX_SND_BUFS];
-
- struct sk_buff_head sndqueue;
-
- spinlock_t send_lock;
- struct list_head spare_senders;
- struct list_head spare_send_buffers;
-
- struct tasklet_struct send_tasklet;
- struct sk_buff *current_skb; /* being emptied */
- struct udsl_send_buffer *current_buffer; /* being filled */
- struct list_head filled_send_buffers;
-};
-
-/* ATM */
-
-static void udsl_atm_dev_close (struct atm_dev *dev);
-static int udsl_atm_open (struct atm_vcc *vcc);
-static void udsl_atm_close (struct atm_vcc *vcc);
-static int udsl_atm_ioctl (struct atm_dev *dev, unsigned int cmd, void __user *arg);
-static int udsl_atm_send (struct atm_vcc *vcc, struct sk_buff *skb);
-static int udsl_atm_proc_read (struct atm_dev *atm_dev, loff_t *pos, char *page);
-
-static struct atmdev_ops udsl_atm_devops = {
- .dev_close = udsl_atm_dev_close,
- .open = udsl_atm_open,
- .close = udsl_atm_close,
- .ioctl = udsl_atm_ioctl,
- .send = udsl_atm_send,
- .proc_read = udsl_atm_proc_read,
- .owner = THIS_MODULE,
-};
-
-/* USB */
-
-static int udsl_usb_probe (struct usb_interface *intf, const struct usb_device_id *id);
-static void udsl_usb_disconnect (struct usb_interface *intf);
-static int udsl_usb_ioctl (struct usb_interface *intf, unsigned int code, void *user_data);
-
-static struct usb_driver udsl_usb_driver = {
- .owner = THIS_MODULE,
- .name = udsl_driver_name,
- .probe = udsl_usb_probe,
- .disconnect = udsl_usb_disconnect,
- .ioctl = udsl_usb_ioctl,
- .id_table = udsl_usb_ids,
-};
-
-
-/***********
-** misc **
-***********/
-
-static inline void udsl_pop (struct atm_vcc *vcc, struct sk_buff *skb)
-{
- if (vcc->pop)
- vcc->pop (vcc, skb);
- else
- dev_kfree_skb (skb);
-}
-
-
-/*************
-** decode **
-*************/
-
-static inline struct udsl_vcc_data *udsl_find_vcc (struct udsl_instance_data *instance, short vpi, int vci)
-{
- struct udsl_vcc_data *vcc;
-
- list_for_each_entry (vcc, &instance->vcc_list, list)
- if ((vcc->vci == vci) && (vcc->vpi == vpi))
- return vcc;
- return NULL;
-}
-
-static void udsl_extract_cells (struct udsl_instance_data *instance, unsigned char *source, unsigned int howmany)
-{
- struct udsl_vcc_data *cached_vcc = NULL;
- struct atm_vcc *vcc;
- struct sk_buff *sarb;
- struct udsl_vcc_data *vcc_data;
- int cached_vci = 0;
- unsigned int i;
- int pti;
- int vci;
- short cached_vpi = 0;
- short vpi;
-
- for (i = 0; i < howmany; i++, source += ATM_CELL_SIZE) {
- vpi = ((source [0] & 0x0f) << 4) | (source [1] >> 4);
- vci = ((source [1] & 0x0f) << 12) | (source [2] << 4) | (source [3] >> 4);
- pti = (source [3] & 0x2) != 0;
-
- vdbg ("udsl_extract_cells: vpi %hd, vci %d, pti %d", vpi, vci, pti);
-
- if (cached_vcc && (vci == cached_vci) && (vpi == cached_vpi))
- vcc_data = cached_vcc;
- else if ((vcc_data = udsl_find_vcc (instance, vpi, vci))) {
- cached_vcc = vcc_data;
- cached_vpi = vpi;
- cached_vci = vci;
- } else {
- dbg ("udsl_extract_cells: unknown vpi/vci (%hd/%d)!", vpi, vci);
- continue;
- }
-
- vcc = vcc_data->vcc;
- sarb = vcc_data->sarb;
-
- if (sarb->tail + ATM_CELL_PAYLOAD > sarb->end) {
- dbg ("udsl_extract_cells: buffer overrun (sarb->len %u, vcc: 0x%p)!", sarb->len, vcc);
- /* discard cells already received */
- skb_trim (sarb, 0);
- }
-
- memcpy (sarb->tail, source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD);
- __skb_put (sarb, ATM_CELL_PAYLOAD);
-
- if (pti) {
- struct sk_buff *skb;
- unsigned int length;
- unsigned int pdu_length;
-
- length = (source [ATM_CELL_SIZE - 6] << 8) + source [ATM_CELL_SIZE - 5];
-
- /* guard against overflow */
- if (length > ATM_MAX_AAL5_PDU) {
- dbg ("udsl_extract_cells: bogus length %u (vcc: 0x%p)!", length, vcc);
- atomic_inc (&vcc->stats->rx_err);
- goto out;
- }
-
- pdu_length = UDSL_NUM_CELLS (length) * ATM_CELL_PAYLOAD;
-
- if (sarb->len < pdu_length) {
- dbg ("udsl_extract_cells: bogus pdu_length %u (sarb->len: %u, vcc: 0x%p)!", pdu_length, sarb->len, vcc);
- atomic_inc (&vcc->stats->rx_err);
- goto out;
- }
-
- if (crc32_be (~0, sarb->tail - pdu_length, pdu_length) != 0xc704dd7b) {
- dbg ("udsl_extract_cells: packet failed crc check (vcc: 0x%p)!", vcc);
- atomic_inc (&vcc->stats->rx_err);
- goto out;
- }
-
- vdbg ("udsl_extract_cells: got packet (length: %u, pdu_length: %u, vcc: 0x%p)", length, pdu_length, vcc);
-
- if (!(skb = dev_alloc_skb (length))) {
- dbg ("udsl_extract_cells: no memory for skb (length: %u)!", length);
- atomic_inc (&vcc->stats->rx_drop);
- goto out;
- }
-
- vdbg ("udsl_extract_cells: allocated new sk_buff (skb: 0x%p, skb->truesize: %u)", skb, skb->truesize);
-
- if (!atm_charge (vcc, skb->truesize)) {
- dbg ("udsl_extract_cells: failed atm_charge (skb->truesize: %u)!", skb->truesize);
- dev_kfree_skb (skb);
- goto out; /* atm_charge increments rx_drop */
- }
-
- memcpy (skb->data, sarb->tail - pdu_length, length);
- __skb_put (skb, length);
-
- vdbg ("udsl_extract_cells: sending skb 0x%p, skb->len %u, skb->truesize %u", skb, skb->len, skb->truesize);
-
- PACKETDEBUG (skb->data, skb->len);
-
- vcc->push (vcc, skb);
-
- atomic_inc (&vcc->stats->rx);
-out:
- skb_trim (sarb, 0);
- }
- }
-}
-
-
-/*************
-** encode **
-*************/
-
-static const unsigned char zeros [ATM_CELL_PAYLOAD];
-
-static void udsl_groom_skb (struct atm_vcc *vcc, struct sk_buff *skb)
-{
- struct udsl_control *ctrl = UDSL_SKB (skb);
- unsigned int zero_padding;
- u32 crc;
-
- ctrl->atm_data.vcc = vcc;
- ctrl->cell_header [0] = vcc->vpi >> 4;
- ctrl->cell_header [1] = (vcc->vpi << 4) | (vcc->vci >> 12);
- ctrl->cell_header [2] = vcc->vci >> 4;
- ctrl->cell_header [3] = vcc->vci << 4;
- ctrl->cell_header [4] = 0xec;
-
- ctrl->num_cells = UDSL_NUM_CELLS (skb->len);
- ctrl->num_entire = skb->len / ATM_CELL_PAYLOAD;
-
- zero_padding = ctrl->num_cells * ATM_CELL_PAYLOAD - skb->len - ATM_AAL5_TRAILER;
-
- if (ctrl->num_entire + 1 < ctrl->num_cells)
- ctrl->pdu_padding = zero_padding - (ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER);
- else
- ctrl->pdu_padding = zero_padding;
-
- ctrl->aal5_trailer [0] = 0; /* UU = 0 */
- ctrl->aal5_trailer [1] = 0; /* CPI = 0 */
- ctrl->aal5_trailer [2] = skb->len >> 8;
- ctrl->aal5_trailer [3] = skb->len;
-
- crc = crc32_be (~0, skb->data, skb->len);
- crc = crc32_be (crc, zeros, zero_padding);
- crc = crc32_be (crc, ctrl->aal5_trailer, 4);
- crc = ~crc;
-
- ctrl->aal5_trailer [4] = crc >> 24;
- ctrl->aal5_trailer [5] = crc >> 16;
- ctrl->aal5_trailer [6] = crc >> 8;
- ctrl->aal5_trailer [7] = crc;
-}
-
-static 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;
-
- vdbg ("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);
-
- nc = ctrl->num_cells;
- ne = min (howmany, 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);
- }
-
- 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) {
- 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;
-
- DEBUG_ON (--ctrl->num_cells);
- }
-
- 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;
-}
-
-
-/**************
-** receive **
-**************/
-
-static void udsl_complete_receive (struct urb *urb, struct pt_regs *regs)
-{
- struct udsl_receive_buffer *buf;
- struct udsl_instance_data *instance;
- struct udsl_receiver *rcv;
- unsigned long flags;
-
- if (!urb || !(rcv = urb->context)) {
- dbg ("udsl_complete_receive: bad urb!");
- return;
- }
-
- instance = rcv->instance;
- buf = rcv->buffer;
-
- buf->filled_cells = urb->actual_length / ATM_CELL_SIZE;
-
- vdbg ("udsl_complete_receive: urb 0x%p, status %d, actual_length %d, filled_cells %u, rcv 0x%p, buf 0x%p", urb, urb->status, urb->actual_length, buf->filled_cells, rcv, buf);
-
- DEBUG_ON (buf->filled_cells > rcv_buf_size);
-
- /* may not be in_interrupt() */
- spin_lock_irqsave (&instance->receive_lock, flags);
- list_add (&rcv->list, &instance->spare_receivers);
- list_add_tail (&buf->list, &instance->filled_receive_buffers);
- if (likely (!urb->status))
- tasklet_schedule (&instance->receive_tasklet);
- spin_unlock_irqrestore (&instance->receive_lock, flags);
-}
-
-static void udsl_process_receive (unsigned long data)
-{
- struct udsl_receive_buffer *buf;
- struct udsl_instance_data *instance = (struct udsl_instance_data *) data;
- struct udsl_receiver *rcv;
- int err;
-
-made_progress:
- while (!list_empty (&instance->spare_receive_buffers)) {
- spin_lock_irq (&instance->receive_lock);
- if (list_empty (&instance->spare_receivers)) {
- spin_unlock_irq (&instance->receive_lock);
- break;
- }
- rcv = list_entry (instance->spare_receivers.next, struct udsl_receiver, list);
- list_del (&rcv->list);
- spin_unlock_irq (&instance->receive_lock);
-
- buf = list_entry (instance->spare_receive_buffers.next, struct udsl_receive_buffer, list);
- list_del (&buf->list);
-
- rcv->buffer = buf;
-
- usb_fill_bulk_urb (rcv->urb,
- instance->usb_dev,
- usb_rcvbulkpipe (instance->usb_dev, UDSL_ENDPOINT_DATA_IN),
- buf->base,
- rcv_buf_size * ATM_CELL_SIZE,
- udsl_complete_receive,
- rcv);
-
- vdbg ("udsl_process_receive: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf);
-
- if ((err = usb_submit_urb(rcv->urb, GFP_ATOMIC)) < 0) {
- dbg ("udsl_process_receive: urb submission failed (%d)!", err);
- list_add (&buf->list, &instance->spare_receive_buffers);
- spin_lock_irq (&instance->receive_lock);
- list_add (&rcv->list, &instance->spare_receivers);
- spin_unlock_irq (&instance->receive_lock);
- break;
- }
- }
-
- spin_lock_irq (&instance->receive_lock);
- if (list_empty (&instance->filled_receive_buffers)) {
- spin_unlock_irq (&instance->receive_lock);
- return; /* done - no more buffers */
- }
- buf = list_entry (instance->filled_receive_buffers.next, struct udsl_receive_buffer, list);
- list_del (&buf->list);
- spin_unlock_irq (&instance->receive_lock);
- vdbg ("udsl_process_receive: processing buf 0x%p", buf);
- udsl_extract_cells (instance, buf->base, buf->filled_cells);
- list_add (&buf->list, &instance->spare_receive_buffers);
- goto made_progress;
-}
-
-
-/***********
-** send **
-***********/
-
-static void udsl_complete_send (struct urb *urb, struct pt_regs *regs)
-{
- struct udsl_instance_data *instance;
- struct udsl_sender *snd;
- unsigned long flags;
-
- if (!urb || !(snd = urb->context) || !(instance = snd->instance)) {
- dbg ("udsl_complete_send: bad urb!");
- return;
- }
-
- vdbg ("udsl_complete_send: urb 0x%p, status %d, snd 0x%p, buf 0x%p", urb, urb->status, snd, snd->buffer);
-
- /* may not be in_interrupt() */
- spin_lock_irqsave (&instance->send_lock, flags);
- list_add (&snd->list, &instance->spare_senders);
- list_add (&snd->buffer->list, &instance->spare_send_buffers);
- tasklet_schedule (&instance->send_tasklet);
- spin_unlock_irqrestore (&instance->send_lock, flags);
-}
-
-static void udsl_process_send (unsigned long data)
-{
- struct udsl_send_buffer *buf;
- struct udsl_instance_data *instance = (struct udsl_instance_data *) data;
- struct sk_buff *skb;
- struct udsl_sender *snd;
- int err;
- unsigned int num_written;
-
-made_progress:
- spin_lock_irq (&instance->send_lock);
- while (!list_empty (&instance->spare_senders)) {
- if (!list_empty (&instance->filled_send_buffers)) {
- buf = list_entry (instance->filled_send_buffers.next, struct udsl_send_buffer, list);
- list_del (&buf->list);
- } else if ((buf = instance->current_buffer)) {
- instance->current_buffer = NULL;
- } else /* all buffers empty */
- break;
-
- snd = list_entry (instance->spare_senders.next, struct udsl_sender, list);
- list_del (&snd->list);
- spin_unlock_irq (&instance->send_lock);
-
- snd->buffer = buf;
- usb_fill_bulk_urb (snd->urb,
- instance->usb_dev,
- usb_sndbulkpipe (instance->usb_dev, UDSL_ENDPOINT_DATA_OUT),
- buf->base,
- (snd_buf_size - buf->free_cells) * ATM_CELL_SIZE,
- udsl_complete_send,
- snd);
-
- vdbg ("udsl_process_send: submitting urb 0x%p (%d cells), snd 0x%p, buf 0x%p", snd->urb, snd_buf_size - buf->free_cells, snd, buf);
-
- if ((err = usb_submit_urb(snd->urb, GFP_ATOMIC)) < 0) {
- dbg ("udsl_process_send: urb submission failed (%d)!", err);
- spin_lock_irq (&instance->send_lock);
- list_add (&snd->list, &instance->spare_senders);
- spin_unlock_irq (&instance->send_lock);
- list_add (&buf->list, &instance->filled_send_buffers);
- return; /* bail out */
- }
-
- spin_lock_irq (&instance->send_lock);
- } /* while */
- spin_unlock_irq (&instance->send_lock);
-
- if (!instance->current_skb && !(instance->current_skb = skb_dequeue (&instance->sndqueue)))
- return; /* done - no more skbs */
-
- skb = instance->current_skb;
-
- if (!(buf = instance->current_buffer)) {
- spin_lock_irq (&instance->send_lock);
- if (list_empty (&instance->spare_send_buffers)) {
- instance->current_buffer = NULL;
- spin_unlock_irq (&instance->send_lock);
- return; /* done - no more buffers */
- }
- buf = list_entry (instance->spare_send_buffers.next, struct udsl_send_buffer, list);
- list_del (&buf->list);
- spin_unlock_irq (&instance->send_lock);
-
- buf->free_start = buf->base;
- buf->free_cells = snd_buf_size;
-
- instance->current_buffer = buf;
- }
-
- num_written = udsl_write_cells (buf->free_cells, skb, &buf->free_start);
-
- vdbg ("udsl_process_send: wrote %u cells from skb 0x%p to buffer 0x%p", num_written, skb, buf);
-
- if (!(buf->free_cells -= num_written)) {
- list_add_tail (&buf->list, &instance->filled_send_buffers);
- instance->current_buffer = NULL;
- }
-
- vdbg ("udsl_process_send: buffer contains %d cells, %d left", snd_buf_size - buf->free_cells, buf->free_cells);
-
- if (!UDSL_SKB (skb)->num_cells) {
- struct atm_vcc *vcc = UDSL_SKB (skb)->atm_data.vcc;
-
- udsl_pop (vcc, skb);
- instance->current_skb = NULL;
-
- atomic_inc (&vcc->stats->tx);
- }
-
- goto made_progress;
-}
-
-static void udsl_cancel_send (struct udsl_instance_data *instance, struct atm_vcc *vcc)
-{
- struct sk_buff *skb, *n;
-
- dbg ("udsl_cancel_send entered");
- spin_lock_irq (&instance->sndqueue.lock);
- for (skb = instance->sndqueue.next, n = skb->next; skb != (struct sk_buff *)&instance->sndqueue; skb = n, n = skb->next)
- if (UDSL_SKB (skb)->atm_data.vcc == vcc) {
- dbg ("udsl_cancel_send: popping skb 0x%p", skb);
- __skb_unlink (skb, &instance->sndqueue);
- udsl_pop (vcc, skb);
- }
- spin_unlock_irq (&instance->sndqueue.lock);
-
- tasklet_disable (&instance->send_tasklet);
- if ((skb = instance->current_skb) && (UDSL_SKB (skb)->atm_data.vcc == vcc)) {
- dbg ("udsl_cancel_send: popping current skb (0x%p)", skb);
- instance->current_skb = NULL;
- udsl_pop (vcc, skb);
- }
- tasklet_enable (&instance->send_tasklet);
- dbg ("udsl_cancel_send done");
-}
-
-static int udsl_atm_send (struct atm_vcc *vcc, struct sk_buff *skb)
-{
- struct udsl_instance_data *instance = vcc->dev->dev_data;
- int err;
-
- vdbg ("udsl_atm_send called (skb 0x%p, len %u)", skb, skb->len);
-
- if (!instance || !instance->usb_dev) {
- dbg ("udsl_atm_send: NULL data!");
- err = -ENODEV;
- goto fail;
- }
-
- if (vcc->qos.aal != ATM_AAL5) {
- dbg ("udsl_atm_send: unsupported ATM type %d!", vcc->qos.aal);
- err = -EINVAL;
- goto fail;
- }
-
- if (skb->len > ATM_MAX_AAL5_PDU) {
- dbg ("udsl_atm_send: packet too long (%d vs %d)!", skb->len, ATM_MAX_AAL5_PDU);
- err = -EINVAL;
- goto fail;
- }
-
- PACKETDEBUG (skb->data, skb->len);
-
- udsl_groom_skb (vcc, skb);
- skb_queue_tail (&instance->sndqueue, skb);
- tasklet_schedule (&instance->send_tasklet);
-
- return 0;
-
-fail:
- udsl_pop (vcc, skb);
- return err;
-}
-
-
-/**********
-** ATM **
-**********/
-
-static void udsl_atm_dev_close (struct atm_dev *dev)
-{
- struct udsl_instance_data *instance = dev->dev_data;
-
- if (!instance) {
- dbg ("udsl_atm_dev_close: NULL instance!");
- return;
- }
-
- dbg ("udsl_atm_dev_close: queue has %u elements", instance->sndqueue.qlen);
-
- tasklet_kill (&instance->receive_tasklet);
- tasklet_kill (&instance->send_tasklet);
- kfree (instance);
- dev->dev_data = NULL;
-}
-
-static int udsl_atm_proc_read (struct atm_dev *atm_dev, loff_t *pos, char *page)
-{
- struct udsl_instance_data *instance = atm_dev->dev_data;
- int left = *pos;
-
- if (!instance) {
- dbg ("udsl_atm_proc_read: NULL instance!");
- return -ENODEV;
- }
-
- if (!left--)
- return sprintf (page, "%s\n", instance->description);
-
- if (!left--)
- return sprintf (page, "MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
- atm_dev->esi [0], atm_dev->esi [1], atm_dev->esi [2],
- atm_dev->esi [3], atm_dev->esi [4], atm_dev->esi [5]);
-
- if (!left--)
- return sprintf (page, "AAL5: tx %d ( %d err ), rx %d ( %d err, %d drop )\n",
- atomic_read (&atm_dev->stats.aal5.tx),
- atomic_read (&atm_dev->stats.aal5.tx_err),
- atomic_read (&atm_dev->stats.aal5.rx),
- atomic_read (&atm_dev->stats.aal5.rx_err),
- atomic_read (&atm_dev->stats.aal5.rx_drop));
-
- if (!left--) {
- switch (atm_dev->signal) {
- case ATM_PHY_SIG_FOUND:
- sprintf (page, "Line up");
- break;
- case ATM_PHY_SIG_LOST:
- sprintf (page, "Line down");
- break;
- default:
- sprintf (page, "Line state unknown");
- break;
- }
-
- if (instance->usb_dev) {
- if (!instance->firmware_loaded)
- strcat (page, ", no firmware\n");
- else
- strcat (page, ", firmware loaded\n");
- } else
- strcat (page, ", disconnected\n");
-
- return strlen (page);
- }
-
- return 0;
-}
-
-static int udsl_atm_open (struct atm_vcc *vcc)
-{
- struct udsl_instance_data *instance = vcc->dev->dev_data;
- struct udsl_vcc_data *new;
- unsigned int max_pdu;
- int vci = vcc->vci;
- short vpi = vcc->vpi;
-
- dbg ("udsl_atm_open: vpi %hd, vci %d", vpi, vci);
-
- if (!instance || !instance->usb_dev) {
- dbg ("udsl_atm_open: NULL data!");
- return -ENODEV;
- }
-
- /* only support AAL5 */
- if ((vcc->qos.aal != ATM_AAL5) || (vcc->qos.rxtp.max_sdu < 0) || (vcc->qos.rxtp.max_sdu > ATM_MAX_AAL5_PDU)) {
- dbg ("udsl_atm_open: unsupported ATM type %d!", vcc->qos.aal);
- return -EINVAL;
- }
-
- if (!instance->firmware_loaded) {
- dbg ("udsl_atm_open: firmware not loaded!");
- return -EAGAIN;
- }
-
- down (&instance->serialize); /* vs self, udsl_atm_close */
-
- if (udsl_find_vcc (instance, vpi, vci)) {
- dbg ("udsl_atm_open: %hd/%d already in use!", vpi, vci);
- up (&instance->serialize);
- return -EADDRINUSE;
- }
-
- if (!(new = kmalloc (sizeof (struct udsl_vcc_data), GFP_KERNEL))) {
- dbg ("udsl_atm_open: no memory for vcc_data!");
- up (&instance->serialize);
- return -ENOMEM;
- }
-
- memset (new, 0, sizeof (struct udsl_vcc_data));
- new->vcc = vcc;
- new->vpi = vpi;
- new->vci = vci;
-
- /* udsl_extract_cells requires at least one cell */
- max_pdu = max (1, UDSL_NUM_CELLS (vcc->qos.rxtp.max_sdu)) * ATM_CELL_PAYLOAD;
- if (!(new->sarb = alloc_skb (max_pdu, GFP_KERNEL))) {
- dbg ("udsl_atm_open: no memory for SAR buffer!");
- kfree (new);
- up (&instance->serialize);
- return -ENOMEM;
- }
-
- vcc->dev_data = new;
-
- tasklet_disable (&instance->receive_tasklet);
- list_add (&new->list, &instance->vcc_list);
- tasklet_enable (&instance->receive_tasklet);
-
- set_bit (ATM_VF_ADDR, &vcc->flags);
- set_bit (ATM_VF_PARTIAL, &vcc->flags);
- set_bit (ATM_VF_READY, &vcc->flags);
-
- up (&instance->serialize);
-
- tasklet_schedule (&instance->receive_tasklet);
-
- dbg ("udsl_atm_open: allocated vcc data 0x%p (max_pdu: %u)", new, max_pdu);
-
- return 0;
-}
-
-static void udsl_atm_close (struct atm_vcc *vcc)
-{
- struct udsl_instance_data *instance = vcc->dev->dev_data;
- struct udsl_vcc_data *vcc_data = vcc->dev_data;
-
- dbg ("udsl_atm_close called");
-
- if (!instance || !vcc_data) {
- dbg ("udsl_atm_close: NULL data!");
- return;
- }
-
- dbg ("udsl_atm_close: deallocating vcc 0x%p with vpi %d vci %d", vcc_data, vcc_data->vpi, vcc_data->vci);
-
- udsl_cancel_send (instance, vcc);
-
- down (&instance->serialize); /* vs self, udsl_atm_open */
-
- tasklet_disable (&instance->receive_tasklet);
- list_del (&vcc_data->list);
- tasklet_enable (&instance->receive_tasklet);
-
- kfree_skb (vcc_data->sarb);
- vcc_data->sarb = NULL;
-
- kfree (vcc_data);
- vcc->dev_data = NULL;
-
- vcc->vpi = ATM_VPI_UNSPEC;
- vcc->vci = ATM_VCI_UNSPEC;
- clear_bit (ATM_VF_READY, &vcc->flags);
- clear_bit (ATM_VF_PARTIAL, &vcc->flags);
- clear_bit (ATM_VF_ADDR, &vcc->flags);
-
- up (&instance->serialize);
-
- dbg ("udsl_atm_close successful");
-}
-
-static int udsl_atm_ioctl (struct atm_dev *dev, unsigned int cmd, void __user *arg)
-{
- switch (cmd) {
- case ATM_QUERYLOOP:
- return put_user (ATM_LM_NONE, (int __user *)arg) ? -EFAULT : 0;
- default:
- return -ENOIOCTLCMD;
- }
-}
-
-
-/**********
-** USB **
-**********/
-
-static int udsl_set_alternate (struct udsl_instance_data *instance)
-{
- down (&instance->serialize); /* vs self */
- if (!instance->firmware_loaded) {
- int ret;
-
- if ((ret = usb_set_interface (instance->usb_dev, 1, 1)) < 0) {
- dbg ("udsl_set_alternate: usb_set_interface returned %d!", ret);
- up (&instance->serialize);
- return ret;
- }
- instance->firmware_loaded = 1;
- }
- up (&instance->serialize);
-
- tasklet_schedule (&instance->receive_tasklet);
-
- return 0;
-}
-
-static int udsl_usb_ioctl (struct usb_interface *intf, unsigned int code, void *user_data)
-{
- struct udsl_instance_data *instance = usb_get_intfdata (intf);
-
- dbg ("udsl_usb_ioctl entered");
-
- if (!instance) {
- dbg ("udsl_usb_ioctl: NULL instance!");
- return -ENODEV;
- }
-
- switch (code) {
- case UDSL_IOCTL_LINE_UP:
- instance->atm_dev->signal = ATM_PHY_SIG_FOUND;
- return udsl_set_alternate (instance);
- case UDSL_IOCTL_LINE_DOWN:
- instance->atm_dev->signal = ATM_PHY_SIG_LOST;
- return 0;
- default:
- return -ENOTTY;
- }
-}
-
-static int udsl_usb_probe (struct usb_interface *intf, const struct usb_device_id *id)
-{
- struct usb_device *dev = interface_to_usbdev(intf);
- int ifnum = intf->altsetting->desc.bInterfaceNumber;
- struct udsl_instance_data *instance;
- unsigned char mac_str [13];
- int i, length;
- char *buf;
-
- dbg ("udsl_usb_probe: trying device with vendor=0x%x, product=0x%x, ifnum %d",
- dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum);
-
- if ((dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) ||
- (dev->descriptor.idVendor != SPEEDTOUCH_VENDORID) ||
- (dev->descriptor.idProduct != SPEEDTOUCH_PRODUCTID) || (ifnum != 1))
- return -ENODEV;
-
- dbg ("udsl_usb_probe: device accepted");
-
- /* instance init */
- if (!(instance = kmalloc (sizeof (struct udsl_instance_data), GFP_KERNEL))) {
- dbg ("udsl_usb_probe: no memory for instance data!");
- return -ENOMEM;
- }
-
- memset (instance, 0, sizeof (struct udsl_instance_data));
-
- init_MUTEX (&instance->serialize);
-
- instance->usb_dev = dev;
-
- INIT_LIST_HEAD (&instance->vcc_list);
-
- spin_lock_init (&instance->receive_lock);
- INIT_LIST_HEAD (&instance->spare_receivers);
- INIT_LIST_HEAD (&instance->filled_receive_buffers);
-
- tasklet_init (&instance->receive_tasklet, udsl_process_receive, (unsigned long) instance);
- INIT_LIST_HEAD (&instance->spare_receive_buffers);
-
- skb_queue_head_init (&instance->sndqueue);
-
- spin_lock_init (&instance->send_lock);
- INIT_LIST_HEAD (&instance->spare_senders);
- INIT_LIST_HEAD (&instance->spare_send_buffers);
-
- tasklet_init (&instance->send_tasklet, udsl_process_send, (unsigned long) instance);
- INIT_LIST_HEAD (&instance->filled_send_buffers);
-
- /* receive init */
- for (i = 0; i < num_rcv_urbs; i++) {
- struct udsl_receiver *rcv = &(instance->receivers [i]);
-
- if (!(rcv->urb = usb_alloc_urb (0, GFP_KERNEL))) {
- dbg ("udsl_usb_probe: no memory for receive urb %d!", i);
- goto fail;
- }
-
- rcv->instance = instance;
-
- list_add (&rcv->list, &instance->spare_receivers);
- }
-
- for (i = 0; i < num_rcv_bufs; i++) {
- struct udsl_receive_buffer *buf = &(instance->receive_buffers [i]);
-
- if (!(buf->base = kmalloc (rcv_buf_size * ATM_CELL_SIZE, GFP_KERNEL))) {
- dbg ("udsl_usb_probe: no memory for receive buffer %d!", i);
- goto fail;
- }
-
- list_add (&buf->list, &instance->spare_receive_buffers);
- }
-
- /* send init */
- for (i = 0; i < num_snd_urbs; i++) {
- struct udsl_sender *snd = &(instance->senders [i]);
-
- if (!(snd->urb = usb_alloc_urb (0, GFP_KERNEL))) {
- dbg ("udsl_usb_probe: no memory for send urb %d!", i);
- goto fail;
- }
-
- snd->instance = instance;
-
- list_add (&snd->list, &instance->spare_senders);
- }
-
- for (i = 0; i < num_snd_bufs; i++) {
- struct udsl_send_buffer *buf = &(instance->send_buffers [i]);
-
- if (!(buf->base = kmalloc (snd_buf_size * ATM_CELL_SIZE, GFP_KERNEL))) {
- dbg ("udsl_usb_probe: no memory for send buffer %d!", i);
- goto fail;
- }
-
- list_add (&buf->list, &instance->spare_send_buffers);
- }
-
- /* ATM init */
- if (!(instance->atm_dev = atm_dev_register (udsl_driver_name, &udsl_atm_devops, -1, NULL))) {
- dbg ("udsl_usb_probe: failed to register ATM device!");
- goto fail;
- }
-
- instance->atm_dev->ci_range.vpi_bits = ATM_CI_MAX;
- instance->atm_dev->ci_range.vci_bits = ATM_CI_MAX;
- instance->atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
-
- /* temp init ATM device, set to 128kbit */
- instance->atm_dev->link_rate = 128 * 1000 / 424;
-
- /* set MAC address, it is stored in the serial number */
- memset (instance->atm_dev->esi, 0, sizeof (instance->atm_dev->esi));
- if (usb_string (dev, dev->descriptor.iSerialNumber, mac_str, sizeof (mac_str)) == 12)
- for (i = 0; i < 6; i++)
- instance->atm_dev->esi [i] = (hex2int (mac_str [i * 2]) * 16) + (hex2int (mac_str [i * 2 + 1]));
-
- /* device description */
- buf = instance->description;
- length = sizeof (instance->description);
-
- if ((i = usb_string (dev, dev->descriptor.iProduct, buf, length)) < 0)
- goto finish;
-
- buf += i;
- length -= i;
-
- i = scnprintf (buf, length, " (");
- buf += i;
- length -= i;
-
- if (length <= 0 || (i = usb_make_path (dev, buf, length)) < 0)
- goto finish;
-
- buf += i;
- length -= i;
-
- snprintf (buf, length, ")");
-
-finish:
- /* ready for ATM callbacks */
- wmb ();
- instance->atm_dev->dev_data = instance;
-
- usb_set_intfdata (intf, instance);
-
- return 0;
-
-fail:
- for (i = 0; i < num_snd_bufs; i++)
- kfree (instance->send_buffers [i].base);
-
- for (i = 0; i < num_snd_urbs; i++)
- usb_free_urb (instance->senders [i].urb);
-
- for (i = 0; i < num_rcv_bufs; i++)
- kfree (instance->receive_buffers [i].base);
-
- for (i = 0; i < num_rcv_urbs; i++)
- usb_free_urb (instance->receivers [i].urb);
-
- kfree (instance);
-
- return -ENOMEM;
-}
-
-static void udsl_usb_disconnect (struct usb_interface *intf)
-{
- struct udsl_instance_data *instance = usb_get_intfdata (intf);
- struct list_head *pos;
- unsigned int count;
- int result, i;
-
- dbg ("udsl_usb_disconnect entered");
-
- usb_set_intfdata (intf, NULL);
-
- if (!instance) {
- dbg ("udsl_usb_disconnect: NULL instance!");
- return;
- }
-
- /* receive finalize */
- tasklet_disable (&instance->receive_tasklet);
-
- for (i = 0; i < num_rcv_urbs; i++)
- if ((result = usb_unlink_urb (instance->receivers [i].urb)) < 0)
- dbg ("udsl_usb_disconnect: usb_unlink_urb on receive urb %d returned %d!", i, result);
-
- /* wait for completion handlers to finish */
- do {
- count = 0;
- spin_lock_irq (&instance->receive_lock);
- list_for_each (pos, &instance->spare_receivers)
- DEBUG_ON (++count > num_rcv_urbs);
- spin_unlock_irq (&instance->receive_lock);
-
- dbg ("udsl_usb_disconnect: found %u spare receivers", count);
-
- if (count == num_rcv_urbs)
- break;
-
- set_current_state (TASK_RUNNING);
- schedule ();
- } while (1);
-
- /* no need to take the spinlock */
- INIT_LIST_HEAD (&instance->filled_receive_buffers);
- INIT_LIST_HEAD (&instance->spare_receive_buffers);
-
- tasklet_enable (&instance->receive_tasklet);
-
- for (i = 0; i < num_rcv_urbs; i++)
- usb_free_urb (instance->receivers [i].urb);
-
- for (i = 0; i < num_rcv_bufs; i++)
- kfree (instance->receive_buffers [i].base);
-
- /* send finalize */
- tasklet_disable (&instance->send_tasklet);
-
- for (i = 0; i < num_snd_urbs; i++)
- if ((result = usb_unlink_urb (instance->senders [i].urb)) < 0)
- dbg ("udsl_usb_disconnect: usb_unlink_urb on send urb %d returned %d!", i, result);
-
- /* wait for completion handlers to finish */
- do {
- count = 0;
- spin_lock_irq (&instance->send_lock);
- list_for_each (pos, &instance->spare_senders)
- DEBUG_ON (++count > num_snd_urbs);
- spin_unlock_irq (&instance->send_lock);
-
- dbg ("udsl_usb_disconnect: found %u spare senders", count);
-
- if (count == num_snd_urbs)
- break;
-
- set_current_state (TASK_RUNNING);
- schedule ();
- } while (1);
-
- /* no need to take the spinlock */
- INIT_LIST_HEAD (&instance->spare_senders);
- INIT_LIST_HEAD (&instance->spare_send_buffers);
- instance->current_buffer = NULL;
-
- tasklet_enable (&instance->send_tasklet);
-
- for (i = 0; i < num_snd_urbs; i++)
- usb_free_urb (instance->senders [i].urb);
-
- for (i = 0; i < num_snd_bufs; i++)
- kfree (instance->send_buffers [i].base);
-
- wmb ();
- instance->usb_dev = NULL;
-
- /* ATM finalize */
- shutdown_atm_dev (instance->atm_dev); /* frees instance, kills tasklets */
-}
-
-
-/***********
-** init **
-***********/
-
-static int __init udsl_usb_init (void)
-{
- dbg ("udsl_usb_init: driver version " DRIVER_VERSION);
-
- if (sizeof (struct udsl_control) > sizeof (((struct sk_buff *)0)->cb)) {
- printk (KERN_ERR __FILE__ ": unusable with this kernel!\n");
- return -EIO;
- }
-
- if ((num_rcv_urbs > UDSL_MAX_RCV_URBS) || (num_snd_urbs > UDSL_MAX_SND_URBS) ||
- (num_rcv_bufs > UDSL_MAX_RCV_BUFS) || (num_snd_bufs > UDSL_MAX_SND_BUFS) ||
- (rcv_buf_size > UDSL_MAX_RCV_BUF_SIZE) || (snd_buf_size > UDSL_MAX_SND_BUF_SIZE))
- return -EINVAL;
-
- return usb_register (&udsl_usb_driver);
-}
-
-static void __exit udsl_usb_cleanup (void)
-{
- dbg ("udsl_usb_cleanup entered");
-
- usb_deregister (&udsl_usb_driver);
-}
-
-module_init (udsl_usb_init);
-module_exit (udsl_usb_cleanup);
-
-MODULE_AUTHOR (DRIVER_AUTHOR);
-MODULE_DESCRIPTION (DRIVER_DESC);
-MODULE_LICENSE ("GPL");
-MODULE_VERSION (DRIVER_VERSION);
-
-
-/************
-** debug **
-************/
-
-#ifdef VERBOSE_DEBUG
-static int udsl_print_packet (const unsigned char *data, int len)
-{
- unsigned char buffer [256];
- int i = 0, j = 0;
-
- for (i = 0; i < len;) {
- buffer [0] = '\0';
- sprintf (buffer, "%.3d :", i);
- for (j = 0; (j < 16) && (i < len); j++, i++) {
- sprintf (buffer, "%s %2.2x", buffer, data [i]);
- }
- dbg ("%s", buffer);
- }
- return i;
-}
-#endif
diff --git a/drivers/usb/misc/tiglusb.c b/drivers/usb/misc/tiglusb.c
index 0f9c5753772d..f902884a7bbb 100644
--- a/drivers/usb/misc/tiglusb.c
+++ b/drivers/usb/misc/tiglusb.c
@@ -115,6 +115,7 @@ tiglusb_open (struct inode *inode, struct file *filp)
return -EBUSY;
}
+ set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout (HZ / 2);
if (signal_pending (current)) {
diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c
index 0bc5ccc244ba..8b22320d5ea6 100644
--- a/drivers/usb/misc/uss720.c
+++ b/drivers/usb/misc/uss720.c
@@ -44,6 +44,7 @@
#include <linux/parport.h>
#include <linux/init.h>
#include <linux/usb.h>
+#include <linux/delay.h>
/*
* Version Information
@@ -159,8 +160,7 @@ static int change_mode(struct parport *pp, int m)
if (time_after_eq (jiffies, expire))
/* The FIFO is stuck. */
return -EBUSY;
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout((HZ + 99) / 100);
+ msleep_interruptible(10);
if (signal_pending (current))
break;
}
diff --git a/drivers/usb/net/catc.c b/drivers/usb/net/catc.c
index 455fe6e3be06..66d0a70a8210 100644
--- a/drivers/usb/net/catc.c
+++ b/drivers/usb/net/catc.c
@@ -765,10 +765,10 @@ static int catc_stop(struct net_device *netdev)
if (!catc->is_f5u011)
del_timer_sync(&catc->timer);
- usb_unlink_urb(catc->rx_urb);
- usb_unlink_urb(catc->tx_urb);
- usb_unlink_urb(catc->irq_urb);
- usb_unlink_urb(catc->ctrl_urb);
+ usb_kill_urb(catc->rx_urb);
+ usb_kill_urb(catc->tx_urb);
+ usb_kill_urb(catc->irq_urb);
+ usb_kill_urb(catc->ctrl_urb);
return 0;
}
diff --git a/drivers/usb/net/kaweth.c b/drivers/usb/net/kaweth.c
index 40e921ade8c8..2092cb9cb33f 100644
--- a/drivers/usb/net/kaweth.c
+++ b/drivers/usb/net/kaweth.c
@@ -668,13 +668,13 @@ static int kaweth_open(struct net_device *net)
INTBUFFERSIZE,
int_callback,
kaweth,
- 8);
+ 250); /* overriding the descriptor */
kaweth->irq_urb->transfer_dma = kaweth->intbufferhandle;
kaweth->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
res = usb_submit_urb(kaweth->irq_urb, GFP_KERNEL);
if (res) {
- usb_unlink_urb(kaweth->rx_urb);
+ usb_kill_urb(kaweth->rx_urb);
return -EIO;
}
@@ -695,15 +695,15 @@ static int kaweth_close(struct net_device *net)
kaweth->status |= KAWETH_STATUS_CLOSING;
- usb_unlink_urb(kaweth->irq_urb);
- usb_unlink_urb(kaweth->rx_urb);
+ usb_kill_urb(kaweth->irq_urb);
+ usb_kill_urb(kaweth->rx_urb);
flush_scheduled_work();
/* a scheduled work may have resubmitted,
we hit them again */
- usb_unlink_urb(kaweth->irq_urb);
- usb_unlink_urb(kaweth->rx_urb);
+ usb_kill_urb(kaweth->irq_urb);
+ usb_kill_urb(kaweth->rx_urb);
kaweth->status &= ~KAWETH_STATUS_CLOSING;
@@ -1173,8 +1173,8 @@ static void kaweth_disconnect(struct usb_interface *intf)
}
kaweth->removed = 1;
- usb_unlink_urb(kaweth->irq_urb);
- usb_unlink_urb(kaweth->rx_urb);
+ usb_kill_urb(kaweth->irq_urb);
+ usb_kill_urb(kaweth->rx_urb);
/* we need to wait for the urb to be cancelled, if it is active */
spin_lock(&kaweth->device_lock);
@@ -1250,19 +1250,17 @@ static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length)
return status;
}
- set_current_state(TASK_UNINTERRUPTIBLE);
while (timeout && !awd.done) {
- timeout = schedule_timeout(timeout);
set_current_state(TASK_UNINTERRUPTIBLE);
+ timeout = schedule_timeout(timeout);
}
- set_current_state(TASK_RUNNING);
remove_wait_queue(&awd.wqh, &wait);
if (!timeout) {
// timeout
kaweth_warn("usb_control/bulk_msg: timeout");
- usb_unlink_urb(urb); // remove urb safely
+ usb_kill_urb(urb); // remove urb safely
status = -ETIMEDOUT;
}
else {
diff --git a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c
index c515916e37a7..98cb5ae3f6f1 100644
--- a/drivers/usb/net/pegasus.c
+++ b/drivers/usb/net/pegasus.c
@@ -854,10 +854,10 @@ static void free_all_urbs(pegasus_t * pegasus)
static void unlink_all_urbs(pegasus_t * pegasus)
{
- usb_unlink_urb(pegasus->intr_urb);
- usb_unlink_urb(pegasus->tx_urb);
- usb_unlink_urb(pegasus->rx_urb);
- usb_unlink_urb(pegasus->ctrl_urb);
+ usb_kill_urb(pegasus->intr_urb);
+ usb_kill_urb(pegasus->tx_urb);
+ usb_kill_urb(pegasus->rx_urb);
+ usb_kill_urb(pegasus->ctrl_urb);
}
static int alloc_urbs(pegasus_t * pegasus)
@@ -920,8 +920,8 @@ static int pegasus_open(struct net_device *net)
if ((res = enable_net_traffic(net, pegasus->usb))) {
err("can't enable_net_traffic() - %d", res);
res = -EIO;
- usb_unlink_urb(pegasus->rx_urb);
- usb_unlink_urb(pegasus->intr_urb);
+ usb_kill_urb(pegasus->rx_urb);
+ usb_kill_urb(pegasus->intr_urb);
free_skb_pool(pegasus);
goto exit;
}
diff --git a/drivers/usb/net/rtl8150.c b/drivers/usb/net/rtl8150.c
index 640aa5b68095..5cd2c9c5987c 100644
--- a/drivers/usb/net/rtl8150.c
+++ b/drivers/usb/net/rtl8150.c
@@ -20,7 +20,7 @@
#include <asm/uaccess.h>
/* Version Information */
-#define DRIVER_VERSION "v0.6.1 (2004/03/13)"
+#define DRIVER_VERSION "v0.6.2 (2004/08/27)"
#define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>"
#define DRIVER_DESC "rtl8150 based usb-ethernet driver"
@@ -392,10 +392,10 @@ static void free_all_urbs(rtl8150_t * dev)
static void unlink_all_urbs(rtl8150_t * dev)
{
- usb_unlink_urb(dev->rx_urb);
- usb_unlink_urb(dev->tx_urb);
- usb_unlink_urb(dev->intr_urb);
- usb_unlink_urb(dev->ctrl_urb);
+ usb_kill_urb(dev->rx_urb);
+ usb_kill_urb(dev->tx_urb);
+ usb_kill_urb(dev->intr_urb);
+ usb_kill_urb(dev->ctrl_urb);
}
static inline struct sk_buff *pull_skb(rtl8150_t *dev)
diff --git a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c
index cca6e72cf2fa..ad65e741fa44 100644
--- a/drivers/usb/net/usbnet.c
+++ b/drivers/usb/net/usbnet.c
@@ -825,7 +825,7 @@ static void ax8817x_unbind(struct usbnet *dev, struct usb_interface *intf)
{
struct ax8817x_data *data = (struct ax8817x_data *)dev->data;
- usb_unlink_urb(data->int_urb);
+ usb_kill_urb(data->int_urb);
usb_free_urb(data->int_urb);
kfree(data->int_buf);
}
@@ -1437,7 +1437,7 @@ static int genelink_free (struct usbnet *dev)
// handling needs to be generic)
// cancel irq urb first
- usb_unlink_urb (priv->irq_urb);
+ usb_kill_urb (priv->irq_urb);
// free irq urb
usb_free_urb (priv->irq_urb);
@@ -3252,6 +3252,10 @@ static const struct usb_device_id products [] = {
// Sitecom LN-029 "USB 2.0 10/100 Ethernet adapter"
USB_DEVICE (0x6189, 0x182d),
.driver_info = (unsigned long) &ax8817x_info,
+}, {
+ // Surecom EP-1427X-2
+ USB_DEVICE (0x1189, 0x0893),
+ .driver_info = (unsigned long) &ax8817x_info,
},
#endif
@@ -3308,11 +3312,18 @@ static const struct usb_device_id products [] = {
*
* PXA25x or PXA210 ... these use a "usb-eth" driver much like
* the sa1100 one, but hardware uses different endpoint numbers.
+ *
+ * Or the Linux "Ethernet" gadget on hardware that can't talk
+ * CDC Ethernet (e.g., no altsettings), in either of two modes:
+ * - acting just like the old "usb-eth" firmware, though
+ * the implementation is different
+ * - supporting RNDIS as the first/default configuration for
+ * MS-Windows interop; Linux needs to use the other config
*/
{
// 1183 = 0x049F, both used as hex values?
// Compaq "Itsy" vendor/product id
- USB_DEVICE (0x049F, 0x505A),
+ USB_DEVICE (0x049F, 0x505A), // usb-eth, or compatible
.driver_info = (unsigned long) &linuxdev_info,
}, {
USB_DEVICE (0x0E7E, 0x1001), // G.Mate "Yopy"
@@ -3320,6 +3331,10 @@ static const struct usb_device_id products [] = {
}, {
USB_DEVICE (0x8086, 0x07d3), // "blob" bootloader
.driver_info = (unsigned long) &blob_info,
+}, {
+ // Linux Ethernet/RNDIS gadget on pxa210/25x/26x
+ USB_DEVICE_VER (0x0525, 0xa4a2, 0x0203, 0x0203),
+ .driver_info = (unsigned long) &linuxdev_info,
},
#endif
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index 88fede7b447e..1499bf2fcd13 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -187,6 +187,16 @@ config USB_SERIAL_EDGEPORT_TI
To compile this driver as a module, choose M here: the
module will be called io_ti.
+config USB_SERIAL_IPW
+ tristate "USB IPWireless (3G UMTS TDD) Driver (EXPERIMENTAL)"
+ depends on USB_SERIAL && EXPERIMENTAL
+ help
+ Say Y here if you want to use a IPWireless USB modem such as
+ the ones supplied by Axity3G/Sentech South Africa.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ipw.
+
config USB_SERIAL_KEYSPAN_PDA
tristate "USB Keyspan PDA Single Port Serial Driver"
depends on USB_SERIAL
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
index 6cfbe3108ccb..dfd43ec80ff3 100644
--- a/drivers/usb/serial/Makefile
+++ b/drivers/usb/serial/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_USB_SERIAL_EDGEPORT_TI) += io_ti.o
obj-$(CONFIG_USB_SERIAL_EMPEG) += empeg.o
obj-$(CONFIG_USB_SERIAL_FTDI_SIO) += ftdi_sio.o
obj-$(CONFIG_USB_SERIAL_IPAQ) += ipaq.o
+obj-$(CONFIG_USB_SERIAL_IPW) += ipw.o
obj-$(CONFIG_USB_SERIAL_IR) += ir-usb.o
obj-$(CONFIG_USB_SERIAL_KEYSPAN) += keyspan.o
obj-$(CONFIG_USB_SERIAL_KEYSPAN_PDA) += keyspan_pda.o
diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c
index bcf56011ec22..a44cb9d96113 100644
--- a/drivers/usb/serial/belkin_sa.c
+++ b/drivers/usb/serial/belkin_sa.c
@@ -228,7 +228,7 @@ static int belkin_sa_open (struct usb_serial_port *port, struct file *filp)
port->interrupt_in_urb->dev = port->serial->dev;
retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (retval) {
- usb_unlink_urb(port->read_urb);
+ usb_kill_urb(port->read_urb);
err(" usb_submit_urb(read int) failed");
}
@@ -242,9 +242,9 @@ static void belkin_sa_close (struct usb_serial_port *port, struct file *filp)
dbg("%s port %d", __FUNCTION__, port->number);
/* shutdown our bulk reads and writes */
- usb_unlink_urb (port->write_urb);
- usb_unlink_urb (port->read_urb);
- usb_unlink_urb (port->interrupt_in_urb);
+ usb_kill_urb(port->write_urb);
+ usb_kill_urb(port->read_urb);
+ usb_kill_urb(port->interrupt_in_urb);
} /* belkin_sa_close */
diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c
index 33613b008557..6b5ec2af32ad 100644
--- a/drivers/usb/serial/cyberjack.c
+++ b/drivers/usb/serial/cyberjack.c
@@ -149,7 +149,7 @@ static void cyberjack_shutdown (struct usb_serial *serial)
dbg("%s", __FUNCTION__);
for (i=0; i < serial->num_ports; ++i) {
- usb_unlink_urb (serial->port[i]->interrupt_in_urb);
+ usb_kill_urb(serial->port[i]->interrupt_in_urb);
/* My special items, the standard routines free my urbs */
kfree(usb_get_serial_port_data(serial->port[i]));
usb_set_serial_port_data(serial->port[i], NULL);
@@ -189,8 +189,8 @@ static void cyberjack_close (struct usb_serial_port *port, struct file *filp)
if (port->serial->dev) {
/* shutdown any bulk reads that might be going on */
- usb_unlink_urb (port->write_urb);
- usb_unlink_urb (port->read_urb);
+ usb_kill_urb(port->write_urb);
+ usb_kill_urb(port->read_urb);
}
}
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
index 3ac69f2c2510..b904bfcbffcf 100644
--- a/drivers/usb/serial/digi_acceleport.c
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -1615,7 +1615,7 @@ dbg( "digi_close: TOP: port=%d, open_count=%d", priv->dp_port_num, port->open_co
DIGI_CLOSE_TIMEOUT );
/* shutdown any outstanding bulk writes */
- usb_unlink_urb (port->write_urb);
+ usb_kill_urb(port->write_urb);
}
tty->closing = 0;
@@ -1754,8 +1754,8 @@ dbg( "digi_shutdown: TOP, in_interrupt()=%ld", in_interrupt() );
/* stop reads and writes on all ports */
for( i=0; i<serial->type->num_ports+1; i++ ) {
- usb_unlink_urb( serial->port[i]->read_urb );
- usb_unlink_urb( serial->port[i]->write_urb );
+ usb_kill_urb(serial->port[i]->read_urb);
+ usb_kill_urb(serial->port[i]->write_urb);
}
/* free the private data structures for all ports */
diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c
index 251a50fdab86..e94113e0dd43 100644
--- a/drivers/usb/serial/empeg.c
+++ b/drivers/usb/serial/empeg.c
@@ -185,7 +185,7 @@ static void empeg_close (struct usb_serial_port *port, struct file * filp)
dbg("%s - port %d", __FUNCTION__, port->number);
/* shutdown our bulk read */
- usb_unlink_urb (port->read_urb);
+ usb_kill_urb(port->read_urb);
/* Uncomment the following line if you want to see some statistics in your syslog */
/* dev_info (&port->dev, "Bytes In = %d Bytes Out = %d\n", bytes_in, bytes_out); */
}
@@ -406,7 +406,7 @@ static void empeg_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
static void empeg_throttle (struct usb_serial_port *port)
{
dbg("%s - port %d", __FUNCTION__, port->number);
- usb_unlink_urb (port->read_urb);
+ usb_kill_urb(port->read_urb);
}
@@ -579,10 +579,10 @@ static void __exit empeg_exit (void)
for (i = 0; i < NUM_URBS; ++i) {
if (write_urb_pool[i]) {
- /* FIXME - uncomment the following usb_unlink_urb call when
+ /* FIXME - uncomment the following usb_kill_urb call when
* the host controllers get fixed to set urb->dev = NULL after
* the urb is finished. Otherwise this call oopses. */
- /* usb_unlink_urb(write_urb_pool[i]); */
+ /* usb_kill_urb(write_urb_pool[i]); */
if (write_urb_pool[i]->transfer_buffer)
kfree(write_urb_pool[i]->transfer_buffer);
usb_free_urb (write_urb_pool[i]);
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 9073ea84d8d3..b1a985470e42 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -368,6 +368,10 @@ static struct usb_device_id id_table_8U232AM [] = {
{ USB_DEVICE_VER(INTREPID_VID, INTREPID_NEOVI_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(FALCOM_VID, FALCOM_TWIST_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(FTDI_VID, FTDI_SUUNTO_SPORTS_PID, 0, 0x3ff) },
+ { USB_DEVICE_VER(FTDI_RM_VID, FTDI_RMCANVIEW_PID, 0, 0x3ff) },
+ { USB_DEVICE_VER(BANDB_VID, BANDB_USOTL4_PID, 0, 0x3ff) },
+ { USB_DEVICE_VER(BANDB_VID, BANDB_USTL4_PID, 0, 0x3ff) },
+ { USB_DEVICE_VER(BANDB_VID, BANDB_USO9ML2_PID, 0, 0x3ff) },
{ } /* Terminating entry */
};
@@ -478,6 +482,10 @@ static struct usb_device_id id_table_FT232BM [] = {
{ USB_DEVICE_VER(INTREPID_VID, INTREPID_NEOVI_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(FALCOM_VID, FALCOM_TWIST_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(FTDI_VID, FTDI_SUUNTO_SPORTS_PID, 0x400, 0xffff) },
+ { USB_DEVICE_VER(FTDI_RM_VID, FTDI_RMCANVIEW_PID, 0x400, 0xffff) },
+ { USB_DEVICE_VER(BANDB_VID, BANDB_USOTL4_PID, 0x400, 0xffff) },
+ { USB_DEVICE_VER(BANDB_VID, BANDB_USTL4_PID, 0x400, 0xffff) },
+ { USB_DEVICE_VER(BANDB_VID, BANDB_USO9ML2_PID, 0x400, 0xffff) },
{ } /* Terminating entry */
};
@@ -595,6 +603,10 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(INTREPID_VID, INTREPID_NEOVI_PID) },
{ USB_DEVICE(FALCOM_VID, FALCOM_TWIST_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_SUUNTO_SPORTS_PID) },
+ { USB_DEVICE(FTDI_RM_VID, FTDI_RMCANVIEW_PID) },
+ { USB_DEVICE(BANDB_VID, BANDB_USOTL4_PID) },
+ { USB_DEVICE(BANDB_VID, BANDB_USTL4_PID) },
+ { USB_DEVICE(BANDB_VID, BANDB_USO9ML2_PID) },
{ } /* Terminating entry */
};
@@ -1479,16 +1491,8 @@ static void ftdi_close (struct usb_serial_port *port, struct file *filp)
} /* Note change no line if hupcl is off */
/* shutdown our bulk read */
- if (port->read_urb) {
- if (usb_unlink_urb (port->read_urb) < 0) {
- /* Generally, this isn't an error. If the previous
- read bulk callback occurred (or is about to occur)
- while the port was being closed or was throtted
- (and is still throttled), the read urb will not
- have been submitted. */
- dbg("%s - failed to unlink read urb (generally not an error)", __FUNCTION__);
- }
- }
+ if (port->read_urb)
+ usb_kill_urb(port->read_urb);
} /* ftdi_close */
diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h
index 232213b02860..e7a7e0aba4f2 100644
--- a/drivers/usb/serial/ftdi_sio.h
+++ b/drivers/usb/serial/ftdi_sio.h
@@ -225,6 +225,21 @@
*/
#define FTDI_SUUNTO_SPORTS_PID 0xF680 /* Suunto Sports instrument */
+/*
+ * Definitions for B&B Electronics products.
+ */
+#define BANDB_VID 0x0856 /* B&B Electronics Vendor ID */
+#define BANDB_USOTL4_PID 0xAC01 /* USOTL4 Isolated RS-485 Converter */
+#define BANDB_USTL4_PID 0xAC02 /* USTL4 RS-485 Converter */
+#define BANDB_USO9ML2_PID 0xAC03 /* USO9ML2 Isolated RS-232 Converter */
+
+/*
+ * RM Michaelides CANview USB (http://www.rmcan.com)
+ * CAN filedbus interface adapter, addad by port GmbH www.port.de)
+ */
+#define FTDI_RM_VID 0x0403 /* Vendor Id */
+#define FTDI_RMCANVIEW_PID 0xfd60 /* Product Id */
+
/* Commands */
#define FTDI_SIO_RESET 0 /* Reset the port */
#define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */
diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c
index 196fea084f21..c88885f8ca05 100644
--- a/drivers/usb/serial/generic.c
+++ b/drivers/usb/serial/generic.c
@@ -147,9 +147,9 @@ static void generic_cleanup (struct usb_serial_port *port)
if (serial->dev) {
/* shutdown any bulk reads that might be going on */
if (serial->num_bulk_out)
- usb_unlink_urb (port->write_urb);
+ usb_kill_urb(port->write_urb);
if (serial->num_bulk_in)
- usb_unlink_urb (port->read_urb);
+ usb_kill_urb(port->read_urb);
}
}
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index 1ef47613b958..b5d18c699a0e 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -1238,7 +1238,7 @@ static void edge_close (struct usb_serial_port *port, struct file * filp)
edge_port->openPending = FALSE;
if (edge_port->write_urb) {
- usb_unlink_urb (edge_port->write_urb);
+ usb_kill_urb(edge_port->write_urb);
}
if (edge_port->write_urb) {
@@ -2443,8 +2443,8 @@ static int write_cmd_usb (struct edgeport_port *edge_port, unsigned char *buffer
if (status) {
/* something went wrong */
dbg("%s - usb_submit_urb(write bulk) failed", __FUNCTION__);
- usb_unlink_urb (urb);
- usb_free_urb (urb);
+ usb_kill_urb(urb);
+ usb_free_urb(urb);
return status;
}
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
index ace42c870727..359d3c0b8a15 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -1972,7 +1972,7 @@ static void edge_close (struct usb_serial_port *port, struct file * filp)
/* chase the port close */
TIChasePort (edge_port);
- usb_unlink_urb (port->read_urb);
+ usb_kill_urb(port->read_urb);
/* assuming we can still talk to the device,
* send a close port command to it */
@@ -1987,7 +1987,7 @@ static void edge_close (struct usb_serial_port *port, struct file * filp)
--edge_port->edge_serial->num_ports_open;
if (edge_port->edge_serial->num_ports_open <= 0) {
/* last port is now closed, let's shut down our interrupt urb */
- usb_unlink_urb (port->serial->port[0]->interrupt_in_urb);
+ usb_kill_urb(port->serial->port[0]->interrupt_in_urb);
edge_port->edge_serial->num_ports_open = 0;
}
edge_port->close_pending = 0;
@@ -2121,7 +2121,7 @@ static void edge_throttle (struct usb_serial_port *port)
status = TIClearRts (edge_port);
}
- usb_unlink_urb (port->read_urb);
+ usb_kill_urb(port->read_urb);
}
static void edge_unthrottle (struct usb_serial_port *port)
diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c
index 8fbce2732c51..1a748b515112 100644
--- a/drivers/usb/serial/ipaq.c
+++ b/drivers/usb/serial/ipaq.c
@@ -288,8 +288,8 @@ static void ipaq_close(struct usb_serial_port *port, struct file *filp)
/*
* shut down bulk read and write
*/
- usb_unlink_urb(port->write_urb);
- usb_unlink_urb(port->read_urb);
+ usb_kill_urb(port->write_urb);
+ usb_kill_urb(port->read_urb);
ipaq_destroy_lists(port);
kfree(priv);
usb_set_serial_port_data(port, NULL);
@@ -419,9 +419,8 @@ static void ipaq_write_gather(struct usb_serial_port *port)
struct ipaq_private *priv = usb_get_serial_port_data(port);
struct usb_serial *serial = port->serial;
int count, room;
- struct ipaq_packet *pkt;
+ struct ipaq_packet *pkt, *tmp;
struct urb *urb = port->write_urb;
- struct list_head *tmp;
if (urb->status == -EINPROGRESS) {
/* Should never happen */
@@ -429,9 +428,7 @@ static void ipaq_write_gather(struct usb_serial_port *port)
return;
}
room = URBDATA_SIZE;
- for (tmp = priv->queue.next; tmp != &priv->queue;) {
- pkt = list_entry(tmp, struct ipaq_packet, list);
- tmp = tmp->next;
+ list_for_each_entry_safe(pkt, tmp, &priv->queue, list) {
count = min(room, (int)(pkt->len - pkt->written));
memcpy(urb->transfer_buffer + (URBDATA_SIZE - room),
pkt->data + pkt->written, count);
@@ -503,22 +500,16 @@ static int ipaq_chars_in_buffer(struct usb_serial_port *port)
static void ipaq_destroy_lists(struct usb_serial_port *port)
{
struct ipaq_private *priv = usb_get_serial_port_data(port);
- struct list_head *tmp;
- struct ipaq_packet *pkt;
+ struct ipaq_packet *pkt, *tmp;
- for (tmp = priv->queue.next; tmp != &priv->queue;) {
- pkt = list_entry(tmp, struct ipaq_packet, list);
- tmp = tmp->next;
+ list_for_each_entry_safe(pkt, tmp, &priv->queue, list) {
kfree(pkt->data);
kfree(pkt);
}
- for (tmp = priv->freelist.next; tmp != &priv->freelist;) {
- pkt = list_entry(tmp, struct ipaq_packet, list);
- tmp = tmp->next;
+ list_for_each_entry_safe(pkt, tmp, &priv->freelist, list) {
kfree(pkt->data);
kfree(pkt);
}
- return;
}
diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c
new file mode 100644
index 000000000000..2fc04f905a5c
--- /dev/null
+++ b/drivers/usb/serial/ipw.c
@@ -0,0 +1,496 @@
+/*
+ * IPWireless 3G UMTS TDD Modem driver (USB connected)
+ *
+ * Copyright (C) 2004 Roelf Diedericks <roelfd@inet.co.za>
+ * Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.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.
+ *
+ * All information about the device was acquired using SnoopyPro
+ * on MSFT's O/S, and examing the MSFT drivers' debug output
+ * (insanely left _on_ in the enduser version)
+ *
+ * It was written out of frustration with the IPWireless USB modem
+ * supplied by Axity3G/Sentech South Africa not supporting
+ * Linux whatsoever.
+ *
+ * Nobody provided any proprietary information that was not already
+ * available for this device.
+ *
+ * The modem adheres to the "3GPP TS 27.007 AT command set for 3G
+ * User Equipment (UE)" standard, available from
+ * http://www.3gpp.org/ftp/Specs/html-info/27007.htm
+ *
+ * The code was only tested the IPWireless handheld modem distributed
+ * in South Africa by Sentech.
+ *
+ * It may work for Woosh Inc in .nz too, as it appears they use the
+ * same kit.
+ *
+ * There is still some work to be done in terms of handling
+ * DCD, DTR, RTS, CTS which are currently faked.
+ * It's good enough for PPP at this point. It's based off all kinds of
+ * code found in usb/serial and usb/class
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+#include <linux/usb.h>
+#include <asm/uaccess.h>
+#include "usb-serial.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.3"
+#define DRIVER_AUTHOR "Roelf Diedericks"
+#define DRIVER_DESC "IPWireless tty driver"
+
+#define IPW_TTY_MAJOR 240 /* real device node major id, experimental range */
+#define IPW_TTY_MINORS 256 /* we support 256 devices, dunno why, it'd be insane :) */
+
+#define USB_IPW_MAGIC 0x6d02 /* magic number for ipw struct */
+
+
+/* Message sizes */
+#define EVENT_BUFFER_SIZE 0xFF
+#define CHAR2INT16(c1,c0) (((u32)((c1) & 0xff) << 8) + (u32)((c0) & 0xff))
+#define NUM_BULK_URBS 24
+#define NUM_CONTROL_URBS 16
+
+/* vendor/product pairs that are known work with this driver*/
+#define IPW_VID 0x0bc3
+#define IPW_PID 0x0001
+
+
+/* Vendor commands: */
+
+/* baud rates */
+enum {
+ ipw_sio_b256000 = 0x000e,
+ ipw_sio_b128000 = 0x001d,
+ ipw_sio_b115200 = 0x0020,
+ ipw_sio_b57600 = 0x0040,
+ ipw_sio_b56000 = 0x0042,
+ ipw_sio_b38400 = 0x0060,
+ ipw_sio_b19200 = 0x00c0,
+ ipw_sio_b14400 = 0x0100,
+ ipw_sio_b9600 = 0x0180,
+ ipw_sio_b4800 = 0x0300,
+ ipw_sio_b2400 = 0x0600,
+ ipw_sio_b1200 = 0x0c00,
+ ipw_sio_b600 = 0x1800
+};
+
+/* data bits */
+#define ipw_dtb_7 0x700
+#define ipw_dtb_8 0x810 // ok so the define is misleading, I know, but forces 8,n,1
+ // I mean, is there a point to any other setting these days? :)
+
+/* usb control request types : */
+#define IPW_SIO_RXCTL 0x00 // control bulk rx channel transmissions, value=1/0 (on/off)
+#define IPW_SIO_SET_BAUD 0x01 // set baud, value=requested ipw_sio_bxxxx
+#define IPW_SIO_SET_LINE 0x03 // set databits, parity. value=ipw_dtb_x
+#define IPW_SIO_SET_PIN 0x03 // set/clear dtr/rts value=ipw_pin_xxx
+#define IPW_SIO_POLL 0x08 // get serial port status byte, call with value=0
+#define IPW_SIO_INIT 0x11 // initializes ? value=0 (appears as first thing todo on open)
+#define IPW_SIO_PURGE 0x12 // purge all transmissions?, call with value=numchar_to_purge
+#define IPW_SIO_HANDFLOW 0x13 // set xon/xoff limits value=0, and a buffer of 0x10 bytes
+#define IPW_SIO_SETCHARS 0x13 // set the flowcontrol special chars, value=0, buf=6 bytes,
+ // last 2 bytes contain flowcontrol chars e.g. 00 00 00 00 11 13
+
+/* values used for request IPW_SIO_SET_PIN */
+#define IPW_PIN_SETDTR 0x101
+#define IPW_PIN_SETRTS 0x202
+#define IPW_PIN_CLRDTR 0x100
+#define IPW_PIN_CLRRTS 0x200 // unconfirmed
+
+/* values used for request IPW_SIO_RXCTL */
+#define IPW_RXBULK_ON 1
+#define IPW_RXBULK_OFF 0
+
+/* various 16 byte hardcoded transferbuffers used by flow control */
+#define IPW_BYTES_FLOWINIT { 0x01, 0, 0, 0, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+
+/* Interpretation of modem status lines */
+/* These need sorting out by individually connecting pins and checking
+ * results. FIXME!
+ * When data is being sent we see 0x30 in the lower byte; this must
+ * contain DSR and CTS ...
+ */
+#define IPW_DSR ((1<<4) | (1<<5))
+#define IPW_CTS ((1<<5) | (1<<4))
+
+#define IPW_WANTS_TO_SEND 0x30
+//#define IPW_DTR /* Data Terminal Ready */
+//#define IPW_CTS /* Clear To Send */
+//#define IPW_CD /* Carrier Detect */
+//#define IPW_DSR /* Data Set Ready */
+//#define IPW_RxD /* Receive pin */
+
+//#define IPW_LE
+//#define IPW_RTS
+//#define IPW_ST
+//#define IPW_SR
+//#define IPW_RI /* Ring Indicator */
+
+static struct usb_device_id usb_ipw_ids[] = {
+ { USB_DEVICE(IPW_VID, IPW_PID) },
+ { },
+};
+
+MODULE_DEVICE_TABLE(usb, usb_ipw_ids);
+
+static struct usb_driver usb_ipw_driver = {
+ .owner = THIS_MODULE,
+ .name = "ipwtty",
+ .probe = usb_serial_probe,
+ .disconnect = usb_serial_disconnect,
+ .id_table = usb_ipw_ids,
+};
+
+static int debug;
+
+static void ipw_read_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+ struct usb_serial_port *port = urb->context;
+ unsigned char *data = urb->transfer_buffer;
+ struct tty_struct *tty;
+ int i;
+ int result;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ if (urb->status) {
+ dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
+ return;
+ }
+
+ usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
+
+ tty = port->tty;
+ if (tty && urb->actual_length) {
+ for (i = 0; i < urb->actual_length ; ++i) {
+ /* if we insert more than TTY_FLIPBUF_SIZE characters, we drop them. */
+ if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
+ tty_flip_buffer_push(tty);
+ }
+ /* this doesn't actually push the data through unless tty->low_latency is set */
+ tty_insert_flip_char(tty, data[i], 0);
+ }
+ tty_flip_buffer_push(tty);
+ }
+
+ /* Continue trying to always read */
+ usb_fill_bulk_urb (port->read_urb, port->serial->dev,
+ usb_rcvbulkpipe(port->serial->dev,
+ port->bulk_in_endpointAddress),
+ port->read_urb->transfer_buffer,
+ port->read_urb->transfer_buffer_length,
+ ipw_read_bulk_callback, port);
+ result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+ if (result)
+ dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
+ return;
+}
+
+static int ipw_open(struct usb_serial_port *port, struct file *filp)
+{
+ struct usb_device *dev = port->serial->dev;
+ u8 buf_flow_static[16] = IPW_BYTES_FLOWINIT;
+ u8 *buf_flow_init;
+ int result;
+
+ dbg("%s", __FUNCTION__);
+
+ buf_flow_init = kmalloc(16, GFP_KERNEL);
+ if (!buf_flow_init)
+ return -ENOMEM;
+ memcpy(buf_flow_init, buf_flow_static, 16);
+
+ if (port->tty)
+ port->tty->low_latency = 1;
+
+ /* --1: Tell the modem to initialize (we think) From sniffs this is always the
+ * first thing that gets sent to the modem during opening of the device */
+ dbg("%s: Sending SIO_INIT (we guess)",__FUNCTION__);
+ result = usb_control_msg(dev, usb_sndctrlpipe(dev,0),
+ IPW_SIO_INIT,
+ USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ 0,
+ 0, /* index */
+ NULL,
+ 0,
+ 100*HZ);
+ if (result < 0)
+ dev_err(&port->dev, "Init of modem failed (error = %d)", result);
+
+ /* reset the bulk pipes */
+ usb_clear_halt(dev, usb_rcvbulkpipe(dev, port->bulk_in_endpointAddress));
+ usb_clear_halt(dev, usb_sndbulkpipe(dev, port->bulk_out_endpointAddress));
+
+ /*--2: Start reading from the device */
+ dbg("%s: setting up bulk read callback",__FUNCTION__);
+ usb_fill_bulk_urb(port->read_urb, dev,
+ usb_rcvbulkpipe(dev, port->bulk_in_endpointAddress),
+ port->bulk_in_buffer,
+ port->bulk_in_size,
+ ipw_read_bulk_callback, port);
+ result = usb_submit_urb(port->read_urb, GFP_KERNEL);
+ if (result < 0)
+ dbg("%s - usb_submit_urb(read bulk) failed with status %d", __FUNCTION__, result);
+
+ /*--3: Tell the modem to open the floodgates on the rx bulk channel */
+ dbg("%s:asking modem for RxRead (RXBULK_ON)",__FUNCTION__);
+ result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ IPW_SIO_RXCTL,
+ USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ IPW_RXBULK_ON,
+ 0, /* index */
+ NULL,
+ 0,
+ 100*HZ);
+ if (result < 0)
+ dev_err(&port->dev, "Enabling bulk RxRead failed (error = %d)", result);
+
+ /*--4: setup the initial flowcontrol */
+ dbg("%s:setting init flowcontrol (%s)",__FUNCTION__,buf_flow_init);
+ result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ IPW_SIO_HANDFLOW,
+ USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ 0,
+ 0,
+ buf_flow_init,
+ 0x10,
+ 200*HZ);
+ if (result < 0)
+ dev_err(&port->dev, "initial flowcontrol failed (error = %d)", result);
+
+
+ /*--5: raise the dtr */
+ dbg("%s:raising dtr",__FUNCTION__);
+ result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ IPW_SIO_SET_PIN,
+ USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ IPW_PIN_SETDTR,
+ 0,
+ NULL,
+ 0,
+ 200*HZ);
+ if (result < 0)
+ dev_err(&port->dev, "setting dtr failed (error = %d)", result);
+
+ /*--6: raise the rts */
+ dbg("%s:raising rts",__FUNCTION__);
+ result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ IPW_SIO_SET_PIN,
+ USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ IPW_PIN_SETRTS,
+ 0,
+ NULL,
+ 0,
+ 200*HZ);
+ if (result < 0)
+ dev_err(&port->dev, "setting dtr failed (error = %d)", result);
+
+ kfree(buf_flow_init);
+ return 0;
+}
+
+static void ipw_close(struct usb_serial_port *port, struct file * filp)
+{
+ struct usb_device *dev = port->serial->dev;
+ int result;
+
+ if (tty_hung_up_p(filp)) {
+ dbg("%s: tty_hung_up_p ...", __FUNCTION__);
+ return;
+ }
+
+ /*--1: drop the dtr */
+ dbg("%s:dropping dtr",__FUNCTION__);
+ result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ IPW_SIO_SET_PIN,
+ USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ IPW_PIN_CLRDTR,
+ 0,
+ NULL,
+ 0,
+ 200*HZ);
+ if (result < 0)
+ dev_err(&port->dev, "dropping dtr failed (error = %d)", result);
+
+ /*--2: drop the rts */
+ dbg("%s:dropping rts",__FUNCTION__);
+ result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ IPW_SIO_SET_PIN, USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ IPW_PIN_CLRRTS,
+ 0,
+ NULL,
+ 0,
+ 200*HZ);
+ if (result < 0)
+ dev_err(&port->dev, "dropping rts failed (error = %d)", result);
+
+
+ /*--3: purge */
+ dbg("%s:sending purge",__FUNCTION__);
+ result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ IPW_SIO_PURGE, USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ 0x03,
+ 0,
+ NULL,
+ 0,
+ 200*HZ);
+ if (result < 0)
+ dev_err(&port->dev, "purge failed (error = %d)", result);
+
+
+ /* send RXBULK_off (tell modem to stop transmitting bulk data on rx chan) */
+ result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ IPW_SIO_RXCTL,
+ USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ IPW_RXBULK_OFF,
+ 0, /* index */
+ NULL,
+ 0,
+ 100*HZ);
+
+ if (result < 0)
+ dev_err(&port->dev, "Disabling bulk RxRead failed (error = %d)", result);
+
+ /* shutdown any in-flight urbs that we know about */
+ usb_kill_urb(port->read_urb);
+ usb_kill_urb(port->write_urb);
+}
+
+static void ipw_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+ struct usb_serial_port *port = urb->context;
+
+ dbg("%s", __FUNCTION__);
+
+ if (urb->status)
+ dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
+
+ schedule_work(&port->work);
+}
+
+static int ipw_write(struct usb_serial_port *port, int from_user, const unsigned char *buf, int count)
+{
+ struct usb_device *dev = port->serial->dev;
+ int ret;
+
+ dbg("%s: TOP: count=%d, from_user=%d, in_interrupt=%ld", __FUNCTION__,
+ count, from_user, in_interrupt() );
+
+ if (count == 0) {
+ dbg("%s - write request of 0 bytes", __FUNCTION__);
+ return 0;
+ }
+
+ /* Racy and broken, FIXME properly! */
+ if (port->write_urb->status == -EINPROGRESS)
+ return 0;
+
+ count = min(count, port->bulk_out_size);
+ if (from_user) {
+ if (copy_from_user(port->bulk_out_buffer, buf, count))
+ return -EFAULT;
+ } else {
+ memcpy(port->bulk_out_buffer, buf, count);
+ }
+
+ dbg("%s count now:%d", __FUNCTION__, count);
+
+ usb_fill_bulk_urb(port->write_urb, dev,
+ usb_sndbulkpipe(dev, port->bulk_out_endpointAddress),
+ port->write_urb->transfer_buffer,
+ count,
+ ipw_write_bulk_callback,
+ port);
+
+ ret = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+ if (ret != 0) {
+ dbg("%s - usb_submit_urb(write bulk) failed with error = %d", __FUNCTION__, ret);
+ return ret;
+ }
+
+ dbg("%s returning %d", __FUNCTION__, count);
+ return count;
+}
+
+static int ipw_probe(struct usb_serial_port *port)
+{
+ return 0;
+}
+
+static int ipw_disconnect(struct usb_serial_port *port)
+{
+ usb_set_serial_port_data(port, NULL);
+ return 0;
+}
+
+static struct usb_serial_device_type ipw_device = {
+ .owner = THIS_MODULE,
+ .name = "IPWireless converter",
+ .short_name = "ipw",
+ .id_table = usb_ipw_ids,
+ .num_interrupt_in = NUM_DONT_CARE,
+ .num_bulk_in = 1,
+ .num_bulk_out = 1,
+ .num_ports = 1,
+ .open = ipw_open,
+ .close = ipw_close,
+ .port_probe = ipw_probe,
+ .port_remove = ipw_disconnect,
+ .write = ipw_write,
+ .write_bulk_callback = ipw_write_bulk_callback,
+ .read_bulk_callback = ipw_read_bulk_callback,
+};
+
+
+
+int usb_ipw_init(void)
+{
+ int retval;
+
+ retval = usb_serial_register(&ipw_device);
+ if (retval)
+ return retval;
+ retval = usb_register(&usb_ipw_driver);
+ if (retval) {
+ usb_serial_deregister(&ipw_device);
+ return retval;
+ }
+ info(DRIVER_DESC " " DRIVER_VERSION);
+ return 0;
+}
+
+void usb_ipw_exit(void)
+{
+ usb_deregister(&usb_ipw_driver);
+ usb_serial_deregister(&ipw_device);
+}
+
+module_init(usb_ipw_init);
+module_exit(usb_ipw_exit);
+
+/* Module information */
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c
index 3d1571695bde..c2c536f194e0 100644
--- a/drivers/usb/serial/ir-usb.c
+++ b/drivers/usb/serial/ir-usb.c
@@ -322,7 +322,7 @@ static void ir_close (struct usb_serial_port *port, struct file * filp)
dbg("%s - port %d", __FUNCTION__, port->number);
/* shutdown our bulk read */
- usb_unlink_urb (port->read_urb);
+ usb_kill_urb(port->read_urb);
}
static int ir_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count)
diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
index cd441d783cbe..0a979e208167 100644
--- a/drivers/usb/serial/keyspan_pda.c
+++ b/drivers/usb/serial/keyspan_pda.c
@@ -285,7 +285,7 @@ static void keyspan_pda_rx_throttle (struct usb_serial_port *port)
upon the device too. */
dbg("keyspan_pda_rx_throttle port %d", port->number);
- usb_unlink_urb(port->interrupt_in_urb);
+ usb_kill_urb(port->interrupt_in_urb);
}
@@ -706,8 +706,8 @@ static void keyspan_pda_close(struct usb_serial_port *port, struct file *filp)
keyspan_pda_set_modem_info(serial, 0);
/* shutdown our bulk reads and writes */
- usb_unlink_urb (port->write_urb);
- usb_unlink_urb (port->interrupt_in_urb);
+ usb_kill_urb(port->write_urb);
+ usb_kill_urb(port->interrupt_in_urb);
}
}
diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c
index c5207d19401a..9e60b3476775 100644
--- a/drivers/usb/serial/kl5kusb105.c
+++ b/drivers/usb/serial/kl5kusb105.c
@@ -336,12 +336,12 @@ static void klsi_105_shutdown (struct usb_serial *serial)
for (j = 0; j < NUM_URBS; j++) {
if (write_urbs[j]) {
/* FIXME - uncomment the following
- * usb_unlink_urb call when the host
+ * usb_kill_urb call when the host
* controllers get fixed to set
* urb->dev = NULL after the urb is
* finished. Otherwise this call
* oopses. */
- /* usb_unlink_urb(write_urbs[j]); */
+ /* usb_kill_urb(write_urbs[j]); */
if (write_urbs[j]->transfer_buffer)
kfree(write_urbs[j]->transfer_buffer);
usb_free_urb (write_urbs[j]);
@@ -467,12 +467,12 @@ static void klsi_105_close (struct usb_serial_port *port, struct file *filp)
err("Disabling read failed (error = %d)", rc);
/* shutdown our bulk reads and writes */
- usb_unlink_urb (port->write_urb);
- usb_unlink_urb (port->read_urb);
+ usb_kill_urb(port->write_urb);
+ usb_kill_urb(port->read_urb);
/* unlink our write pool */
/* FIXME */
/* wgg - do I need this? I think so. */
- usb_unlink_urb (port->interrupt_in_urb);
+ usb_kill_urb(port->interrupt_in_urb);
info("kl5kusb105 port stats: %ld bytes in, %ld bytes out", priv->bytes_in, priv->bytes_out);
} /* klsi_105_close */
@@ -994,7 +994,7 @@ static int klsi_105_ioctl (struct usb_serial_port *port, struct file * file,
static void klsi_105_throttle (struct usb_serial_port *port)
{
dbg("%s - port %d", __FUNCTION__, port->number);
- usb_unlink_urb (port->read_urb);
+ usb_kill_urb(port->read_urb);
}
static void klsi_105_unthrottle (struct usb_serial_port *port)
diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c
index b479916ce269..33539874bd0c 100644
--- a/drivers/usb/serial/kobil_sct.c
+++ b/drivers/usb/serial/kobil_sct.c
@@ -350,14 +350,13 @@ static void kobil_close (struct usb_serial_port *port, struct file *filp)
{
dbg("%s - port %d", __FUNCTION__, port->number);
- if (port->write_urb){
- usb_unlink_urb( port->write_urb );
+ if (port->write_urb) {
+ usb_kill_urb(port->write_urb);
usb_free_urb( port->write_urb );
port->write_urb = NULL;
}
- if (port->interrupt_in_urb){
- usb_unlink_urb (port->interrupt_in_urb);
- }
+ if (port->interrupt_in_urb)
+ usb_kill_urb(port->interrupt_in_urb);
}
@@ -458,9 +457,8 @@ static int kobil_write (struct usb_serial_port *port, int from_user,
((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 3) && (priv->filled >= (priv->buf[2] + 4))) ) {
// stop reading (except TWIN and KAAN SIM)
- if ( (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) || (priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) ) {
- usb_unlink_urb( port->interrupt_in_urb );
- }
+ if ( (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) || (priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) )
+ usb_kill_urb(port->interrupt_in_urb);
todo = priv->filled - priv->cur_pos;
diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
index 212906c74f25..1d7c682aed7a 100644
--- a/drivers/usb/serial/mct_u232.c
+++ b/drivers/usb/serial/mct_u232.c
@@ -480,9 +480,9 @@ static void mct_u232_close (struct usb_serial_port *port, struct file *filp)
if (port->serial->dev) {
/* shutdown our urbs */
- usb_unlink_urb (port->write_urb);
- usb_unlink_urb (port->read_urb);
- usb_unlink_urb (port->interrupt_in_urb);
+ usb_kill_urb(port->write_urb);
+ usb_kill_urb(port->read_urb);
+ usb_kill_urb(port->interrupt_in_urb);
}
} /* mct_u232_close */
diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c
index 371aa2e8e3f1..7032d5f7c6e1 100644
--- a/drivers/usb/serial/omninet.c
+++ b/drivers/usb/serial/omninet.c
@@ -183,8 +183,8 @@ static void omninet_close (struct usb_serial_port *port, struct file * filp)
dbg("%s - port %d", __FUNCTION__, port->number);
wport = serial->port[1];
- usb_unlink_urb(wport->write_urb);
- usb_unlink_urb(port->read_urb);
+ usb_kill_urb(wport->write_urb);
+ usb_kill_urb(port->read_urb);
od = usb_get_serial_port_data(port);
if (od)
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index c61e56829a96..5be9c370313b 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -55,11 +55,26 @@
/*
* Version Information
*/
-#define DRIVER_VERSION "v0.11"
+#define DRIVER_VERSION "v0.12"
#define DRIVER_DESC "Prolific PL2303 USB to serial adaptor driver"
static int debug;
+#define PL2303_CLOSING_WAIT (30*HZ)
+
+#define PL2303_BUF_SIZE 1024
+#define PL2303_TMP_BUF_SIZE 1024
+
+static char pl2303_tmp_buf[PL2303_TMP_BUF_SIZE];
+static DECLARE_MUTEX(pl2303_tmp_buf_sem);
+
+struct pl2303_buf {
+ unsigned int buf_size;
+ char *buf_buf;
+ char *buf_get;
+ char *buf_put;
+};
+
static struct usb_device_id id_table [] = {
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) },
@@ -134,12 +149,24 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs);
static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs);
static int pl2303_write (struct usb_serial_port *port, int from_user,
const unsigned char *buf, int count);
+static void pl2303_send (struct usb_serial_port *port);
+static int pl2303_write_room(struct usb_serial_port *port);
+static int pl2303_chars_in_buffer(struct usb_serial_port *port);
static void pl2303_break_ctl(struct usb_serial_port *port,int break_state);
static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file);
static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file,
unsigned int set, unsigned int clear);
static int pl2303_startup (struct usb_serial *serial);
static void pl2303_shutdown (struct usb_serial *serial);
+static struct pl2303_buf *pl2303_buf_alloc(unsigned int size);
+static void pl2303_buf_free(struct pl2303_buf *pb);
+static void pl2303_buf_clear(struct pl2303_buf *pb);
+static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb);
+static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb);
+static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf,
+ unsigned int count);
+static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf,
+ unsigned int count);
/* All of the device info needed for the PL2303 SIO serial converter */
@@ -162,6 +189,8 @@ static struct usb_serial_device_type pl2303_device = {
.read_bulk_callback = pl2303_read_bulk_callback,
.read_int_callback = pl2303_read_int_callback,
.write_bulk_callback = pl2303_write_bulk_callback,
+ .write_room = pl2303_write_room,
+ .chars_in_buffer = pl2303_chars_in_buffer,
.attach = pl2303_startup,
.shutdown = pl2303_shutdown,
};
@@ -174,6 +203,8 @@ enum pl2303_type {
struct pl2303_private {
spinlock_t lock;
+ struct pl2303_buf *buf;
+ int write_urb_in_use;
wait_queue_head_t delta_msr_wait;
u8 line_control;
u8 line_status;
@@ -201,14 +232,28 @@ static int pl2303_startup (struct usb_serial *serial)
for (i = 0; i < serial->num_ports; ++i) {
priv = kmalloc (sizeof (struct pl2303_private), GFP_KERNEL);
if (!priv)
- return -ENOMEM;
+ goto cleanup;
memset (priv, 0x00, sizeof (struct pl2303_private));
spin_lock_init(&priv->lock);
+ priv->buf = pl2303_buf_alloc(PL2303_BUF_SIZE);
+ if (priv->buf == NULL) {
+ kfree(priv);
+ goto cleanup;
+ }
init_waitqueue_head(&priv->delta_msr_wait);
priv->type = type;
usb_set_serial_port_data(serial->port[i], priv);
}
return 0;
+
+cleanup:
+ for (--i; i>=0; --i) {
+ priv = usb_get_serial_port_data(serial->port[i]);
+ pl2303_buf_free(priv->buf);
+ kfree(priv);
+ usb_set_serial_port_data(serial->port[i], NULL);
+ }
+ return -ENOMEM;
}
static int set_control_lines (struct usb_device *dev, u8 value)
@@ -224,40 +269,109 @@ static int set_control_lines (struct usb_device *dev, u8 value)
static int pl2303_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count)
{
- int result;
+ struct pl2303_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count);
if (!count)
return count;
- if (port->write_urb->status == -EINPROGRESS) {
- dbg("%s - already writing", __FUNCTION__);
- return 0;
- }
-
- count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
if (from_user) {
- if (copy_from_user (port->write_urb->transfer_buffer, buf, count))
+ if (count > PL2303_TMP_BUF_SIZE)
+ count = PL2303_TMP_BUF_SIZE;
+ down(&pl2303_tmp_buf_sem);
+ if (copy_from_user(pl2303_tmp_buf, buf, count)) {
+ up(&pl2303_tmp_buf_sem);
return -EFAULT;
- } else {
- memcpy (port->write_urb->transfer_buffer, buf, count);
+ }
+ buf = pl2303_tmp_buf;
}
-
+
+ spin_lock_irqsave(&priv->lock, flags);
+ count = pl2303_buf_put(priv->buf, buf, count);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ if (from_user)
+ up(&pl2303_tmp_buf_sem);
+
+ pl2303_send(port);
+
+ return count;
+}
+
+static void pl2303_send(struct usb_serial_port *port)
+{
+ int count, result;
+ struct pl2303_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ if (priv->write_urb_in_use) {
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return;
+ }
+
+ count = pl2303_buf_get(priv->buf, port->write_urb->transfer_buffer,
+ port->bulk_out_size);
+
+ if (count == 0) {
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return;
+ }
+
+ priv->write_urb_in_use = 1;
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, port->write_urb->transfer_buffer);
port->write_urb->transfer_buffer_length = count;
port->write_urb->dev = port->serial->dev;
result = usb_submit_urb (port->write_urb, GFP_ATOMIC);
- if (result)
+ if (result) {
dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__, result);
- else
- result = count;
+ priv->write_urb_in_use = 0;
+ // TODO: reschedule pl2303_send
+ }
- return result;
+ schedule_work(&port->work);
}
+static int pl2303_write_room(struct usb_serial_port *port)
+{
+ struct pl2303_private *priv = usb_get_serial_port_data(port);
+ int room = 0;
+ unsigned long flags;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ room = pl2303_buf_space_avail(priv->buf);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ dbg("%s - returns %d", __FUNCTION__, room);
+ return room;
+}
+
+static int pl2303_chars_in_buffer(struct usb_serial_port *port)
+{
+ struct pl2303_private *priv = usb_get_serial_port_data(port);
+ int chars = 0;
+ unsigned long flags;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+ spin_lock_irqsave(&priv->lock, flags);
+ chars = pl2303_buf_data_avail(priv->buf);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ dbg("%s - returns %d", __FUNCTION__, chars);
+ return chars;
+}
static void pl2303_set_termios (struct usb_serial_port *port, struct termios *old_termios)
{
@@ -422,7 +536,7 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol
}
kfree (buf);
-}
+}
static int pl2303_open (struct usb_serial_port *port, struct file *filp)
{
@@ -461,7 +575,7 @@ static int pl2303_open (struct usb_serial_port *port, struct file *filp)
FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8383, 0);
SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0, 1);
SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 1, 0);
-
+
if (priv->type == HX) {
/* HX chip */
SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 2, 0x44);
@@ -504,45 +618,67 @@ static int pl2303_open (struct usb_serial_port *port, struct file *filp)
static void pl2303_close (struct usb_serial_port *port, struct file *filp)
{
- struct pl2303_private *priv;
+ struct pl2303_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
unsigned int c_cflag;
- int result;
+ int bps;
+ long timeout;
+ wait_queue_t wait; \
dbg("%s - port %d", __FUNCTION__, port->number);
- /* shutdown our urbs */
- dbg("%s - shutting down urbs", __FUNCTION__);
- result = usb_unlink_urb (port->write_urb);
- if (result)
- dbg("%s - usb_unlink_urb (write_urb)"
- " failed with reason: %d", __FUNCTION__,
- result);
+ /* wait for data to drain from the buffer */
+ spin_lock_irqsave(&priv->lock, flags);
+ timeout = PL2303_CLOSING_WAIT;
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(&port->tty->write_wait, &wait);
+ for (;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (pl2303_buf_data_avail(priv->buf) == 0
+ || timeout == 0 || signal_pending(current)
+ || !usb_get_intfdata(port->serial->interface)) /* disconnect */
+ break;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ timeout = schedule_timeout(timeout);
+ spin_lock_irqsave(&priv->lock, flags);
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&port->tty->write_wait, &wait);
+ /* clear out any remaining data in the buffer */
+ pl2303_buf_clear(priv->buf);
+ spin_unlock_irqrestore(&priv->lock, flags);
- result = usb_unlink_urb (port->read_urb);
- if (result)
- dbg("%s - usb_unlink_urb (read_urb) "
- "failed with reason: %d", __FUNCTION__,
- result);
+ /* wait for characters to drain from the device */
+ /* (this is long enough for the entire 256 byte */
+ /* pl2303 hardware buffer to drain with no flow */
+ /* control for data rates of 1200 bps or more, */
+ /* for lower rates we should really know how much */
+ /* data is in the buffer to compute a delay */
+ /* that is not unnecessarily long) */
+ bps = tty_get_baud_rate(port->tty);
+ if (bps > 1200)
+ timeout = max((HZ*2560)/bps,HZ/10);
+ else
+ timeout = 2*HZ;
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(timeout);
- result = usb_unlink_urb (port->interrupt_in_urb);
- if (result)
- dbg("%s - usb_unlink_urb (interrupt_in_urb)"
- " failed with reason: %d", __FUNCTION__,
- result);
+ /* shutdown our urbs */
+ dbg("%s - shutting down urbs", __FUNCTION__);
+ usb_kill_urb(port->write_urb);
+ usb_kill_urb(port->read_urb);
+ usb_kill_urb(port->interrupt_in_urb);
if (port->tty) {
c_cflag = port->tty->termios->c_cflag;
if (c_cflag & HUPCL) {
/* drop DTR and RTS */
- priv = usb_get_serial_port_data(port);
spin_lock_irqsave(&priv->lock, flags);
priv->line_control = 0;
spin_unlock_irqrestore (&priv->lock, flags);
set_control_lines (port->serial->dev, 0);
}
}
-
}
static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file,
@@ -672,12 +808,17 @@ static void pl2303_break_ctl (struct usb_serial_port *port, int break_state)
static void pl2303_shutdown (struct usb_serial *serial)
{
int i;
+ struct pl2303_private *priv;
dbg("%s", __FUNCTION__);
for (i = 0; i < serial->num_ports; ++i) {
- kfree (usb_get_serial_port_data(serial->port[i]));
- usb_set_serial_port_data(serial->port[i], NULL);
+ priv = usb_get_serial_port_data(serial->port[i]);
+ if (priv) {
+ pl2303_buf_free(priv->buf);
+ kfree(priv);
+ usb_set_serial_port_data(serial->port[i], NULL);
+ }
}
}
@@ -815,11 +956,23 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
{
struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
+ struct pl2303_private *priv = usb_get_serial_port_data(port);
int result;
dbg("%s - port %d", __FUNCTION__, port->number);
-
- if (urb->status) {
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+ priv->write_urb_in_use = 0;
+ return;
+ default:
/* error in the urb, so we have to resubmit it */
dbg("%s - Overflow in write", __FUNCTION__);
dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
@@ -828,14 +981,199 @@ static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
result = usb_submit_urb (port->write_urb, GFP_ATOMIC);
if (result)
dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n", __FUNCTION__, result);
+ else
+ return;
+ }
- return;
+ priv->write_urb_in_use = 0;
+
+ /* send any buffered data */
+ pl2303_send(port);
+}
+
+
+/*
+ * pl2303_buf_alloc
+ *
+ * Allocate a circular buffer and all associated memory.
+ */
+
+static struct pl2303_buf *pl2303_buf_alloc(unsigned int size)
+{
+
+ struct pl2303_buf *pb;
+
+
+ if (size == 0)
+ return NULL;
+
+ pb = (struct pl2303_buf *)kmalloc(sizeof(struct pl2303_buf), GFP_KERNEL);
+ if (pb == NULL)
+ return NULL;
+
+ pb->buf_buf = kmalloc(size, GFP_KERNEL);
+ if (pb->buf_buf == NULL) {
+ kfree(pb);
+ return NULL;
}
- schedule_work(&port->work);
+ pb->buf_size = size;
+ pb->buf_get = pb->buf_put = pb->buf_buf;
+
+ return pb;
+
+}
+
+
+/*
+ * pl2303_buf_free
+ *
+ * Free the buffer and all associated memory.
+ */
+
+static void pl2303_buf_free(struct pl2303_buf *pb)
+{
+ if (pb != NULL) {
+ if (pb->buf_buf != NULL)
+ kfree(pb->buf_buf);
+ kfree(pb);
+ }
+}
+
+
+/*
+ * pl2303_buf_clear
+ *
+ * Clear out all data in the circular buffer.
+ */
+
+static void pl2303_buf_clear(struct pl2303_buf *pb)
+{
+ if (pb != NULL)
+ pb->buf_get = pb->buf_put;
+ /* equivalent to a get of all data available */
+}
+
+
+/*
+ * pl2303_buf_data_avail
+ *
+ * Return the number of bytes of data available in the circular
+ * buffer.
+ */
+
+static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb)
+{
+ if (pb != NULL)
+ return ((pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size);
+ else
+ return 0;
+}
+
+
+/*
+ * pl2303_buf_space_avail
+ *
+ * Return the number of bytes of space available in the circular
+ * buffer.
+ */
+
+static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb)
+{
+ if (pb != NULL)
+ return ((pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size);
+ else
+ return 0;
+}
+
+
+/*
+ * pl2303_buf_put
+ *
+ * Copy data data from a user buffer and put it into the circular buffer.
+ * Restrict to the amount of space available.
+ *
+ * Return the number of bytes copied.
+ */
+
+static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf,
+ unsigned int count)
+{
+
+ unsigned int len;
+
+
+ if (pb == NULL)
+ return 0;
+
+ len = pl2303_buf_space_avail(pb);
+ if (count > len)
+ count = len;
+
+ if (count == 0)
+ return 0;
+
+ len = pb->buf_buf + pb->buf_size - pb->buf_put;
+ if (count > len) {
+ memcpy(pb->buf_put, buf, len);
+ memcpy(pb->buf_buf, buf+len, count - len);
+ pb->buf_put = pb->buf_buf + count - len;
+ } else {
+ memcpy(pb->buf_put, buf, count);
+ if (count < len)
+ pb->buf_put += count;
+ else /* count == len */
+ pb->buf_put = pb->buf_buf;
+ }
+
+ return count;
+
}
+/*
+ * pl2303_buf_get
+ *
+ * Get data from the circular buffer and copy to the given buffer.
+ * Restrict to the amount of data available.
+ *
+ * Return the number of bytes copied.
+ */
+
+static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf,
+ unsigned int count)
+{
+
+ unsigned int len;
+
+
+ if (pb == NULL)
+ return 0;
+
+ len = pl2303_buf_data_avail(pb);
+ if (count > len)
+ count = len;
+
+ if (count == 0)
+ return 0;
+
+ len = pb->buf_buf + pb->buf_size - pb->buf_get;
+ if (count > len) {
+ memcpy(buf, pb->buf_get, len);
+ memcpy(buf+len, pb->buf_buf, count - len);
+ pb->buf_get = pb->buf_buf + count - len;
+ } else {
+ memcpy(buf, pb->buf_get, count);
+ if (count < len)
+ pb->buf_get += count;
+ else /* count == len */
+ pb->buf_get = pb->buf_buf;
+ }
+
+ return count;
+
+}
+
static int __init pl2303_init (void)
{
int retval;
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 03e8a7e1c8ee..e3b7c1dd2f4c 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -388,7 +388,7 @@ static struct usb_serial *get_free_serial (struct usb_serial *serial, int num_po
good_spot = 1;
for (j = 1; j <= num_ports-1; ++j)
- if ((serial_table[i+j]) || (i+j >= SERIAL_TTY_MINORS)) {
+ if ((i+j >= SERIAL_TTY_MINORS) || (serial_table[i+j])) {
good_spot = 0;
i += j;
break;
@@ -405,7 +405,7 @@ static struct usb_serial *get_free_serial (struct usb_serial *serial, int num_po
return NULL;
}
-static void return_serial (struct usb_serial *serial)
+static void return_serial(struct usb_serial *serial)
{
int i;
@@ -417,8 +417,6 @@ static void return_serial (struct usb_serial *serial)
for (i = 0; i < serial->num_ports; ++i) {
serial_table[serial->minor + i] = NULL;
}
-
- return;
}
static void destroy_serial(struct kref *kref)
@@ -455,15 +453,15 @@ static void destroy_serial(struct kref *kref)
if (!port)
continue;
if (port->read_urb) {
- usb_unlink_urb(port->read_urb);
+ usb_kill_urb(port->read_urb);
usb_free_urb(port->read_urb);
}
if (port->write_urb) {
- usb_unlink_urb(port->write_urb);
+ usb_kill_urb(port->write_urb);
usb_free_urb(port->write_urb);
}
if (port->interrupt_in_urb) {
- usb_unlink_urb(port->interrupt_in_urb);
+ usb_kill_urb(port->interrupt_in_urb);
usb_free_urb(port->interrupt_in_urb);
}
kfree(port->bulk_in_buffer);
@@ -621,15 +619,12 @@ static void serial_throttle (struct tty_struct * tty)
if (!port->open_count) {
dbg ("%s - port not open", __FUNCTION__);
- goto exit;
+ return;
}
/* pass on to the driver specific version of this function */
if (port->serial->type->throttle)
port->serial->type->throttle(port);
-
-exit:
- ;
}
static void serial_unthrottle (struct tty_struct * tty)
@@ -640,15 +635,12 @@ static void serial_unthrottle (struct tty_struct * tty)
if (!port->open_count) {
dbg("%s - port not open", __FUNCTION__);
- goto exit;
+ return;
}
/* pass on to the driver specific version of this function */
if (port->serial->type->unthrottle)
port->serial->type->unthrottle(port);
-
-exit:
- ;
}
static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
@@ -681,15 +673,12 @@ static void serial_set_termios (struct tty_struct *tty, struct termios * old)
if (!port->open_count) {
dbg("%s - port not open", __FUNCTION__);
- goto exit;
+ return;
}
/* pass on to the driver specific version of this function if it is available */
if (port->serial->type->set_termios)
port->serial->type->set_termios(port, old);
-
-exit:
- ;
}
static void serial_break (struct tty_struct *tty, int break_state)
@@ -700,15 +689,12 @@ static void serial_break (struct tty_struct *tty, int break_state)
if (!port->open_count) {
dbg("%s - port not open", __FUNCTION__);
- goto exit;
+ return;
}
/* pass on to the driver specific version of this function if it is available */
if (port->serial->type->break_ctl)
port->serial->type->break_ctl(port, break_state);
-
-exit:
- ;
}
static int serial_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data)
@@ -814,15 +800,15 @@ static void port_release(struct device *dev)
dbg ("%s - %s", __FUNCTION__, dev->bus_id);
if (port->read_urb) {
- usb_unlink_urb(port->read_urb);
+ usb_kill_urb(port->read_urb);
usb_free_urb(port->read_urb);
}
if (port->write_urb) {
- usb_unlink_urb(port->write_urb);
+ usb_kill_urb(port->write_urb);
usb_free_urb(port->write_urb);
}
if (port->interrupt_in_urb) {
- usb_unlink_urb(port->interrupt_in_urb);
+ usb_kill_urb(port->interrupt_in_urb);
usb_free_urb(port->interrupt_in_urb);
}
kfree(port->bulk_in_buffer);
@@ -853,6 +839,25 @@ static struct usb_serial * create_serial (struct usb_device *dev,
return serial;
}
+static struct usb_serial_device_type *search_serial_device(struct usb_interface *iface)
+{
+ struct list_head *p;
+ const struct usb_device_id *id;
+ struct usb_serial_device_type *t;
+
+ /* List trough know devices and see if the usb id matches */
+ list_for_each(p, &usb_serial_driver_list) {
+ t = list_entry(p, struct usb_serial_device_type, driver_list);
+ id = usb_match_id(iface, t->id_table);
+ if (id != NULL) {
+ dbg("descriptor matches");
+ return t;
+ }
+ }
+
+ return NULL;
+}
+
int usb_serial_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
@@ -865,9 +870,7 @@ int usb_serial_probe(struct usb_interface *interface,
struct usb_endpoint_descriptor *bulk_in_endpoint[MAX_NUM_PORTS];
struct usb_endpoint_descriptor *bulk_out_endpoint[MAX_NUM_PORTS];
struct usb_serial_device_type *type = NULL;
- struct list_head *tmp;
int retval;
- int found;
int minor;
int buffer_size;
int i;
@@ -876,22 +879,9 @@ int usb_serial_probe(struct usb_interface *interface,
int num_bulk_out = 0;
int num_ports = 0;
int max_endpoints;
- const struct usb_device_id *id_pattern = NULL;
-
- /* loop through our list of known serial converters, and see if this
- device matches. */
- found = 0;
- list_for_each (tmp, &usb_serial_driver_list) {
- type = list_entry(tmp, struct usb_serial_device_type, driver_list);
- id_pattern = usb_match_id(interface, type->id_table);
- if (id_pattern != NULL) {
- dbg("descriptor matches");
- found = 1;
- break;
- }
- }
- if (!found) {
- /* no match */
+
+ type = search_serial_device(interface);
+ if (!type) {
dbg("none matched");
return -ENODEV;
}
@@ -899,17 +889,21 @@ int usb_serial_probe(struct usb_interface *interface,
serial = create_serial (dev, interface, type);
if (!serial) {
dev_err(&interface->dev, "%s - out of memory\n", __FUNCTION__);
- return -ENODEV;
+ return -ENOMEM;
}
/* if this device type has a probe function, call it */
if (type->probe) {
+ const struct usb_device_id *id;
+
if (!try_module_get(type->owner)) {
dev_err(&interface->dev, "module get failed, exiting\n");
kfree (serial);
return -EIO;
}
- retval = type->probe (serial, id_pattern);
+
+ id = usb_match_id(interface, type->id_table);
+ retval = type->probe(serial, id);
module_put(type->owner);
if (retval) {
@@ -1053,6 +1047,7 @@ int usb_serial_probe(struct usb_interface *interface,
goto probe_error;
}
buffer_size = endpoint->wMaxPacketSize;
+ port->bulk_in_size = buffer_size;
port->bulk_in_endpointAddress = endpoint->bEndpointAddress;
port->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
if (!port->bulk_in_buffer) {
@@ -1224,7 +1219,7 @@ struct tty_driver *usb_serial_tty_driver;
static int __init usb_serial_init(void)
{
int i;
- int result = 0;
+ int result;
usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS);
if (!usb_serial_tty_driver)
@@ -1235,13 +1230,17 @@ static int __init usb_serial_init(void)
serial_table[i] = NULL;
}
- bus_register(&usb_serial_bus_type);
+ result = bus_register(&usb_serial_bus_type);
+ if (result) {
+ err("%s - registering bus driver failed", __FUNCTION__);
+ goto exit_bus;
+ }
/* register the generic driver, if we should */
result = usb_serial_generic_register(debug);
if (result < 0) {
err("%s - registering generic driver failed", __FUNCTION__);
- goto exit;
+ goto exit_generic;
}
usb_serial_tty_driver->owner = THIS_MODULE;
@@ -1259,7 +1258,7 @@ static int __init usb_serial_init(void)
result = tty_register_driver(usb_serial_tty_driver);
if (result) {
err("%s - tty_register_driver failed", __FUNCTION__);
- goto exit_generic;
+ goto exit_reg_driver;
}
/* register the USB driver */
@@ -1276,10 +1275,13 @@ static int __init usb_serial_init(void)
exit_tty:
tty_unregister_driver(usb_serial_tty_driver);
-exit_generic:
+exit_reg_driver:
usb_serial_generic_deregister();
-exit:
+exit_generic:
+ bus_unregister(&usb_serial_bus_type);
+
+exit_bus:
err ("%s - returning with error %d", __FUNCTION__, result);
put_tty_driver(usb_serial_tty_driver);
return result;
@@ -1332,17 +1334,13 @@ int usb_serial_register(struct usb_serial_device_type *new_device)
/* Add this device to our list of devices */
list_add(&new_device->driver_list, &usb_serial_driver_list);
- retval = usb_serial_bus_register (new_device);
-
- if (retval)
- goto error;
-
- info("USB Serial support registered for %s", new_device->name);
-
- return retval;
-error:
- err("problem %d when registering driver %s", retval, new_device->name);
- list_del(&new_device->driver_list);
+ retval = usb_serial_bus_register(new_device);
+ if (retval) {
+ err("problem %d when registering driver %s", retval, new_device->name);
+ list_del(&new_device->driver_list);
+ }
+ else
+ info("USB Serial support registered for %s", new_device->name);
return retval;
}
@@ -1369,6 +1367,7 @@ EXPORT_SYMBOL(usb_serial_port_softint);
/* Module information */
MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_VERSION( DRIVER_VERSION );
MODULE_LICENSE("GPL");
module_param(debug, bool, S_IRUGO | S_IWUSR);
diff --git a/drivers/usb/serial/usb-serial.h b/drivers/usb/serial/usb-serial.h
index e2e59560d7fb..d3702957b609 100644
--- a/drivers/usb/serial/usb-serial.h
+++ b/drivers/usb/serial/usb-serial.h
@@ -100,6 +100,7 @@ struct usb_serial_port {
__u8 interrupt_in_endpointAddress;
unsigned char * bulk_in_buffer;
+ int bulk_in_size;
struct urb * read_urb;
__u8 bulk_in_endpointAddress;
diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c
index 8a0a75533e70..0e5d90552789 100644
--- a/drivers/usb/serial/visor.c
+++ b/drivers/usb/serial/visor.c
@@ -446,9 +446,9 @@ static void visor_close (struct usb_serial_port *port, struct file * filp)
dbg("%s - port %d", __FUNCTION__, port->number);
/* shutdown our urbs */
- usb_unlink_urb (port->read_urb);
+ usb_kill_urb(port->read_urb);
if (port->interrupt_in_urb)
- usb_unlink_urb (port->interrupt_in_urb);
+ usb_kill_urb(port->interrupt_in_urb);
/* Try to send shutdown message, if the device is gone, this will just fail. */
transfer_buffer = kmalloc (0x12, GFP_KERNEL);
@@ -655,7 +655,7 @@ exit:
static void visor_throttle (struct usb_serial_port *port)
{
dbg("%s - port %d", __FUNCTION__, port->number);
- usb_unlink_urb (port->read_urb);
+ usb_kill_urb(port->read_urb);
}
diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c
index 1b759018e8b8..179a671a2770 100644
--- a/drivers/usb/serial/whiteheat.c
+++ b/drivers/usb/serial/whiteheat.c
@@ -679,7 +679,7 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp)
list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) {
wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
urb = wrap->urb;
- usb_unlink_urb(urb);
+ usb_kill_urb(urb);
list_del(tmp);
list_add(tmp, &info->rx_urbs_free);
}
@@ -690,7 +690,7 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp)
list_for_each_safe(tmp, tmp2, &info->tx_urbs_submitted) {
wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
urb = wrap->urb;
- usb_unlink_urb(urb);
+ usb_kill_urb(urb);
list_del(tmp);
list_add(tmp, &info->tx_urbs_free);
}
@@ -1343,7 +1343,7 @@ static void stop_command_port(struct usb_serial *serial)
spin_lock_irqsave(&command_info->lock, flags);
command_info->port_running--;
if (!command_info->port_running)
- usb_unlink_urb(command_port->read_urb);
+ usb_kill_urb(command_port->read_urb);
spin_unlock_irqrestore(&command_info->lock, flags);
}
@@ -1371,7 +1371,7 @@ static int start_port_read(struct usb_serial_port *port)
list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) {
wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
urb = wrap->urb;
- usb_unlink_urb(urb);
+ usb_kill_urb(urb);
list_del(tmp);
list_add(tmp, &info->rx_urbs_free);
}
diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c
index 8aa136b4b380..d599362bebcc 100644
--- a/drivers/usb/storage/isd200.c
+++ b/drivers/usb/storage/isd200.c
@@ -1053,12 +1053,6 @@ static int isd200_get_inquiry_data( struct us_data *us )
/* Standard IDE interface only supports disks */
info->InquiryData.DeviceType = DIRECT_ACCESS_DEVICE;
- /* Fix-up the return data from an INQUIRY command to show
- * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us
- * in Linux.
- */
- info->InquiryData.Versions = 0x2;
-
/* The length must be at least 36 (5 + 31) */
info->InquiryData.AdditionalLength = 0x1F;
diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c
index 99ed4d9bc490..9d3d77452525 100644
--- a/drivers/usb/storage/protocol.c
+++ b/drivers/usb/storage/protocol.c
@@ -58,38 +58,6 @@
***********************************************************************/
/*
- * Fix-up the return data from an INQUIRY command to show
- * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us
- */
-static void fix_inquiry_data(struct scsi_cmnd *srb)
-{
- unsigned char databuf[3];
- unsigned int index, offset;
-
- /* verify that it's an INQUIRY command */
- if (srb->cmnd[0] != INQUIRY)
- return;
-
- index = offset = 0;
- if (usb_stor_access_xfer_buf(databuf, sizeof(databuf), srb,
- &index, &offset, FROM_XFER_BUF) != sizeof(databuf))
- return;
-
- if ((databuf[2] & 7) == 2)
- return;
-
- US_DEBUGP("Fixing INQUIRY data to show SCSI rev 2 - was %d\n",
- databuf[2] & 7);
-
- /* Change the SCSI revision number */
- databuf[2] = (databuf[2] & ~7) | 2;
-
- index = offset = 0;
- usb_stor_access_xfer_buf(databuf, sizeof(databuf), srb,
- &index, &offset, TO_XFER_BUF);
-}
-
-/*
* Fix-up the return data from a READ CAPACITY command. My Feiya reader
* returns a value that is 1 too large.
*/
@@ -137,10 +105,6 @@ void usb_stor_qic157_command(struct scsi_cmnd *srb, struct us_data *us)
/* send the command to the transport layer */
usb_stor_invoke_transport(srb, us);
- if (srb->result == SAM_STAT_GOOD) {
- /* fix the INQUIRY data if necessary */
- fix_inquiry_data(srb);
- }
}
void usb_stor_ATAPI_command(struct scsi_cmnd *srb, struct us_data *us)
@@ -160,11 +124,6 @@ void usb_stor_ATAPI_command(struct scsi_cmnd *srb, struct us_data *us)
/* send the command to the transport layer */
usb_stor_invoke_transport(srb, us);
-
- if (srb->result == SAM_STAT_GOOD) {
- /* fix the INQUIRY data if necessary */
- fix_inquiry_data(srb);
- }
}
@@ -208,11 +167,6 @@ void usb_stor_ufi_command(struct scsi_cmnd *srb, struct us_data *us)
/* send the command to the transport layer */
usb_stor_invoke_transport(srb, us);
-
- if (srb->result == SAM_STAT_GOOD) {
- /* Fix the data for an INQUIRY, if necessary */
- fix_inquiry_data(srb);
- }
}
void usb_stor_transparent_scsi_command(struct scsi_cmnd *srb,
@@ -222,9 +176,6 @@ void usb_stor_transparent_scsi_command(struct scsi_cmnd *srb,
usb_stor_invoke_transport(srb, us);
if (srb->result == SAM_STAT_GOOD) {
- /* Fix the INQUIRY data if necessary */
- fix_inquiry_data(srb);
-
/* Fix the READ CAPACITY result if necessary */
if (us->flags & US_FL_FIX_CAPACITY)
fix_read_capacity(srb);
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index c18cd06ea31c..83d7003861ff 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -98,6 +98,23 @@ static int slave_configure(struct scsi_device *sdev)
* the end, scatter-gather buffers follow page boundaries. */
blk_queue_dma_alignment(sdev->request_queue, (512 - 1));
+ /* Set the SCSI level to at least 2. We'll leave it at 3 if that's
+ * what is originally reported. We need this to avoid confusing
+ * the SCSI layer with devices that report 0 or 1, but need 10-byte
+ * commands (ala ATAPI devices behind certain bridges, or devices
+ * which simply have broken INQUIRY data).
+ *
+ * NOTE: This means /dev/sg programs (ala cdrecord) will get the
+ * actual information. This seems to be the preference for
+ * programs like that.
+ *
+ * NOTE: This also means that /proc/scsi/scsi and sysfs may report
+ * the actual value or the modified one, depending on where the
+ * data comes from.
+ */
+ if (sdev->scsi_level < SCSI_2)
+ sdev->scsi_level = SCSI_2;
+
/* According to the technical support people at Genesys Logic,
* devices using their chips have problems transferring more than
* 32 KB at a time. In practice people have found that 64 KB
@@ -266,7 +283,7 @@ static int device_reset(struct scsi_cmnd *srb)
static int bus_reset(struct scsi_cmnd *srb)
{
struct us_data *us = (struct us_data *)srb->device->host->hostdata[0];
- int result;
+ int result, rc;
US_DEBUGP("%s called\n", __FUNCTION__);
if (us->sm_state != US_STATE_IDLE) {
@@ -291,8 +308,16 @@ static int bus_reset(struct scsi_cmnd *srb)
result = -EBUSY;
US_DEBUGP("Refusing to reset a multi-interface device\n");
} else {
- result = usb_reset_device(us->pusb_dev);
- US_DEBUGP("usb_reset_device returns %d\n", result);
+ rc = usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf);
+ if (rc < 0) {
+ US_DEBUGP("unable to lock device for reset: %d\n", rc);
+ result = rc;
+ } else {
+ result = usb_reset_device(us->pusb_dev);
+ if (rc)
+ usb_unlock_device(us->pusb_dev);
+ US_DEBUGP("usb_reset_device returns %d\n", result);
+ }
}
up(&(us->dev_semaphore));
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
index 42f784321607..07b919d800a1 100644
--- a/drivers/usb/storage/transport.c
+++ b/drivers/usb/storage/transport.c
@@ -911,7 +911,6 @@ int usb_stor_Bulk_max_lun(struct us_data *us)
int result;
/* issue the command */
- us->iobuf[0] = 0;
result = usb_stor_control_msg(us, us->recv_ctrl_pipe,
US_BULK_GET_MAX_LUN,
USB_DIR_IN | USB_TYPE_CLASS |
@@ -922,7 +921,7 @@ int usb_stor_Bulk_max_lun(struct us_data *us)
result, us->iobuf[0]);
/* if we have a successful request, return the result */
- if (result >= 0)
+ if (result > 0)
return us->iobuf[0];
/*
@@ -934,13 +933,16 @@ int usb_stor_Bulk_max_lun(struct us_data *us)
if (result == -EPIPE) {
usb_stor_clear_halt(us, us->recv_bulk_pipe);
usb_stor_clear_halt(us, us->send_bulk_pipe);
- /* return the default -- no LUNs */
- return 0;
}
- /* An answer or a STALL are the only valid responses. If we get
- * something else, return an indication of error */
- return -1;
+ /*
+ * Some devices don't like GetMaxLUN. They may STALL the control
+ * pipe, they may return a zero-length result, they may do nothing at
+ * all and timeout, or they may fail in even more bizarrely creative
+ * ways. In these cases the best approach is to use the default
+ * value: only one LUN.
+ */
+ return 0;
}
int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us)
@@ -1055,8 +1057,13 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us)
/* try to compute the actual residue, based on how much data
* was really transferred and what the device tells us */
- residue = min(residue, transfer_length);
- srb->resid = max(srb->resid, (int) residue);
+ if (residue) {
+ if (!(us->flags & US_FL_IGNORE_RESIDUE) ||
+ srb->sc_data_direction == DMA_TO_DEVICE) {
+ residue = min(residue, transfer_length);
+ srb->resid = max(srb->resid, (int) residue);
+ }
+ }
/* based on the status code, we report good or bad */
switch (bcs->Status) {
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index 4fe25f6b0432..1e0eb58f0c09 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -36,13 +36,16 @@
/* If you edit this file, please try to keep it sorted first by VendorID,
* then by ProductID.
*
- * If you want to add an entry for this file, please send the following
- * to greg@kroah.com:
- * - patch that adds the entry for your device which includes your
- * email address right above the entry.
+ * If you want to add an entry for this file, be sure to include the
+ * following information:
+ * - a patch that adds the entry for your device, including your
+ * email address right above the entry (plus maybe a brief
+ * explanation of the reason for the entry),
* - a copy of /proc/bus/usb/devices with your device plugged in
* running with this patch.
- *
+ * Send your submission to either Phil Dibowitz <phil@ipom.com> or
+ * Alan Stern <stern@rowland.harvard.edu>, and don't forget to CC: the
+ * USB development list <linux-usb-devel@lists.sourceforge.net>.
*/
UNUSUAL_DEV( 0x03ee, 0x6901, 0x0000, 0x0100,
@@ -68,16 +71,6 @@ UNUSUAL_DEV( 0x03f0, 0x0307, 0x0001, 0x0001,
US_SC_8070, US_PR_SCM_ATAPI, init_8200e, 0),
#endif
-/* <torsten.scherer@uni-bielefeld.de>: I don't know the name of the bridge
- * manufacturer, but I've got an external USB drive by the Revoltec company
- * that needs this. otherwise the drive is recognized as /dev/sda, but any
- * access to it blocks indefinitely.
- */
-UNUSUAL_DEV( 0x0402, 0x5621, 0x0103, 0x0103,
- "Revoltec",
- "USB/IDE Bridge (ATA/ATAPI)",
- US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY),
-
/* Deduced by Jonathan Woithe <jwoithe@physics.adelaide.edu.au>
* Entry needed for flags: US_FL_FIX_INQUIRY because initial inquiry message
* always fails and confuses drive.
@@ -95,12 +88,6 @@ UNUSUAL_DEV( 0x0436, 0x0005, 0x0100, 0x0100,
US_SC_SCSI, US_PR_DPCM_USB, NULL, 0 ),
#endif
-/* Patch submitted by Alessandro Fracchetti <al.fracchetti@tin.it> */
-UNUSUAL_DEV( 0x0482, 0x0105, 0x0100, 0x0100,
- "Kyocera",
- "Finecam L3",
- US_SC_SCSI, US_PR_BULK, NULL, US_FL_FIX_INQUIRY),
-
/* Patch submitted by Philipp Friedrich <philipp@void.at> */
UNUSUAL_DEV( 0x0482, 0x0100, 0x0100, 0x0100,
"Kyocera",
@@ -121,6 +108,7 @@ UNUSUAL_DEV( 0x0482, 0x0103, 0x0100, 0x0100,
/* Patch for Kyocera Finecam L3
* Submitted by Michael Krauth <michael.krauth@web.de>
+ * and Alessandro Fracchetti <al.fracchetti@tin.it>
*/
UNUSUAL_DEV( 0x0482, 0x0105, 0x0100, 0x0100,
"Kyocera",
@@ -149,10 +137,13 @@ UNUSUAL_DEV( 0x04b8, 0x0602, 0x0110, 0x0110,
"785EPX Storage",
US_SC_SCSI, US_PR_BULK, NULL, US_FL_SINGLE_LUN),
+/* Not sure who reported this originally but
+ * Pavel Machek <pavel@ucw.cz> reported that the extra US_FL_SINGLE_LUN
+ * flag be added */
UNUSUAL_DEV( 0x04cb, 0x0100, 0x0000, 0x2210,
"Fujifilm",
"FinePix 1400Zoom",
- US_SC_UFI, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY),
+ US_SC_UFI, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY | US_FL_SINGLE_LUN),
/* Reported by Peter Wächtler <pwaechtler@loewe-komp.de>
* The device needs the flags only.
@@ -180,6 +171,16 @@ UNUSUAL_DEV( 0x04da, 0x0d05, 0x0000, 0x0000,
"CD-R/RW Drive",
US_SC_8070, US_PR_CB, NULL, 0),
+/* Reported by Adriaan Penning <a.penning@luon.net>
+ * Note that these cameras report "Medium not present" after
+ * ALLOW_MEDIUM_REMOVAL, so they also need to be marked
+ * NOT_LOCKABLE in the SCSI blacklist (and the vendor is MATSHITA). */
+UNUSUAL_DEV( 0x04da, 0x2372, 0x0000, 0x9999,
+ "Panasonic",
+ "DMC-LCx Camera",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_CAPACITY ),
+
/* Most of the following entries were developed with the help of
* Shuttle/SCM directly.
*/
@@ -265,6 +266,21 @@ UNUSUAL_DEV( 0x0525, 0xa140, 0x0100, 0x0100,
US_SC_8070, US_PR_BULK, NULL,
US_FL_FIX_INQUIRY ),
+/* Reported by Iacopo Spalletti <avvisi@spalletti.it> */
+UNUSUAL_DEV( 0x052b, 0x1807, 0x0100, 0x0100,
+ "Tekom Technologies, Inc",
+ "300_CAMERA",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE ),
+
+/* Yakumo Mega Image 37
+ * Submitted by Stephan Fuhrmann <atomenergie@t-online.de> */
+UNUSUAL_DEV( 0x052b, 0x1801, 0x0100, 0x0100,
+ "Tekom Technologies, Inc",
+ "300_CAMERA",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE ),
+
/* This entry is needed because the device reports Sub=ff */
UNUSUAL_DEV( 0x054c, 0x0010, 0x0106, 0x0450,
"Sony",
@@ -383,10 +399,17 @@ UNUSUAL_DEV( 0x0595, 0x4343, 0x0000, 0x2210,
"Digital Camera EX-20 DSC",
US_SC_8070, US_PR_DEVICE, NULL, 0 ),
+/* The entry was here before I took over, and had US_SC_RBC. It turns
+ * out that isn't needed. Additionally, Torsten Eriksson
+ * <Torsten.Eriksson@bergianska.se> is able to use his device fine
+ * without this entry at all - but I don't suspect that will be true
+ * for all users (the protocol is likely needed), so is staying at
+ * this time. - Phil Dibowitz <phil@ipom.com>
+ */
UNUSUAL_DEV( 0x059f, 0xa601, 0x0200, 0x0200,
"LaCie",
"USB Hard Disk",
- US_SC_RBC, US_PR_CB, NULL, 0 ),
+ US_SC_DEVICE, US_PR_CB, NULL, 0 ),
/* Submitted by Joel Bourquard <numlock@freesurf.ch>
* Some versions of this device need the SubClass and Protocol overrides
@@ -439,36 +462,6 @@ UNUSUAL_DEV( 0x05dc, 0xb002, 0x0000, 0x0113,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_FIX_INQUIRY ),
-/* Reported by Carlos Villegas <cav@uniscope.co.jp>
- * This device needs an INQUIRY of exactly 36-bytes to function.
- * That is the only reason this entry is needed.
- */
-UNUSUAL_DEV( 0x05e3, 0x0700, 0x0000, 0xffff,
- "Genesys Logic",
- "USB to IDE Card Reader",
- US_SC_DEVICE, US_PR_DEVICE, NULL,
- US_FL_FIX_INQUIRY ),
-
-/* Submitted Alexander Oltu <alexander@all-2.com> */
-UNUSUAL_DEV( 0x05e3, 0x0701, 0x0000, 0xffff,
- "Genesys Logic",
- "USB to IDE Optical",
- US_SC_DEVICE, US_PR_DEVICE, NULL,
- US_FL_MODE_XLATE ),
-
-/* Reported by Peter Marks <peter.marks@turner.com>
- * Like the SIIG unit above, this unit needs an INQUIRY to ask for exactly
- * 36 bytes of data. No more, no less. That is the only reason this entry
- * is needed.
- *
- * ST818 slim drives (rev 0.02) don't need special care.
-*/
-UNUSUAL_DEV( 0x05e3, 0x0702, 0x0000, 0xffff,
- "Genesys Logic",
- "USB to IDE Disk",
- US_SC_DEVICE, US_PR_DEVICE, NULL,
- US_FL_FIX_INQUIRY ),
-
/* Reported by Hanno Boeck <hanno@gmx.de>
* Taken from the Lycoris Kernel */
UNUSUAL_DEV( 0x0636, 0x0003, 0x0000, 0x9999,
@@ -554,6 +547,13 @@ UNUSUAL_DEV( 0x07ab, 0xfc01, 0x0000, 0x9999,
US_SC_QIC, US_PR_FREECOM, freecom_init, 0),
#endif
+/* Reported by Eero Volotinen <eero@ping-viini.org> */
+UNUSUAL_DEV( 0x07ab, 0xfccd, 0x0406, 0x0406,
+ "Freecom Technologies",
+ "FHD-Classic",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_CAPACITY),
+
UNUSUAL_DEV( 0x07af, 0x0004, 0x0100, 0x0133,
"Microtech",
"USB-SCSI-DB25",
@@ -724,12 +724,6 @@ UNUSUAL_DEV( 0x097a, 0x0001, 0x0000, 0x0001,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_MODE_XLATE ),
-UNUSUAL_DEV( 0x0a16, 0x8888, 0x0100, 0x0100,
- "IBM",
- "IBM USB Memory Key",
- US_SC_DEVICE, US_PR_DEVICE, NULL,
- US_FL_FIX_INQUIRY ),
-
/* This Pentax still camera is not conformant
* to the USB storage specification: -
* - It does not like the INQUIRY command. So we must handle this command
@@ -802,13 +796,28 @@ UNUSUAL_DEV( 0x0dd8, 0x1060, 0x0000, 0xffff,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_FIX_INQUIRY ),
+/* Patch by Stephan Walter <stephan.walter@epfl.ch>
+ * I don't know why, but it works... */
+UNUSUAL_DEV( 0x0dda, 0x0001, 0x0012, 0x0012,
+ "WINWARD",
+ "Music Disk",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE ),
+
/* Submitted by Antoine Mairesse <antoine.mairesse@free.fr> */
UNUSUAL_DEV( 0x0ed1, 0x6660, 0x0100, 0x0300,
"USB",
"Solid state disk",
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_FIX_INQUIRY ),
-
+
+/* Reported by Rastislav Stanik <rs_kernel@yahoo.com> */
+UNUSUAL_DEV( 0x0ea0, 0x6828, 0x0110, 0x0110,
+ "USB",
+ "Flash Disk",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE ),
+
/* Reported by Kevin Cernekee <kpc-usbdev@gelato.uiuc.edu>
* Tested on hardware version 1.10.
* Entry is needed only for the initializer function override.
@@ -830,6 +839,13 @@ UNUSUAL_DEV( 0x1065, 0x2136, 0x0000, 0x9999,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_MODE_XLATE ),
+/* Reported by Kotrla Vitezslav <kotrla@ceb.cz> */
+UNUSUAL_DEV( 0x1370, 0x6828, 0x0110, 0x0110,
+ "SWISSBIT",
+ "Black Silver",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE ),
+
#ifdef CONFIG_USB_STORAGE_SDDR55
UNUSUAL_DEV( 0x55aa, 0xa103, 0x0000, 0x9999,
"Sandisk",
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index 9579bdf4969a..32e2f1482c57 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -97,6 +97,11 @@ MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>");
MODULE_DESCRIPTION("USB Mass Storage driver for Linux");
MODULE_LICENSE("GPL");
+static unsigned int delay_use = 5;
+module_param(delay_use, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device");
+
+
static int storage_probe(struct usb_interface *iface,
const struct usb_device_id *id);
@@ -882,6 +887,42 @@ static void dissociate_dev(struct us_data *us)
kfree(us);
}
+/* Thread to carry out delayed SCSI-device scanning */
+static int usb_stor_scan_thread(void * __us)
+{
+ struct us_data *us = (struct us_data *)__us;
+
+ /*
+ * This thread doesn't need any user-level access,
+ * so get rid of all our resources.
+ */
+ lock_kernel();
+ daemonize("usb-stor");
+ current->flags |= PF_NOFREEZE;
+ unlock_kernel();
+
+ printk(KERN_DEBUG
+ "usb-storage: device found at %d\n", us->pusb_dev->devnum);
+
+ /* Wait for the timeout to expire or for a disconnect */
+ if (delay_use > 0) {
+ printk(KERN_DEBUG "usb-storage: waiting for device "
+ "to settle before scanning\n");
+ wait_event_interruptible_timeout(us->scsi_scan_wait,
+ test_bit(US_FLIDX_DISCONNECTING, &us->flags),
+ delay_use * HZ);
+ }
+
+ /* If the device is still connected, perform the scanning */
+ if (!test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
+ scsi_scan_host(us->host);
+ printk(KERN_DEBUG "usb-storage: device scan complete\n");
+ }
+
+ complete_and_exit(&us->scsi_scan_done, 0);
+}
+
+
/* Probe to see if we can drive a newly-connected USB device */
static int storage_probe(struct usb_interface *intf,
const struct usb_device_id *id)
@@ -903,6 +944,8 @@ static int storage_probe(struct usb_interface *intf,
init_MUTEX_LOCKED(&(us->sema));
init_completion(&(us->notify));
init_waitqueue_head(&us->dev_reset_wait);
+ init_waitqueue_head(&us->scsi_scan_wait);
+ init_completion(&us->scsi_scan_done);
/* Associate the us_data structure with the USB device */
result = associate_dev(us, intf);
@@ -951,12 +994,10 @@ static int storage_probe(struct usb_interface *intf,
if (result)
goto BadDevice;
- /* Acquire all the other resources */
+ /* Acquire all the other resources and add the host */
result = usb_stor_acquire_resources(us);
if (result)
goto BadDevice;
-
- /* Finally, add the host (this does SCSI device scanning) */
result = scsi_add_host(us->host, &intf->dev);
if (result) {
printk(KERN_WARNING USB_STORAGE
@@ -964,10 +1005,15 @@ static int storage_probe(struct usb_interface *intf,
goto BadDevice;
}
- scsi_scan_host(us->host);
+ /* Start up the thread for delayed SCSI-device scanning */
+ result = kernel_thread(usb_stor_scan_thread, us, CLONE_VM);
+ if (result < 0) {
+ printk(KERN_WARNING USB_STORAGE
+ "Unable to start the device-scanning thread\n");
+ scsi_remove_host(us->host);
+ goto BadDevice;
+ }
- printk(KERN_DEBUG
- "USB Mass Storage device found at %d\n", us->pusb_dev->devnum);
return 0;
/* We come here if there are any problems */
@@ -991,6 +1037,11 @@ static void storage_disconnect(struct usb_interface *intf)
usb_stor_stop_transport(us);
wake_up(&us->dev_reset_wait);
+ /* Interrupt the SCSI-device-scanning thread's time delay, and
+ * wait for the thread to finish */
+ wake_up(&us->scsi_scan_wait);
+ wait_for_completion(&us->scsi_scan_done);
+
/* Wait for the current command to finish, then remove the host */
down(&us->dev_semaphore);
up(&us->dev_semaphore);
@@ -1012,12 +1063,9 @@ static int __init usb_stor_init(void)
/* register the driver, return usb_register return code if error */
retval = usb_register(&usb_storage_driver);
- if (retval)
- goto out;
+ if (retval == 0)
+ printk(KERN_INFO "USB Mass Storage support registered.\n");
- /* we're all set */
- printk(KERN_INFO "USB Mass Storage support registered.\n");
-out:
return retval;
}
diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h
index 785f30be68c5..199594967870 100644
--- a/drivers/usb/storage/usb.h
+++ b/drivers/usb/storage/usb.h
@@ -73,6 +73,7 @@ struct us_unusual_dev {
#define US_FL_SCM_MULT_TARG 0x00000020 /* supports multiple targets */
#define US_FL_FIX_INQUIRY 0x00000040 /* INQUIRY response needs faking */
#define US_FL_FIX_CAPACITY 0x00000080 /* READ CAPACITY response too big */
+#define US_FL_IGNORE_RESIDUE 0x00000100 /* reported residue is wrong */
/* Dynamic flag definitions: used in set_bit() etc. */
#define US_FLIDX_URB_ACTIVE 18 /* 0x00040000 current_urb is in use */
@@ -161,6 +162,8 @@ struct us_data {
struct semaphore sema; /* to sleep thread on */
struct completion notify; /* thread begin/end */
wait_queue_head_t dev_reset_wait; /* wait during reset */
+ wait_queue_head_t scsi_scan_wait; /* wait before scanning */
+ struct completion scsi_scan_done; /* scan thread end */
/* subdriver information */
void *extra; /* Any extra data */
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index ab23abe01649..8fff0829a2b6 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -49,6 +49,24 @@ config FB_MODE_HELPERS
your driver does not take advantage of this feature, choosing Y will
just increase the kernel size by about 5K.
+config FB_TILEBLITTING
+ bool "Enable Tile Blitting Support"
+ depends on FB
+ default n
+ ---help---
+ This enables tile blitting. Tile blitting is a drawing technique
+ where the screen is divided into rectangular sections (tiles), whereas
+ the standard blitting divides the screen into pixels. Because the
+ default drawing element is a tile, drawing functions will be passed
+ parameters in terms of number of tiles instead of number of pixels.
+ For example, to draw a single character, instead of using bitmaps,
+ an index to an array of bitmaps will be used. To clear or move a
+ rectangular section of a screen, the rectangle willbe described in
+ terms of number of tiles in the x- and y-axis.
+
+ This is particularly important to one driver, the matroxfb. If
+ unsure, say N.
+
config FB_CIRRUS
tristate "Cirrus Logic support"
depends on FB && (ZORRO || PCI)
diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c
index 2d753a150405..264b53179321 100644
--- a/drivers/video/aty/atyfb_base.c
+++ b/drivers/video/aty/atyfb_base.c
@@ -1974,7 +1974,7 @@ int __init atyfb_do_init(void)
info->fix = atyfb_fix;
info->par = default_par;
-
+ info->device = &pdev->dev;
#ifdef __sparc__
/*
* Map memory-mapped registers.
diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c
index 325b83b3017c..abecde5c4ed5 100644
--- a/drivers/video/aty/radeon_base.c
+++ b/drivers/video/aty/radeon_base.c
@@ -61,6 +61,7 @@
#include <linux/tty.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/time.h>
#include <linux/fb.h>
#include <linux/ioport.h>
#include <linux/init.h>
@@ -527,8 +528,7 @@ static int __devinit radeon_probe_pll_params(struct radeonfb_info *rinfo)
break;
}
- OUTREG8(CLOCK_CNTL_INDEX, 1);
- ppll_div_sel = INREG8(CLOCK_CNTL_DATA + 1) & 0x3;
+ ppll_div_sel = INREG(CLOCK_CNTL_INDEX + 1) & 0x3;
n = (INPLL(PPLL_DIV_0 + ppll_div_sel) & 0x7ff);
m = (INPLL(PPLL_REF_DIV) & 0x3ff);
@@ -595,53 +595,10 @@ static int __devinit radeon_probe_pll_params(struct radeonfb_info *rinfo)
*/
static void __devinit radeon_get_pllinfo(struct radeonfb_info *rinfo)
{
-#ifdef CONFIG_PPC_OF
- /*
- * Retreive PLL infos from Open Firmware first
- */
- if (!force_measure_pll && radeon_read_xtal_OF(rinfo) == 0) {
- printk(KERN_INFO "radeonfb: Retreived PLL infos from Open Firmware\n");
- rinfo->pll.ref_div = INPLL(PPLL_REF_DIV) & 0x3ff;
- /* FIXME: Max clock may be higher on newer chips */
- rinfo->pll.ppll_min = 12000;
- rinfo->pll.ppll_max = 35000;
- goto found;
- }
-#endif /* CONFIG_PPC_OF */
-
- /*
- * Check out if we have an X86 which gave us some PLL informations
- * and if yes, retreive them
- */
- if (!force_measure_pll && rinfo->bios_seg) {
- u16 pll_info_block = BIOS_IN16(rinfo->fp_bios_start + 0x30);
-
- rinfo->pll.sclk = BIOS_IN16(pll_info_block + 0x08);
- rinfo->pll.mclk = BIOS_IN16(pll_info_block + 0x0a);
- rinfo->pll.ref_clk = BIOS_IN16(pll_info_block + 0x0e);
- rinfo->pll.ref_div = BIOS_IN16(pll_info_block + 0x10);
- rinfo->pll.ppll_min = BIOS_IN32(pll_info_block + 0x12);
- rinfo->pll.ppll_max = BIOS_IN32(pll_info_block + 0x16);
-
- printk(KERN_INFO "radeonfb: Retreived PLL infos from BIOS\n");
- goto found;
- }
-
- /*
- * We didn't get PLL parameters from either OF or BIOS, we try to
- * probe them
- */
- if (radeon_probe_pll_params(rinfo) == 0) {
- printk(KERN_INFO "radeonfb: Retreived PLL infos from registers\n");
- /* FIXME: Max clock may be higher on newer chips */
- rinfo->pll.ppll_min = 12000;
- rinfo->pll.ppll_max = 35000;
- goto found;
- }
-
/*
- * Neither of the above worked, we have a few default values, though
- * that's mostly incomplete
+ * In the case nothing works, these are defaults; they are mostly
+ * incomplete, however. It does provide ppll_max and _min values
+ * even for most other methods, however.
*/
switch (rinfo->chipset) {
case PCI_DEVICE_ID_ATI_RADEON_QW:
@@ -697,6 +654,47 @@ static void __devinit radeon_get_pllinfo(struct radeonfb_info *rinfo)
}
rinfo->pll.ref_div = INPLL(PPLL_REF_DIV) & 0x3ff;
+
+#ifdef CONFIG_PPC_OF
+ /*
+ * Retreive PLL infos from Open Firmware first
+ */
+ if (!force_measure_pll && radeon_read_xtal_OF(rinfo) == 0) {
+ printk(KERN_INFO "radeonfb: Retreived PLL infos from Open Firmware\n");
+ goto found;
+ }
+#endif /* CONFIG_PPC_OF */
+
+ /*
+ * Check out if we have an X86 which gave us some PLL informations
+ * and if yes, retreive them
+ */
+ if (!force_measure_pll && rinfo->bios_seg) {
+ u16 pll_info_block = BIOS_IN16(rinfo->fp_bios_start + 0x30);
+
+ rinfo->pll.sclk = BIOS_IN16(pll_info_block + 0x08);
+ rinfo->pll.mclk = BIOS_IN16(pll_info_block + 0x0a);
+ rinfo->pll.ref_clk = BIOS_IN16(pll_info_block + 0x0e);
+ rinfo->pll.ref_div = BIOS_IN16(pll_info_block + 0x10);
+ rinfo->pll.ppll_min = BIOS_IN32(pll_info_block + 0x12);
+ rinfo->pll.ppll_max = BIOS_IN32(pll_info_block + 0x16);
+
+ printk(KERN_INFO "radeonfb: Retreived PLL infos from BIOS\n");
+ goto found;
+ }
+
+ /*
+ * We didn't get PLL parameters from either OF or BIOS, we try to
+ * probe them
+ */
+ if (radeon_probe_pll_params(rinfo) == 0) {
+ printk(KERN_INFO "radeonfb: Retreived PLL infos from registers\n");
+ goto found;
+ }
+
+ /*
+ * Fall back to already-set defaults...
+ */
printk(KERN_INFO "radeonfb: Used default PLL infos\n");
found:
@@ -715,6 +713,7 @@ found:
rinfo->pll.ref_div,
rinfo->pll.mclk / 100, rinfo->pll.mclk % 100,
rinfo->pll.sclk / 100, rinfo->pll.sclk % 100);
+ printk("radeonfb: PLL min %d max %d\n", rinfo->pll.ppll_min, rinfo->pll.ppll_max);
}
static int radeonfb_check_var (struct fb_var_screeninfo *var, struct fb_info *info)
@@ -934,43 +933,94 @@ static int radeonfb_ioctl (struct inode *inode, struct file *file, unsigned int
}
-static int radeon_screen_blank (struct radeonfb_info *rinfo, int blank)
+static int radeon_screen_blank (struct radeonfb_info *rinfo, int blank, int mode_switch)
{
- u32 val = INREG(CRTC_EXT_CNTL);
- u32 val2 = 0;
+ u32 val;
+ u32 tmp_pix_clks;
- if (rinfo->mon1_type == MT_LCD)
- val2 = INREG(LVDS_GEN_CNTL) & ~LVDS_DISPLAY_DIS;
-
- /* reset it */
+ if (rinfo->lock_blank)
+ return 0;
+
+ radeon_engine_idle();
+
+ val = INREG(CRTC_EXT_CNTL);
val &= ~(CRTC_DISPLAY_DIS | CRTC_HSYNC_DIS |
CRTC_VSYNC_DIS);
-
switch (blank) {
- case VESA_NO_BLANKING:
- break;
- case VESA_VSYNC_SUSPEND:
- val |= (CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS);
- break;
- case VESA_HSYNC_SUSPEND:
- val |= (CRTC_DISPLAY_DIS | CRTC_HSYNC_DIS);
- break;
- case VESA_POWERDOWN:
- val |= (CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS |
- CRTC_HSYNC_DIS);
- val2 |= (LVDS_DISPLAY_DIS);
- break;
+ case VESA_NO_BLANKING:
+ break;
+ case VESA_VSYNC_SUSPEND:
+ val |= (CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS);
+ break;
+ case VESA_HSYNC_SUSPEND:
+ val |= (CRTC_DISPLAY_DIS | CRTC_HSYNC_DIS);
+ break;
+ case VESA_POWERDOWN:
+ val |= (CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS |
+ CRTC_HSYNC_DIS);
+ break;
}
+ OUTREG(CRTC_EXT_CNTL, val);
+
- radeon_fifo_wait(1);
switch (rinfo->mon1_type) {
- case MT_LCD:
- OUTREG(LVDS_GEN_CNTL, val2);
- break;
- case MT_CRT:
- default:
- OUTREG(CRTC_EXT_CNTL, val);
+ case MT_DFP:
+ if (mode_switch)
break;
+ if (blank == VESA_NO_BLANKING)
+ OUTREGP(FP_GEN_CNTL, (FP_FPON | FP_TMDS_EN),
+ ~(FP_FPON | FP_TMDS_EN));
+ else
+ OUTREGP(FP_GEN_CNTL, 0, ~(FP_FPON | FP_TMDS_EN));
+ break;
+ case MT_LCD:
+ val = INREG(LVDS_GEN_CNTL);
+ if (blank == VESA_NO_BLANKING) {
+ u32 target_val = (val & ~LVDS_DISPLAY_DIS) | LVDS_BLON | LVDS_ON
+ | LVDS_ON | (rinfo->init_state.lvds_gen_cntl & LVDS_DIGON);
+ if ((val ^ target_val) == LVDS_DISPLAY_DIS)
+ OUTREG(LVDS_GEN_CNTL, target_val);
+ else if ((val ^ target_val) != 0) {
+ del_timer_sync(&rinfo->lvds_timer);
+ OUTREG(LVDS_GEN_CNTL, target_val & ~LVDS_ON);
+ rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
+ rinfo->init_state.lvds_gen_cntl |= target_val & LVDS_STATE_MASK;
+ if (mode_switch) {
+ msleep(rinfo->panel_info.pwr_delay);
+ OUTREG(LVDS_GEN_CNTL, target_val);
+ }
+ else {
+ rinfo->pending_lvds_gen_cntl = target_val;
+ mod_timer(&rinfo->lvds_timer,
+ jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay));
+ }
+ }
+ } else {
+ val |= LVDS_DISPLAY_DIS;
+ OUTREG(LVDS_GEN_CNTL, val);
+
+ /* We don't do a full switch-off on a simple mode switch */
+ if (mode_switch)
+ break;
+
+ /* Asic bug, when turning off LVDS_ON, we have to make sure
+ * RADEON_PIXCLK_LVDS_ALWAYS_ON bit is off
+ */
+ tmp_pix_clks = INPLL(PIXCLKS_CNTL);
+ if (rinfo->is_mobility || rinfo->is_IGP)
+ OUTPLLP(PIXCLKS_CNTL, 0, ~PIXCLK_LVDS_ALWAYS_ONb);
+ val &= ~(LVDS_BLON | LVDS_ON);
+ OUTREG(LVDS_GEN_CNTL, val);
+ rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
+ rinfo->init_state.lvds_gen_cntl |= val & LVDS_STATE_MASK;
+ if (rinfo->is_mobility || rinfo->is_IGP)
+ OUTPLL(PIXCLKS_CNTL, tmp_pix_clks);
+ }
+ break;
+ case MT_CRT:
+ // todo: powerdown DAC
+ default:
+ break;
}
return 0;
@@ -983,17 +1033,7 @@ int radeonfb_blank (int blank, struct fb_info *info)
if (rinfo->asleep)
return 0;
-#ifdef CONFIG_PMAC_BACKLIGHT
- if (rinfo->mon1_type == MT_LCD && _machine == _MACH_Pmac && blank)
- set_backlight_enable(0);
-#endif
-
- radeon_screen_blank(rinfo, blank);
-
-#ifdef CONFIG_PMAC_BACKLIGHT
- if (rinfo->mon1_type == MT_LCD && _machine == _MACH_Pmac && !blank)
- set_backlight_enable(1);
-#endif
+ radeon_screen_blank(rinfo, blank, 0);
return 0;
}
@@ -1225,7 +1265,8 @@ static void radeon_write_mode (struct radeonfb_info *rinfo,
del_timer_sync(&rinfo->lvds_timer);
- radeon_screen_blank(rinfo, VESA_POWERDOWN);
+ radeon_screen_blank(rinfo, VESA_POWERDOWN, 1);
+ msleep(100);
radeon_fifo_wait(31);
for (i=0; i<10; i++)
@@ -1265,35 +1306,9 @@ static void radeon_write_mode (struct radeonfb_info *rinfo,
OUTREG(FP_GEN_CNTL, mode->fp_gen_cntl);
OUTREG(TMDS_CRC, mode->tmds_crc);
OUTREG(TMDS_TRANSMITTER_CNTL, mode->tmds_transmitter_cntl);
-
- if (primary_mon == MT_LCD) {
- unsigned int tmp = INREG(LVDS_GEN_CNTL);
-
- /* HACK: The backlight control code may have modified init_state.lvds_gen_cntl,
- * so we update ourselves
- */
- mode->lvds_gen_cntl &= ~LVDS_STATE_MASK;
- mode->lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_STATE_MASK);
-
- if ((tmp & (LVDS_ON | LVDS_BLON)) ==
- (mode->lvds_gen_cntl & (LVDS_ON | LVDS_BLON))) {
- OUTREG(LVDS_GEN_CNTL, mode->lvds_gen_cntl);
- } else {
- rinfo->pending_pixclks_cntl = INPLL(PIXCLKS_CNTL);
- if (rinfo->is_mobility || rinfo->is_IGP)
- OUTPLLP(PIXCLKS_CNTL, 0, ~PIXCLK_LVDS_ALWAYS_ONb);
- if (!(tmp & (LVDS_ON | LVDS_BLON)))
- OUTREG(LVDS_GEN_CNTL, mode->lvds_gen_cntl | LVDS_BLON);
- rinfo->pending_lvds_gen_cntl = mode->lvds_gen_cntl;
- mod_timer(&rinfo->lvds_timer,
- jiffies + MS_TO_HZ(rinfo->panel_info.pwr_delay));
- }
- }
}
- RTRACE("lvds_gen_cntl: %08x\n", INREG(LVDS_GEN_CNTL));
-
- radeon_screen_blank(rinfo, VESA_NO_BLANKING);
+ radeon_screen_blank(rinfo, VESA_NO_BLANKING, 1);
radeon_fifo_wait(2);
OUTPLL(VCLK_ECP_CNTL, mode->vclk_ecp_cntl);
@@ -1329,7 +1344,7 @@ static void radeon_calc_pll_regs(struct radeonfb_info *rinfo, struct radeon_regs
* not sure which model starts having FP2_GEN_CNTL, I assume anything more
* recent than an r(v)100...
*/
-#if 0
+#if 1
/* XXX I had reports of flicker happening with the cinema display
* on TMDS1 that seem to be fixed if I also forbit odd dividers in
* this case. This could just be a bandwidth calculation issue, I
@@ -1379,6 +1394,8 @@ static void radeon_calc_pll_regs(struct radeonfb_info *rinfo, struct radeon_regs
freq = rinfo->pll.ppll_max;
if (freq*12 < rinfo->pll.ppll_min)
freq = rinfo->pll.ppll_min / 12;
+ RTRACE("freq = %lu, PLL min = %u, PLL max = %u\n",
+ freq, rinfo->pll.ppll_min, rinfo->pll.ppll_max);
for (post_div = &post_divs[0]; post_div->divider; ++post_div) {
pll_output_freq = post_div->divider * freq;
@@ -1392,6 +1409,16 @@ static void radeon_calc_pll_regs(struct radeonfb_info *rinfo, struct radeon_regs
break;
}
+ /* If we fall through the bottom, try the "default value"
+ given by the terminal post_div->bitvalue */
+ if ( !post_div->divider ) {
+ post_div = &post_divs[post_div->bitvalue];
+ pll_output_freq = post_div->divider * freq;
+ }
+ RTRACE("ref_div = %d, ref_clk = %d, output_freq = %d\n",
+ rinfo->pll.ref_div, rinfo->pll.ref_clk,
+ pll_output_freq);
+
fb_div = round_div(rinfo->pll.ref_div*pll_output_freq,
rinfo->pll.ref_clk);
regs->ppll_ref_div = rinfo->pll.ref_div;
@@ -1780,8 +1807,7 @@ static int backlight_conv_m7[] = {
static int radeon_set_backlight_enable(int on, int level, void *data)
{
struct radeonfb_info *rinfo = (struct radeonfb_info *)data;
- unsigned int lvds_gen_cntl = INREG(LVDS_GEN_CNTL);
- unsigned long tmpPixclksCntl = INPLL(PIXCLKS_CNTL);
+ u32 lvds_gen_cntl, tmpPixclksCntl;
int* conv_table;
if (rinfo->mon1_type != MT_LCD)
@@ -1803,42 +1829,47 @@ static int radeon_set_backlight_enable(int on, int level, void *data)
conv_table = backlight_conv_m6;
del_timer_sync(&rinfo->lvds_timer);
+ radeon_engine_idle();
- lvds_gen_cntl |= (LVDS_BL_MOD_EN | LVDS_BLON);
- radeon_fifo_wait(3);
+ lvds_gen_cntl = INREG(LVDS_GEN_CNTL);
if (on && (level > BACKLIGHT_OFF)) {
- lvds_gen_cntl |= LVDS_DIGON;
- if (!(lvds_gen_cntl & LVDS_ON)) {
- lvds_gen_cntl &= ~LVDS_BLON;
+ lvds_gen_cntl &= ~LVDS_DISPLAY_DIS;
+ if (!(lvds_gen_cntl & LVDS_BLON) || !(lvds_gen_cntl & LVDS_ON)) {
+ lvds_gen_cntl |= LVDS_BLON /* | LVDS_EN | LVDS_DIGON */;
OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
- (void)INREG(LVDS_GEN_CNTL);
- mdelay(rinfo->panel_info.pwr_delay);/* OUCH !!! FIXME */
- lvds_gen_cntl |= LVDS_BLON;
+ lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK;
+ lvds_gen_cntl |= (conv_table[level] <<
+ LVDS_BL_MOD_LEVEL_SHIFT);
+ lvds_gen_cntl |= LVDS_ON;
+ rinfo->pending_lvds_gen_cntl = lvds_gen_cntl;
+ mod_timer(&rinfo->lvds_timer,
+ jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay));
+ } else {
+ lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK;
+ lvds_gen_cntl |= (conv_table[level] <<
+ LVDS_BL_MOD_LEVEL_SHIFT);
OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
}
- lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK;
- lvds_gen_cntl |= (conv_table[level] <<
- LVDS_BL_MOD_LEVEL_SHIFT);
- lvds_gen_cntl |= (LVDS_ON | LVDS_EN);
- lvds_gen_cntl &= ~LVDS_DISPLAY_DIS;
+ rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
+ rinfo->init_state.lvds_gen_cntl |= rinfo->pending_lvds_gen_cntl
+ & LVDS_STATE_MASK;
} else {
/* Asic bug, when turning off LVDS_ON, we have to make sure
RADEON_PIXCLK_LVDS_ALWAYS_ON bit is off
*/
+ tmpPixclksCntl = INPLL(PIXCLKS_CNTL);
if (rinfo->is_mobility || rinfo->is_IGP)
OUTPLLP(PIXCLKS_CNTL, 0, ~PIXCLK_LVDS_ALWAYS_ONb);
lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK;
lvds_gen_cntl |= (conv_table[0] <<
LVDS_BL_MOD_LEVEL_SHIFT);
- lvds_gen_cntl |= LVDS_DISPLAY_DIS | LVDS_BLON;
+ lvds_gen_cntl |= LVDS_DISPLAY_DIS;
+ OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
+ lvds_gen_cntl &= ~(LVDS_ON | LVDS_BLON /* | LVDS_EN | LVDS_DIGON */);
OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
- mdelay(rinfo->panel_info.pwr_delay);/* OUCH !!! FIXME */
- lvds_gen_cntl &= ~(LVDS_ON | LVDS_EN | LVDS_BLON | LVDS_DIGON);
+ if (rinfo->is_mobility || rinfo->is_IGP)
+ OUTPLL(PIXCLKS_CNTL, tmpPixclksCntl);
}
-
- OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
- if (rinfo->is_mobility || rinfo->is_IGP)
- OUTPLL(PIXCLKS_CNTL, tmpPixclksCntl);
rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
rinfo->init_state.lvds_gen_cntl |= (lvds_gen_cntl & LVDS_STATE_MASK);
diff --git a/drivers/video/aty/radeon_monitor.c b/drivers/video/aty/radeon_monitor.c
index e6c01642cfdc..22cf8b0c9c57 100644
--- a/drivers/video/aty/radeon_monitor.c
+++ b/drivers/video/aty/radeon_monitor.c
@@ -69,11 +69,12 @@ static int __devinit radeon_parse_montype_prop(struct device_node *dp, u8 **out_
mt = MT_DFP;
else if (!strcmp(pmt, "CRT"))
mt = MT_CRT;
- else if (strcmp(pmt, "NONE")) {
- printk(KERN_WARNING "radeonfb: Unknown OF display-type: %s\n", pmt);
- return MT_NONE;
- } else
+ else {
+ if (strcmp(pmt, "NONE") != 0)
+ printk(KERN_WARNING "radeonfb: Unknown OF display-type: %s\n",
+ pmt);
return MT_NONE;
+ }
for (i = 0; propnames[i] != NULL; ++i) {
pedid = (u8 *)get_property(dp, propnames[i], NULL);
@@ -632,43 +633,25 @@ void __devinit radeon_probe_screens(struct radeonfb_info *rinfo,
*/
static void radeon_fixup_panel_info(struct radeonfb_info *rinfo)
{
+ #ifdef CONFIG_PPC_OF
/*
- * A few iBook laptop panels seem to need a fixed PLL setting
- *
- * We should probably do this differently based on the panel
- * type/model or eventually some other device-tree informations,
- * but these tweaks below work enough for now. --BenH
+ * LCD Flat panels should use fixed dividers, we enfore that on
+ * PowerMac only for now...
*/
-#ifdef CONFIG_PPC_OF
- /* iBook2's */
- if (machine_is_compatible("PowerBook4,3")) {
- rinfo->panel_info.ref_divider = rinfo->pll.ref_div;
- rinfo->panel_info.post_divider = 0x6;
- rinfo->panel_info.fbk_divider = 0xad;
- rinfo->panel_info.use_bios_dividers = 1;
- }
- /* Aluminium PowerBook 15" */
- if (machine_is_compatible("PowerBook5,4")) {
+ if (!rinfo->panel_info.use_bios_dividers && rinfo->mon1_type == MT_LCD
+ && rinfo->is_mobility) {
+ int ppll_div_sel = INREG8(CLOCK_CNTL_INDEX + 1) & 0x3;
+ u32 ppll_divn = INPLL(PPLL_DIV_0 + ppll_div_sel);
rinfo->panel_info.ref_divider = rinfo->pll.ref_div;
- rinfo->panel_info.post_divider = 0x2;
- rinfo->panel_info.fbk_divider = 0x8e;
- rinfo->panel_info.use_bios_dividers = 1;
- }
- /* Aluminium PowerBook 17" */
- if (machine_is_compatible("PowerBook5,3") ||
- machine_is_compatible("PowerBook5,5")) {
- rinfo->panel_info.ref_divider = rinfo->pll.ref_div;
- rinfo->panel_info.post_divider = 0x4;
- rinfo->panel_info.fbk_divider = 0x80;
- rinfo->panel_info.use_bios_dividers = 1;
- }
- /* iBook G4 */
- if (machine_is_compatible("PowerBook6,3") ||
- machine_is_compatible("PowerBook6,5")) {
- rinfo->panel_info.ref_divider = rinfo->pll.ref_div;
- rinfo->panel_info.post_divider = 0x6;
- rinfo->panel_info.fbk_divider = 0xad;
+ rinfo->panel_info.fbk_divider = ppll_divn & 0x7ff;
+ rinfo->panel_info.post_divider = (ppll_divn >> 16) & 0x7;
rinfo->panel_info.use_bios_dividers = 1;
+
+ printk(KERN_DEBUG "radeonfb: Using Firmware dividers 0x%08x "
+ "from PPLL %d\n",
+ rinfo->panel_info.fbk_divider |
+ (rinfo->panel_info.post_divider << 16),
+ ppll_div_sel);
}
#endif /* CONFIG_PPC_OF */
}
diff --git a/drivers/video/aty/radeon_pm.c b/drivers/video/aty/radeon_pm.c
index c26f43c04155..cad299f160c1 100644
--- a/drivers/video/aty/radeon_pm.c
+++ b/drivers/video/aty/radeon_pm.c
@@ -465,7 +465,9 @@ static void radeon_pm_setup_for_suspend(struct radeonfb_info *rinfo)
OUTPLL( pllPIXCLKS_CNTL, pixclks_cntl);
-
+ /* Switch off LVDS interface */
+ OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) &
+ ~(LVDS_BLON | LVDS_EN | LVDS_ON | LVDS_DIGON));
/* Enable System power management */
pll_pwrmgt_cntl = INPLL( pllPLL_PWRMGT_CNTL);
diff --git a/drivers/video/aty/radeonfb.h b/drivers/video/aty/radeonfb.h
index 447eb58411d7..7377cb7b14b9 100644
--- a/drivers/video/aty/radeonfb.h
+++ b/drivers/video/aty/radeonfb.h
@@ -425,8 +425,6 @@ static inline u32 _INPLL(struct radeonfb_info *rinfo, u32 addr)
spin_unlock_irqrestore(&rinfo->reg_lock, flags); \
} while (0)
-#define MS_TO_HZ(ms) ((ms * HZ + 999) / 1000)
-
#define BIOS_IN8(v) (readb(rinfo->bios_seg + (v)))
#define BIOS_IN16(v) (readb(rinfo->bios_seg + (v)) | \
(readb(rinfo->bios_seg + (v) + 1) << 8))
diff --git a/drivers/video/bw2.c b/drivers/video/bw2.c
index 65b65addff6b..3b2c5a5e6c12 100644
--- a/drivers/video/bw2.c
+++ b/drivers/video/bw2.c
@@ -386,7 +386,7 @@ int __init bw2_init(void)
struct sbus_bus *sbus;
struct sbus_dev *sdev;
- if (fb_get_options("bw2fb", &option))
+ if (fb_get_options("bw2fb", NULL))
return -ENODEV;
#ifdef CONFIG_SUN4
diff --git a/drivers/video/chipsfb.c b/drivers/video/chipsfb.c
index 4ee5a25db298..a51f4d2b69d8 100644
--- a/drivers/video/chipsfb.c
+++ b/drivers/video/chipsfb.c
@@ -416,7 +416,7 @@ chipsfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent)
release_mem_region(addr, size);
return -ENOMEM;
}
-
+ p->device = &dp->dev;
init_chips(p, addr);
#ifdef CONFIG_PMAC_PBOOK
diff --git a/drivers/video/console/Makefile b/drivers/video/console/Makefile
index bfdd40665b6c..e5fa93b68043 100644
--- a/drivers/video/console/Makefile
+++ b/drivers/video/console/Makefile
@@ -24,7 +24,8 @@ obj-$(CONFIG_PROM_CONSOLE) += promcon.o promcon_tbl.o
obj-$(CONFIG_STI_CONSOLE) += sticon.o sticore.o
obj-$(CONFIG_VGA_CONSOLE) += vgacon.o
obj-$(CONFIG_MDA_CONSOLE) += mdacon.o
-obj-$(CONFIG_FRAMEBUFFER_CONSOLE) += fbcon.o font.o
+obj-$(CONFIG_FRAMEBUFFER_CONSOLE) += fbcon.o bitblit.o font.o
+obj-$(CONFIG_FB_TILEBLITTING) += tileblit.o
obj-$(CONFIG_FB_STI) += sticore.o
diff --git a/drivers/video/console/bitblit.c b/drivers/video/console/bitblit.c
new file mode 100644
index 000000000000..43044f99c37f
--- /dev/null
+++ b/drivers/video/console/bitblit.c
@@ -0,0 +1,370 @@
+/*
+ * linux/drivers/video/console/bitblit.c -- BitBlitting Operation
+ *
+ * Originally from the 'accel_*' routines in drivers/video/console/fbcon.c
+ *
+ * Copyright (C) 2004 Antonino Daplas <adaplas @pol.net>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+#include <linux/vt_kern.h>
+#include <linux/console.h>
+#include <asm/types.h>
+#include "fbcon.h"
+
+/*
+ * Accelerated handlers.
+ */
+#define FBCON_ATTRIBUTE_UNDERLINE 1
+#define FBCON_ATTRIBUTE_REVERSE 2
+#define FBCON_ATTRIBUTE_BOLD 4
+
+static inline int real_y(struct display *p, int ypos)
+{
+ int rows = p->vrows;
+
+ ypos += p->yscroll;
+ return ypos < rows ? ypos : ypos - rows;
+}
+
+
+static inline int get_attribute(struct fb_info *info, u16 c)
+{
+ int attribute = 0;
+
+ if (fb_get_color_depth(info) == 1) {
+ if (attr_underline(c))
+ attribute |= FBCON_ATTRIBUTE_UNDERLINE;
+ if (attr_reverse(c))
+ attribute |= FBCON_ATTRIBUTE_REVERSE;
+ if (attr_bold(c))
+ attribute |= FBCON_ATTRIBUTE_BOLD;
+ }
+
+ return attribute;
+}
+
+static inline void update_attr(u8 *dst, u8 *src, int attribute,
+ struct vc_data *vc)
+{
+ int i, offset = (vc->vc_font.height < 10) ? 1 : 2;
+ int width = (vc->vc_font.width + 7) >> 3;
+ unsigned int cellsize = vc->vc_font.height * width;
+ u8 c;
+
+ offset = cellsize - (offset * width);
+ for (i = 0; i < cellsize; i++) {
+ c = src[i];
+ if (attribute & FBCON_ATTRIBUTE_UNDERLINE && i >= offset)
+ c = 0xff;
+ if (attribute & FBCON_ATTRIBUTE_BOLD)
+ c |= c >> 1;
+ if (attribute & FBCON_ATTRIBUTE_REVERSE)
+ c = ~c;
+ dst[i] = c;
+ }
+}
+
+static void bit_bmove(struct vc_data *vc, struct fb_info *info, int sy,
+ int sx, int dy, int dx, int height, int width)
+{
+ struct fb_copyarea area;
+
+ area.sx = sx * vc->vc_font.width;
+ area.sy = sy * vc->vc_font.height;
+ area.dx = dx * vc->vc_font.width;
+ area.dy = dy * vc->vc_font.height;
+ area.height = height * vc->vc_font.height;
+ area.width = width * vc->vc_font.width;
+
+ info->fbops->fb_copyarea(info, &area);
+}
+
+static void bit_clear(struct vc_data *vc, struct fb_info *info, int sy,
+ int sx, int height, int width)
+{
+ int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
+ struct fb_fillrect region;
+
+ region.color = attr_bgcol_ec(bgshift, vc);
+ region.dx = sx * vc->vc_font.width;
+ region.dy = sy * vc->vc_font.height;
+ region.width = width * vc->vc_font.width;
+ region.height = height * vc->vc_font.height;
+ region.rop = ROP_COPY;
+
+ info->fbops->fb_fillrect(info, &region);
+}
+
+static void bit_putcs(struct vc_data *vc, struct fb_info *info,
+ const unsigned short *s, int count, int yy, int xx,
+ int fg, int bg)
+{
+ void (*move_unaligned)(struct fb_info *info, struct fb_pixmap *buf,
+ u8 *dst, u32 d_pitch, u8 *src, u32 idx,
+ u32 height, u32 shift_high, u32 shift_low,
+ u32 mod);
+ void (*move_aligned)(struct fb_info *info, struct fb_pixmap *buf,
+ u8 *dst, u32 d_pitch, u8 *src, u32 s_pitch,
+ u32 height);
+ unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
+ unsigned int width = (vc->vc_font.width + 7) >> 3;
+ unsigned int cellsize = vc->vc_font.height * width;
+ unsigned int maxcnt = info->pixmap.size/cellsize;
+ unsigned int scan_align = info->pixmap.scan_align - 1;
+ unsigned int buf_align = info->pixmap.buf_align - 1;
+ unsigned int shift_low = 0, mod = vc->vc_font.width % 8;
+ unsigned int shift_high = 8, pitch, cnt, size, k;
+ unsigned int idx = vc->vc_font.width >> 3;
+ unsigned int attribute = get_attribute(info, scr_readw(s));
+ struct fb_image image;
+ u8 *src, *dst, *buf = NULL;
+
+ if (attribute) {
+ buf = kmalloc(cellsize, GFP_KERNEL);
+ if (!buf)
+ return;
+ }
+
+ image.fg_color = fg;
+ image.bg_color = bg;
+
+ image.dx = xx * vc->vc_font.width;
+ image.dy = yy * vc->vc_font.height;
+ image.height = vc->vc_font.height;
+ image.depth = 1;
+
+ if (info->pixmap.outbuf && info->pixmap.inbuf) {
+ move_aligned = fb_iomove_buf_aligned;
+ move_unaligned = fb_iomove_buf_unaligned;
+ } else {
+ move_aligned = fb_sysmove_buf_aligned;
+ move_unaligned = fb_sysmove_buf_unaligned;
+ }
+ while (count) {
+ if (count > maxcnt)
+ cnt = k = maxcnt;
+ else
+ cnt = k = count;
+
+ image.width = vc->vc_font.width * cnt;
+ pitch = ((image.width + 7) >> 3) + scan_align;
+ pitch &= ~scan_align;
+ size = pitch * image.height + buf_align;
+ size &= ~buf_align;
+ dst = fb_get_buffer_offset(info, &info->pixmap, size);
+ image.data = dst;
+ if (mod) {
+ while (k--) {
+ src = vc->vc_font.data + (scr_readw(s++)&
+ charmask)*cellsize;
+
+ if (attribute) {
+ update_attr(buf, src, attribute, vc);
+ src = buf;
+ }
+
+ move_unaligned(info, &info->pixmap, dst, pitch,
+ src, idx, image.height,
+ shift_high, shift_low, mod);
+ shift_low += mod;
+ dst += (shift_low >= 8) ? width : width - 1;
+ shift_low &= 7;
+ shift_high = 8 - shift_low;
+ }
+ } else {
+ while (k--) {
+ src = vc->vc_font.data + (scr_readw(s++)&
+ charmask)*cellsize;
+
+ if (attribute) {
+ update_attr(buf, src, attribute, vc);
+ src = buf;
+ }
+
+ move_aligned(info, &info->pixmap, dst, pitch,
+ src, idx, image.height);
+ dst += width;
+ }
+ }
+ info->fbops->fb_imageblit(info, &image);
+ image.dx += cnt * vc->vc_font.width;
+ count -= cnt;
+ }
+
+ if (buf)
+ kfree(buf);
+}
+
+static void bit_clear_margins(struct vc_data *vc, struct fb_info *info,
+ int bottom_only)
+{
+ int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
+ unsigned int cw = vc->vc_font.width;
+ unsigned int ch = vc->vc_font.height;
+ unsigned int rw = info->var.xres - (vc->vc_cols*cw);
+ unsigned int bh = info->var.yres - (vc->vc_rows*ch);
+ unsigned int rs = info->var.xres - rw;
+ unsigned int bs = info->var.yres - bh;
+ struct fb_fillrect region;
+
+ region.color = attr_bgcol_ec(bgshift, vc);
+ region.rop = ROP_COPY;
+
+ if (rw && !bottom_only) {
+ region.dx = info->var.xoffset + rs;
+ region.dy = 0;
+ region.width = rw;
+ region.height = info->var.yres_virtual;
+ info->fbops->fb_fillrect(info, &region);
+ }
+
+ if (bh) {
+ region.dx = info->var.xoffset;
+ region.dy = info->var.yoffset + bs;
+ region.width = rs;
+ region.height = bh;
+ info->fbops->fb_fillrect(info, &region);
+ }
+}
+
+static void bit_cursor(struct vc_data *vc, struct fb_info *info,
+ struct display *p, int mode, int fg, int bg)
+{
+ struct fb_cursor cursor;
+ unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
+ int w = (vc->vc_font.width + 7) >> 3, c;
+ int y = real_y(p, vc->vc_y);
+ int attribute;
+ char *src;
+
+ c = scr_readw((u16 *) vc->vc_pos);
+ attribute = get_attribute(info, c);
+ src = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height));
+ if (attribute) {
+ u8 *dst;
+
+ dst = kmalloc(w * vc->vc_font.height, GFP_ATOMIC);
+ if (!dst)
+ return;
+ if (info->cursor.data)
+ kfree(info->cursor.data);
+ info->cursor.data = dst;
+ update_attr(dst, src, attribute, vc);
+ src = dst;
+ }
+
+ cursor.image.data = src;
+ cursor.set = FB_CUR_SETCUR;
+ cursor.image.depth = 1;
+
+ switch (mode) {
+ case CM_ERASE:
+ if (info->cursor.rop == ROP_XOR) {
+ info->cursor.enable = 0;
+ info->cursor.rop = ROP_COPY;
+ info->fbops->fb_cursor(info, &cursor);
+ }
+ break;
+ case CM_MOVE:
+ case CM_DRAW:
+ info->cursor.enable = 1;
+ info->cursor.rop = ROP_XOR;
+
+ if (info->cursor.image.fg_color != fg ||
+ info->cursor.image.bg_color != bg) {
+ cursor.image.fg_color = fg;
+ cursor.image.bg_color = bg;
+ cursor.set |= FB_CUR_SETCMAP;
+ }
+
+ if ((info->cursor.image.dx != (vc->vc_font.width * vc->vc_x)) ||
+ (info->cursor.image.dy != (vc->vc_font.height * y))) {
+ cursor.image.dx = vc->vc_font.width * vc->vc_x;
+ cursor.image.dy = vc->vc_font.height * y;
+ cursor.set |= FB_CUR_SETPOS;
+ }
+
+ if (info->cursor.image.height != vc->vc_font.height ||
+ info->cursor.image.width != vc->vc_font.width) {
+ cursor.image.height = vc->vc_font.height;
+ cursor.image.width = vc->vc_font.width;
+ cursor.set |= FB_CUR_SETSIZE;
+ }
+
+ if (info->cursor.hot.x || info->cursor.hot.y) {
+ cursor.hot.x = cursor.hot.y = 0;
+ cursor.set |= FB_CUR_SETHOT;
+ }
+
+ if ((cursor.set & FB_CUR_SETSIZE) ||
+ ((vc->vc_cursor_type & 0x0f) != p->cursor_shape)
+ || info->cursor.mask == NULL) {
+ char *mask = kmalloc(w*vc->vc_font.height, GFP_ATOMIC);
+ int cur_height, size, i = 0;
+ u8 msk = 0xff;
+
+ if (!mask)
+ return;
+
+ if (info->cursor.mask)
+ kfree(info->cursor.mask);
+ info->cursor.mask = mask;
+ p->cursor_shape = vc->vc_cursor_type & 0x0f;
+ cursor.set |= FB_CUR_SETSHAPE;
+
+ switch (vc->vc_cursor_type & 0x0f) {
+ case CUR_NONE:
+ cur_height = 0;
+ break;
+ case CUR_UNDERLINE:
+ cur_height = (vc->vc_font.height < 10) ? 1 : 2;
+ break;
+ case CUR_LOWER_THIRD:
+ cur_height = vc->vc_font.height/3;
+ break;
+ case CUR_LOWER_HALF:
+ cur_height = vc->vc_font.height >> 1;
+ break;
+ case CUR_TWO_THIRDS:
+ cur_height = (vc->vc_font.height << 1)/3;
+ break;
+ case CUR_BLOCK:
+ default:
+ cur_height = vc->vc_font.height;
+ break;
+ }
+ size = (vc->vc_font.height - cur_height) * w;
+ while (size--)
+ mask[i++] = ~msk;
+ size = cur_height * w;
+ while (size--)
+ mask[i++] = msk;
+ }
+ info->fbops->fb_cursor(info, &cursor);
+ break;
+ }
+}
+
+void fbcon_set_bitops(struct fbcon_ops *ops)
+{
+ ops->bmove = bit_bmove;
+ ops->clear = bit_clear;
+ ops->putcs = bit_putcs;
+ ops->clear_margins = bit_clear_margins;
+ ops->cursor = bit_cursor;
+}
+
+EXPORT_SYMBOL(fbcon_set_bitops);
+
+MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
+MODULE_DESCRIPTION("Bit Blitting Operation");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
index 2fba5cfcbcee..aa4019d5a4e7 100644
--- a/drivers/video/console/fbcon.c
+++ b/drivers/video/console/fbcon.c
@@ -102,6 +102,7 @@
struct display fb_display[MAX_NR_CONSOLES];
signed char con2fb_map[MAX_NR_CONSOLES];
+signed char con2fb_map_boot[MAX_NR_CONSOLES];
static int logo_height;
static int logo_lines;
static int logo_shown = -1;
@@ -119,13 +120,7 @@ static int fbcon_is_default = 1;
static char fontname[40];
/* current fb_info */
-static int info_idx = -1;
-
-#define REFCOUNT(fd) (((int *)(fd))[-1])
-#define FNTSIZE(fd) (((int *)(fd))[-2])
-#define FNTCHARCNT(fd) (((int *)(fd))[-3])
-#define FNTSUM(fd) (((int *)(fd))[-4])
-#define FONT_EXTRA_WORDS 4
+static int info_idx = -1;
#define CM_SOFTBACK (8)
@@ -158,6 +153,7 @@ static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos);
static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
int count, int ypos, int xpos);
+static void fbcon_clear_margins(struct vc_data *vc, int bottom_only);
static void fbcon_cursor(struct vc_data *vc, int mode);
static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
int count);
@@ -167,9 +163,6 @@ static int fbcon_switch(struct vc_data *vc);
static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch);
static int fbcon_set_palette(struct vc_data *vc, unsigned char *table);
static int fbcon_scrolldelta(struct vc_data *vc, int lines);
-void accel_clear_margins(struct vc_data *vc, struct fb_info *info,
- int bottom_only);
-
/*
* Internal routines
@@ -182,6 +175,7 @@ static __inline__ void ypan_down(struct vc_data *vc, int count);
static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx,
int dy, int dx, int height, int width, u_int y_break);
static void fbcon_set_disp(struct fb_info *info, struct vc_data *vc);
+static void fbcon_preset_disp(struct fb_info *info, int unit);
static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
int line, int count, int dy);
@@ -198,21 +192,69 @@ static irqreturn_t fb_vbl_detect(int irq, void *dummy, struct pt_regs *fp)
}
#endif
+static inline int get_color(struct vc_data *vc, struct fb_info *info,
+ u16 c, int is_fg)
+{
+ int depth = fb_get_color_depth(info);
+ int color = 0;
+
+ if (depth != 1)
+ color = (is_fg) ? attr_fgcol((vc->vc_hi_font_mask) ? 9 : 8, c)
+ : attr_bgcol((vc->vc_hi_font_mask) ? 13 : 12, c);
+
+ switch (depth) {
+ case 1:
+ {
+ /* 0 or 1 */
+ int fg = (info->fix.visual != FB_VISUAL_MONO01) ? 1 : 0;
+ int bg = (info->fix.visual != FB_VISUAL_MONO01) ? 0 : 1;
+
+ color = (is_fg) ? fg : bg;
+ break;
+ }
+ case 2:
+ /*
+ * Scale down 16-colors to 4 colors. Default 4-color palette
+ * is grayscale.
+ */
+ color /= 4;
+ break;
+ case 3:
+ /*
+ * Last 8 entries of default 16-color palette is a more intense
+ * version of the first 8 (i.e., same chrominance, different
+ * luminance).
+ */
+ color &= 7;
+ break;
+ }
+
+ return color;
+}
+
static void fb_flashcursor(void *private)
{
struct fb_info *info = (struct fb_info *) private;
+ struct fbcon_ops *ops = (struct fbcon_ops *) info->fbcon_par;
+ struct display *p;
struct vc_data *vc = NULL;
+ int c;
+ int mode;
if (info->currcon != -1)
vc = vc_cons[info->currcon].d;
if (info->state != FBINFO_STATE_RUNNING ||
- info->cursor.rop == ROP_COPY || !vc || !CON_IS_VISIBLE(vc)
- || registered_fb[(int) con2fb_map[vc->vc_num]] != info)
+ !vc || !CON_IS_VISIBLE(vc) || !info->cursor.flash ||
+ vt_cons[vc->vc_num]->vc_mode != KD_TEXT ||
+ registered_fb[(int) con2fb_map[vc->vc_num]] != info)
return;
+ p = &fb_display[vc->vc_num];
+ c = scr_readw((u16 *) vc->vc_pos);
acquire_console_sem();
- info->cursor.enable ^= 1;
- info->fbops->fb_cursor(info, &info->cursor);
+ mode = (info->cursor.enable) ? CM_ERASE : CM_DRAW;
+ ops->cursor(vc, info, p, mode, get_color(vc, info, c, 1),
+ get_color(vc, info, c, 0));
release_console_sem();
}
@@ -271,7 +313,8 @@ int __init fb_console_setup(char *this_opt)
for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) {
if (!options[j])
j = 0;
- con2fb_map[i] = (options[j++]-'0') % FB_MAX;
+ con2fb_map_boot[i] =
+ (options[j++]-'0') % FB_MAX;
}
return 0;
}
@@ -314,13 +357,16 @@ static int search_for_mapped_con(void)
return 0;
}
-static int fbcon_takeover(void)
+static int fbcon_takeover(int show_logo)
{
int err, i;
if (!num_registered_fb)
return -ENODEV;
+ if (!show_logo)
+ logo_shown = -3;
+
for (i = first_fb_vc; i <= last_fb_vc; i++)
con2fb_map[i] = info_idx;
@@ -336,15 +382,112 @@ static int fbcon_takeover(void)
return err;
}
+static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
+ int cols, int rows, int new_cols, int new_rows)
+{
+ /* Need to make room for the logo */
+ int cnt, erase = vc->vc_video_erase_char, step;
+ unsigned short *save = NULL, *r, *q;
+
+ /*
+ * remove underline attribute from erase character
+ * if black and white framebuffer.
+ */
+ if (fb_get_color_depth(info) == 1)
+ erase &= ~0x400;
+ logo_height = fb_prepare_logo(info);
+ logo_lines = (logo_height + vc->vc_font.height - 1) /
+ vc->vc_font.height;
+ q = (unsigned short *) (vc->vc_origin +
+ vc->vc_size_row * rows);
+ step = logo_lines * cols;
+ for (r = q - logo_lines * cols; r < q; r++)
+ if (scr_readw(r) != vc->vc_video_erase_char)
+ break;
+ if (r != q && new_rows >= rows + logo_lines) {
+ save = kmalloc(logo_lines * new_cols * 2, GFP_KERNEL);
+ if (save) {
+ int i = cols < new_cols ? cols : new_cols;
+ scr_memsetw(save, erase, logo_lines * new_cols * 2);
+ r = q - step;
+ for (cnt = 0; cnt < logo_lines; cnt++, r += i)
+ scr_memcpyw(save + cnt * new_cols, r, 2 * i);
+ r = q;
+ }
+ }
+ if (r == q) {
+ /* We can scroll screen down */
+ r = q - step - cols;
+ for (cnt = rows - logo_lines; cnt > 0; cnt--) {
+ scr_memcpyw(r + step, r, vc->vc_size_row);
+ r -= cols;
+ }
+ if (!save) {
+ vc->vc_y += logo_lines;
+ vc->vc_pos += logo_lines * vc->vc_size_row;
+ }
+ }
+ scr_memsetw((unsigned short *) vc->vc_origin,
+ erase,
+ vc->vc_size_row * logo_lines);
+
+ if (CON_IS_VISIBLE(vc) && vt_cons[vc->vc_num]->vc_mode == KD_TEXT) {
+ fbcon_clear_margins(vc, 0);
+ update_screen(vc->vc_num);
+ }
+
+ if (save) {
+ q = (unsigned short *) (vc->vc_origin +
+ vc->vc_size_row *
+ rows);
+ scr_memcpyw(q, save, logo_lines * new_cols * 2);
+ vc->vc_y += logo_lines;
+ vc->vc_pos += logo_lines * vc->vc_size_row;
+ kfree(save);
+ }
+
+ if (logo_lines > vc->vc_bottom) {
+ logo_shown = -1;
+ printk(KERN_INFO
+ "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n");
+ } else if (logo_shown != -3) {
+ logo_shown = -2;
+ vc->vc_top = logo_lines;
+ }
+}
+
+#ifdef CONFIG_FB_TILEBLITTING
+static void set_blitting_type(struct vc_data *vc, struct fb_info *info,
+ struct display *p)
+{
+ struct fbcon_ops *ops = (struct fbcon_ops *) info->fbcon_par;
+
+ if ((info->flags & FBINFO_MISC_TILEBLITTING))
+ fbcon_set_tileops(vc, info, p, ops);
+ else
+ fbcon_set_bitops(ops);
+}
+#else
+static void set_blitting_type(struct vc_data *vc, struct fb_info *info,
+ struct display *p)
+{
+ struct fbcon_ops *ops = (struct fbcon_ops *) info->fbcon_par;
+
+ info->flags &= ~FBINFO_MISC_TILEBLITTING;
+ fbcon_set_bitops(ops);
+}
+#endif /* CONFIG_MISC_TILEBLITTING */
+
/**
* set_con2fb_map - map console to frame buffer device
* @unit: virtual console number to map
* @newidx: frame buffer index to map virtual console to
+ * @user: user request
*
* Maps a virtual console @unit to a frame buffer device
* @newidx.
*/
-static int set_con2fb_map(int unit, int newidx)
+static int set_con2fb_map(int unit, int newidx, int user)
{
struct vc_data *vc = vc_cons[unit].d;
int oldidx = con2fb_map[unit];
@@ -355,12 +498,12 @@ static int set_con2fb_map(int unit, int newidx)
if (oldidx == newidx)
return 0;
- if (!vc)
- return -ENODEV;
+ if (!info)
+ return -EINVAL;
if (!search_for_mapped_con()) {
info_idx = newidx;
- return fbcon_takeover();
+ return fbcon_takeover(0);
}
if (oldidx != -1)
@@ -370,17 +513,35 @@ static int set_con2fb_map(int unit, int newidx)
acquire_console_sem();
con2fb_map[unit] = newidx;
+
if (!found) {
+ struct fbcon_ops *ops = NULL;
+ int err = 0;
if (!try_module_get(info->fbops->owner)) {
- con2fb_map[unit] = oldidx;
- release_console_sem();
- return -ENODEV;
+ err = -ENODEV;
}
- if (info->fbops->fb_open && info->fbops->fb_open(info, 0)) {
- module_put(info->fbops->owner);
+
+ if (!err && info->fbops->fb_open &&
+ info->fbops->fb_open(info, 0)) {
+ err = -ENODEV;
+ }
+
+ if (!err) {
+ ops = kmalloc(sizeof(struct fbcon_ops), GFP_KERNEL);
+ if (!ops)
+ err = -ENOMEM;
+ }
+
+ if (!err) {
+ info->fbcon_par = ops;
+ set_blitting_type(vc, info, NULL);
+ }
+
+ if (err) {
con2fb_map[unit] = oldidx;
+ module_put(info->fbops->owner);
release_console_sem();
- return -ENODEV;
+ return err;
}
}
@@ -399,11 +560,14 @@ static int set_con2fb_map(int unit, int newidx)
release_console_sem();
return -ENODEV;
}
+
if (oldinfo->queue.func == fb_flashcursor)
del_timer_sync(&oldinfo->cursor_timer);
+
+ kfree(oldinfo->fbcon_par);
module_put(oldinfo->fbops->owner);
}
- info->currcon = -1;
+
if (!found) {
if (!info->queue.func || info->queue.func == fb_flashcursor) {
if (!info->queue.func)
@@ -416,259 +580,31 @@ static int set_con2fb_map(int unit, int newidx)
add_timer(&info->cursor_timer);
}
}
- if (info->fbops->fb_set_par)
- info->fbops->fb_set_par(info);
- fbcon_set_disp(info, vc);
- release_console_sem();
- return 0;
-}
-
-/*
- * Accelerated handlers.
- */
-static inline int get_color(struct vc_data *vc, struct fb_info *info,
- u16 c, int is_fg)
-{
- int depth = fb_get_color_depth(info);
- int color = 0;
-
- if (depth != 1)
- color = (is_fg) ? attr_fgcol((vc->vc_hi_font_mask) ? 9 : 8, c)
- : attr_bgcol((vc->vc_hi_font_mask) ? 13 : 12, c);
-
- switch (depth) {
- case 1:
- {
- /* 0 or 1 */
- int fg = (info->fix.visual != FB_VISUAL_MONO01) ? 1 : 0;
- int bg = (info->fix.visual != FB_VISUAL_MONO01) ? 0 : 1;
-
- color = (is_fg) ? fg : bg;
- break;
- }
- case 2:
- /*
- * Scale down 16-colors to 4 colors. Default 4-color palette
- * is grayscale.
- */
- color /= 4;
- break;
- case 3:
- /*
- * Last 8 entries of default 16-color palette is a more intense
- * version of the first 8 (i.e., same chrominance, different
- * luminance).
- */
- color &= 7;
- break;
- }
- return color;
-}
+ info->currcon = fg_console;
+ con2fb_map_boot[unit] = newidx;
-#define FBCON_ATTRIBUTE_UNDERLINE 1
-#define FBCON_ATTRIBUTE_REVERSE 2
-#define FBCON_ATTRIBUTE_BOLD 4
-
-static inline int get_attribute(struct fb_info *info, u16 c)
-{
- int attribute = 0;
-
- if (fb_get_color_depth(info) == 1) {
- if (attr_underline(c))
- attribute |= FBCON_ATTRIBUTE_UNDERLINE;
- if (attr_reverse(c))
- attribute |= FBCON_ATTRIBUTE_REVERSE;
- if (attr_bold(c))
- attribute |= FBCON_ATTRIBUTE_BOLD;
- }
-
- return attribute;
-}
-
-static inline void update_attr(u8 *dst, u8 *src, int attribute,
- struct vc_data *vc)
-{
- int i, offset = (vc->vc_font.height < 10) ? 1 : 2;
- int width = (vc->vc_font.width + 7) >> 3;
- unsigned int cellsize = vc->vc_font.height * width;
- u8 c;
-
- offset = cellsize - (offset * width);
- for (i = 0; i < cellsize; i++) {
- c = src[i];
- if (attribute & FBCON_ATTRIBUTE_UNDERLINE && i >= offset)
- c = 0xff;
- if (attribute & FBCON_ATTRIBUTE_BOLD)
- c |= c >> 1;
- if (attribute & FBCON_ATTRIBUTE_REVERSE)
- c = ~c;
- dst[i] = c;
- }
-}
-
-void accel_bmove(struct vc_data *vc, struct fb_info *info, int sy,
- int sx, int dy, int dx, int height, int width)
-{
- struct fb_copyarea area;
-
- area.sx = sx * vc->vc_font.width;
- area.sy = sy * vc->vc_font.height;
- area.dx = dx * vc->vc_font.width;
- area.dy = dy * vc->vc_font.height;
- area.height = height * vc->vc_font.height;
- area.width = width * vc->vc_font.width;
-
- info->fbops->fb_copyarea(info, &area);
-}
-
-void accel_clear(struct vc_data *vc, struct fb_info *info, int sy,
- int sx, int height, int width)
-{
- int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
- struct fb_fillrect region;
-
- region.color = attr_bgcol_ec(bgshift, vc);
- region.dx = sx * vc->vc_font.width;
- region.dy = sy * vc->vc_font.height;
- region.width = width * vc->vc_font.width;
- region.height = height * vc->vc_font.height;
- region.rop = ROP_COPY;
-
- info->fbops->fb_fillrect(info, &region);
-}
-
-void accel_putcs(struct vc_data *vc, struct fb_info *info,
- const unsigned short *s, int count, int yy, int xx)
-{
- void (*move_unaligned)(struct fb_info *info, struct fb_pixmap *buf,
- u8 *dst, u32 d_pitch, u8 *src, u32 idx,
- u32 height, u32 shift_high, u32 shift_low,
- u32 mod);
- void (*move_aligned)(struct fb_info *info, struct fb_pixmap *buf,
- u8 *dst, u32 d_pitch, u8 *src, u32 s_pitch,
- u32 height);
- unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
- unsigned int width = (vc->vc_font.width + 7) >> 3;
- unsigned int cellsize = vc->vc_font.height * width;
- unsigned int maxcnt = info->pixmap.size/cellsize;
- unsigned int scan_align = info->pixmap.scan_align - 1;
- unsigned int buf_align = info->pixmap.buf_align - 1;
- unsigned int shift_low = 0, mod = vc->vc_font.width % 8;
- unsigned int shift_high = 8, pitch, cnt, size, k;
- unsigned int idx = vc->vc_font.width >> 3;
- unsigned int attribute = get_attribute(info, scr_readw(s));
- struct fb_image image;
- u8 *src, *dst, *buf = NULL;
-
- if (attribute) {
- buf = kmalloc(cellsize, GFP_KERNEL);
- if (!buf)
- return;
- }
-
- image.fg_color = get_color(vc, info, scr_readw(s), 1);
- image.bg_color = get_color(vc, info, scr_readw(s), 0);
-
- image.dx = xx * vc->vc_font.width;
- image.dy = yy * vc->vc_font.height;
- image.height = vc->vc_font.height;
- image.depth = 1;
-
- if (info->pixmap.outbuf && info->pixmap.inbuf) {
- move_aligned = fb_iomove_buf_aligned;
- move_unaligned = fb_iomove_buf_unaligned;
- } else {
- move_aligned = fb_sysmove_buf_aligned;
- move_unaligned = fb_sysmove_buf_unaligned;
- }
- while (count) {
- if (count > maxcnt)
- cnt = k = maxcnt;
- else
- cnt = k = count;
-
- image.width = vc->vc_font.width * cnt;
- pitch = ((image.width + 7) >> 3) + scan_align;
- pitch &= ~scan_align;
- size = pitch * image.height + buf_align;
- size &= ~buf_align;
- dst = fb_get_buffer_offset(info, &info->pixmap, size);
- image.data = dst;
- if (mod) {
- while (k--) {
- src = vc->vc_font.data + (scr_readw(s++)&
- charmask)*cellsize;
-
- if (attribute) {
- update_attr(buf, src, attribute, vc);
- src = buf;
- }
+ if (info->fbops->fb_set_par)
+ info->fbops->fb_set_par(info);
- move_unaligned(info, &info->pixmap, dst, pitch,
- src, idx, image.height,
- shift_high, shift_low, mod);
- shift_low += mod;
- dst += (shift_low >= 8) ? width : width - 1;
- shift_low &= 7;
- shift_high = 8 - shift_low;
- }
- } else {
- while (k--) {
- src = vc->vc_font.data + (scr_readw(s++)&
- charmask)*cellsize;
+ if (vc)
+ fbcon_set_disp(info, vc);
+ else
+ fbcon_preset_disp(info, unit);
- if (attribute) {
- update_attr(buf, src, attribute, vc);
- src = buf;
- }
+ if (fg_console == 0 && !user && logo_shown != -3) {
+ struct vc_data *vc = vc_cons[fg_console].d;
+ struct fb_info *fg_info = registered_fb[(int) con2fb_map[fg_console]];
- move_aligned(info, &info->pixmap, dst, pitch,
- src, idx, image.height);
- dst += width;
- }
- }
- info->fbops->fb_imageblit(info, &image);
- image.dx += cnt * vc->vc_font.width;
- count -= cnt;
+ fbcon_prepare_logo(vc, fg_info, vc->vc_cols, vc->vc_rows,
+ vc->vc_cols, vc->vc_rows);
}
- if (buf)
- kfree(buf);
+ switch_screen(fg_console);
+ release_console_sem();
+ return 0;
}
-void accel_clear_margins(struct vc_data *vc, struct fb_info *info,
- int bottom_only)
-{
- int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
- unsigned int cw = vc->vc_font.width;
- unsigned int ch = vc->vc_font.height;
- unsigned int rw = info->var.xres - (vc->vc_cols*cw);
- unsigned int bh = info->var.yres - (vc->vc_rows*ch);
- unsigned int rs = info->var.xres - rw;
- unsigned int bs = info->var.yres - bh;
- struct fb_fillrect region;
-
- region.color = attr_bgcol_ec(bgshift, vc);
- region.rop = ROP_COPY;
-
- if (rw && !bottom_only) {
- region.dx = info->var.xoffset + rs;
- region.dy = 0;
- region.width = rw;
- region.height = info->var.yres_virtual;
- info->fbops->fb_fillrect(info, &region);
- }
-
- if (bh) {
- region.dx = info->var.xoffset;
- region.dy = info->var.yoffset + bs;
- region.width = rs;
- region.height = bh;
- info->fbops->fb_fillrect(info, &region);
- }
-}
-
/*
* Low Level Operations
*/
@@ -722,6 +658,7 @@ static const char *fbcon_startup(void)
struct font_desc *font = NULL;
struct module *owner;
struct fb_info *info = NULL;
+ struct fbcon_ops *ops;
int rows, cols;
int irqres;
@@ -748,6 +685,16 @@ static const char *fbcon_startup(void)
module_put(owner);
return NULL;
}
+
+ ops = kmalloc(sizeof(struct fbcon_ops), GFP_KERNEL);
+ if (!ops) {
+ module_put(owner);
+ return NULL;
+ }
+
+ info->fbcon_par = ops;
+ set_blitting_type(vc, info, NULL);
+
if (info->fix.type != FB_TYPE_TEXT) {
if (fbcon_softback_size) {
if (!softback_buf) {
@@ -777,7 +724,7 @@ static const char *fbcon_startup(void)
if (!p->fontdata) {
if (!fontname[0] || !(font = find_font(fontname)))
font = get_default_font(info->var.xres,
- info->var.yres);
+ info->var.yres);
vc->vc_font.width = font->width;
vc->vc_font.height = font->height;
vc->vc_font.data = p->fontdata = font->data;
@@ -793,7 +740,6 @@ static const char *fbcon_startup(void)
DPRINTK("res: %dx%d-%d\n", info->var.xres,
info->var.yres,
info->var.bits_per_pixel);
- con_set_default_unimap(vc->vc_num);
#ifdef CONFIG_ATARI
if (MACH_IS_ATARI) {
@@ -873,15 +819,15 @@ static void fbcon_init(struct vc_data *vc, int init)
{
struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
struct vc_data **default_mode = vc->vc_display_fg;
+ struct vc_data *svc = *default_mode;
struct display *t, *p = &fb_display[vc->vc_num];
int display_fg = (*default_mode)->vc_num;
int logo = 1, new_rows, new_cols, rows, cols, charcnt = 256;
- unsigned short *save = NULL, *r, *q;
int cap = info->flags;
if (info_idx == -1 || info == NULL)
return;
- if (vc->vc_num != display_fg || (info->flags & FBINFO_MODULE) ||
+ if (vc->vc_num != display_fg || logo_shown == -3 ||
(info->fix.type == FB_TYPE_TEXT))
logo = 0;
@@ -900,7 +846,6 @@ static void fbcon_init(struct vc_data *vc, int init)
p->userfont = t->userfont;
if (p->userfont)
REFCOUNT(p->fontdata)++;
- con_copy_unimap(vc->vc_num, display_fg);
}
if (p->userfont)
charcnt = FNTCHARCNT(p->fontdata);
@@ -913,6 +858,12 @@ static void fbcon_init(struct vc_data *vc, int init)
if (vc->vc_can_do_color)
vc->vc_complement_mask <<= 1;
}
+
+ if (!*svc->vc_uni_pagedir_loc)
+ con_set_default_unimap(display_fg);
+ if (!*vc->vc_uni_pagedir_loc)
+ con_copy_unimap(vc->vc_num, display_fg);
+
cols = vc->vc_cols;
rows = vc->vc_rows;
new_cols = info->var.xres / vc->vc_font.width;
@@ -945,75 +896,8 @@ static void fbcon_init(struct vc_data *vc, int init)
vc->vc_rows = new_rows;
}
- if (logo) {
- /* Need to make room for the logo */
- int cnt, erase = vc->vc_video_erase_char;
- int step;
-
- /*
- * remove underline attribute from erase character
- * if black and white framebuffer.
- */
- if (fb_get_color_depth(info) == 1)
- erase &= ~0x400;
- logo_height = fb_prepare_logo(info);
- logo_lines = (logo_height + vc->vc_font.height - 1) /
- vc->vc_font.height;
- q = (unsigned short *) (vc->vc_origin +
- vc->vc_size_row * rows);
- step = logo_lines * cols;
- for (r = q - logo_lines * cols; r < q; r++)
- if (scr_readw(r) != vc->vc_video_erase_char)
- break;
- if (r != q && new_rows >= rows + logo_lines) {
- save = kmalloc(logo_lines * new_cols * 2, GFP_KERNEL);
- if (save) {
- int i = cols < new_cols ? cols : new_cols;
- scr_memsetw(save, erase, logo_lines * new_cols * 2);
- r = q - step;
- for (cnt = 0; cnt < logo_lines; cnt++, r += i)
- scr_memcpyw(save + cnt * new_cols, r, 2 * i);
- r = q;
- }
- }
- if (r == q) {
- /* We can scroll screen down */
- r = q - step - cols;
- for (cnt = rows - logo_lines; cnt > 0; cnt--) {
- scr_memcpyw(r + step, r, vc->vc_size_row);
- r -= cols;
- }
- if (!save) {
- vc->vc_y += logo_lines;
- vc->vc_pos += logo_lines * vc->vc_size_row;
- }
- }
- scr_memsetw((unsigned short *) vc->vc_origin,
- erase,
- vc->vc_size_row * logo_lines);
-
- if (CON_IS_VISIBLE(vc) && vt_cons[vc->vc_num]->vc_mode == KD_TEXT) {
- accel_clear_margins(vc, info, 0);
- update_screen(vc->vc_num);
- }
- if (save) {
- q = (unsigned short *) (vc->vc_origin +
- vc->vc_size_row *
- rows);
- scr_memcpyw(q, save, logo_lines * new_cols * 2);
- vc->vc_y += logo_lines;
- vc->vc_pos += logo_lines * vc->vc_size_row;
- kfree(save);
- }
- if (logo_lines > vc->vc_bottom) {
- logo_shown = -1;
- printk(KERN_INFO
- "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n");
- } else {
- logo_shown = -2;
- vc->vc_top = logo_lines;
- }
- }
+ if (logo)
+ fbcon_prepare_logo(vc, info, cols, rows, new_cols, new_rows);
if (vc->vc_num == display_fg && softback_buf) {
int l = fbcon_softback_size / vc->vc_size_row;
@@ -1043,7 +927,7 @@ static void fbcon_deinit(struct vc_data *vc)
* This system is now divided into two levels because of complications
* caused by hardware scrolling. Top level functions:
*
- * fbcon_bmove(), fbcon_clear(), fbcon_putc()
+ * fbcon_bmove(), fbcon_clear(), fbcon_putc(), fbcon_clear_margins()
*
* handles y values in range [0, scr_height-1] that correspond to real
* screen positions. y_wrap shift means that first line of bitmap may be
@@ -1074,7 +958,8 @@ static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
int width)
{
struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
-
+ struct fbcon_ops *ops = (struct fbcon_ops *) info->fbcon_par;
+
struct display *p = &fb_display[vc->vc_num];
u_int y_break;
@@ -1091,11 +976,11 @@ static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
y_break = p->vrows - p->yscroll;
if (sy < y_break && sy + height - 1 >= y_break) {
u_int b = y_break - sy;
- accel_clear(vc, info, real_y(p, sy), sx, b, width);
- accel_clear(vc, info, real_y(p, sy + b), sx, height - b,
+ ops->clear(vc, info, real_y(p, sy), sx, b, width);
+ ops->clear(vc, info, real_y(p, sy + b), sx, height - b,
width);
} else
- accel_clear(vc, info, real_y(p, sy), sx, height, width);
+ ops->clear(vc, info, real_y(p, sy), sx, height, width);
}
static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
@@ -1103,6 +988,7 @@ static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
{
struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
struct display *p = &fb_display[vc->vc_num];
+ struct fbcon_ops *ops = (struct fbcon_ops *) info->fbcon_par;
if (!info->fbops->fb_blank && console_blanked)
return;
@@ -1112,7 +998,9 @@ static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT)
return;
- accel_putcs(vc, info, s, count, real_y(p, ypos), xpos);
+ ops->putcs(vc, info, s, count, real_y(p, ypos), xpos,
+ get_color(vc, info, scr_readw(s), 1),
+ get_color(vc, info, scr_readw(s), 0));
}
static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos)
@@ -1120,137 +1008,39 @@ static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos)
fbcon_putcs(vc, (const unsigned short *) &c, 1, ypos, xpos);
}
+static void fbcon_clear_margins(struct vc_data *vc, int bottom_only)
+{
+ struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
+ struct fbcon_ops *ops = (struct fbcon_ops *) info->fbcon_par;
+
+ ops->clear_margins(vc, info, bottom_only);
+}
+
static void fbcon_cursor(struct vc_data *vc, int mode)
{
- struct fb_cursor cursor;
struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
- unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
+ struct fbcon_ops *ops = (struct fbcon_ops *) info->fbcon_par;
struct display *p = &fb_display[vc->vc_num];
- int w = (vc->vc_font.width + 7) >> 3, c;
- int y = real_y(p, vc->vc_y), fg, bg;
- int attribute;
- u8 *src;
+ int y = real_y(p, vc->vc_y);
+ int c = scr_readw((u16 *) vc->vc_pos);
+ info->cursor.flash = 1;
if (mode & CM_SOFTBACK) {
mode &= ~CM_SOFTBACK;
if (softback_lines) {
- if (y + softback_lines >= vc->vc_rows)
+ if (y + softback_lines >= vc->vc_rows) {
mode = CM_ERASE;
+ info->cursor.flash = 0;
+ }
else
y += softback_lines;
}
} else if (softback_lines)
fbcon_set_origin(vc);
- c = scr_readw((u16 *) vc->vc_pos);
- attribute = get_attribute(info, c);
- src = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height));
- if (attribute) {
- u8 *dst;
-
- dst = kmalloc(w * vc->vc_font.height, GFP_ATOMIC);
- if (!dst)
- return;
- if (info->cursor.data)
- kfree(info->cursor.data);
- info->cursor.data = dst;
- update_attr(dst, src, attribute, vc);
- src = dst;
- }
-
- cursor.image.data = src;
- cursor.set = FB_CUR_SETCUR;
- cursor.image.depth = 1;
-
- switch (mode) {
- case CM_ERASE:
- if (info->cursor.rop == ROP_XOR) {
- info->cursor.enable = 0;
- info->cursor.rop = ROP_COPY;
- info->fbops->fb_cursor(info, &cursor);
- }
- break;
- case CM_MOVE:
- case CM_DRAW:
- info->cursor.enable = 1;
- fg = get_color(vc, info, c, 1);
- bg = get_color(vc, info, c, 0);
-
- if (info->cursor.image.fg_color != fg ||
- info->cursor.image.bg_color != bg) {
- cursor.image.fg_color = fg;
- cursor.image.bg_color = bg;
- cursor.set |= FB_CUR_SETCMAP;
- }
-
- if ((info->cursor.image.dx != (vc->vc_font.width * vc->vc_x)) ||
- (info->cursor.image.dy != (vc->vc_font.height * y))) {
- cursor.image.dx = vc->vc_font.width * vc->vc_x;
- cursor.image.dy = vc->vc_font.height * y;
- cursor.set |= FB_CUR_SETPOS;
- }
-
- if (info->cursor.image.height != vc->vc_font.height ||
- info->cursor.image.width != vc->vc_font.width) {
- cursor.image.height = vc->vc_font.height;
- cursor.image.width = vc->vc_font.width;
- cursor.set |= FB_CUR_SETSIZE;
- }
-
- if (info->cursor.hot.x || info->cursor.hot.y) {
- cursor.hot.x = cursor.hot.y = 0;
- cursor.set |= FB_CUR_SETHOT;
- }
-
- if ((cursor.set & FB_CUR_SETSIZE) ||
- ((vc->vc_cursor_type & 0x0f) != p->cursor_shape)
- || info->cursor.mask == NULL) {
- char *mask = kmalloc(w*vc->vc_font.height, GFP_ATOMIC);
- int cur_height, size, i = 0;
- u8 msk = 0xff;
-
- if (!mask)
- return;
-
- if (info->cursor.mask)
- kfree(info->cursor.mask);
- info->cursor.mask = mask;
- p->cursor_shape = vc->vc_cursor_type & 0x0f;
- cursor.set |= FB_CUR_SETSHAPE;
-
- switch (vc->vc_cursor_type & 0x0f) {
- case CUR_NONE:
- cur_height = 0;
- break;
- case CUR_UNDERLINE:
- cur_height = (vc->vc_font.height < 10) ? 1 : 2;
- break;
- case CUR_LOWER_THIRD:
- cur_height = vc->vc_font.height/3;
- break;
- case CUR_LOWER_HALF:
- cur_height = vc->vc_font.height >> 1;
- break;
- case CUR_TWO_THIRDS:
- cur_height = (vc->vc_font.height << 1)/3;
- break;
- case CUR_BLOCK:
- default:
- cur_height = vc->vc_font.height;
- break;
- }
- size = (vc->vc_font.height - cur_height) * w;
- while (size--)
- mask[i++] = ~msk;
- size = cur_height * w;
- while (size--)
- mask[i++] = msk;
- }
- info->cursor.rop = ROP_XOR;
- info->fbops->fb_cursor(info, &cursor);
- vbl_cursor_cnt = CURSOR_DRAW_DELAY;
- break;
- }
+ ops->cursor(vc, info, p, mode, get_color(vc, info, c, 1),
+ get_color(vc, info, c, 0));
+ vbl_cursor_cnt = CURSOR_DRAW_DELAY;
}
static int scrollback_phys_max = 0;
@@ -1264,10 +1054,29 @@ int update_var(int con, struct fb_info *info)
return 0;
}
+/*
+ * If no vc is existent yet, just set struct display
+ */
+static void fbcon_preset_disp(struct fb_info *info, int unit)
+{
+ struct display *p = &fb_display[unit];
+ struct display *t = &fb_display[fg_console];
+
+ info->var.xoffset = info->var.yoffset = p->yscroll = 0;
+ if (var_to_display(p, &info->var, info))
+ return;
+
+ p->fontdata = t->fontdata;
+ p->userfont = t->userfont;
+ if (p->userfont)
+ REFCOUNT(p->fontdata)++;
+}
+
static void fbcon_set_disp(struct fb_info *info, struct vc_data *vc)
{
struct display *p = &fb_display[vc->vc_num], *t;
struct vc_data **default_mode = vc->vc_display_fg;
+ struct vc_data *svc = *default_mode;
int display_fg = (*default_mode)->vc_num;
int rows, cols, charcnt = 256;
@@ -1282,7 +1091,6 @@ static void fbcon_set_disp(struct fb_info *info, struct vc_data *vc)
p->userfont = t->userfont;
if (p->userfont)
REFCOUNT(p->fontdata)++;
- con_copy_unimap(vc->vc_num, display_fg);
}
if (p->userfont)
charcnt = FNTCHARCNT(p->fontdata);
@@ -1296,6 +1104,12 @@ static void fbcon_set_disp(struct fb_info *info, struct vc_data *vc)
if (vc->vc_can_do_color)
vc->vc_complement_mask <<= 1;
}
+
+ if (!*svc->vc_uni_pagedir_loc)
+ con_set_default_unimap(display_fg);
+ if (!*vc->vc_uni_pagedir_loc)
+ con_copy_unimap(vc->vc_num, display_fg);
+
cols = info->var.xres / vc->vc_font.width;
rows = info->var.yres / vc->vc_font.height;
vc_resize(vc->vc_num, cols, rows);
@@ -1314,7 +1128,6 @@ static void fbcon_set_disp(struct fb_info *info, struct vc_data *vc)
}
}
}
- switch_screen(fg_console);
}
static __inline__ void ywrap_up(struct vc_data *vc, int count)
@@ -1357,18 +1170,19 @@ static __inline__ void ypan_up(struct vc_data *vc, int count)
{
struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
struct display *p = &fb_display[vc->vc_num];
-
+ struct fbcon_ops *ops = (struct fbcon_ops *) info->fbcon_par;
+
p->yscroll += count;
if (p->yscroll > p->vrows - vc->vc_rows) {
- accel_bmove(vc, info, p->vrows - vc->vc_rows,
- 0, 0, 0, vc->vc_rows, vc->vc_cols);
+ ops->bmove(vc, info, p->vrows - vc->vc_rows,
+ 0, 0, 0, vc->vc_rows, vc->vc_cols);
p->yscroll -= p->vrows - vc->vc_rows;
}
info->var.xoffset = 0;
info->var.yoffset = p->yscroll * vc->vc_font.height;
info->var.vmode &= ~FB_VMODE_YWRAP;
update_var(vc->vc_num, info);
- accel_clear_margins(vc, info, 1);
+ fbcon_clear_margins(vc, 1);
scrollback_max += count;
if (scrollback_max > scrollback_phys_max)
scrollback_max = scrollback_phys_max;
@@ -1393,7 +1207,7 @@ static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count)
if (redraw)
fbcon_redraw_move(vc, p, t + count, vc->vc_rows - count, t);
update_var(vc->vc_num, info);
- accel_clear_margins(vc, info, 1);
+ fbcon_clear_margins(vc, 1);
scrollback_max += count;
if (scrollback_max > scrollback_phys_max)
scrollback_max = scrollback_phys_max;
@@ -1404,18 +1218,19 @@ static __inline__ void ypan_down(struct vc_data *vc, int count)
{
struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
struct display *p = &fb_display[vc->vc_num];
+ struct fbcon_ops *ops = (struct fbcon_ops *) info->fbcon_par;
p->yscroll -= count;
if (p->yscroll < 0) {
- accel_bmove(vc, info, 0, 0, p->vrows - vc->vc_rows,
- 0, vc->vc_rows, vc->vc_cols);
+ ops->bmove(vc, info, 0, 0, p->vrows - vc->vc_rows,
+ 0, vc->vc_rows, vc->vc_cols);
p->yscroll += p->vrows - vc->vc_rows;
}
info->var.xoffset = 0;
info->var.yoffset = p->yscroll * vc->vc_font.height;
info->var.vmode &= ~FB_VMODE_YWRAP;
update_var(vc->vc_num, info);
- accel_clear_margins(vc, info, 1);
+ fbcon_clear_margins(vc, 1);
scrollback_max -= count;
if (scrollback_max < 0)
scrollback_max = 0;
@@ -1439,7 +1254,7 @@ static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count)
if (redraw)
fbcon_redraw_move(vc, p, t, vc->vc_rows - count, t + count);
update_var(vc->vc_num, info);
- accel_clear_margins(vc, info, 1);
+ fbcon_clear_margins(vc, 1);
scrollback_max -= count;
if (scrollback_max < 0)
scrollback_max = 0;
@@ -1449,7 +1264,6 @@ static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count)
static void fbcon_redraw_softback(struct vc_data *vc, struct display *p,
long delta)
{
- struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
int count = vc->vc_rows;
unsigned short *d, *s;
unsigned long n;
@@ -1506,16 +1320,16 @@ static void fbcon_redraw_softback(struct vc_data *vc, struct display *p,
if (attr != (c & 0xff00)) {
attr = c & 0xff00;
if (s > start) {
- accel_putcs(vc, info, start, s - start,
- real_y(p, line), x);
+ fbcon_putcs(vc, start, s - start,
+ line, x);
x += s - start;
start = s;
}
}
if (c == scr_readw(d)) {
if (s > start) {
- accel_putcs(vc, info, start, s - start,
- real_y(p, line), x);
+ fbcon_putcs(vc, start, s - start,
+ line, x);
x += s - start + 1;
start = s + 1;
} else {
@@ -1527,8 +1341,7 @@ static void fbcon_redraw_softback(struct vc_data *vc, struct display *p,
d++;
} while (s < le);
if (s > start)
- accel_putcs(vc, info, start, s - start,
- real_y(p, line), x);
+ fbcon_putcs(vc, start, s - start, line, x);
line++;
if (d == (u16 *) softback_end)
d = (u16 *) softback_buf;
@@ -1544,7 +1357,6 @@ static void fbcon_redraw_softback(struct vc_data *vc, struct display *p,
static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
int line, int count, int dy)
{
- struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
unsigned short *s = (unsigned short *)
(vc->vc_origin + vc->vc_size_row * line);
@@ -1560,8 +1372,8 @@ static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
if (attr != (c & 0xff00)) {
attr = c & 0xff00;
if (s > start) {
- accel_putcs(vc, info, start, s - start,
- real_y(p, dy), x);
+ fbcon_putcs(vc, start, s - start,
+ dy, x);
x += s - start;
start = s;
}
@@ -1570,8 +1382,7 @@ static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
s++;
} while (s < le);
if (s > start)
- accel_putcs(vc, info, start, s - start,
- real_y(p, dy), x);
+ fbcon_putcs(vc, start, s - start, dy, x);
console_conditional_schedule();
dy++;
}
@@ -1582,7 +1393,6 @@ static void fbcon_redraw(struct vc_data *vc, struct display *p,
{
unsigned short *d = (unsigned short *)
(vc->vc_origin + vc->vc_size_row * line);
- struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
unsigned short *s = d + offset;
while (count--) {
@@ -1597,16 +1407,16 @@ static void fbcon_redraw(struct vc_data *vc, struct display *p,
if (attr != (c & 0xff00)) {
attr = c & 0xff00;
if (s > start) {
- accel_putcs(vc, info, start, s - start,
- real_y(p, line), x);
+ fbcon_putcs(vc, start, s - start,
+ line, x);
x += s - start;
start = s;
}
}
if (c == scr_readw(d)) {
if (s > start) {
- accel_putcs(vc, info, start, s - start,
- real_y(p, line), x);
+ fbcon_putcs(vc, start, s - start,
+ line, x);
x += s - start + 1;
start = s + 1;
} else {
@@ -1620,8 +1430,7 @@ static void fbcon_redraw(struct vc_data *vc, struct display *p,
d++;
} while (s < le);
if (s > start)
- accel_putcs(vc, info, start, s - start,
- real_y(p, line), x);
+ fbcon_putcs(vc, start, s - start, line, x);
console_conditional_schedule();
if (offset > 0)
line++;
@@ -1664,6 +1473,7 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
{
struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
struct display *p = &fb_display[vc->vc_num];
+ struct fbcon_ops *ops = (struct fbcon_ops *) info->fbcon_par;
int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK;
if (!info->fbops->fb_blank && console_blanked)
@@ -1690,10 +1500,10 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
goto redraw_up;
switch (p->scrollmode) {
case SCROLL_MOVE:
- accel_bmove(vc, info, t + count, 0, t, 0,
- b - t - count, vc->vc_cols);
- accel_clear(vc, info, b - count, 0, count,
- vc->vc_cols);
+ ops->bmove(vc, info, t + count, 0, t, 0,
+ b - t - count, vc->vc_cols);
+ ops->clear(vc, info, b - count, 0, count,
+ vc->vc_cols);
break;
case SCROLL_WRAP_MOVE:
@@ -1759,8 +1569,7 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
redraw_up:
fbcon_redraw(vc, p, t, b - t - count,
count * vc->vc_cols);
- accel_clear(vc, info, real_y(p, b - count), 0,
- count, vc->vc_cols);
+ fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
scr_memsetw((unsigned short *) (vc->vc_origin +
vc->vc_size_row *
(b - count)),
@@ -1775,9 +1584,9 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
count = vc->vc_rows;
switch (p->scrollmode) {
case SCROLL_MOVE:
- accel_bmove(vc, info, t, 0, t + count, 0,
- b - t - count, vc->vc_cols);
- accel_clear(vc, info, t, 0, count, vc->vc_cols);
+ ops->bmove(vc, info, t, 0, t + count, 0,
+ b - t - count, vc->vc_cols);
+ ops->clear(vc, info, t, 0, count, vc->vc_cols);
break;
case SCROLL_WRAP_MOVE:
@@ -1841,8 +1650,7 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
redraw_down:
fbcon_redraw(vc, p, b - 1, b - t - count,
-count * vc->vc_cols);
- accel_clear(vc, info, real_y(p, t), 0, count,
- vc->vc_cols);
+ fbcon_clear(vc, t, 0, count, vc->vc_cols);
scr_memsetw((unsigned short *) (vc->vc_origin +
vc->vc_size_row *
t),
@@ -1882,6 +1690,7 @@ static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int s
int dy, int dx, int height, int width, u_int y_break)
{
struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
+ struct fbcon_ops *ops = (struct fbcon_ops *) info->fbcon_par;
u_int b;
if (sy < y_break && sy + height > y_break) {
@@ -1915,8 +1724,8 @@ static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int s
}
return;
}
- accel_bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx,
- height, width);
+ ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx,
+ height, width);
}
static __inline__ void updatescrollmode(struct display *p, struct fb_info *info,
@@ -2010,11 +1819,12 @@ static int fbcon_resize(struct vc_data *vc, unsigned int width,
static int fbcon_switch(struct vc_data *vc)
{
- struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
+ struct fb_info *info;
struct display *p = &fb_display[vc->vc_num];
struct fb_var_screeninfo var;
- int i;
+ int i, prev_console, do_set_par = 0;
+ info = registered_fb[(int) con2fb_map[vc->vc_num]];
if (softback_top) {
int l = fbcon_softback_size / vc->vc_size_row;
if (softback_lines)
@@ -2039,6 +1849,8 @@ static int fbcon_switch(struct vc_data *vc)
logo_shown = -1;
}
+ prev_console = info->currcon;
+
/*
* FIXME: If we have multiple fbdev's loaded, we need to
* update all info->currcon. Perhaps, we can place this
@@ -2053,7 +1865,6 @@ static int fbcon_switch(struct vc_data *vc)
}
memset(&var, 0, sizeof(struct fb_var_screeninfo));
- fb_videomode_to_var(&var, p->mode);
display_to_var(&var, p);
var.activate = FB_ACTIVATE_NOW;
@@ -2065,12 +1876,18 @@ static int fbcon_switch(struct vc_data *vc)
info->var.yoffset = info->var.xoffset = p->yscroll = 0;
fb_set_var(info, &var);
- if (info->flags & FBINFO_MISC_MODESWITCH) {
+ if (prev_console != -1 &&
+ registered_fb[(int) con2fb_map[prev_console]] != info)
+ do_set_par = 1;
+
+ if (do_set_par || info->flags & FBINFO_MISC_MODESWITCH) {
if (info->fbops->fb_set_par)
info->fbops->fb_set_par(info);
info->flags &= ~FBINFO_MISC_MODESWITCH;
}
+ set_blitting_type(vc, info, p);
+
vc->vc_can_do_color = (fb_get_color_depth(info) != 1);
vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
updatescrollmode(p, info, vc);
@@ -2096,8 +1913,9 @@ static int fbcon_switch(struct vc_data *vc)
fbcon_set_palette(vc, color_table);
if (vt_cons[vc->vc_num]->vc_mode == KD_TEXT)
- accel_clear_margins(vc, info, 0);
+ fbcon_clear_margins(vc, 0);
if (logo_shown == -2) {
+
logo_shown = fg_console;
/* This is protected above by initmem_freed */
fb_show_logo(info);
@@ -2143,6 +1961,7 @@ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
}
fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW);
+ info->cursor.flash = (!blank);
if (!info->fbops->fb_blank) {
if (blank) {
@@ -2155,14 +1974,11 @@ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
height = vc->vc_rows;
y_break = p->vrows - p->yscroll;
if (height > y_break) {
- accel_clear(vc, info, real_y(p, 0),
- 0, y_break, vc->vc_cols);
- accel_clear(vc, info, real_y(p, y_break),
- 0, height - y_break,
+ fbcon_clear(vc, 0, 0, y_break, vc->vc_cols);
+ fbcon_clear(vc, y_break, 0, height - y_break,
vc->vc_cols);
} else
- accel_clear(vc, info, real_y(p, 0),
- 0, height, vc->vc_cols);
+ fbcon_clear(vc, 0, 0, height, vc->vc_cols);
vc->vc_video_erase_char = oldc;
} else
update_screen(vc->vc_num);
@@ -2329,7 +2145,7 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h,
}
} else if (CON_IS_VISIBLE(vc)
&& vt_cons[vc->vc_num]->vc_mode == KD_TEXT) {
- accel_clear_margins(vc, info, 0);
+ fbcon_clear_margins(vc, 0);
update_screen(vc->vc_num);
}
@@ -2672,9 +2488,14 @@ static int fbcon_set_origin(struct vc_data *vc)
static void fbcon_suspended(struct fb_info *info)
{
+ struct vc_data *vc = NULL;
+
+ if (info->currcon < 0)
+ return;
+ vc = vc_cons[info->currcon].d;
+
/* Clear cursor, restore saved data */
- info->cursor.enable = 0;
- info->fbops->fb_cursor(info, &info->cursor);
+ fbcon_cursor(vc, CM_ERASE);
}
static void fbcon_resumed(struct fb_info *info)
@@ -2753,11 +2574,22 @@ static int fbcon_mode_deleted(struct fb_info *info,
static int fbcon_fb_registered(int idx)
{
- int ret = 0;
+ int ret = 0, i;
if (info_idx == -1) {
- info_idx = idx;
- ret = fbcon_takeover();
+ for (i = 0; i < MAX_NR_CONSOLES; i++) {
+ if (con2fb_map_boot[i] == idx) {
+ info_idx = idx;
+ break;
+ }
+ }
+ if (info_idx != -1)
+ ret = fbcon_takeover(1);
+ } else {
+ for (i = 0; i < MAX_NR_CONSOLES; i++) {
+ if (con2fb_map_boot[i] == idx)
+ set_con2fb_map(i, idx, 0);
+ }
}
return ret;
@@ -2791,7 +2623,8 @@ static int fbcon_event_notify(struct notifier_block *self,
break;
case FB_EVENT_SET_CONSOLE_MAP:
con2fb = (struct fb_con2fbmap *) event->data;
- ret = set_con2fb_map(con2fb->console - 1, con2fb->framebuffer);
+ ret = set_con2fb_map(con2fb->console - 1,
+ con2fb->framebuffer, 1);
break;
case FB_EVENT_GET_CONSOLE_MAP:
con2fb = (struct fb_con2fbmap *) event->data;
@@ -2854,7 +2687,7 @@ int __init fb_console_init(void)
break;
}
}
- fbcon_takeover();
+ fbcon_takeover(0);
}
return 0;
diff --git a/drivers/video/console/fbcon.h b/drivers/video/console/fbcon.h
index a89001a40856..2b8d413772d9 100644
--- a/drivers/video/console/fbcon.h
+++ b/drivers/video/console/fbcon.h
@@ -48,6 +48,19 @@ struct display {
struct fb_videomode *mode;
};
+struct fbcon_ops {
+ void (*bmove)(struct vc_data *vc, struct fb_info *info, int sy,
+ int sx, int dy, int dx, int height, int width);
+ void (*clear)(struct vc_data *vc, struct fb_info *info, int sy,
+ int sx, int height, int width);
+ void (*putcs)(struct vc_data *vc, struct fb_info *info,
+ const unsigned short *s, int count, int yy, int xx,
+ int fg, int bg);
+ void (*clear_margins)(struct vc_data *vc, struct fb_info *info,
+ int bottom_only);
+ void (*cursor)(struct vc_data *vc, struct fb_info *info,
+ struct display *p, int mode, int fg, int bg);
+};
/*
* Attribute Decoding
*/
@@ -72,6 +85,13 @@ struct display {
#define attr_blink(s) \
((s) & 0x8000)
+/* Font */
+#define REFCOUNT(fd) (((int *)(fd))[-1])
+#define FNTSIZE(fd) (((int *)(fd))[-2])
+#define FNTCHARCNT(fd) (((int *)(fd))[-3])
+#define FNTSUM(fd) (((int *)(fd))[-4])
+#define FONT_EXTRA_WORDS 4
+
/*
* Scroll Method
*/
@@ -129,5 +149,9 @@ struct display {
#define SCROLL_PAN_REDRAW 0x005
extern int fb_console_init(void);
-
+#ifdef CONFIG_FB_TILEBLITTING
+extern void fbcon_set_tileops(struct vc_data *vc, struct fb_info *info,
+ struct display *p, struct fbcon_ops *ops);
+#endif
+extern void fbcon_set_bitops(struct fbcon_ops *ops);
#endif /* _VIDEO_FBCON_H */
diff --git a/drivers/video/console/tileblit.c b/drivers/video/console/tileblit.c
new file mode 100644
index 000000000000..93927180b298
--- /dev/null
+++ b/drivers/video/console/tileblit.c
@@ -0,0 +1,146 @@
+/*
+ * linux/drivers/video/console/tileblit.c -- Tile Blitting Operation
+ *
+ * Copyright (C) 2004 Antonino Daplas <adaplas @pol.net>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+#include <linux/vt_kern.h>
+#include <linux/console.h>
+#include <asm/types.h>
+#include "fbcon.h"
+
+static void tile_bmove(struct vc_data *vc, struct fb_info *info, int sy,
+ int sx, int dy, int dx, int height, int width)
+{
+ struct fb_tilearea area;
+
+ area.sx = sx;
+ area.sy = sy;
+ area.dx = dx;
+ area.dy = dy;
+ area.height = height;
+ area.width = width;
+
+ info->tileops->fb_tilecopy(info, &area);
+}
+
+static void tile_clear(struct vc_data *vc, struct fb_info *info, int sy,
+ int sx, int height, int width)
+{
+ struct fb_tilerect rect;
+ int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
+ int fgshift = (vc->vc_hi_font_mask) ? 9 : 8;
+
+ rect.index = vc->vc_video_erase_char &
+ ((vc->vc_hi_font_mask) ? 0x1ff : 0xff);
+ rect.fg = attr_fgcol_ec(fgshift, vc);
+ rect.bg = attr_bgcol_ec(bgshift, vc);
+ rect.sx = sx;
+ rect.sy = sy;
+ rect.width = width;
+ rect.height = height;
+ rect.rop = ROP_COPY;
+
+ info->tileops->fb_tilefill(info, &rect);
+}
+
+static void tile_putcs(struct vc_data *vc, struct fb_info *info,
+ const unsigned short *s, int count, int yy, int xx,
+ int fg, int bg)
+{
+ struct fb_tileblit blit;
+ unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
+ int size = sizeof(u32) * count, i;
+
+ blit.sx = xx;
+ blit.sy = yy;
+ blit.width = count;
+ blit.height = 1;
+ blit.fg = fg;
+ blit.bg = bg;
+ blit.length = count;
+ blit.indices = (u32 *) fb_get_buffer_offset(info, &info->pixmap, size);
+ for (i = 0; i < count; i++)
+ blit.indices[i] = (u32)(scr_readw(s++) & charmask);
+
+ info->tileops->fb_tileblit(info, &blit);
+}
+
+static void tile_clear_margins(struct vc_data *vc, struct fb_info *info,
+ int bottom_only)
+{
+ return;
+}
+
+static void tile_cursor(struct vc_data *vc, struct fb_info *info,
+ struct display *p, int mode, int fg, int bg)
+{
+ struct fb_tilecursor cursor;
+
+ cursor.sx = vc->vc_x;
+ cursor.sy = vc->vc_y;
+ cursor.mode = (mode == CM_ERASE) ? 0 : 1;
+ cursor.fg = fg;
+ cursor.bg = bg;
+
+ switch (vc->vc_cursor_type & 0x0f) {
+ case CUR_NONE:
+ cursor.shape = FB_TILE_CURSOR_NONE;
+ break;
+ case CUR_UNDERLINE:
+ cursor.shape = FB_TILE_CURSOR_UNDERLINE;
+ break;
+ case CUR_LOWER_THIRD:
+ cursor.shape = FB_TILE_CURSOR_LOWER_THIRD;
+ break;
+ case CUR_LOWER_HALF:
+ cursor.shape = FB_TILE_CURSOR_LOWER_HALF;
+ break;
+ case CUR_TWO_THIRDS:
+ cursor.shape = FB_TILE_CURSOR_TWO_THIRDS;
+ break;
+ case CUR_BLOCK:
+ default:
+ cursor.shape = FB_TILE_CURSOR_BLOCK;
+ break;
+ }
+
+ info->tileops->fb_tilecursor(info, &cursor);
+}
+
+void fbcon_set_tileops(struct vc_data *vc, struct fb_info *info,
+ struct display *p, struct fbcon_ops *ops)
+{
+ struct fb_tilemap map;
+
+ ops->bmove = tile_bmove;
+ ops->clear = tile_clear;
+ ops->putcs = tile_putcs;
+ ops->clear_margins = tile_clear_margins;
+ ops->cursor = tile_cursor;
+
+ if (p) {
+ map.width = vc->vc_font.width;
+ map.height = vc->vc_font.height;
+ map.depth = 1;
+ map.length = (p->userfont) ?
+ FNTCHARCNT(p->fontdata) : 256;
+ map.data = p->fontdata;
+ info->tileops->fb_settile(info, &map);
+ }
+}
+
+EXPORT_SYMBOL(fbcon_set_tileops);
+
+MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
+MODULE_DESCRIPTION("Tile Blitting Operation");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c
index 0e91d6b577e4..61181b2d7a19 100644
--- a/drivers/video/console/vgacon.c
+++ b/drivers/video/console/vgacon.c
@@ -901,8 +901,10 @@ static int vgacon_adjust_height(struct vc_data *vc, unsigned fontheight)
for (i = 0; i < MAX_NR_CONSOLES; i++) {
struct vc_data *c = vc_cons[i].d;
- if (c && c->vc_sw == &vga_con)
+ if (c && c->vc_sw == &vga_con) {
+ c->vc_font.height = fontheight;
vc_resize(c->vc_num, 0, rows); /* Adjust console size */
+ }
}
return 0;
}
diff --git a/drivers/video/cyber2000fb.c b/drivers/video/cyber2000fb.c
index 4dca34fdf767..0e3ab898b1e8 100644
--- a/drivers/video/cyber2000fb.c
+++ b/drivers/video/cyber2000fb.c
@@ -1399,6 +1399,8 @@ static int __devinit cyberpro_common_probe(struct cfb_info *cfb)
cfb->fb.var.xres, cfb->fb.var.yres,
h_sync / 1000, h_sync % 1000, v_sync);
+ if (cfb->dev)
+ cfb->fb.device = &cfb->dev->dev;
err = register_framebuffer(&cfb->fb);
failed:
@@ -1722,7 +1724,7 @@ int __init cyber2000fb_init(void)
#ifndef MODULE
char *option = NULL;
- if (fb_get_options("cyber2000fb", NULL))
+ if (fb_get_options("cyber2000fb", &option))
return -ENODEV;
cyber2000fb_setup(option);
#endif
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index ef5cea5a0fb4..c21b42e4b09a 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -368,6 +368,9 @@ int fb_prepare_logo(struct fb_info *info)
memset(&fb_logo, 0, sizeof(struct logo_data));
+ if (info->flags & FBINFO_MISC_TILEBLITTING)
+ return 0;
+
if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
depth = info->var.blue.length;
if (info->var.red.length < depth)
@@ -504,7 +507,8 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
struct inode *inode = file->f_dentry->d_inode;
int fbidx = iminor(inode);
struct fb_info *info = registered_fb[fbidx];
- u32 *buffer, *dst, *src;
+ u32 *buffer, *dst;
+ u32 __iomem *src;
int c, i, cnt = 0, err = 0;
unsigned long total_size;
@@ -534,7 +538,7 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
if (!buffer)
return -ENOMEM;
- src = (u32 *) (info->screen_base + p);
+ src = (u32 __iomem *) (info->screen_base + p);
if (info->fbops->fb_sync)
info->fbops->fb_sync(info);
@@ -546,12 +550,12 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
*dst++ = fb_readl(src++);
if (c & 3) {
u8 *dst8 = (u8 *) dst;
- u8 *src8 = (u8 *) src;
+ u8 __iomem *src8 = (u8 __iomem *) src;
for (i = c & 3; i--;)
*dst8++ = fb_readb(src8++);
- src = (u32 *) src8;
+ src = (u32 __iomem *) src8;
}
if (copy_to_user(buf, buffer, c)) {
@@ -575,7 +579,8 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
struct inode *inode = file->f_dentry->d_inode;
int fbidx = iminor(inode);
struct fb_info *info = registered_fb[fbidx];
- u32 *buffer, *dst, *src;
+ u32 *buffer, *src;
+ u32 __iomem *dst;
int c, i, cnt = 0, err;
unsigned long total_size;
@@ -607,7 +612,7 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
if (!buffer)
return -ENOMEM;
- dst = (u32 *) (info->screen_base + p);
+ dst = (u32 __iomem *) (info->screen_base + p);
if (info->fbops->fb_sync)
info->fbops->fb_sync(info);
@@ -623,12 +628,12 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
fb_writel(*src++, dst++);
if (c & 3) {
u8 *src8 = (u8 *) src;
- u8 *dst8 = (u8 *) dst;
+ u8 __iomem *dst8 = (u8 __iomem *) dst;
for (i = c & 3; i--; )
fb_writeb(*src8++, dst8++);
- dst = (u32 *) dst8;
+ dst = (u32 __iomem *) dst8;
}
*ppos += c;
buf += c;
@@ -654,7 +659,8 @@ fb_load_cursor_image(struct fb_info *info)
u8 *data = (u8 *) info->cursor.image.data;
if (info->sprite.outbuf)
- info->sprite.outbuf(info, info->sprite.addr, data, width);
+ info->sprite.outbuf(info, info->sprite.addr, data,
+ width);
else
memcpy(info->sprite.addr, data, width);
}
@@ -679,6 +685,7 @@ fb_cursor(struct fb_info *info, struct fb_cursor_user __user *sprite)
cursor.image.cmap.blue = info->cursor.image.cmap.blue;
cursor.image.cmap.transp = info->cursor.image.cmap.transp;
cursor.data = NULL;
+ cursor.flash = 0;
if (cursor.set & FB_CUR_SETCUR)
info->cursor.enable = 1;
@@ -847,20 +854,28 @@ int
fb_blank(struct fb_info *info, int blank)
{
/* ??? Variable sized stack allocation. */
- u16 black[info->cmap.len];
struct fb_cmap cmap;
+ u16 *black = NULL;
+ int err = 0;
if (info->fbops->fb_blank && !info->fbops->fb_blank(blank, info))
return 0;
if (blank) {
- memset(black, 0, info->cmap.len * sizeof(u16));
- cmap.red = cmap.green = cmap.blue = black;
- cmap.transp = info->cmap.transp ? black : NULL;
- cmap.start = info->cmap.start;
- cmap.len = info->cmap.len;
+ black = kmalloc(sizeof(u16) * info->cmap.len, GFP_KERNEL);
+ if (!black) {
+ memset(black, 0, info->cmap.len * sizeof(u16));
+ cmap.red = cmap.green = cmap.blue = black;
+ cmap.transp = info->cmap.transp ? black : NULL;
+ cmap.start = info->cmap.start;
+ cmap.len = info->cmap.len;
+ }
} else
cmap = info->cmap;
- return fb_set_cmap(&cmap, info);
+
+ err = fb_set_cmap(&cmap, info);
+ kfree(black);
+
+ return err;
}
static int
@@ -948,14 +963,11 @@ fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
#endif /* CONFIG_KMOD */
if (!registered_fb[con2fb.framebuffer])
return -EINVAL;
- if (con2fb.console > 0 && con2fb.console < MAX_NR_CONSOLES) {
- event.info = info;
- event.data = &con2fb;
- return notifier_call_chain(&fb_notifier_list,
- FB_EVENT_SET_CONSOLE_MAP,
- &event);
- }
- return -EINVAL;
+ event.info = info;
+ event.data = &con2fb;
+ return notifier_call_chain(&fb_notifier_list,
+ FB_EVENT_SET_CONSOLE_MAP,
+ &event);
case FBIOBLANK:
acquire_console_sem();
i = fb_blank(info, arg);
@@ -1144,7 +1156,8 @@ register_framebuffer(struct fb_info *fb_info)
break;
fb_info->node = i;
- c = class_simple_device_add(fb_class, MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
+ c = class_simple_device_add(fb_class, MKDEV(FB_MAJOR, i),
+ fb_info->device, "fb%d", i);
if (IS_ERR(c)) {
/* Not fatal */
printk(KERN_WARNING "Unable to create class_device for framebuffer %d; errno = %ld\n", i, PTR_ERR(c));
diff --git a/drivers/video/fbsysfs.c b/drivers/video/fbsysfs.c
index 8b3bbe0c641b..4fec33dc8e31 100644
--- a/drivers/video/fbsysfs.c
+++ b/drivers/video/fbsysfs.c
@@ -51,6 +51,8 @@ struct fb_info *framebuffer_alloc(size_t size, struct device *dev)
if (size)
info->par = p + fb_info_size;
+ info->device = dev;
+
return info;
#undef PADDING
#undef BYTES_PER_LONG
diff --git a/drivers/video/fm2fb.c b/drivers/video/fm2fb.c
index 3adb65df1fac..cda492a55f61 100644
--- a/drivers/video/fm2fb.c
+++ b/drivers/video/fm2fb.c
@@ -292,18 +292,7 @@ static int __devinit fm2fb_probe(struct zorro_dev *z,
return 0;
}
-int __init fm2fb_setup(char *options);
-
-int __init fm2fb_init(void)
-{
- char *option = NULL;
-
- if (fb_get_options("fm2fb", &option))
- return -ENODEV;
- fm2fb_setup(option);
- return zorro_register_driver(&fm2fb_driver);
-}
-
+int __init fm2fb_setup(char *options)
{
char *this_opt;
@@ -319,5 +308,15 @@ int __init fm2fb_init(void)
return 0;
}
+int __init fm2fb_init(void)
+{
+ char *option = NULL;
+
+ if (fb_get_options("fm2fb", &option))
+ return -ENODEV;
+ fm2fb_setup(option);
+ return zorro_register_driver(&fm2fb_driver);
+}
+
module_init(fm2fb_init);
MODULE_LICENSE("GPL");
diff --git a/drivers/video/i810/i810.h b/drivers/video/i810/i810.h
index 68b64233a1aa..4df3b7766ff5 100644
--- a/drivers/video/i810/i810.h
+++ b/drivers/video/i810/i810.h
@@ -222,7 +222,7 @@ struct mode_registers {
struct heap_data {
unsigned long physical;
- __u8 *virtual;
+ __u8 __iomem *virtual;
u32 offset;
u32 size;
};
@@ -256,7 +256,7 @@ struct i810fb_par {
u32 pseudo_palette[17];
u32 pci_state[16];
unsigned long mmio_start_phys;
- u8 *mmio_start_virtual;
+ u8 __iomem *mmio_start_virtual;
u32 pitch;
u32 pixconf;
u32 watermark;
diff --git a/drivers/video/i810/i810_accel.c b/drivers/video/i810/i810_accel.c
index 71f649f55592..9921db8a48c7 100644
--- a/drivers/video/i810/i810_accel.c
+++ b/drivers/video/i810/i810_accel.c
@@ -32,7 +32,7 @@ extern void flush_cache(void);
/************************************************************/
/* BLT Engine Routines */
-static inline void i810_report_error(u8 *mmio)
+static inline void i810_report_error(u8 __iomem *mmio)
{
printk("IIR : 0x%04x\n"
"EIR : 0x%04x\n"
@@ -59,7 +59,7 @@ static inline int wait_for_space(struct fb_info *info, u32 space)
{
struct i810fb_par *par = (struct i810fb_par *) info->par;
u32 head, count = WAIT_COUNT, tail;
- u8 *mmio = par->mmio_start_virtual;
+ u8 __iomem *mmio = par->mmio_start_virtual;
tail = par->cur_tail;
while (count--) {
@@ -89,7 +89,7 @@ static inline int wait_for_space(struct fb_info *info, u32 space)
static inline int wait_for_engine_idle(struct fb_info *info)
{
struct i810fb_par *par = (struct i810fb_par *) info->par;
- u8 *mmio = par->mmio_start_virtual;
+ u8 __iomem *mmio = par->mmio_start_virtual;
int count = WAIT_COUNT;
if (wait_for_space(info, par->iring.size)) /* flush */
@@ -133,7 +133,7 @@ static inline u32 begin_iring(struct fb_info *info, u32 space)
*/
static inline void end_iring(struct i810fb_par *par)
{
- u8 *mmio = par->mmio_start_virtual;
+ u8 __iomem *mmio = par->mmio_start_virtual;
i810_writel(IRING, mmio, par->cur_tail);
}
@@ -326,7 +326,7 @@ static inline void load_front(int offset, struct fb_info *info)
static inline void i810fb_iring_enable(struct i810fb_par *par, u32 mode)
{
u32 tmp;
- u8 *mmio = par->mmio_start_virtual;
+ u8 __iomem *mmio = par->mmio_start_virtual;
tmp = i810_readl(IRING + 12, mmio);
if (mode == OFF)
@@ -451,7 +451,7 @@ int i810fb_sync(struct fb_info *info)
void i810fb_load_front(u32 offset, struct fb_info *info)
{
struct i810fb_par *par = (struct i810fb_par *) info->par;
- u8 *mmio = par->mmio_start_virtual;
+ u8 __iomem *mmio = par->mmio_start_virtual;
if (!info->var.accel_flags || par->dev_flags & LOCKUP)
i810_writel(DPLYBASE, mmio, par->fb.physical + offset);
@@ -472,7 +472,7 @@ void i810fb_init_ringbuffer(struct fb_info *info)
{
struct i810fb_par *par = (struct i810fb_par *) info->par;
u32 tmp1, tmp2;
- u8 *mmio = par->mmio_start_virtual;
+ u8 __iomem *mmio = par->mmio_start_virtual;
wait_for_engine_idle(info);
i810fb_iring_enable(par, OFF);
diff --git a/drivers/video/i810/i810_gtf.c b/drivers/video/i810/i810_gtf.c
index 7d0c02ca0a42..814698b90fb6 100644
--- a/drivers/video/i810/i810_gtf.c
+++ b/drivers/video/i810/i810_gtf.c
@@ -124,7 +124,8 @@ void i810fb_encode_registers(const struct fb_var_screeninfo *var,
struct i810fb_par *par, u32 xres, u32 yres)
{
int n, blank_s, blank_e;
- u8 *mmio = par->mmio_start_virtual, msr = 0;
+ u8 __iomem *mmio = par->mmio_start_virtual;
+ u8 msr = 0;
/* Horizontal */
/* htotal */
diff --git a/drivers/video/i810/i810_main.c b/drivers/video/i810/i810_main.c
index 6acde2569975..144c82785f59 100644
--- a/drivers/video/i810/i810_main.c
+++ b/drivers/video/i810/i810_main.c
@@ -121,7 +121,7 @@ static int dcolor __initdata = 0;
* DESCRIPTION:
* Blanks/unblanks the display
*/
-static void i810_screen_off(u8 *mmio, u8 mode)
+static void i810_screen_off(u8 __iomem *mmio, u8 mode)
{
u32 count = WAIT_COUNT;
u8 val;
@@ -145,7 +145,7 @@ static void i810_screen_off(u8 *mmio, u8 mode)
* Turns off DRAM refresh. Must be off for only 2 vsyncs
* before data becomes corrupt
*/
-static void i810_dram_off(u8 *mmio, u8 mode)
+static void i810_dram_off(u8 __iomem *mmio, u8 mode)
{
u8 val;
@@ -164,7 +164,7 @@ static void i810_dram_off(u8 *mmio, u8 mode)
* The IBM VGA standard allows protection of certain VGA registers.
* This will protect or unprotect them.
*/
-static void i810_protect_regs(u8 *mmio, int mode)
+static void i810_protect_regs(u8 __iomem *mmio, int mode)
{
u8 reg;
@@ -187,7 +187,7 @@ static void i810_protect_regs(u8 *mmio, int mode)
static void i810_load_pll(struct i810fb_par *par)
{
u32 tmp1, tmp2;
- u8 *mmio = par->mmio_start_virtual;
+ u8 __iomem *mmio = par->mmio_start_virtual;
tmp1 = par->regs.M | par->regs.N << 16;
tmp2 = i810_readl(DCLK_2D, mmio);
@@ -212,7 +212,7 @@ static void i810_load_pll(struct i810fb_par *par)
*/
static void i810_load_vga(struct i810fb_par *par)
{
- u8 *mmio = par->mmio_start_virtual;
+ u8 __iomem *mmio = par->mmio_start_virtual;
/* interlace */
i810_writeb(CR_INDEX_CGA, mmio, CR70);
@@ -255,7 +255,7 @@ static void i810_load_vga(struct i810fb_par *par)
*/
static void i810_load_vgax(struct i810fb_par *par)
{
- u8 *mmio = par->mmio_start_virtual;
+ u8 __iomem *mmio = par->mmio_start_virtual;
i810_writeb(CR_INDEX_CGA, mmio, CR30);
i810_writeb(CR_DATA_CGA, mmio, par->regs.cr30);
@@ -281,7 +281,8 @@ static void i810_load_vgax(struct i810fb_par *par)
static void i810_load_2d(struct i810fb_par *par)
{
u32 tmp;
- u8 tmp8, *mmio = par->mmio_start_virtual;
+ u8 tmp8;
+ u8 __iomem *mmio = par->mmio_start_virtual;
i810_writel(FW_BLC, mmio, par->watermark);
tmp = i810_readl(PIXCONF, mmio);
@@ -301,7 +302,7 @@ static void i810_load_2d(struct i810fb_par *par)
* i810_hires - enables high resolution mode
* @mmio: address of register space
*/
-static void i810_hires(u8 *mmio)
+static void i810_hires(u8 __iomem *mmio)
{
u8 val;
@@ -321,7 +322,8 @@ static void i810_hires(u8 *mmio)
static void i810_load_pitch(struct i810fb_par *par)
{
u32 tmp, pitch;
- u8 val, *mmio = par->mmio_start_virtual;
+ u8 val;
+ u8 __iomem *mmio = par->mmio_start_virtual;
pitch = par->pitch >> 3;
i810_writeb(SR_INDEX, mmio, SR01);
@@ -351,9 +353,10 @@ static void i810_load_pitch(struct i810fb_par *par)
*/
static void i810_load_color(struct i810fb_par *par)
{
- u8 *mmio = par->mmio_start_virtual;
+ u8 __iomem *mmio = par->mmio_start_virtual;
u32 reg1;
u16 reg2;
+
reg1 = i810_readl(PIXCONF, mmio) & ~(0xF0000 | 1 << 27);
reg2 = i810_readw(BLTCNTL, mmio) & ~0x30;
@@ -372,7 +375,7 @@ static void i810_load_color(struct i810fb_par *par)
*/
static void i810_load_regs(struct i810fb_par *par)
{
- u8 *mmio = par->mmio_start_virtual;
+ u8 __iomem *mmio = par->mmio_start_virtual;
i810_screen_off(mmio, OFF);
i810_protect_regs(mmio, OFF);
@@ -390,7 +393,7 @@ static void i810_load_regs(struct i810fb_par *par)
}
static void i810_write_dac(u8 regno, u8 red, u8 green, u8 blue,
- u8 *mmio)
+ u8 __iomem *mmio)
{
i810_writeb(CLUT_INDEX_WRITE, mmio, regno);
i810_writeb(CLUT_DATA, mmio, red);
@@ -399,7 +402,7 @@ static void i810_write_dac(u8 regno, u8 red, u8 green, u8 blue,
}
static void i810_read_dac(u8 regno, u8 *red, u8 *green, u8 *blue,
- u8 *mmio)
+ u8 __iomem *mmio)
{
i810_writeb(CLUT_INDEX_READ, mmio, regno);
*red = i810_readb(CLUT_DATA, mmio);
@@ -413,7 +416,7 @@ static void i810_read_dac(u8 regno, u8 *red, u8 *green, u8 *blue,
static void i810_restore_pll(struct i810fb_par *par)
{
u32 tmp1, tmp2;
- u8 *mmio = par->mmio_start_virtual;
+ u8 __iomem *mmio = par->mmio_start_virtual;
tmp1 = par->hw_state.dclk_2d;
tmp2 = i810_readl(DCLK_2D, mmio);
@@ -433,7 +436,7 @@ static void i810_restore_pll(struct i810fb_par *par)
static void i810_restore_dac(struct i810fb_par *par)
{
u32 tmp1, tmp2;
- u8 *mmio = par->mmio_start_virtual;
+ u8 __iomem *mmio = par->mmio_start_virtual;
tmp1 = par->hw_state.pixconf;
tmp2 = i810_readl(PIXCONF, mmio);
@@ -444,7 +447,8 @@ static void i810_restore_dac(struct i810fb_par *par)
static void i810_restore_vgax(struct i810fb_par *par)
{
- u8 i, j, *mmio = par->mmio_start_virtual;
+ u8 i, j;
+ u8 __iomem *mmio = par->mmio_start_virtual;
for (i = 0; i < 4; i++) {
i810_writeb(CR_INDEX_CGA, mmio, CR30+i);
@@ -477,7 +481,8 @@ static void i810_restore_vgax(struct i810fb_par *par)
static void i810_restore_vga(struct i810fb_par *par)
{
- u8 i, *mmio = par->mmio_start_virtual;
+ u8 i;
+ u8 __iomem *mmio = par->mmio_start_virtual;
for (i = 0; i < 10; i++) {
i810_writeb(CR_INDEX_CGA, mmio, CR00 + i);
@@ -491,7 +496,8 @@ static void i810_restore_vga(struct i810fb_par *par)
static void i810_restore_addr_map(struct i810fb_par *par)
{
- u8 tmp, *mmio = par->mmio_start_virtual;
+ u8 tmp;
+ u8 __iomem *mmio = par->mmio_start_virtual;
i810_writeb(GR_INDEX, mmio, GR10);
tmp = i810_readb(GR_DATA, mmio);
@@ -505,7 +511,7 @@ static void i810_restore_2d(struct i810fb_par *par)
{
u32 tmp_long;
u16 tmp_word;
- u8 *mmio = par->mmio_start_virtual;
+ u8 __iomem *mmio = par->mmio_start_virtual;
tmp_word = i810_readw(BLTCNTL, mmio);
tmp_word &= ~(3 << 4);
@@ -534,7 +540,7 @@ static void i810_restore_2d(struct i810fb_par *par)
static void i810_restore_vga_state(struct i810fb_par *par)
{
- u8 *mmio = par->mmio_start_virtual;
+ u8 __iomem *mmio = par->mmio_start_virtual;
i810_screen_off(mmio, OFF);
i810_protect_regs(mmio, OFF);
@@ -556,7 +562,8 @@ static void i810_restore_vga_state(struct i810fb_par *par)
static void i810_save_vgax(struct i810fb_par *par)
{
- u8 i, *mmio = par->mmio_start_virtual;
+ u8 i;
+ u8 __iomem *mmio = par->mmio_start_virtual;
for (i = 0; i < 4; i++) {
i810_writeb(CR_INDEX_CGA, mmio, CR30 + i);
@@ -579,7 +586,8 @@ static void i810_save_vgax(struct i810fb_par *par)
static void i810_save_vga(struct i810fb_par *par)
{
- u8 i, *mmio = par->mmio_start_virtual;
+ u8 i;
+ u8 __iomem *mmio = par->mmio_start_virtual;
for (i = 0; i < 10; i++) {
i810_writeb(CR_INDEX_CGA, mmio, CR00 + i);
@@ -593,7 +601,7 @@ static void i810_save_vga(struct i810fb_par *par)
static void i810_save_2d(struct i810fb_par *par)
{
- u8 *mmio = par->mmio_start_virtual;
+ u8 __iomem *mmio = par->mmio_start_virtual;
par->hw_state.dclk_2d = i810_readl(DCLK_2D, mmio);
par->hw_state.dclk_1d = i810_readl(DCLK_1D, mmio);
@@ -716,7 +724,7 @@ static void i810_calc_dclk(u32 freq, u32 *m, u32 *n, u32 *p)
* Description:
* Shows or hides the hardware cursor
*/
-void i810_enable_cursor(u8 *mmio, int mode)
+void i810_enable_cursor(u8 __iomem *mmio, int mode)
{
u32 temp;
@@ -729,7 +737,7 @@ void i810_enable_cursor(u8 *mmio, int mode)
static void i810_reset_cursor_image(struct i810fb_par *par)
{
- u8 *addr = par->cursor_heap.virtual;
+ u8 __iomem *addr = par->cursor_heap.virtual;
int i, j;
for (i = 64; i--; ) {
@@ -744,7 +752,7 @@ static void i810_reset_cursor_image(struct i810fb_par *par)
static void i810_load_cursor_image(int width, int height, u8 *data,
struct i810fb_par *par)
{
- u8 *addr = par->cursor_heap.virtual;
+ u8 __iomem *addr = par->cursor_heap.virtual;
int i, j, w = width/8;
int mod = width % 8, t_mask, d_mask;
@@ -766,8 +774,8 @@ static void i810_load_cursor_image(int width, int height, u8 *data,
static void i810_load_cursor_colors(int fg, int bg, struct fb_info *info)
{
struct i810fb_par *par = (struct i810fb_par *) info->par;
- u8 *mmio = par->mmio_start_virtual, temp;
- u8 red, green, blue, trans;
+ u8 __iomem *mmio = par->mmio_start_virtual;
+ u8 red, green, blue, trans, temp;
i810fb_getcolreg(bg, &red, &green, &blue, &trans, info);
@@ -796,7 +804,7 @@ static void i810_load_cursor_colors(int fg, int bg, struct fb_info *info)
*/
static void i810_init_cursor(struct i810fb_par *par)
{
- u8 *mmio = par->mmio_start_virtual;
+ u8 __iomem *mmio = par->mmio_start_virtual;
i810_enable_cursor(mmio, OFF);
i810_writel(CURBASE, mmio, par->cursor_heap.physical);
@@ -1124,7 +1132,8 @@ static int i810fb_getcolreg(u8 regno, u8 *red, u8 *green, u8 *blue,
u8 *transp, struct fb_info *info)
{
struct i810fb_par *par = (struct i810fb_par *) info->par;
- u8 *mmio = par->mmio_start_virtual, temp;
+ u8 __iomem *mmio = par->mmio_start_virtual;
+ u8 temp;
if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
if ((info->var.green.length == 5 && regno > 31) ||
@@ -1167,7 +1176,7 @@ static int i810fb_open(struct fb_info *info, int user)
if (count == 0) {
memset(&par->state, 0, sizeof(struct vgastate));
par->state.flags = VGA_SAVE_CMAP;
- par->state.vgabase = (caddr_t) par->mmio_start_virtual;
+ par->state.vgabase = par->mmio_start_virtual;
save_vga(&par->state);
i810_save_vga_state(par);
@@ -1203,7 +1212,8 @@ static int i810fb_setcolreg(unsigned regno, unsigned red, unsigned green,
struct fb_info *info)
{
struct i810fb_par *par = (struct i810fb_par *) info->par;
- u8 *mmio = par->mmio_start_virtual, temp;
+ u8 __iomem *mmio = par->mmio_start_virtual;
+ u8 temp;
int i;
if (regno > 255) return 1;
@@ -1308,7 +1318,7 @@ static int i810fb_pan_display(struct fb_var_screeninfo *var,
static int i810fb_blank (int blank_mode, struct fb_info *info)
{
struct i810fb_par *par = (struct i810fb_par *) info->par;
- u8 *mmio = par->mmio_start_virtual;
+ u8 __iomem *mmio = par->mmio_start_virtual;
int mode = 0, pwr, scr_off = 0;
pwr = i810_readl(PWR_CLKC, mmio);
@@ -1391,7 +1401,7 @@ static int i810fb_check_var(struct fb_var_screeninfo *var,
static int i810fb_cursor(struct fb_info *info, struct fb_cursor *cursor)
{
struct i810fb_par *par = (struct i810fb_par *)info->par;
- u8 *mmio = par->mmio_start_virtual;
+ u8 __iomem *mmio = par->mmio_start_virtual;
if (!info->var.accel_flags || par->dev_flags & LOCKUP)
return soft_cursor(info, cursor);
@@ -1724,7 +1734,8 @@ static void __devinit i810_init_defaults(struct i810fb_par *par,
*/
static void __devinit i810_init_device(struct i810fb_par *par)
{
- u8 reg, *mmio = par->mmio_start_virtual;
+ u8 reg;
+ u8 __iomem *mmio = par->mmio_start_virtual;
if (mtrr) set_mtrr(par);
@@ -1855,20 +1866,13 @@ static int __devinit i810fb_init_pci (struct pci_dev *dev,
int i, err = -1, vfreq, hfreq, pixclock;
i = 0;
- if (!(info = kmalloc(sizeof(struct fb_info), GFP_KERNEL))) {
- i810fb_release_resource(info, par);
- return -ENOMEM;
- }
- memset(info, 0, sizeof(struct fb_info));
- if(!(par = kmalloc(sizeof(struct i810fb_par), GFP_KERNEL))) {
- i810fb_release_resource(info, par);
+ info = framebuffer_alloc(sizeof(struct i810fb_par), &dev->dev);
+ if (!info)
return -ENOMEM;
- }
- memset(par, 0, sizeof(struct i810fb_par));
+ par = (struct i810fb_par *) info->par;
par->dev = dev;
- info->par = par;
if (!(info->pixmap.addr = kmalloc(64*1024, GFP_KERNEL))) {
i810fb_release_resource(info, par);
@@ -1941,38 +1945,36 @@ static int __devinit i810fb_init_pci (struct pci_dev *dev,
static void i810fb_release_resource(struct fb_info *info,
struct i810fb_par *par)
{
- if (par) {
- unset_mtrr(par);
- if (par->drm_agp) {
- drm_agp_t *agp = par->drm_agp;
- struct gtt_data *gtt = &par->i810_gtt;
-
- if (par->i810_gtt.i810_cursor_memory)
- agp->free_memory(gtt->i810_cursor_memory);
- if (par->i810_gtt.i810_fb_memory)
- agp->free_memory(gtt->i810_fb_memory);
-
- inter_module_put("drm_agp");
- par->drm_agp = NULL;
- }
+ unset_mtrr(par);
+ if (par->drm_agp) {
+ drm_agp_t *agp = par->drm_agp;
+ struct gtt_data *gtt = &par->i810_gtt;
+
+ if (par->i810_gtt.i810_cursor_memory)
+ agp->free_memory(gtt->i810_cursor_memory);
+ if (par->i810_gtt.i810_fb_memory)
+ agp->free_memory(gtt->i810_fb_memory);
+
+ inter_module_put("drm_agp");
+ par->drm_agp = NULL;
+ }
- if (par->mmio_start_virtual)
- iounmap(par->mmio_start_virtual);
- if (par->aperture.virtual)
- iounmap(par->aperture.virtual);
+ if (par->mmio_start_virtual)
+ iounmap(par->mmio_start_virtual);
+ if (par->aperture.virtual)
+ iounmap(par->aperture.virtual);
- if (par->res_flags & FRAMEBUFFER_REQ)
- release_mem_region(par->aperture.physical,
- par->aperture.size);
- if (par->res_flags & MMIO_REQ)
- release_mem_region(par->mmio_start_phys, MMIO_SIZE);
+ if (par->res_flags & FRAMEBUFFER_REQ)
+ release_mem_region(par->aperture.physical,
+ par->aperture.size);
+ if (par->res_flags & MMIO_REQ)
+ release_mem_region(par->mmio_start_phys, MMIO_SIZE);
- if (par->res_flags & PCI_DEVICE_ENABLED)
- pci_disable_device(par->dev);
+ if (par->res_flags & PCI_DEVICE_ENABLED)
+ pci_disable_device(par->dev);
+
+ framebuffer_release(info);
- kfree(par);
- }
- kfree(info);
}
static void __exit i810fb_remove_pci(struct pci_dev *dev)
diff --git a/drivers/video/igafb.c b/drivers/video/igafb.c
index dd6b16c845c2..f9d77b0f51ab 100644
--- a/drivers/video/igafb.c
+++ b/drivers/video/igafb.c
@@ -531,6 +531,7 @@ int __init igafb_init(void)
info->var = default_var;
info->fix = igafb_fix;
info->pseudo_palette = (void *)(par + 1);
+ info->device = &pdev->dev;
if (!iga_init(info, par)) {
iounmap((void *)par->io_base);
diff --git a/drivers/video/imsttfb.c b/drivers/video/imsttfb.c
index c4a07f27c18c..d51e8f080fe1 100644
--- a/drivers/video/imsttfb.c
+++ b/drivers/video/imsttfb.c
@@ -1524,6 +1524,7 @@ imsttfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
par->cmap_regs = (__u8 *)ioremap(addr + 0x840000, 0x1000);
info->par = par;
info->pseudo_palette = (void *) (par + 1);
+ info->device = &pdev->dev;
init_imstt(info);
pci_set_drvdata(pdev, info);
diff --git a/drivers/video/kyro/fbdev.c b/drivers/video/kyro/fbdev.c
index 2352a9142a77..51a6de200f73 100644
--- a/drivers/video/kyro/fbdev.c
+++ b/drivers/video/kyro/fbdev.c
@@ -735,6 +735,7 @@ static int __devinit kyrofb_probe(struct pci_dev *pdev,
fb_memset(info->screen_base, 0, size);
+ info->device = &pdev->dev;
if (register_framebuffer(info) < 0)
goto out_unmap;
diff --git a/drivers/video/matrox/matroxfb_accel.c b/drivers/video/matrox/matroxfb_accel.c
index fd38e0536e94..8f14c9b300c2 100644
--- a/drivers/video/matrox/matroxfb_accel.c
+++ b/drivers/video/matrox/matroxfb_accel.c
@@ -411,12 +411,7 @@ static void matroxfb_1bpp_imageblit(WPMINFO u_int32_t fgx, u_int32_t bgx,
CRITBEGIN
-#ifdef __BIG_ENDIAN
- WaitTillIdle();
- mga_outl(M_OPMODE, M_OPMODE_8BPP);
-#else
mga_fifo(3);
-#endif
if (easy)
mga_outl(M_DWGCTL, M_DWG_ILOAD | M_DWG_SGNZERO | M_DWG_SHIFTZERO | M_DWG_BMONOWF | M_DWG_LINEAR | M_DWG_REPLACE);
else
@@ -432,32 +427,24 @@ static void matroxfb_1bpp_imageblit(WPMINFO u_int32_t fgx, u_int32_t bgx,
mga_writel(mmio, M_AR3, 0);
if (easy) {
mga_writel(mmio, M_YDSTLEN | M_EXEC, ydstlen);
- mga_memcpy_toio(mmio, 0, chardata, xlen);
+ mga_memcpy_toio(mmio, chardata, xlen);
} else {
mga_writel(mmio, M_AR5, 0);
mga_writel(mmio, M_YDSTLEN | M_EXEC, ydstlen);
if ((step & 3) == 0) {
/* Great. Source has 32bit aligned lines, so we can feed them
directly to the accelerator. */
- mga_memcpy_toio(mmio, 0, chardata, charcell);
+ mga_memcpy_toio(mmio, chardata, charcell);
} else if (step == 1) {
/* Special case for 1..8bit widths */
while (height--) {
-#ifdef __LITTLE_ENDIAN
mga_writel(mmio, 0, *chardata);
-#else
- mga_writel(mmio, 0, (*chardata) << 24);
-#endif
chardata++;
}
} else if (step == 2) {
/* Special case for 9..15bit widths */
while (height--) {
-#ifdef __LITTLE_ENDIAN
mga_writel(mmio, 0, *(u_int16_t*)chardata);
-#else
- mga_writel(mmio, 0, (*(u_int16_t*)chardata) << 16);
-#endif
chardata += 2;
}
} else {
@@ -474,9 +461,6 @@ static void matroxfb_1bpp_imageblit(WPMINFO u_int32_t fgx, u_int32_t bgx,
}
}
WaitTillIdle();
-#ifdef __BIG_ENDIAN
- mga_outl(M_OPMODE, ACCESS_FBINFO(accel.m_opmode));
-#endif
CRITEND
}
@@ -486,7 +470,7 @@ static void matroxfb_imageblit(struct fb_info* info, const struct fb_image* imag
DBG_HEAVY(__FUNCTION__);
- if (image->depth == 0) {
+ if (image->depth == 1) {
u_int32_t fgx, bgx;
fgx = ((u_int32_t*)info->pseudo_palette)[image->fg_color];
diff --git a/drivers/video/matrox/matroxfb_base.c b/drivers/video/matrox/matroxfb_base.c
index e2a2290b628a..47439c4d276a 100644
--- a/drivers/video/matrox/matroxfb_base.c
+++ b/drivers/video/matrox/matroxfb_base.c
@@ -1143,6 +1143,7 @@ static int matroxfb_ioctl(struct inode *inode, struct file *file,
return -EFAULT;
return err;
}
+ case VIDIOC_S_CTRL_OLD:
case VIDIOC_S_CTRL:
{
struct v4l2_control ctrl;
@@ -1750,6 +1751,12 @@ static int initMatrox2(WPMINFO struct board* b){
ACCESS_FBINFO(fbcon.pseudo_palette) = ACCESS_FBINFO(cmap);
/* after __init time we are like module... no logo */
ACCESS_FBINFO(fbcon.flags) = hotplug ? FBINFO_FLAG_MODULE : FBINFO_FLAG_DEFAULT;
+ ACCESS_FBINFO(fbcon.flags) |= FBINFO_PARTIAL_PAN_OK | /* Prefer panning for scroll under MC viewer/edit */
+ FBINFO_HWACCEL_COPYAREA | /* We have hw-assisted bmove */
+ FBINFO_HWACCEL_FILLRECT | /* And fillrect */
+ FBINFO_HWACCEL_IMAGEBLIT | /* And imageblit */
+ FBINFO_HWACCEL_XPAN | /* And we support both horizontal */
+ FBINFO_HWACCEL_YPAN; /* And vertical panning */
ACCESS_FBINFO(video.len_usable) &= PAGE_MASK;
fb_alloc_cmap(&ACCESS_FBINFO(fbcon.cmap), 256, 1);
@@ -1864,6 +1871,7 @@ static int initMatrox2(WPMINFO struct board* b){
/* We do not have to set currcon to 0... register_framebuffer do it for us on first console
* and we do not want currcon == 0 for subsequent framebuffers */
+ ACCESS_FBINFO(fbcon).device = &ACCESS_FBINFO(pcidev)->dev;
if (register_framebuffer(&ACCESS_FBINFO(fbcon)) < 0) {
goto failVideoIO;
}
diff --git a/drivers/video/matrox/matroxfb_base.h b/drivers/video/matrox/matroxfb_base.h
index 79a6873f7a59..e62fcc84054b 100644
--- a/drivers/video/matrox/matroxfb_base.h
+++ b/drivers/video/matrox/matroxfb_base.h
@@ -93,29 +93,6 @@
#endif /* MATROXFB_DEBUG */
-#if !defined(__i386__) && !defined(__x86_64__)
-#ifndef ioremap_nocache
-#define ioremap_nocache(X,Y) ioremap(X,Y)
-#endif
-#endif
-
-#if defined(__alpha__) || defined(__mc68000__) || defined(__i386__) || defined(__x86_64__)
-#define READx_WORKS
-#define MEMCPYTOIO_WORKS
-#else
-/* ppc/ppc64 must use __raw_{read,write}[bwl] as we drive adapter
- in big-endian mode for compatibility with XFree mga driver, and
- so we do not want little-endian {read,write}[bwl] */
-#define READx_FAILS
-#define MEMCPYTOIO_WRITEL
-#endif
-
-#if defined(__mc68000__)
-#define MAP_BUSTOVIRT
-#else
-#define MAP_IOREMAP
-#endif
-
#ifdef DEBUG
#define dprintk(X...) printk(X)
#else
@@ -155,22 +132,13 @@
#endif
typedef struct {
- u_int8_t __iomem* vaddr;
+ void __iomem* vaddr;
} vaddr_t;
-#ifdef READx_WORKS
static inline unsigned int mga_readb(vaddr_t va, unsigned int offs) {
return readb(va.vaddr + offs);
}
-static inline unsigned int mga_readw(vaddr_t va, unsigned int offs) {
- return readw(va.vaddr + offs);
-}
-
-static inline u_int32_t mga_readl(vaddr_t va, unsigned int offs) {
- return readl(va.vaddr + offs);
-}
-
static inline void mga_writeb(vaddr_t va, unsigned int offs, u_int8_t value) {
writeb(value, va.vaddr + offs);
}
@@ -179,62 +147,42 @@ static inline void mga_writew(vaddr_t va, unsigned int offs, u_int16_t value) {
writew(value, va.vaddr + offs);
}
-static inline void mga_writel(vaddr_t va, unsigned int offs, u_int32_t value) {
- writel(value, va.vaddr + offs);
-}
-#else
-static inline unsigned int mga_readb(vaddr_t va, unsigned int offs) {
- return __raw_readb(va.vaddr + offs);
-}
-
-static inline unsigned int mga_readw(vaddr_t va, unsigned int offs) {
- return __raw_readw(va.vaddr + offs);
-}
-
static inline u_int32_t mga_readl(vaddr_t va, unsigned int offs) {
- return __raw_readl(va.vaddr + offs);
-}
-
-static inline void mga_writeb(vaddr_t va, unsigned int offs, u_int8_t value) {
- __raw_writeb(value, va.vaddr + offs);
-}
-
-static inline void mga_writew(vaddr_t va, unsigned int offs, u_int16_t value) {
- __raw_writew(value, va.vaddr + offs);
+ return readl(va.vaddr + offs);
}
static inline void mga_writel(vaddr_t va, unsigned int offs, u_int32_t value) {
- __raw_writel(value, va.vaddr + offs);
+ writel(value, va.vaddr + offs);
}
-#endif
-static inline void mga_memcpy_toio(vaddr_t va, unsigned int offs, const void* src, int len) {
-#ifdef MEMCPYTOIO_WORKS
- memcpy_toio(va.vaddr + offs, src, len);
-#elif defined(MEMCPYTOIO_WRITEL)
- if (offs & 3) {
+static inline void mga_memcpy_toio(vaddr_t va, const void* src, int len) {
+#if defined(__alpha__) || defined(__i386__) || defined(__x86_64__)
+ /*
+ * memcpy_toio works for us if:
+ * (1) Copies data as 32bit quantities, not byte after byte,
+ * (2) Performs LE ordered stores, and
+ * (3) It copes with unaligned source (destination is guaranteed to be page
+ * aligned and length is guaranteed to be multiple of 4).
+ */
+ memcpy_toio(va.vaddr, src, len);
+#else
+ u_int32_t __iomem* addr = va.vaddr;
+
+ if ((unsigned long)src & 3) {
while (len >= 4) {
- mga_writel(va, offs, get_unaligned((u32 *)src));
- offs += 4;
+ writel(get_unaligned((u32 *)src), addr);
+ addr++;
len -= 4;
src += 4;
}
} else {
while (len >= 4) {
- mga_writel(va, offs, *(u32 *)src);
- offs += 4;
+ writel(*(u32 *)src, addr);
+ addr++;
len -= 4;
src += 4;
}
}
- if (len) {
- u_int32_t tmp;
-
- memcpy(&tmp, src, len);
- mga_writel(va, offs, tmp);
- }
-#else
-#error "Sorry, do not know how to write block of data to device"
#endif
}
@@ -252,25 +200,15 @@ static inline void __iomem* vaddr_va(vaddr_t va) {
#define MGA_IOREMAP_FB MGA_IOREMAP_NOCACHE
#define MGA_IOREMAP_MMIO MGA_IOREMAP_NOCACHE
static inline int mga_ioremap(unsigned long phys, unsigned long size, int flags, vaddr_t* virt) {
-#ifdef MAP_IOREMAP
if (flags & MGA_IOREMAP_NOCACHE)
virt->vaddr = ioremap_nocache(phys, size);
else
virt->vaddr = ioremap(phys, size);
-#else
-#ifdef MAP_BUSTOVIRT
- virt->vaddr = bus_to_virt(phys);
-#else
-#error "Your architecture does not have neither ioremap nor bus_to_virt... Giving up"
-#endif
-#endif
return (virt->vaddr == 0); /* 0, !0... 0, error_code in future */
}
static inline void mga_iounmap(vaddr_t va) {
-#ifdef MAP_IOREMAP
iounmap(va.vaddr);
-#endif
}
struct my_timming {
@@ -774,11 +712,11 @@ void matroxfb_unregister_driver(struct matroxfb_driver* drv);
#define DAC_XGENIOCTRL 0x2A
#define DAC_XGENIODATA 0x2B
-#define M_C2CTL 0x3E10
+#define M_C2CTL 0x3C10
-#ifdef __LITTLE_ENDIAN
-#define MX_OPTION_BSWAP 0x00000000
+#define MX_OPTION_BSWAP 0x00000000
+#ifdef __LITTLE_ENDIAN
#define M_OPMODE_4BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_LE | M_OPMODE_DMA_BLIT)
#define M_OPMODE_8BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_LE | M_OPMODE_DMA_BLIT)
#define M_OPMODE_16BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_LE | M_OPMODE_DMA_BLIT)
@@ -786,29 +724,23 @@ void matroxfb_unregister_driver(struct matroxfb_driver* drv);
#define M_OPMODE_32BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_LE | M_OPMODE_DMA_BLIT)
#else
#ifdef __BIG_ENDIAN
-#define MX_OPTION_BSWAP 0x80000000
-
-#define M_OPMODE_4BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_LE | M_OPMODE_DMA_BLIT) /* TODO */
-#define M_OPMODE_8BPP (M_OPMODE_DMA_BE_8BPP | M_OPMODE_DIR_BE_8BPP | M_OPMODE_DMA_BLIT)
-#define M_OPMODE_16BPP (M_OPMODE_DMA_BE_16BPP | M_OPMODE_DIR_BE_16BPP | M_OPMODE_DMA_BLIT)
-#define M_OPMODE_24BPP (M_OPMODE_DMA_BE_8BPP | M_OPMODE_DIR_BE_8BPP | M_OPMODE_DMA_BLIT) /* TODO, ?32 */
-#define M_OPMODE_32BPP (M_OPMODE_DMA_BE_32BPP | M_OPMODE_DIR_BE_32BPP | M_OPMODE_DMA_BLIT)
+#define M_OPMODE_4BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_LE | M_OPMODE_DMA_BLIT) /* TODO */
+#define M_OPMODE_8BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_BE_8BPP | M_OPMODE_DMA_BLIT)
+#define M_OPMODE_16BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_BE_16BPP | M_OPMODE_DMA_BLIT)
+#define M_OPMODE_24BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_BE_8BPP | M_OPMODE_DMA_BLIT) /* TODO, ?32 */
+#define M_OPMODE_32BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_BE_32BPP | M_OPMODE_DMA_BLIT)
#else
#error "Byte ordering have to be defined. Cannot continue."
#endif
#endif
-#define mga_inb(addr) mga_readb(ACCESS_FBINFO(mmio.vbase), (addr))
-#define mga_inl(addr) mga_readl(ACCESS_FBINFO(mmio.vbase), (addr))
-#define mga_outb(addr,val) mga_writeb(ACCESS_FBINFO(mmio.vbase), (addr), (val))
-#define mga_outw(addr,val) mga_writew(ACCESS_FBINFO(mmio.vbase), (addr), (val))
-#define mga_outl(addr,val) mga_writel(ACCESS_FBINFO(mmio.vbase), (addr), (val))
-#define mga_readr(port,idx) (mga_outb((port),(idx)), mga_inb((port)+1))
-#ifdef __LITTLE_ENDIAN
-#define mga_setr(addr,port,val) mga_outw(addr, ((val)<<8) | (port))
-#else
-#define mga_setr(addr,port,val) do { mga_outb(addr, port); mga_outb((addr)+1, val); } while (0)
-#endif
+#define mga_inb(addr) mga_readb(ACCESS_FBINFO(mmio.vbase), (addr))
+#define mga_inl(addr) mga_readl(ACCESS_FBINFO(mmio.vbase), (addr))
+#define mga_outb(addr,val) mga_writeb(ACCESS_FBINFO(mmio.vbase), (addr), (val))
+#define mga_outw(addr,val) mga_writew(ACCESS_FBINFO(mmio.vbase), (addr), (val))
+#define mga_outl(addr,val) mga_writel(ACCESS_FBINFO(mmio.vbase), (addr), (val))
+#define mga_readr(port,idx) (mga_outb((port),(idx)), mga_inb((port)+1))
+#define mga_setr(addr,port,val) mga_outw(addr, ((val)<<8) | (port))
#define mga_fifo(n) do {} while ((mga_inl(M_FIFOSTATUS) & 0xFF) < (n))
diff --git a/drivers/video/matrox/matroxfb_crtc2.c b/drivers/video/matrox/matroxfb_crtc2.c
index 75d19696ffe4..c5230bbe1da4 100644
--- a/drivers/video/matrox/matroxfb_crtc2.c
+++ b/drivers/video/matrox/matroxfb_crtc2.c
@@ -603,6 +603,8 @@ static int matroxfb_dh_regit(CPMINFO struct matroxfb_dh_fb_info* m2info) {
m2info->fbcon.fbops = &matroxfb_dh_ops;
m2info->fbcon.flags = FBINFO_FLAG_DEFAULT;
+ m2info->fbcon.flags |= FBINFO_HWACCEL_XPAN |
+ FBINFO_HWACCEL_YPAN;
m2info->fbcon.currcon = -1;
m2info->fbcon.pseudo_palette = m2info->cmap;
fb_alloc_cmap(&m2info->fbcon.cmap, 256, 1);
diff --git a/drivers/video/pvr2fb.c b/drivers/video/pvr2fb.c
index a6ecf9674f40..f4b20704db6c 100644
--- a/drivers/video/pvr2fb.c
+++ b/drivers/video/pvr2fb.c
@@ -939,6 +939,7 @@ static int __devinit pvr2fb_pci_probe(struct pci_dev *pdev,
pvr2_fix.mmio_start = pci_resource_start(pdev, 1);
pvr2_fix.mmio_len = pci_resource_len(pdev, 1);
+ fbinfo->device = &pdev->dev;
return pvr2fb_common_init();
}
diff --git a/drivers/video/radeonfb.c b/drivers/video/radeonfb.c
index eedb2b8e93a5..82f8e1529498 100644
--- a/drivers/video/radeonfb.c
+++ b/drivers/video/radeonfb.c
@@ -3040,7 +3040,7 @@ static int radeonfb_pci_register (struct pci_dev *pdev,
pci_set_drvdata(pdev, rinfo);
rinfo->next = board_list;
board_list = rinfo;
-
+ ((struct fb_info *) rinfo)->device = &pdev->dev;
if (register_framebuffer ((struct fb_info *) rinfo) < 0) {
printk ("radeonfb: could not register framebuffer\n");
iounmap(rinfo->fb_base);
diff --git a/drivers/video/riva/fbdev.c b/drivers/video/riva/fbdev.c
index 825b39dbe91a..f8772da6a59a 100644
--- a/drivers/video/riva/fbdev.c
+++ b/drivers/video/riva/fbdev.c
@@ -1858,21 +1858,17 @@ static int __devinit rivafb_probe(struct pci_dev *pd,
NVTRACE_ENTER();
assert(pd != NULL);
- info = kmalloc(sizeof(struct fb_info), GFP_KERNEL);
+ info = framebuffer_alloc(sizeof(struct riva_par), &pd->dev);
+
if (!info)
goto err_out;
- default_par = kmalloc(sizeof(struct riva_par), GFP_KERNEL);
- if (!default_par)
- goto err_out_kfree;
-
- memset(info, 0, sizeof(struct fb_info));
- memset(default_par, 0, sizeof(struct riva_par));
+ default_par = (struct riva_par *) info->par;
default_par->pdev = pd;
info->pixmap.addr = kmalloc(64 * 1024, GFP_KERNEL);
if (info->pixmap.addr == NULL)
- goto err_out_kfree1;
+ goto err_out_kfree;
memset(info->pixmap.addr, 0, 64 * 1024);
if (pci_enable_device(pd)) {
@@ -1896,7 +1892,7 @@ static int __devinit rivafb_probe(struct pci_dev *pd,
if(default_par->riva.Architecture == 0) {
printk(KERN_ERR PFX "unknown NV_ARCH\n");
- goto err_out_kfree1;
+ goto err_out_free_base0;
}
if(default_par->riva.Architecture == NV_ARCH_10 ||
default_par->riva.Architecture == NV_ARCH_20 ||
@@ -2001,7 +1997,6 @@ static int __devinit rivafb_probe(struct pci_dev *pd,
fb_destroy_modedb(info->monspecs.modedb);
info->monspecs.modedb_len = 0;
info->monspecs.modedb = NULL;
-
if (register_framebuffer(info) < 0) {
printk(KERN_ERR PFX
"error registering riva framebuffer\n");
@@ -2040,10 +2035,8 @@ err_out_request:
pci_disable_device(pd);
err_out_enable:
kfree(info->pixmap.addr);
-err_out_kfree1:
- kfree(default_par);
err_out_kfree:
- kfree(info);
+ framebuffer_release(info);
err_out:
return -ENODEV;
}
@@ -2077,8 +2070,7 @@ static void __exit rivafb_remove(struct pci_dev *pd)
pci_release_regions(pd);
pci_disable_device(pd);
kfree(info->pixmap.addr);
- kfree(par);
- kfree(info);
+ framebuffer_release(info);
pci_set_drvdata(pd, NULL);
NVTRACE_LEAVE();
}
diff --git a/drivers/video/sstfb.c b/drivers/video/sstfb.c
index e3ea32b6cafb..46dfcce9fcaf 100644
--- a/drivers/video/sstfb.c
+++ b/drivers/video/sstfb.c
@@ -1507,6 +1507,7 @@ static int __devinit sstfb_probe(struct pci_dev *pdev,
fb_alloc_cmap(&info->cmap, 256, 0);
/* register fb */
+ info->device = &pdev->dev;
if (register_framebuffer(info) < 0) {
eprintk("can't register framebuffer.\n");
goto fail;
diff --git a/drivers/video/tdfxfb.c b/drivers/video/tdfxfb.c
index 503ecc3569e8..263a40288118 100644
--- a/drivers/video/tdfxfb.c
+++ b/drivers/video/tdfxfb.c
@@ -202,7 +202,6 @@ static unsigned long do_lfb_size(struct tdfx_par *par, unsigned short);
*/
static int nopan = 0;
static int nowrap = 1; // not implemented (yet)
-static int inverse = 0;
static char *mode_option __initdata = NULL;
/* -------------------------------------------------------------------------
@@ -921,7 +920,6 @@ static void tdfxfb_fillrect(struct fb_info *info, const struct fb_fillrect *rect
tdfx_outl(par, COMMAND_2D, COMMAND_2D_FILLRECT | (tdfx_rop << 24));
tdfx_outl(par, DSTSIZE, rect->width | (rect->height << 16));
tdfx_outl(par, LAUNCH_2D, rect->dx | (rect->dy << 16));
- banshee_wait_idle(info);
}
/*
@@ -957,7 +955,6 @@ static void tdfxfb_copyarea(struct fb_info *info, const struct fb_copyarea *area
tdfx_outl(par, DSTSIZE, area->width | (area->height << 16));
tdfx_outl(par, DSTXY, dx | (dy << 16));
tdfx_outl(par, LAUNCH_2D, sx | (sy << 16));
- banshee_wait_idle(info);
}
static void tdfxfb_imageblit(struct fb_info *info, const struct fb_image *image)
@@ -1025,7 +1022,6 @@ static void tdfxfb_imageblit(struct fb_info *info, const struct fb_image *image)
case 2: tdfx_outl(par, LAUNCH_2D,*(u16*)chardata); break;
case 3: tdfx_outl(par, LAUNCH_2D,*(u16*)chardata | ((chardata[3]) << 24)); break;
}
- banshee_wait_idle(info);
}
#endif /* CONFIG_FB_3DFX_ACCEL */
@@ -1397,10 +1393,7 @@ void tdfxfb_setup(char *options)
while ((this_opt = strsep(&options, ",")) != NULL) {
if (!*this_opt)
continue;
- if (!strcmp(this_opt, "inverse")) {
- inverse = 1;
- fb_invert_cmaps();
- } else if(!strcmp(this_opt, "nopan")) {
+ if(!strcmp(this_opt, "nopan")) {
nopan = 1;
} else if(!strcmp(this_opt, "nowrap")) {
nowrap = 1;
diff --git a/drivers/video/tgafb.c b/drivers/video/tgafb.c
index 07ee3202e92c..ffe811038a9a 100644
--- a/drivers/video/tgafb.c
+++ b/drivers/video/tgafb.c
@@ -1454,6 +1454,7 @@ tgafb_pci_register(struct pci_dev *pdev, const struct pci_device_id *ent)
tgafb_set_par(&all->info);
tgafb_init_fix(&all->info);
+ all->info.device = &pdev->dev;
if (register_framebuffer(&all->info) < 0) {
printk(KERN_ERR "tgafb: Could not register framebuffer\n");
ret = -EINVAL;
diff --git a/drivers/video/tridentfb.c b/drivers/video/tridentfb.c
index aea0e05c882f..83184c78606a 100644
--- a/drivers/video/tridentfb.c
+++ b/drivers/video/tridentfb.c
@@ -1164,6 +1164,7 @@ static int __devinit trident_pci_probe(struct pci_dev * dev, const struct pci_de
default_var.accel_flags &= ~FB_ACCELF_TEXT;
default_var.activate |= FB_ACTIVATE_NOW;
fb_info.var = default_var;
+ fb_info.device = &dev->dev;
if (register_framebuffer(&fb_info) < 0) {
output("Could not register Trident framebuffer\n");
return -EINVAL;
diff --git a/drivers/video/vesafb.c b/drivers/video/vesafb.c
index c5c649d9b1ae..9429e6c63708 100644
--- a/drivers/video/vesafb.c
+++ b/drivers/video/vesafb.c
@@ -49,7 +49,8 @@ static struct fb_fix_screeninfo vesafb_fix __initdata = {
static int inverse = 0;
static int mtrr = 1;
-static int vram __initdata = 0; /* Set amount of memory to be used */
+static int vram_remap __initdata = 0; /* Set amount of memory to be used */
+static int vram_total __initdata = 0; /* Set total amount of memory */
static int pmi_setpal = 0; /* pmi for palette changes ??? */
static int ypan = 0; /* 0..nothing, 1..ypan, 2..ywrap */
static unsigned short *pmi_base = NULL;
@@ -209,8 +210,10 @@ int __init vesafb_setup(char *options)
mtrr=1;
else if (! strcmp(this_opt, "nomtrr"))
mtrr=0;
- else if (! strncmp(this_opt, "vram:", 5))
- vram = simple_strtoul(this_opt+5, NULL, 0);
+ else if (! strncmp(this_opt, "vtotal:", 7))
+ vram_total = simple_strtoul(this_opt+7, NULL, 0);
+ else if (! strncmp(this_opt, "vremap:", 7))
+ vram_remap = simple_strtoul(this_opt+7, NULL, 0);
}
return 0;
}
@@ -220,6 +223,9 @@ static int __init vesafb_probe(struct device *device)
struct platform_device *dev = to_platform_device(device);
struct fb_info *info;
int i, err;
+ unsigned int size_vmode;
+ unsigned int size_remap;
+ unsigned int size_total;
if (screen_info.orig_video_isVGA != VIDEO_TYPE_VLFB)
return -ENXIO;
@@ -231,32 +237,41 @@ static int __init vesafb_probe(struct device *device)
vesafb_defined.xres = screen_info.lfb_width;
vesafb_defined.yres = screen_info.lfb_height;
vesafb_fix.line_length = screen_info.lfb_linelength;
-
- /* Allocate enough memory for double buffering */
- vesafb_fix.smem_len = screen_info.lfb_width * screen_info.lfb_height * vesafb_defined.bits_per_pixel >> 2;
-
- /* check that we don't remap more memory than old cards have */
- if (vesafb_fix.smem_len > (screen_info.lfb_size * 65536))
- vesafb_fix.smem_len = screen_info.lfb_size * 65536;
-
- /* Set video size according to vram boot option */
- if (vram)
- vesafb_fix.smem_len = vram * 1024 * 1024;
-
vesafb_fix.visual = (vesafb_defined.bits_per_pixel == 8) ?
FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
- /* limit framebuffer size to 16 MB. Otherwise we'll eat tons of
- * kernel address space for nothing if the gfx card has alot of
- * memory (>= 128 MB isn't uncommon these days ...) */
- if (vesafb_fix.smem_len > 16 * 1024 * 1024)
- vesafb_fix.smem_len = 16 * 1024 * 1024;
+ /* size_vmode -- that is the amount of memory needed for the
+ * used video mode, i.e. the minimum amount of
+ * memory we need. */
+ size_vmode = vesafb_defined.yres * vesafb_fix.line_length;
+
+ /* size_total -- all video memory we have. Used for mtrr
+ * entries, ressource allocation and bounds
+ * checking. */
+ size_total = screen_info.lfb_size * 65536;
+ if (vram_total)
+ size_total = vram_total * 1024 * 1024;
+ if (size_total < size_vmode)
+ size_total = size_vmode;
+
+ /* size_remap -- the amount of video memory we are going to
+ * use for vesafb. With modern cards it is no
+ * option to simply use size_total as that
+ * wastes plenty of kernel address space. */
+ size_remap = size_vmode * 2;
+ if (vram_remap)
+ size_remap = vram_remap * 1024 * 1024;
+ if (size_remap < size_vmode)
+ size_remap = size_vmode;
+ if (size_remap > size_total)
+ size_remap = size_total;
+ vesafb_fix.smem_len = size_remap;
#ifndef __i386__
screen_info.vesapm_seg = 0;
#endif
- if (!request_mem_region(vesafb_fix.smem_start, vesafb_fix.smem_len, "vesafb")) {
+ if (!request_mem_region(vesafb_fix.smem_start, size_total, "vesafb")) {
printk(KERN_WARNING
"vesafb: abort, cannot reserve video memory at 0x%lx\n",
vesafb_fix.smem_start);
@@ -281,8 +296,10 @@ static int __init vesafb_probe(struct device *device)
goto err;
}
- printk(KERN_INFO "vesafb: framebuffer at 0x%lx, mapped to 0x%p, size %dk\n",
- vesafb_fix.smem_start, info->screen_base, vesafb_fix.smem_len/1024);
+ printk(KERN_INFO "vesafb: framebuffer at 0x%lx, mapped to 0x%p, "
+ "using %dk, total %dk\n",
+ vesafb_fix.smem_start, info->screen_base,
+ size_remap/1024, size_total/1024);
printk(KERN_INFO "vesafb: mode is %dx%dx%d, linelength=%d, pages=%d\n",
vesafb_defined.xres, vesafb_defined.yres, vesafb_defined.bits_per_pixel, vesafb_fix.line_length, screen_info.pages);
@@ -362,7 +379,7 @@ static int __init vesafb_probe(struct device *device)
request_region(0x3c0, 32, "vesafb");
if (mtrr) {
- int temp_size = vesafb_fix.smem_len;
+ int temp_size = size_total;
/* Find the largest power-of-two */
while (temp_size & (temp_size - 1))
temp_size &= (temp_size - 1);
@@ -393,7 +410,7 @@ static int __init vesafb_probe(struct device *device)
return 0;
err:
framebuffer_release(info);
- release_mem_region(vesafb_fix.smem_start, vesafb_fix.smem_len);
+ release_mem_region(vesafb_fix.smem_start, size_total);
return err;
}
diff --git a/drivers/video/vga16fb.c b/drivers/video/vga16fb.c
index 2e132fb61562..f2b053c0f214 100644
--- a/drivers/video/vga16fb.c
+++ b/drivers/video/vga16fb.c
@@ -123,7 +123,7 @@ static struct fb_fix_screeninfo vga16fb_fix __initdata = {
suitable instruction is the x86 bitwise OR. The following
read-modify-write routine should optimize to one such bitwise
OR. */
-static inline void rmw(volatile char *p)
+static inline void rmw(volatile char __iomem *p)
{
readb(p);
writeb(1, p);
@@ -883,7 +883,7 @@ void vga_8planes_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
char oldmask = selectmask();
int line_ofs, height;
char oldop, oldsr;
- char *where;
+ char __iomem *where;
dx /= 4;
where = info->screen_base + dx + rect->dy * info->fix.line_length;
@@ -932,7 +932,7 @@ void vga_8planes_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
void vga16fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
{
int x, x2, y2, vxres, vyres, width, height, line_ofs;
- char *dst;
+ char __iomem *dst;
vxres = info->var.xres_virtual;
vyres = info->var.yres_virtual;
@@ -1012,7 +1012,8 @@ void vga_8planes_copyarea(struct fb_info *info, const struct fb_copyarea *area)
char oldsr = setsr(0xf);
int height, line_ofs, x;
u32 sx, dx, width;
- char *dest, *src;
+ char __iomem *dest;
+ char __iomem *src;
height = area->height;
@@ -1063,7 +1064,8 @@ void vga16fb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
int x, x2, y2, old_dx, old_dy, vxres, vyres;
int height, width, line_ofs;
- char *dst = NULL, *src = NULL;
+ char __iomem *dst = NULL;
+ char __iomem *src = NULL;
vxres = info->var.xres_virtual;
vyres = info->var.yres_virtual;
@@ -1174,7 +1176,7 @@ void vga_8planes_imageblit(struct fb_info *info, const struct fb_image *image)
char oldmask = selectmask();
const char *cdat = image->data;
u32 dx = image->dx;
- char *where;
+ char __iomem *where;
int y;
dx /= 4;
@@ -1198,10 +1200,11 @@ void vga_8planes_imageblit(struct fb_info *info, const struct fb_image *image)
void vga_imageblit_expand(struct fb_info *info, const struct fb_image *image)
{
- char *where = info->screen_base + (image->dx/8) +
+ char __iomem *where = info->screen_base + (image->dx/8) +
image->dy * info->fix.line_length;
struct vga16fb_par *par = (struct vga16fb_par *) info->par;
- char *cdat = (char *) image->data, *dst;
+ char *cdat = (char *) image->data;
+ char __iomem *dst;
int x, y;
switch (info->fix.type) {
@@ -1265,9 +1268,11 @@ void vga_imageblit_color(struct fb_info *info, const struct fb_image *image)
* Draw logo
*/
struct vga16fb_par *par = (struct vga16fb_par *) info->par;
- char *where = info->screen_base + image->dy * info->fix.line_length +
+ char __iomem *where =
+ info->screen_base + image->dy * info->fix.line_length +
image->dx/8;
- const char *cdat = image->data, *dst;
+ const char *cdat = image->data;
+ char __iomem *dst;
int x, y;
switch (info->fix.type) {
@@ -1306,7 +1311,7 @@ void vga16fb_imageblit(struct fb_info *info, const struct fb_image *image)
{
if (image->depth == 1)
vga_imageblit_expand(info, image);
- else if (image->depth <= info->var.bits_per_pixel)
+ else
vga_imageblit_color(info, image);
}
@@ -1354,7 +1359,7 @@ int __init vga16fb_init(void)
/* XXX share VGA_FB_PHYS and I/O region with vgacon and others */
- vga16fb.screen_base = (void *)VGA_MAP_MEM(VGA_FB_PHYS);
+ vga16fb.screen_base = (void __iomem *)VGA_MAP_MEM(VGA_FB_PHYS);
if (!vga16fb.screen_base) {
printk(KERN_ERR "vga16fb: unable to map device\n");
ret = -ENOMEM;