bk://kernel.bkbits.net/gregkh/linux/usb-2.6 dwmw2@infradead.org|ChangeSet|20050121191853|56607 dwmw2 # This is a BitKeeper generated diff -Nru style patch. # # ChangeSet # 2005/01/21 12:16:25-08:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # drivers/usb/misc/Kconfig # 2005/01/21 12:16:21-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2005/01/21 11:18:53-08:00 dwmw2@infradead.org # [PATCH] USB: fix libusb endian issues # # On Wed, 2005-01-19 at 15:39 -0800, John Mock wrote: # > New to 2.6.11-rc1 is that 'lsusb' exhibits 'endian' problems on the # > PowerMac. # # Is that really new to 2.6.11-rc1? The kernel byte-swaps the bcdUSB, # idVendor, idProduct, and bcdDevice fields in the device descriptor. It # should probably swap them back before copying it up to userspace. # # From: David Woodhouse # Signed-off-by: Greg Kroah-Hartman # # drivers/usb/core/devio.c # 2005/01/21 11:18:27-08:00 dwmw2@infradead.org +14 -1 # [PATCH] USB: fix libusb endian issues # # On Wed, 2005-01-19 at 15:39 -0800, John Mock wrote: # > New to 2.6.11-rc1 is that 'lsusb' exhibits 'endian' problems on the # > PowerMac. # # Is that really new to 2.6.11-rc1? The kernel byte-swaps the bcdUSB, # idVendor, idProduct, and bcdDevice fields in the device descriptor. It # should probably swap them back before copying it up to userspace. # # From: David Woodhouse # Signed-off-by: Greg Kroah-Hartman # # ChangeSet # 2005/01/21 00:43:41-08:00 greg@kroah.com # Cset exclude: dwmw2@infradead.org|ChangeSet|20050121001308|53341 # # Reverted endian patch from dwmw2, as it broke the userspace ABI. # # Signed-off-by: Greg Kroah-Hartman # # drivers/usb/core/devio.c # 2005/01/21 00:43:36-08:00 greg@kroah.com +0 -0 # Exclude # # ChangeSet # 2005/01/20 16:35:04-08:00 greg@kroah.com # [PATCH] USB: fix sparse bitwise warnings in the sisusb.c driver # # Signed-off-by: Greg Kroah-Hartman # # drivers/usb/misc/sisusbvga/sisusb.c # 2005/01/20 16:31:34-08:00 greg@kroah.com +2 -2 # USB: fix sparse bitwise warnings in the sisusb.c driver # # ChangeSet # 2005/01/20 16:34:34-08:00 thomas@winischhofer.net # [PATCH] USB: add SiS USB2VGA kernel driver # # Signed-off-by: Thomas Winischhofer # Signed-off-by: Greg Kroah-Hartman # # drivers/usb/misc/sisusbvga/sisusb.h # 2005/01/20 14:01:22-08:00 thomas@winischhofer.net +278 -0 # USB: add SiS USB2VGA kernel driver # # drivers/usb/misc/sisusbvga/sisusb.c # 2005/01/20 14:01:22-08:00 thomas@winischhofer.net +3143 -0 # USB: add SiS USB2VGA kernel driver # # drivers/usb/misc/sisusbvga/sisusb.h # 2005/01/20 14:01:22-08:00 thomas@winischhofer.net +0 -0 # BitKeeper file /home/greg/linux/BK/usb-2.6/drivers/usb/misc/sisusbvga/sisusb.h # # drivers/usb/misc/sisusbvga/sisusb.c # 2005/01/20 14:01:22-08:00 thomas@winischhofer.net +0 -0 # BitKeeper file /home/greg/linux/BK/usb-2.6/drivers/usb/misc/sisusbvga/sisusb.c # # drivers/usb/misc/sisusbvga/Makefile # 2005/01/18 07:07:17-08:00 thomas@winischhofer.net +6 -0 # USB: add SiS USB2VGA kernel driver # # drivers/usb/misc/Makefile # 2005/01/18 17:22:17-08:00 thomas@winischhofer.net +2 -0 # USB: add SiS USB2VGA kernel driver # # drivers/usb/misc/Kconfig # 2005/01/18 07:11:18-08:00 thomas@winischhofer.net +2 -0 # USB: add SiS USB2VGA kernel driver # # MAINTAINERS # 2005/01/18 07:23:48-08:00 thomas@winischhofer.net +6 -0 # USB: add SiS USB2VGA kernel driver # # drivers/usb/misc/sisusbvga/Makefile # 2005/01/18 07:07:17-08:00 thomas@winischhofer.net +0 -0 # BitKeeper file /home/greg/linux/BK/usb-2.6/drivers/usb/misc/sisusbvga/Makefile # # drivers/usb/misc/sisusbvga/Kconfig # 2005/01/18 07:19:25-08:00 thomas@winischhofer.net +14 -0 # USB: add SiS USB2VGA kernel driver # # drivers/usb/misc/sisusbvga/Kconfig # 2005/01/18 07:19:25-08:00 thomas@winischhofer.net +0 -0 # BitKeeper file /home/greg/linux/BK/usb-2.6/drivers/usb/misc/sisusbvga/Kconfig # # ChangeSet # 2005/01/20 16:02:37-08:00 stern@rowland.harvard.edu # [PATCH] USB: Fix EHCI boot oops on AMD # # > > ----- Forwarded message from Andi Kleen ----- # > > Someone added an test for AMD 8111 in EHCI, returning # > > an error in reset. # > > # > > When triggered it would cause an NULL pointer oops because # > > it would usb_hcd_put an half initialized hcd without # > > initialized class. I added a new usb_hcd_free function # > > to handle such half baked objects. # # Andi's diagnosis is correct -- the embedded class_device was not fully # initialized -- but the solution is wrong. The correct patch # is below. This was clearly my fault, an error in driver-model # programming brought on by lack of documentation about which fields in the # driver-model structures need to be set for which API calls. (Greg, long # ago I sent you a documentation patch to try and help remedy this problem, # but it seems to have fallen by the wayside.) # # # Signed-off-by: Alan Stern # Signed-off-by: Greg Kroah-Hartman # # drivers/usb/core/hcd.c # 2005/01/14 15:18:16-08:00 stern@rowland.harvard.edu +1 -1 # USB: Fix EHCI boot oops on AMD # # ChangeSet # 2005/01/20 16:02:12-08:00 R.E.Wolff@harddisk-recovery.nl # [PATCH] Re: Bug when using custom baud rates.... # # When using custom baud rates, the code does: # # # if ((new_serial.baud_base != priv->baud_base) || # (new_serial.baud_base < 9600)) # return -EINVAL; # # Which translates to english as: # # If you changed the baud-base, OR the new one is # invalid, return invalid. # # but it should be: # # If you changed the baud-base, OR the new one is # invalid, return invalid. # # # From: Rogier Wolff # Signed-off-by: Greg Kroah-Hartman # # drivers/usb/serial/ftdi_sio.c # 2005/01/20 08:20:24-08:00 R.E.Wolff@harddisk-recovery.nl +1 -1 # Re: Bug when using custom baud rates.... # # ChangeSet # 2005/01/20 15:59:44-08:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # drivers/usb/core/hub.c # 2005/01/20 15:59:40-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2005/01/20 15:56:49-08:00 greg@kroah.com # USB: remove UB checks in the usb-storage driver. # # This allows either the ub or usb-storage driver to bind to the same device, # allowing people to use both without rebuilding their kernels. It can be # a bit messy at times... # # Signed-off-by: Greg Kroah-Hartman # Acked-by: Matthew Dharm # Acked-by: Pete Zaitcev # # drivers/usb/storage/usb.c # 2005/01/20 15:56:35-08:00 greg@kroah.com +0 -4 # USB: remove UB checks in the usb-storage driver. # # This allows either the ub or usb-storage driver to bind to the same device, # allowing people to use both without rebuilding their kernels. It can be # a bit messy at times... # # Signed-off-by: Greg Kroah-Hartman # Acked-by: Matthew Dharm # Acked-by: Pete Zaitcev # # drivers/usb/storage/unusual_devs.h # 2005/01/20 15:56:35-08:00 greg@kroah.com +0 -2 # USB: remove UB checks in the usb-storage driver. # # This allows either the ub or usb-storage driver to bind to the same device, # allowing people to use both without rebuilding their kernels. It can be # a bit messy at times... # # Signed-off-by: Greg Kroah-Hartman # Acked-by: Matthew Dharm # Acked-by: Pete Zaitcev # # ChangeSet # 2005/01/20 15:19:25-08:00 stern@rowland.harvard.edu # [PATCH] USB: Make use_both_schemes=y the default # # Enough people are experiencing problems with the new device initialization # scheme that it seems like a good idea to make the default behavior be to # try the old scheme when the new one fails. That's what this patch does, # simply by changing the initial value of the use_both_schemes module # parameter. # # Signed-off-by: Alan Stern # Signed-off-by: Greg Kroah-Hartman # # drivers/usb/core/hub.c # 2005/01/14 08:28:39-08:00 stern@rowland.harvard.edu +1 -1 # USB: Make use_both_schemes=y the default # # ChangeSet # 2005/01/20 15:18:59-08:00 stern@rowland.harvard.edu # [PATCH] USB: Retry more aggressively during device initialization # # This patch make the hub driver's device initialization routine more # aggressive about detecting errors and retrying. It checks the result of # the 64-byte GET-DESCRIPTOR request to verify that the descriptor tag is # set correctly and the ep0-maxpacket value is legal; if either is not true # it will retry the request immediately. David Brownell has said that this # kind of approach is necessary to make certain buggy devices work. # # # Signed-off-by: Alan Stern # Signed-off-by: Greg Kroah-Hartman # # drivers/usb/core/hub.c # 2005/01/13 08:51:32-08:00 stern@rowland.harvard.edu +18 -10 # USB: Retry more aggressively during device initialization # # ChangeSet # 2005/01/20 15:18:37-08:00 rddunlap@osdl.org # [PATCH] USB speedtch: fix printk format warnings # # Fix printk format warnings: use %zd for size_t: # drivers/usb/atm/speedtch.c:497: warning: int format, different type arg (arg 3) # drivers/usb/atm/speedtch.c:525: warning: int format, different type arg (arg 3) # # Signed-off-by: Randy Dunlap # Signed-off-by: Greg Kroah-Hartman # # drivers/usb/atm/speedtch.c # 2005/01/14 14:43:55-08:00 rddunlap@osdl.org +2 -2 # USB speedtch: fix printk format warnings # # ChangeSet # 2005/01/20 15:18:10-08:00 radford@golemgroup.com # [PATCH] USB ftdi_sio: an rs485 adaptor from 4n-galaxy.de # # This patch adds support for an rs485 adaptor from 4n-galaxy.de. # # # Signed-off-by: Jim Radford # Signed-off-by: Greg Kroah-Hartman # # drivers/usb/serial/ftdi_sio.h # 2004/10/06 17:22:44-07:00 radford@golemgroup.com +1 -0 # USB ftdi_sio: an rs485 adaptor from 4n-galaxy.de # # drivers/usb/serial/ftdi_sio.c # 2004/10/06 17:22:44-07:00 radford@golemgroup.com +3 -0 # USB ftdi_sio: an rs485 adaptor from 4n-galaxy.de # # ChangeSet # 2005/01/20 15:17:47-08:00 mdharm-usb@one-eyed-alien.net # [PATCH] USB storage: make IGNORE_RESIDUE apply for reads (in addition to writes) # # This patch was originally as406. I've rediffed it against a current tree. # # This patch makes the iGNORE_RESIDUE flag apply to reads (as well as writes, # which it already does). This is done because we've found devices which # improperly report residue in the 'read' case. # # Phil will send in a follow-up patch with the appropriate unusual_devs # entry for Ian's device soon. # # # Signed-off-by: Alan Stern # Signed-off-by: Matthew Dharm # Signed-off-by: Greg Kroah-Hartman # # drivers/usb/storage/transport.c # 2005/01/16 14:53:47-08:00 mdharm-usb@one-eyed-alien.net +1 -2 # USB storage: make IGNORE_RESIDUE apply for reads (in addition to writes) # # ChangeSet # 2005/01/20 15:17:21-08:00 mdharm-usb@one-eyed-alien.net # [PATCH] USB Storage: Remove fix_capacity routine # # This is patch as422 from Alan Stern. # # This is the second half of the two-part patch to move the fix_capacity # functionality up into the sd driver. James Bottomley has applied the SCSI # half, so now the usb-storage part is ready to go. # # In short, the patch removes the fix_capacity routine and in its place, # sets a device flag to tell sd that the reported capacity is one sector too # high. It's a simple change and shouldn't cause any problems. # # # Signed-off-by: Alan Stern # Signed-off-by: Matthew Dharm # Signed-off-by: Greg Kroah-Hartman # # drivers/usb/storage/scsiglue.c # 2004/11/08 12:43:05-08:00 mdharm-usb@one-eyed-alien.net +5 -0 # USB Storage: Remove fix_capacity routine # # drivers/usb/storage/protocol.c # 2004/11/08 12:37:22-08:00 mdharm-usb@one-eyed-alien.net +0 -39 # USB Storage: Remove fix_capacity routine # # ChangeSet # 2005/01/20 15:16:55-08:00 nacc@us.ibm.com # [PATCH] usb/cypress_m8: replace schedule_timeout() with msleep() # # Description: Use msleep() instead of schedule_timeout() to guarantee the task # delays as expected. The current code is not incorrect. Using msleep(), though, # encourages specifying time delays in human time-units and consistency across the # kernel. # # Signed-off-by: Nishanth Aravamudan # Signed-off-by: Greg Kroah-Hartman # # drivers/usb/serial/cypress_m8.c # 2005/01/17 00:17:27-08:00 nacc@us.ibm.com +3 -3 # usb/cypress_m8: replace schedule_timeout() with msleep() # # ChangeSet # 2005/01/20 15:16:31-08:00 luca.risolia@studio.unibo.it # [PATCH] USB: SN9C10x driver bugfix # # SN9C10x bugfix and small updates. # # Changes: # @ Allocate the correct number of buffer memory bytes for read() # * Wakeup interruptible events on DEV_MISCONFIGURED status # * Allocate the exact number of buffers (nreadbuffers) in poll() # * Documentation updates # # Signed-off-by: Luca Risolia # Signed-off-by: Greg Kroah-Hartman # # drivers/usb/media/sn9c102_core.c # 2005/01/18 17:56:36-08:00 luca.risolia@studio.unibo.it +26 -12 # USB: SN9C10x driver bugfix # # drivers/usb/media/sn9c102.h # 2005/01/18 17:56:18-08:00 luca.risolia@studio.unibo.it +3 -3 # USB: SN9C10x driver bugfix # # Documentation/usb/sn9c102.txt # 2005/01/18 17:38:57-08:00 luca.risolia@studio.unibo.it +3 -10 # USB: SN9C10x driver bugfix # # ChangeSet # 2005/01/18 01:56:09-08:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # drivers/usb/misc/Kconfig # 2005/01/18 01:56:05-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/usb/core/hub.c # 2005/01/18 01:56:05-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2005/01/15 21:15:09-08:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6 # into bix.(none):/usr/src/bk-usb # # drivers/usb/misc/Kconfig # 2005/01/15 21:15:05-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/usb/core/hub.c # 2005/01/15 21:15:05-08:00 akpm@bix.(none) +0 -0 # Auto merged # diff -Nru a/Documentation/usb/sn9c102.txt b/Documentation/usb/sn9c102.txt --- a/Documentation/usb/sn9c102.txt 2005-01-23 22:34:17 -08:00 +++ b/Documentation/usb/sn9c102.txt 2005-01-23 22:34:17 -08:00 @@ -210,8 +210,8 @@ SN9C10x bridge, while the other two control the sensor chip. "reg" and "i2c_reg" hold the values of the current register index where the following reading/writing operations are addressed at through "val" and "i2c_val". Their -use is not intended for end-users. Note that "i2c_reg" and "i2c_val" won't be -created if the sensor does not actually support the standard I2C protocol or +use is not intended for end-users. Note that "i2c_reg" and "i2c_val" will not +be created if the sensor does not actually support the standard I2C protocol or its registers are not 8-bit long. Also, remember that you must be logged in as root before writing to them. @@ -341,15 +341,8 @@ All the available control settings of each image sensor are supported through the V4L2 interface. -If you think your camera is based on the above hardware and is not actually -listed in the above table, you may try to add the specific USB VendorID and -ProductID identifiers to the sn9c102_id_table[] in the file "sn9c102_sensor.h"; -then compile, load the module again and look at the kernel output. -If this works, please send an email to the author reporting the kernel -messages, so that a new entry in the list of supported devices can be added. - Donations of new models for further testing and support would be much -appreciated. Non-available hardware won't be supported by the author of this +appreciated. Non-available hardware will not be supported by the author of this driver. diff -Nru a/MAINTAINERS b/MAINTAINERS --- a/MAINTAINERS 2005-01-23 22:34:17 -08:00 +++ b/MAINTAINERS 2005-01-23 22:34:17 -08:00 @@ -2021,6 +2021,12 @@ W: http://www.winischhofer.net/linuxsisvga.shtml S: Maintained +SIS USB2VGA DRIVER +P: Thomas Winischhofer +M: thomas@winischhofer.net +W: http://www.winischhofer.at/linuxsisusbvga.shtml +S: Maintained + SMSC47M1 HARDWARE MONITOR DRIVER P: Jean Delvare M: khali@linux-fr.org diff -Nru a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c --- a/drivers/usb/atm/speedtch.c 2005-01-23 22:34:17 -08:00 +++ b/drivers/usb/atm/speedtch.c 2005-01-23 22:34:17 -08:00 @@ -494,7 +494,7 @@ dbg("speedtch_upload_firmware: write BLOCK1 to modem failed (%d)!", ret); goto fail_release; } - dbg("speedtch_upload_firmware: BLOCK1 uploaded (%d bytes)", fw1->size); + dbg("speedtch_upload_firmware: BLOCK1 uploaded (%zd bytes)", fw1->size); } /* USB led blinking green, ADSL led off */ @@ -522,7 +522,7 @@ goto fail_release; } } - dbg("speedtch_upload_firmware: BLOCK3 uploaded (%d bytes)", fw2->size); + dbg("speedtch_upload_firmware: BLOCK3 uploaded (%zd bytes)", fw2->size); /* USB led static green, ADSL led static red */ diff -Nru a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c --- a/drivers/usb/core/devio.c 2005-01-23 22:34:17 -08:00 +++ b/drivers/usb/core/devio.c 2005-01-23 22:34:17 -08:00 @@ -123,13 +123,26 @@ } if (pos < sizeof(struct usb_device_descriptor)) { + struct usb_device_descriptor *desc = kmalloc(sizeof(*desc), GFP_KERNEL); + if (!desc) { + ret = -ENOMEM; + goto err; + } + memcpy(desc, &dev->descriptor, sizeof(dev->descriptor)); + le16_to_cpus(&desc->bcdUSB); + le16_to_cpus(&desc->idVendor); + le16_to_cpus(&desc->idProduct); + le16_to_cpus(&desc->bcdDevice); + len = sizeof(struct usb_device_descriptor) - pos; if (len > nbytes) len = nbytes; - if (copy_to_user(buf, ((char *)&dev->descriptor) + pos, len)) { + if (copy_to_user(buf, ((char *)desc) + pos, len)) { + kfree(desc); ret = -EFAULT; goto err; } + kfree(desc); *ppos += len; buf += len; diff -Nru a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c --- a/drivers/usb/core/hcd.c 2005-01-23 22:34:17 -08:00 +++ b/drivers/usb/core/hcd.c 2005-01-23 22:34:17 -08:00 @@ -676,6 +676,7 @@ INIT_LIST_HEAD (&bus->bus_list); class_device_initialize(&bus->class_dev); + bus->class_dev.class = &usb_host_class; } EXPORT_SYMBOL (usb_bus_init); @@ -732,7 +733,6 @@ } snprintf(bus->class_dev.class_id, BUS_ID_SIZE, "usb%d", busnum); - bus->class_dev.class = &usb_host_class; bus->class_dev.dev = bus->controller; retval = class_device_add(&bus->class_dev); if (retval) { diff -Nru a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c --- a/drivers/usb/core/hub.c 2005-01-23 22:34:17 -08:00 +++ b/drivers/usb/core/hub.c 2005-01-23 22:34:17 -08:00 @@ -74,7 +74,7 @@ MODULE_PARM_DESC(old_scheme_first, "start with the old device initialization scheme"); -static int use_both_schemes = 0; +static int use_both_schemes = 1; module_param(use_both_schemes, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(use_both_schemes, "try the other device initialization scheme if the " @@ -2178,24 +2178,35 @@ retval = -ENOMEM; continue; } - buf->bMaxPacketSize0 = 0; /* Use a short timeout the first time through, * so that recalcitrant full-speed devices with * 8- or 16-byte ep0-maxpackets won't slow things * down tremendously by NAKing the unexpectedly - * early status stage. Also, retry on length 0 - * or stall; some devices are flakey. + * early status stage. Also, retry on all errors; + * some devices are flakey. */ for (j = 0; j < 3; ++j) { + buf->bMaxPacketSize0 = 0; r = usb_control_msg(udev, usb_rcvaddr0pipe(), USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, USB_DT_DEVICE << 8, 0, buf, GET_DESCRIPTOR_BUFSIZE, (i ? HZ * USB_CTRL_GET_TIMEOUT : HZ)); - if (r == 0 || r == -EPIPE) - continue; - if (r < 0) + switch (buf->bMaxPacketSize0) { + case 8: case 16: case 32: case 64: + if (buf->bDescriptorType == + USB_DT_DEVICE) { + r = 0; + break; + } + /* FALL THROUGH */ + default: + if (r == 0) + r = -EPROTO; + break; + } + if (r == 0) break; } udev->descriptor.bMaxPacketSize0 = @@ -2211,10 +2222,7 @@ retval = -ENODEV; goto fail; } - switch (udev->descriptor.bMaxPacketSize0) { - case 64: case 32: case 16: case 8: - break; - default: + if (r) { dev_err(&udev->dev, "device descriptor " "read/%s, error %d\n", "64", r); diff -Nru a/drivers/usb/media/sn9c102.h b/drivers/usb/media/sn9c102.h --- a/drivers/usb/media/sn9c102.h 2005-01-23 22:34:17 -08:00 +++ b/drivers/usb/media/sn9c102.h 2005-01-23 22:34:17 -08:00 @@ -53,11 +53,11 @@ /*****************************************************************************/ #define SN9C102_MODULE_NAME "V4L2 driver for SN9C10x PC Camera Controllers" -#define SN9C102_MODULE_AUTHOR "(C) 2004 Luca Risolia" +#define SN9C102_MODULE_AUTHOR "(C) 2004-2005 Luca Risolia" #define SN9C102_AUTHOR_EMAIL "" #define SN9C102_MODULE_LICENSE "GPL" -#define SN9C102_MODULE_VERSION "1:1.22" -#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 22) +#define SN9C102_MODULE_VERSION "1:1.23" +#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 23) enum sn9c102_bridge { BRIDGE_SN9C101 = 0x01, diff -Nru a/drivers/usb/media/sn9c102_core.c b/drivers/usb/media/sn9c102_core.c --- a/drivers/usb/media/sn9c102_core.c 2005-01-23 22:34:17 -08:00 +++ b/drivers/usb/media/sn9c102_core.c 2005-01-23 22:34:17 -08:00 @@ -164,8 +164,8 @@ struct v4l2_rect* r = &(cam->sensor->cropcap.bounds); const size_t imagesize = cam->module_param.force_munmap || io == IO_READ ? - (p->width * p->height * p->priv)/8 : - (r->width * r->height * p->priv)/8; + (p->width * p->height * p->priv) / 8 : + (r->width * r->height * p->priv) / 8; void* buff = NULL; u32 i; @@ -516,9 +516,14 @@ wake_up_interruptible(&cam->wait_stream); } - if ((cam->state & DEV_DISCONNECTED)||(cam->state & DEV_MISCONFIGURED)) + if (cam->state & DEV_DISCONNECTED) return; + if (cam->state & DEV_MISCONFIGURED) { + wake_up_interruptible(&cam->wait_frame); + return; + } + if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue)) goto resubmit_urb; @@ -1558,7 +1563,8 @@ err = wait_event_interruptible ( cam->wait_frame, (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) ); + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED) ); if (err) { up(&cam->fileop_sem); return err; @@ -1567,6 +1573,10 @@ up(&cam->fileop_sem); return -ENODEV; } + if (cam->state & DEV_MISCONFIGURED) { + up(&cam->fileop_sem); + return -EIO; + } } f = list_entry(cam->outqueue.prev, struct sn9c102_frame_t, frame); @@ -1615,7 +1625,8 @@ } if (cam->io == IO_NONE) { - if (!sn9c102_request_buffers(cam, 2, IO_READ)) { + if (!sn9c102_request_buffers(cam, cam->nreadbuffers, + IO_READ)) { DBG(1, "poll() failed, not enough memory") goto error; } @@ -1729,7 +1740,7 @@ } -static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, +static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, unsigned int cmd, void __user * arg) { struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); @@ -1970,7 +1981,7 @@ return -EFAULT; } - if (cam->module_param.force_munmap) + if (cam->module_param.force_munmap || cam->io == IO_READ) sn9c102_release_buffers(cam); err = sn9c102_set_crop(cam, rect); @@ -1990,7 +2001,7 @@ s->pix_format.height = rect->height/scale; memcpy(&(s->_rect), rect, sizeof(*rect)); - if (cam->module_param.force_munmap && + if ((cam->module_param.force_munmap || cam->io == IO_READ) && nbuffers != sn9c102_request_buffers(cam, nbuffers, cam->io)) { cam->state |= DEV_MISCONFIGURED; @@ -2146,7 +2157,7 @@ return -EFAULT; } - if (cam->module_param.force_munmap) + if (cam->module_param.force_munmap || cam->io == IO_READ) sn9c102_release_buffers(cam); err += sn9c102_set_pix_format(cam, pix); @@ -2168,7 +2179,7 @@ memcpy(pfmt, pix, sizeof(*pix)); memcpy(&(s->_rect), &rect, sizeof(rect)); - if (cam->module_param.force_munmap && + if ((cam->module_param.force_munmap || cam->io == IO_READ) && nbuffers != sn9c102_request_buffers(cam, nbuffers, cam->io)) { cam->state |= DEV_MISCONFIGURED; @@ -2346,11 +2357,14 @@ err = wait_event_interruptible ( cam->wait_frame, (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) ); + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED) ); if (err) return err; if (cam->state & DEV_DISCONNECTED) return -ENODEV; + if (cam->state & DEV_MISCONFIGURED) + return -EIO; } spin_lock_irqsave(&cam->queue_lock, lock_flags); @@ -2495,7 +2509,7 @@ return -EIO; } - err = sn9c102_v4l2_ioctl(inode, filp, cmd, (void __user *)arg); + err = sn9c102_ioctl_v4l2(inode, filp, cmd, (void __user *)arg); up(&cam->fileop_sem); diff -Nru a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig --- a/drivers/usb/misc/Kconfig 2005-01-23 22:34:17 -08:00 +++ b/drivers/usb/misc/Kconfig 2005-01-23 22:34:17 -08:00 @@ -137,6 +137,8 @@ See also . +source "drivers/usb/misc/sisusbvga/Kconfig" + config USB_TEST tristate "USB testing driver (DEVELOPMENT)" depends on USB && USB_DEVICEFS && EXPERIMENTAL diff -Nru a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile --- a/drivers/usb/misc/Makefile 2005-01-23 22:34:17 -08:00 +++ b/drivers/usb/misc/Makefile 2005-01-23 22:34:17 -08:00 @@ -16,3 +16,5 @@ obj-$(CONFIG_USB_RIO500) += rio500.o obj-$(CONFIG_USB_TEST) += usbtest.o obj-$(CONFIG_USB_USS720) += uss720.o + +obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/ \ No newline at end of file diff -Nru a/drivers/usb/misc/sisusbvga/Kconfig b/drivers/usb/misc/sisusbvga/Kconfig --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/usb/misc/sisusbvga/Kconfig 2005-01-23 22:34:17 -08:00 @@ -0,0 +1,14 @@ + +config USB_SISUSBVGA + tristate "USB 2.0 SVGA dongle support (Net2280/SiS315)" + depends on USB && USB_EHCI_HCD + ---help--- + Say Y here if you intend to attach a USB2VGA dongle based on a + Net2280 and a SiS315 chip. + + Note that this device requires a USB 2.0 host controller. It will not + work with USB 1.x controllers. + + To compile this driver as a module, choose M here: the module will be + called sisusb. If unsure, say N. + diff -Nru a/drivers/usb/misc/sisusbvga/Makefile b/drivers/usb/misc/sisusbvga/Makefile --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/usb/misc/sisusbvga/Makefile 2005-01-23 22:34:17 -08:00 @@ -0,0 +1,6 @@ +# +# Makefile for the sisusb driver (if driver is inside kernel tree). +# + +obj-$(CONFIG_USB_SISUSBVGA) += sisusb.o + diff -Nru a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/usb/misc/sisusbvga/sisusb.c 2005-01-23 22:34:17 -08:00 @@ -0,0 +1,3143 @@ +/* + * sisusb - usb kernel driver for SiS315(E) based USB2VGA dongles + * + * Copyright (C) 2005 by Thomas Winischhofer, Vienna, Austria + * + * If distributed as part of the Linux kernel, this code is licensed under the + * terms of the GPL v2. + * + * Otherwise, the following license terms apply: + * + * * Redistribution and use in source and binary forms, with or without + * * modification, are permitted provided that the following conditions + * * are met: + * * 1) Redistributions of source code must retain the above copyright + * * notice, this list of conditions and the following disclaimer. + * * 2) Redistributions in binary form must reproduce the above copyright + * * notice, this list of conditions and the following disclaimer in the + * * documentation and/or other materials provided with the distribution. + * * 3) The name of the author may not be used to endorse or promote products + * * derived from this software without specific psisusbr written permission. + * * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED OR + * * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Author: Thomas Winischhofer + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sisusb.h" + +#define SISUSB_DONTSYNC + +/* Forward declarations / clean-up routines */ + +static struct usb_driver sisusb_driver; + +static DECLARE_MUTEX(disconnect_sem); + +static void +sisusb_free_buffers(struct sisusb_usb_data *sisusb) +{ + int i; + + for (i = 0; i < NUMOBUFS; i++) { + if (sisusb->obuf[i]) { + usb_buffer_free(sisusb->sisusb_dev, sisusb->obufsize, + sisusb->obuf[i], sisusb->transfer_dma_out[i]); + sisusb->obuf[i] = NULL; + } + } + if (sisusb->ibuf) { + usb_buffer_free(sisusb->sisusb_dev, sisusb->ibufsize, + sisusb->ibuf, sisusb->transfer_dma_in); + sisusb->ibuf = NULL; + } +} + +static void +sisusb_free_urbs(struct sisusb_usb_data *sisusb) +{ + int i; + + for (i = 0; i < NUMOBUFS; i++) { + usb_free_urb(sisusb->sisurbout[i]); + sisusb->sisurbout[i] = NULL; + } + usb_free_urb(sisusb->sisurbin); + sisusb->sisurbin = NULL; +} + +/* Level 0: USB transport layer */ + +/* 1. out-bulks */ + +/* out-urb management */ + +/* Return 1 if all free, 0 otherwise */ +static int +sisusb_all_free(struct sisusb_usb_data *sisusb) +{ + int i; + + for (i = 0; i < sisusb->numobufs; i++) { + + if (sisusb->urbstatus[i] & SU_URB_BUSY) + return 0; + + } + + return 1; +} + +/* Kill all busy URBs */ +static void +sisusb_kill_all_busy(struct sisusb_usb_data *sisusb) +{ + int i; + + if (sisusb_all_free(sisusb)) + return; + + for (i = 0; i < sisusb->numobufs; i++) { + + if (sisusb->urbstatus[i] & SU_URB_BUSY) + usb_kill_urb(sisusb->sisurbout[i]); + + } +} + +/* Return 1 if ok, 0 if error (not all complete within timeout) */ +static int +sisusb_wait_all_out_complete(struct sisusb_usb_data *sisusb) +{ + int timeout = 5 * HZ, i = 1; + + wait_event_timeout(sisusb->wait_q, + (i = sisusb_all_free(sisusb)), + timeout); + + return i; +} + +static int +sisusb_outurb_available(struct sisusb_usb_data *sisusb) +{ + int i; + + for (i = 0; i < sisusb->numobufs; i++) { + + if ((sisusb->urbstatus[i] & (SU_URB_BUSY|SU_URB_ALLOC)) == 0) + return i; + + } + + return -1; +} + +static int +sisusb_get_free_outbuf(struct sisusb_usb_data *sisusb) +{ + int i, timeout = 5 * HZ; + + wait_event_timeout(sisusb->wait_q, + ((i = sisusb_outurb_available(sisusb)) >= 0), + timeout); + + return i; +} + +static int +sisusb_alloc_outbuf(struct sisusb_usb_data *sisusb) +{ + int i; + + i = sisusb_outurb_available(sisusb); + + if (i >= 0) + sisusb->urbstatus[i] |= SU_URB_ALLOC; + + return i; +} + +static void +sisusb_free_outbuf(struct sisusb_usb_data *sisusb, int index) +{ + if ((index >= 0) && (index < sisusb->numobufs)) + sisusb->urbstatus[index] &= ~SU_URB_ALLOC; +} + +/* completion callback */ + +static void +sisusb_bulk_completeout(struct urb *urb, struct pt_regs *regs) +{ + struct sisusb_urb_context *context = urb->context; + struct sisusb_usb_data *sisusb; + + if (!context) + return; + + sisusb = context->sisusb; + + if (!sisusb || !sisusb->sisusb_dev || !sisusb->present) + return; + +#ifndef SISUSB_DONTSYNC + if (context->actual_length) + *(context->actual_length) += urb->actual_length; +#endif + + sisusb->urbstatus[context->urbindex] &= ~SU_URB_BUSY; + wake_up(&sisusb->wait_q); +} + +static int +sisusb_bulkout_msg(struct sisusb_usb_data *sisusb, int index, unsigned int pipe, void *data, + int len, int *actual_length, int timeout, unsigned int tflags, + dma_addr_t transfer_dma) +{ + struct urb *urb = sisusb->sisurbout[index]; + int retval, byteswritten = 0; + + /* Set up URB */ + urb->transfer_flags = 0; + + usb_fill_bulk_urb(urb, sisusb->sisusb_dev, pipe, data, len, + sisusb_bulk_completeout, &sisusb->urbout_context[index]); + + urb->transfer_flags |= (tflags | URB_ASYNC_UNLINK); + urb->actual_length = 0; + + if ((urb->transfer_dma = transfer_dma)) + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + /* Set up context */ + sisusb->urbout_context[index].actual_length = (timeout) ? + NULL : actual_length; + + /* Declare this urb/buffer in use */ + sisusb->urbstatus[index] |= SU_URB_BUSY; + + /* Submit URB */ + retval = usb_submit_urb(urb, GFP_ATOMIC); + + /* If OK, and if timeout > 0, wait for completion */ + if ((retval == 0) && timeout) { + wait_event_timeout(sisusb->wait_q, + (!(sisusb->urbstatus[index] & SU_URB_BUSY)), + timeout); + if (sisusb->urbstatus[index] & SU_URB_BUSY) { + /* URB timed out... kill it and report error */ + usb_kill_urb(urb); + retval = -ETIMEDOUT; + } else { + /* Otherwise, report urb status */ + retval = urb->status; + byteswritten = urb->actual_length; + } + } + + if (actual_length) + *actual_length = byteswritten; + + return retval; +} + +/* 2. in-bulks */ + +/* completion callback */ + +static void +sisusb_bulk_completein(struct urb *urb, struct pt_regs *regs) +{ + struct sisusb_usb_data *sisusb = urb->context; + + if (!sisusb || !sisusb->sisusb_dev || !sisusb->present) + return; + + sisusb->completein = 1; + wake_up(&sisusb->wait_q); +} + +static int +sisusb_bulkin_msg(struct sisusb_usb_data *sisusb, unsigned int pipe, void *data, int len, + int *actual_length, int timeout, unsigned int tflags, dma_addr_t transfer_dma) +{ + struct urb *urb = sisusb->sisurbin; + int retval, readbytes = 0; + + urb->transfer_flags = 0; + + usb_fill_bulk_urb(urb, sisusb->sisusb_dev, pipe, data, len, + sisusb_bulk_completein, sisusb); + + urb->transfer_flags |= (tflags | URB_ASYNC_UNLINK); + urb->actual_length = 0; + + if ((urb->transfer_dma = transfer_dma)) + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + sisusb->completein = 0; + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval == 0) { + wait_event_timeout(sisusb->wait_q, sisusb->completein, timeout); + if (!sisusb->completein) { + /* URB timed out... kill it and report error */ + usb_kill_urb(urb); + retval = -ETIMEDOUT; + } else { + /* URB completed within timout */ + retval = urb->status; + readbytes = urb->actual_length; + } + } + + if (actual_length) + *actual_length = readbytes; + + return retval; +} + + +/* Level 1: */ + +/* Send a bulk message of variable size + * + * To copy the data from userspace, give pointer to "userbuffer", + * to copy from (non-DMA) kernel memory, give "kernbuffer". If + * both of these are NULL, it is assumed, that the transfer + * buffer "sisusb->obuf[index]" is set up with the data to send. + * Index is ignored if either kernbuffer or userbuffer is set. + * If async is nonzero, URBs will be sent without waiting for + * completion of the previous URB. + * + * (return 0 on success) + */ + +static int sisusb_send_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len, + char *kernbuffer, const char __user *userbuffer, int index, + ssize_t *bytes_written, unsigned int tflags, int async) +{ + int result = 0, retry, count = len; + int passsize, thispass, transferred_len = 0; + int fromuser = (userbuffer != NULL) ? 1 : 0; + int fromkern = (kernbuffer != NULL) ? 1 : 0; + unsigned int pipe; + char *buffer; + + (*bytes_written) = 0; + + /* Sanity check */ + if (!sisusb || !sisusb->present || !sisusb->sisusb_dev) + return -ENODEV; + + /* If we copy data from kernel or userspace, force the + * allocation of a buffer/urb. If we have the data in + * the transfer buffer[index] already, reuse the buffer/URB + * if the length is > buffer size. (So, transmitting + * large data amounts directly from the transfer buffer + * treats the buffer as a ring buffer. However, we need + * to sync in this case.) + */ + if (fromuser || fromkern) + index = -1; + else if (len > sisusb->obufsize) + async = 0; + + pipe = usb_sndbulkpipe(sisusb->sisusb_dev, ep); + + do { + passsize = thispass = (sisusb->obufsize < count) ? + sisusb->obufsize : count; + + if (index < 0) + index = sisusb_get_free_outbuf(sisusb); + + if (index < 0) + return -EIO; + + buffer = sisusb->obuf[index]; + + if (fromuser) { + + if (copy_from_user(buffer, userbuffer, passsize)) + return -EFAULT; + + userbuffer += passsize; + + } else if (fromkern) { + + memcpy(buffer, kernbuffer, passsize); + kernbuffer += passsize; + + } + + retry = 5; + while (thispass) { + + if (!sisusb->sisusb_dev) + return -ENODEV; + + result = sisusb_bulkout_msg(sisusb, + index, + pipe, + buffer, + thispass, + &transferred_len, + async ? 0 : 5 * HZ, + tflags, + sisusb->transfer_dma_out[index]); + + if (result == -ETIMEDOUT) { + + /* Will not happen if async */ + if (!retry--) + return -ETIME; + + continue; + + } else if ((result == 0) && !async && transferred_len) { + + thispass -= transferred_len; + if (thispass) { + if (sisusb->transfer_dma_out) { + /* If DMA, copy remaining + * to beginning of buffer + */ + memcpy(buffer, + buffer + transferred_len, + thispass); + } else { + /* If not DMA, simply increase + * the pointer + */ + buffer += transferred_len; + } + } + + } else + break; + }; + + if (result) + return result; + + (*bytes_written) += passsize; + count -= passsize; + + /* Force new allocation in next iteration */ + if (fromuser || fromkern) + index = -1; + + } while (count > 0); + + if (async) { +#ifdef SISUSB_DONTSYNC + (*bytes_written) = len; + /* Some URBs/buffers might be busy */ +#else + sisusb_wait_all_out_complete(sisusb); + (*bytes_written) = transferred_len; + /* All URBs and all buffers are available */ +#endif + } + + return ((*bytes_written) == len) ? 0 : -EIO; +} + +/* Receive a bulk message of variable size + * + * To copy the data to userspace, give pointer to "userbuffer", + * to copy to kernel memory, give "kernbuffer". One of them + * MUST be set. (There is no technique for letting the caller + * read directly from the ibuf.) + * + */ + +static int sisusb_recv_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len, + void *kernbuffer, char __user *userbuffer, ssize_t *bytes_read, + unsigned int tflags) +{ + int result = 0, retry, count = len; + int bufsize, thispass, transferred_len; + unsigned int pipe; + char *buffer; + + (*bytes_read) = 0; + + /* Sanity check */ + if (!sisusb || !sisusb->present || !sisusb->sisusb_dev) + return -ENODEV; + + pipe = usb_rcvbulkpipe(sisusb->sisusb_dev, ep); + buffer = sisusb->ibuf; + bufsize = sisusb->ibufsize; + + retry = 5; + +#ifdef SISUSB_DONTSYNC + if (!(sisusb_wait_all_out_complete(sisusb))) + return -EIO; +#endif + + while (count > 0) { + + if (!sisusb->sisusb_dev) + return -ENODEV; + + thispass = (bufsize < count) ? bufsize : count; + + result = sisusb_bulkin_msg(sisusb, + pipe, + buffer, + thispass, + &transferred_len, + 5 * HZ, + tflags, + sisusb->transfer_dma_in); + + if (transferred_len) + thispass = transferred_len; + + else if (result == -ETIMEDOUT) { + + if (!retry--) + return -ETIME; + + continue; + + } else + return -EIO; + + + if (thispass) { + + (*bytes_read) += thispass; + count -= thispass; + + if (userbuffer) { + + if (copy_to_user(userbuffer, buffer, thispass)) + return -EFAULT; + + userbuffer += thispass; + + } else { + + memcpy(kernbuffer, buffer, thispass); + kernbuffer += thispass; + + } + + } + + } + + return ((*bytes_read) == len) ? 0 : -EIO; +} + +static int sisusb_send_packet(struct sisusb_usb_data *sisusb, int len, + struct sisusb_packet *packet) +{ + int ret; + int bytes_transferred = 0; + __le32 tmp; + + if (len == 6) + packet->data = 0; + +#ifdef SISUSB_DONTSYNC + if (!(sisusb_wait_all_out_complete(sisusb))) + return 1; +#endif + + /* Eventually correct endianness */ + SISUSB_CORRECT_ENDIANNESS_PACKET(packet); + + /* 1. send the packet */ + ret = sisusb_send_bulk_msg(sisusb, SISUSB_EP_GFX_OUT, len, + (char *)packet, NULL, 0, &bytes_transferred, 0, 0); + + if ((ret == 0) && (len == 6)) { + + /* 2. if packet len == 6, it means we read, so wait for 32bit + * return value and write it to packet->data + */ + ret = sisusb_recv_bulk_msg(sisusb, SISUSB_EP_GFX_IN, 4, + (char *)&tmp, NULL, &bytes_transferred, 0); + + packet->data = le32_to_cpu(tmp); + } + + return ret; +} + +static int sisusb_send_bridge_packet(struct sisusb_usb_data *sisusb, int len, + struct sisusb_packet *packet, + unsigned int tflags) +{ + int ret; + int bytes_transferred = 0; + __le32 tmp; + + if (len == 6) + packet->data = 0; + +#ifdef SISUSB_DONTSYNC + if (!(sisusb_wait_all_out_complete(sisusb))) + return 1; +#endif + + /* Eventually correct endianness */ + SISUSB_CORRECT_ENDIANNESS_PACKET(packet); + + /* 1. send the packet */ + ret = sisusb_send_bulk_msg(sisusb, SISUSB_EP_BRIDGE_OUT, len, + (char *)packet, NULL, 0, &bytes_transferred, tflags, 0); + + if ((ret == 0) && (len == 6)) { + + /* 2. if packet len == 6, it means we read, so wait for 32bit + * return value and write it to packet->data + */ + ret = sisusb_recv_bulk_msg(sisusb, SISUSB_EP_BRIDGE_IN, 4, + (char *)&tmp, NULL, &bytes_transferred, 0); + + packet->data = le32_to_cpu(tmp); + } + + return ret; +} + +/* access video memory and mmio (return 0 on success) */ + +/* Low level */ + +/* The following routines assume being used to transfer byte, word, + * long etc. + * This means that they assume "data" in machine endianness format. + */ + +static int sisusb_write_memio_byte(struct sisusb_usb_data *sisusb, int type, + u32 addr, u8 data) +{ + struct sisusb_packet packet; + int ret; + + packet.header = (1 << (addr & 3)) | (type << 6); + packet.address = addr & ~3; + packet.data = data << ((addr & 3) << 3); + ret = sisusb_send_packet(sisusb, 10, &packet); + return ret; +} + +static int sisusb_write_memio_word(struct sisusb_usb_data *sisusb, int type, + u32 addr, u16 data) +{ + struct sisusb_packet packet; + int ret = 0; + + packet.address = addr & ~3; + + switch (addr & 3) { + case 0: + packet.header = (type << 6) | 0x0003; + packet.data = (u32)data; + ret = sisusb_send_packet(sisusb, 10, &packet); + break; + case 1: + packet.header = (type << 6) | 0x0006; + packet.data = (u32)data << 8; + ret = sisusb_send_packet(sisusb, 10, &packet); + break; + case 2: + packet.header = (type << 6) | 0x000c; + packet.data = (u32)data << 16; + ret = sisusb_send_packet(sisusb, 10, &packet); + break; + case 3: + packet.header = (type << 6) | 0x0008; + packet.data = (u32)data << 24; + ret = sisusb_send_packet(sisusb, 10, &packet); + packet.header = (type << 6) | 0x0001; + packet.address = (addr & ~3) + 4; + packet.data = (u32)data >> 8; + ret |= sisusb_send_packet(sisusb, 10, &packet); + } + + return ret; +} + +static int sisusb_write_memio_24bit(struct sisusb_usb_data *sisusb, int type, + u32 addr, u32 data) +{ + struct sisusb_packet packet; + int ret = 0; + + packet.address = addr & ~3; + + switch (addr & 3) { + case 0: + packet.header = (type << 6) | 0x0007; + packet.data = data & 0x00ffffff; + ret = sisusb_send_packet(sisusb, 10, &packet); + break; + case 1: + packet.header = (type << 6) | 0x000e; + packet.data = data << 8; + ret = sisusb_send_packet(sisusb, 10, &packet); + break; + case 2: + packet.header = (type << 6) | 0x000c; + packet.data = data << 16; + ret = sisusb_send_packet(sisusb, 10, &packet); + packet.header = (type << 6) | 0x0001; + packet.address = (addr & ~3) + 4; + packet.data = (data >> 16) & 0x00ff; + ret |= sisusb_send_packet(sisusb, 10, &packet); + break; + case 3: + packet.header = (type << 6) | 0x0008; + packet.data = data << 24; + ret = sisusb_send_packet(sisusb, 10, &packet); + packet.header = (type << 6) | 0x0003; + packet.address = (addr & ~3) + 4; + packet.data = (data >> 8) & 0xffff; + ret |= sisusb_send_packet(sisusb, 10, &packet); + } + + return ret; +} + +static int sisusb_write_memio_long(struct sisusb_usb_data *sisusb, int type, + u32 addr, u32 data) +{ + struct sisusb_packet packet; + int ret = 0; + + packet.address = addr & ~3; + + switch (addr & 3) { + case 0: + packet.header = (type << 6) | 0x000f; + packet.data = data; + ret = sisusb_send_packet(sisusb, 10, &packet); + break; + case 1: + packet.header = (type << 6) | 0x000e; + packet.data = data << 8; + ret = sisusb_send_packet(sisusb, 10, &packet); + packet.header = (type << 6) | 0x0001; + packet.address = (addr & ~3) + 4; + packet.data = data >> 24; + ret |= sisusb_send_packet(sisusb, 10, &packet); + break; + case 2: + packet.header = (type << 6) | 0x000c; + packet.data = data << 16; + ret = sisusb_send_packet(sisusb, 10, &packet); + packet.header = (type << 6) | 0x0003; + packet.address = (addr & ~3) + 4; + packet.data = data >> 16; + ret |= sisusb_send_packet(sisusb, 10, &packet); + break; + case 3: + packet.header = (type << 6) | 0x0008; + packet.data = data << 24; + ret = sisusb_send_packet(sisusb, 10, &packet); + packet.header = (type << 6) | 0x0007; + packet.address = (addr & ~3) + 4; + packet.data = data >> 8; + ret |= sisusb_send_packet(sisusb, 10, &packet); + } + + return ret; +} + +/* The xxx_bulk routines copy a buffer of variable size. They treat the + * buffer as chars, therefore lsb/msb has to be corrected if using the + * byte/word/long/etc routines for speed-up + * + * If data is from userland, set "userbuffer" (and clear "kernbuffer"), + * if data is in kernel space, set "kernbuffer" (and clear "userbuffer"); + * if neither "kernbuffer" nor "userbuffer" are given, it is assumed + * that the data already is in the transfer buffer "sisusb->obuf[index]". + */ + +static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, + char *kernbuffer, int length, + const char __user *userbuffer, int index, + ssize_t *bytes_written) +{ + struct sisusb_packet packet; + int ret = 0; + static int msgcount = 0; + u8 swap8, fromkern = kernbuffer ? 1 : 0; + u16 swap16; + u32 swap32, flag = (length >> 28) & 1; + char buf[4]; + + /* if neither kernbuffer not userbuffer are given, assume + * data in obuf + */ + if (!fromkern && !userbuffer) + kernbuffer = sisusb->obuf[index]; + + (*bytes_written = 0); + + length &= 0x00ffffff; + + while (length) { + + switch (length) { + + case 0: + return ret; + + case 1: + if (userbuffer) { + if (get_user(swap8, (u8 __user *)userbuffer)) + return -EFAULT; + } else + swap8 = kernbuffer[0]; + + ret = sisusb_write_memio_byte(sisusb, + SISUSB_TYPE_MEM, + addr, swap8); + + if (!ret) + (*bytes_written)++; + + return ret; + + case 2: + if (userbuffer) { + if (get_user(swap16, (u16 __user *)userbuffer)) + return -EFAULT; + } else + swap16 = (kernbuffer[0] << 8) | kernbuffer[1]; + + ret = sisusb_write_memio_word(sisusb, + SISUSB_TYPE_MEM, + addr, + swap16); + + if (!ret) + (*bytes_written) += 2; + + return ret; + + case 3: + if (userbuffer) { + if (copy_from_user(&buf, userbuffer, 3)) + return -EFAULT; + + swap32 = (buf[0] << 16) | + (buf[1] << 8) | + buf[2]; + } else + swap32 = (kernbuffer[0] << 16) | + (kernbuffer[1] << 8) | + kernbuffer[2]; + + ret = sisusb_write_memio_24bit(sisusb, + SISUSB_TYPE_MEM, + addr, + swap32); + + if (!ret) + (*bytes_written) += 3; + + return ret; + + case 4: + if (userbuffer) { + if (get_user(swap32, (u32 __user *)userbuffer)) + return -EFAULT; + } else + swap32 = (kernbuffer[0] << 24) | + (kernbuffer[1] << 16) | + (kernbuffer[2] << 8) | + kernbuffer[3]; + + ret = sisusb_write_memio_long(sisusb, + SISUSB_TYPE_MEM, + addr, + swap32); + if (!ret) + (*bytes_written) += 4; + + return ret; + + default: + if ((length & ~3) > 0x10000) { + + packet.header = 0x001f; + packet.address = 0x000001d4; + packet.data = addr; + ret = sisusb_send_bridge_packet(sisusb, 10, + &packet, 0); + packet.header = 0x001f; + packet.address = 0x000001d0; + packet.data = (length & ~3); + ret |= sisusb_send_bridge_packet(sisusb, 10, + &packet, 0); + packet.header = 0x001f; + packet.address = 0x000001c0; + packet.data = flag | 0x16; + ret |= sisusb_send_bridge_packet(sisusb, 10, + &packet, 0); + if (userbuffer) { + ret |= sisusb_send_bulk_msg(sisusb, + SISUSB_EP_GFX_LBULK_OUT, + (length & ~3), + NULL, userbuffer, 0, + bytes_written, 0, 1); + userbuffer += (*bytes_written); + } else if (fromkern) { + ret |= sisusb_send_bulk_msg(sisusb, + SISUSB_EP_GFX_LBULK_OUT, + (length & ~3), + kernbuffer, NULL, 0, + bytes_written, 0, 1); + kernbuffer += (*bytes_written); + } else { + ret |= sisusb_send_bulk_msg(sisusb, + SISUSB_EP_GFX_LBULK_OUT, + (length & ~3), + NULL, NULL, index, + bytes_written, 0, 1); + kernbuffer += ((*bytes_written) & + (sisusb->obufsize-1)); + } + + } else { + + packet.header = 0x001f; + packet.address = 0x00000194; + packet.data = addr; + ret = sisusb_send_bridge_packet(sisusb, 10, + &packet, 0); + packet.header = 0x001f; + packet.address = 0x00000190; + packet.data = (length & ~3); + ret |= sisusb_send_bridge_packet(sisusb, 10, + &packet, 0); + if (sisusb->flagb0 != 0x16) { + packet.header = 0x001f; + packet.address = 0x00000180; + packet.data = flag | 0x16; + ret |= sisusb_send_bridge_packet(sisusb, 10, + &packet, 0); + sisusb->flagb0 = 0x16; + } + if (userbuffer) { + ret |= sisusb_send_bulk_msg(sisusb, + SISUSB_EP_GFX_BULK_OUT, + (length & ~3), + NULL, userbuffer, 0, + bytes_written, 0, 1); + userbuffer += (*bytes_written); + } else if (fromkern) { + ret |= sisusb_send_bulk_msg(sisusb, + SISUSB_EP_GFX_BULK_OUT, + (length & ~3), + kernbuffer, NULL, 0, + bytes_written, 0, 1); + kernbuffer += (*bytes_written); + } else { + ret |= sisusb_send_bulk_msg(sisusb, + SISUSB_EP_GFX_BULK_OUT, + (length & ~3), + NULL, NULL, index, + bytes_written, 0, 1); + kernbuffer += ((*bytes_written) & + (sisusb->obufsize-1)); + } + } + if (ret) { + msgcount++; + if (msgcount < 500) + printk(KERN_ERR + "sisusbvga[%d]: Wrote %d of " + "%d bytes, error %d\n", + sisusb->minor, *bytes_written, + length, ret); + else if (msgcount == 500) + printk(KERN_ERR + "sisusbvga[%d]: Too many errors" + ", logging stopped\n", + sisusb->minor); + } + addr += (*bytes_written); + length -= (*bytes_written); + } + + if (ret) + break; + + } + + return ret ? -EIO : 0; +} + +static int sisusb_read_memio_byte(struct sisusb_usb_data *sisusb, int type, + u32 addr, u8 *data) +{ + struct sisusb_packet packet; + int ret; + + CLEARPACKET(&packet); + packet.header = (1 << (addr & 3)) | (type << 6); + packet.address = addr & ~3; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = (u8)(packet.data >> ((addr & 3) << 3)); + return ret; +} + +static int sisusb_read_memio_word(struct sisusb_usb_data *sisusb, int type, + u32 addr, u16 *data) +{ + struct sisusb_packet packet; + int ret = 0; + + CLEARPACKET(&packet); + + packet.address = addr & ~3; + + switch (addr & 3) { + case 0: + packet.header = (type << 6) | 0x0003; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = (u16)(packet.data); + break; + case 1: + packet.header = (type << 6) | 0x0006; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = (u16)(packet.data >> 8); + break; + case 2: + packet.header = (type << 6) | 0x000c; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = (u16)(packet.data >> 16); + break; + case 3: + packet.header = (type << 6) | 0x0008; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = (u16)(packet.data >> 24); + packet.header = (type << 6) | 0x0001; + packet.address = (addr & ~3) + 4; + ret |= sisusb_send_packet(sisusb, 6, &packet); + *data |= (u16)(packet.data << 8); + } + + return ret; +} + +static int sisusb_read_memio_24bit(struct sisusb_usb_data *sisusb, int type, + u32 addr, u32 *data) +{ + struct sisusb_packet packet; + int ret = 0; + + packet.address = addr & ~3; + + switch (addr & 3) { + case 0: + packet.header = (type << 6) | 0x0007; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = packet.data & 0x00ffffff; + break; + case 1: + packet.header = (type << 6) | 0x000e; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = packet.data >> 8; + break; + case 2: + packet.header = (type << 6) | 0x000c; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = packet.data >> 16; + packet.header = (type << 6) | 0x0001; + packet.address = (addr & ~3) + 4; + ret |= sisusb_send_packet(sisusb, 6, &packet); + *data |= ((packet.data & 0xff) << 16); + break; + case 3: + packet.header = (type << 6) | 0x0008; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = packet.data >> 24; + packet.header = (type << 6) | 0x0003; + packet.address = (addr & ~3) + 4; + ret |= sisusb_send_packet(sisusb, 6, &packet); + *data |= ((packet.data & 0xffff) << 8); + } + + return ret; +} + +static int sisusb_read_memio_long(struct sisusb_usb_data *sisusb, int type, + u32 addr, u32 *data) +{ + struct sisusb_packet packet; + int ret = 0; + + packet.address = addr & ~3; + + switch (addr & 3) { + case 0: + packet.header = (type << 6) | 0x000f; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = packet.data; + break; + case 1: + packet.header = (type << 6) | 0x000e; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = packet.data >> 8; + packet.header = (type << 6) | 0x0001; + packet.address = (addr & ~3) + 4; + ret |= sisusb_send_packet(sisusb, 6, &packet); + *data |= (packet.data << 24); + break; + case 2: + packet.header = (type << 6) | 0x000c; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = packet.data >> 16; + packet.header = (type << 6) | 0x0003; + packet.address = (addr & ~3) + 4; + ret |= sisusb_send_packet(sisusb, 6, &packet); + *data |= (packet.data << 16); + break; + case 3: + packet.header = (type << 6) | 0x0008; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = packet.data >> 24; + packet.header = (type << 6) | 0x0007; + packet.address = (addr & ~3) + 4; + ret |= sisusb_send_packet(sisusb, 6, &packet); + *data |= (packet.data << 8); + } + + return ret; +} + +static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, + char *kernbuffer, int length, + char __user *userbuffer, ssize_t *bytes_read) +{ + int ret = 0; + char buf[4]; + u16 swap16; + u32 swap32; + + (*bytes_read = 0); + + length &= 0x00ffffff; + + while (length) { + + switch (length) { + + case 0: + return ret; + + case 1: + + ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, + addr, &buf[0]); + if (!ret) { + (*bytes_read)++; + if (userbuffer) { + if (put_user(buf[0], + (u8 __user *)userbuffer)) { + return -EFAULT; + } + } else { + kernbuffer[0] = buf[0]; + } + } + return ret; + + case 2: + ret |= sisusb_read_memio_word(sisusb, SISUSB_TYPE_MEM, + addr, &swap16); + if (!ret) { + (*bytes_read) += 2; + if (userbuffer) { + if (put_user(swap16, + (u16 __user *)userbuffer)) + return -EFAULT; + } else { + kernbuffer[0] = swap16 >> 8; + kernbuffer[1] = swap16 & 0xff; + } + } + return ret; + + case 3: + ret |= sisusb_read_memio_24bit(sisusb, SISUSB_TYPE_MEM, + addr, &swap32); + if (!ret) { + (*bytes_read) += 3; + buf[0] = (swap32 >> 16) & 0xff; + buf[1] = (swap32 >> 8) & 0xff; + buf[2] = swap32 & 0xff; + if (userbuffer) { + if (copy_to_user(userbuffer, &buf[0], 3)) + return -EFAULT; + } else { + kernbuffer[0] = buf[0]; + kernbuffer[1] = buf[1]; + kernbuffer[2] = buf[2]; + } + } + return ret; + + default: + ret |= sisusb_read_memio_long(sisusb, SISUSB_TYPE_MEM, + addr, &swap32); + if (!ret) { + (*bytes_read) += 4; + if (userbuffer) { + if (put_user(swap32, + (u32 __user *)userbuffer)) + return -EFAULT; + + userbuffer += 4; + } else { + kernbuffer[0] = (swap32 >> 24) & 0xff; + kernbuffer[1] = (swap32 >> 16) & 0xff; + kernbuffer[2] = (swap32 >> 8) & 0xff; + kernbuffer[3] = swap32 & 0xff; + kernbuffer += 4; + } + addr += 4; + length -= 4; + } +#if 0 /* That does not work, as EP 2 is an OUT EP! */ + default: + CLEARPACKET(&packet); + packet.header = 0x001f; + packet.address = 0x000001a0; + packet.data = 0x00000006; + ret |= sisusb_send_bridge_packet(sisusb, 10, + &packet, 0); + packet.header = 0x001f; + packet.address = 0x000001b0; + packet.data = (length & ~3) | 0x40000000; + ret |= sisusb_send_bridge_packet(sisusb, 10, + &packet, 0); + packet.header = 0x001f; + packet.address = 0x000001b4; + packet.data = addr; + ret |= sisusb_send_bridge_packet(sisusb, 10, + &packet, 0); + packet.header = 0x001f; + packet.address = 0x000001a4; + packet.data = 0x00000001; + ret |= sisusb_send_bridge_packet(sisusb, 10, + &packet, 0); + if (userbuffer) { + ret |= sisusb_recv_bulk_msg(sisusb, + SISUSB_EP_GFX_BULK_IN, + (length & ~3), + NULL, userbuffer, + bytes_read, 0); + if (!ret) userbuffer += (*bytes_read); + } else { + ret |= sisusb_recv_bulk_msg(sisusb, + SISUSB_EP_GFX_BULK_IN, + (length & ~3), + kernbuffer, NULL, + bytes_read, 0); + if (!ret) kernbuffer += (*bytes_read); + } + addr += (*bytes_read); + length -= (*bytes_read); +#endif + } + + if (ret) + break; + } + + return ret; +} + +/* High level: Gfx (indexed) register access */ + +static int +sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 data) +{ + int ret; + ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, index); + ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, data); + return ret; +} + +static int +sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 *data) +{ + int ret; + ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, index); + ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, data); + return ret; +} + +static int +sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port, u8 idx, + u8 myand, u8 myor) +{ + int ret; + u8 tmp; + + ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, idx); + ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, &tmp); + tmp &= myand; + tmp |= myor; + ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, tmp); + return ret; +} + +static int +sisusb_setidxregmask(struct sisusb_usb_data *sisusb, int port, u8 idx, + u8 data, u8 mask) +{ + int ret; + u8 tmp; + ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, idx); + ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, &tmp); + tmp &= ~(mask); + tmp |= (data & mask); + ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, tmp); + return ret; +} + +static int +sisusb_setidxregor(struct sisusb_usb_data *sisusb, int port, u8 index, u8 myor) +{ + return(sisusb_setidxregandor(sisusb, port, index, 0xff, myor)); +} + +static int +sisusb_setidxregand(struct sisusb_usb_data *sisusb, int port, u8 idx, u8 myand) +{ + return(sisusb_setidxregandor(sisusb, port, idx, myand, 0x00)); +} + +/* access pci config registers (reg numbers 0, 4, 8, etc) */ + +static int +sisusb_write_pci_config(struct sisusb_usb_data *sisusb, int regnum, u32 data) +{ + struct sisusb_packet packet; + int ret; + + packet.header = 0x008f; + packet.address = regnum | 0x10000; + packet.data = data; + ret = sisusb_send_packet(sisusb, 10, &packet); + return ret; +} + +static int +sisusb_read_pci_config(struct sisusb_usb_data *sisusb, int regnum, u32 *data) +{ + struct sisusb_packet packet; + int ret; + + packet.header = 0x008f; + packet.address = (u32)regnum | 0x10000; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = packet.data; + return ret; +} + +/* Clear video RAM */ + +static int +sisusb_clear_vram(struct sisusb_usb_data *sisusb, u32 address, int length) +{ + int ret, i, j; + + if (address < sisusb->vrambase) + return 1; + + if (address >= sisusb->vrambase + sisusb->vramsize) + return 1; + + if (address + length > sisusb->vrambase + sisusb->vramsize) + length = sisusb->vrambase + sisusb->vramsize - address; + + if (length <= 0) + return 0; + + /* allocate free buffer/urb and clear the buffer */ + if ((i = sisusb_alloc_outbuf(sisusb)) < 0) + return -EBUSY; + + memset(sisusb->obuf[i], 0, sisusb->obufsize); + + /* We can write a length > buffer size here. The buffer + * data will simply be re-used (like a ring-buffer). + */ + ret = sisusb_write_mem_bulk(sisusb, address, NULL, length, NULL, i, &j); + + /* Free the buffer/urb */ + sisusb_free_outbuf(sisusb, i); + + return ret; +} + +/* Initialize the graphics core (return 0 on success) + * This resets the graphics hardware and puts it into + * a defined mode (640x480@60Hz) + */ + +#define GETREG(r,d) sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, r, d) +#define SETREG(r,d) sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, r, d) +#define SETIREG(r,i,d) sisusb_setidxreg(sisusb, r, i, d) +#define GETIREG(r,i,d) sisusb_getidxreg(sisusb, r, i, d) +#define SETIREGOR(r,i,o) sisusb_setidxregor(sisusb, r, i, o) +#define SETIREGAND(r,i,a) sisusb_setidxregand(sisusb, r, i, a) +#define SETIREGANDOR(r,i,a,o) sisusb_setidxregandor(sisusb, r, i, a, o) +#define READL(a,d) sisusb_read_memio_long(sisusb, SISUSB_TYPE_MEM, a, d) +#define WRITEL(a,d) sisusb_write_memio_long(sisusb, SISUSB_TYPE_MEM, a, d) +#define READB(a,d) sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d) +#define WRITEB(a,d) sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d) + +static int +sisusb_triggersr16(struct sisusb_usb_data *sisusb, u8 ramtype) +{ + int ret; + u8 tmp8; + + ret = GETIREG(SISSR, 0x16, &tmp8); + if (ramtype <= 1) { + tmp8 &= 0x3f; + ret |= SETIREG(SISSR, 0x16, tmp8); + tmp8 |= 0x80; + ret |= SETIREG(SISSR, 0x16, tmp8); + } else { + tmp8 |= 0xc0; + ret |= SETIREG(SISSR, 0x16, tmp8); + tmp8 &= 0x0f; + ret |= SETIREG(SISSR, 0x16, tmp8); + tmp8 |= 0x80; + ret |= SETIREG(SISSR, 0x16, tmp8); + tmp8 &= 0x0f; + ret |= SETIREG(SISSR, 0x16, tmp8); + tmp8 |= 0xd0; + ret |= SETIREG(SISSR, 0x16, tmp8); + tmp8 &= 0x0f; + ret |= SETIREG(SISSR, 0x16, tmp8); + tmp8 |= 0xa0; + ret |= SETIREG(SISSR, 0x16, tmp8); + } + return ret; +} + +static int +sisusb_getbuswidth(struct sisusb_usb_data *sisusb, int *bw, int *chab) +{ + int ret; + u8 ramtype, done = 0; + u32 t0, t1, t2, t3; + u32 ramptr = SISUSB_PCI_MEMBASE; + + ret = GETIREG(SISSR, 0x3a, &ramtype); + ramtype &= 3; + + ret |= SETIREG(SISSR, 0x13, 0x00); + + if (ramtype <= 1) { + ret |= SETIREG(SISSR, 0x14, 0x12); + ret |= SETIREGAND(SISSR, 0x15, 0xef); + } else { + ret |= SETIREG(SISSR, 0x14, 0x02); + } + + ret |= sisusb_triggersr16(sisusb, ramtype); + ret |= WRITEL(ramptr + 0, 0x01234567); + ret |= WRITEL(ramptr + 4, 0x456789ab); + ret |= WRITEL(ramptr + 8, 0x89abcdef); + ret |= WRITEL(ramptr + 12, 0xcdef0123); + ret |= WRITEL(ramptr + 16, 0x55555555); + ret |= WRITEL(ramptr + 20, 0x55555555); + ret |= WRITEL(ramptr + 24, 0xffffffff); + ret |= WRITEL(ramptr + 28, 0xffffffff); + ret |= READL(ramptr + 0, &t0); + ret |= READL(ramptr + 4, &t1); + ret |= READL(ramptr + 8, &t2); + ret |= READL(ramptr + 12, &t3); + + if (ramtype <= 1) { + + *chab = 0; *bw = 64; + + if ((t3 != 0xcdef0123) || (t2 != 0x89abcdef)) { + if ((t1 == 0x456789ab) && (t0 == 0x01234567)) { + *chab = 0; *bw = 64; + ret |= SETIREGAND(SISSR, 0x14, 0xfd); + } + } + if ((t1 != 0x456789ab) || (t0 != 0x01234567)) { + *chab = 1; *bw = 64; + ret |= SETIREGANDOR(SISSR, 0x14, 0xfc,0x01); + + ret |= sisusb_triggersr16(sisusb, ramtype); + ret |= WRITEL(ramptr + 0, 0x89abcdef); + ret |= WRITEL(ramptr + 4, 0xcdef0123); + ret |= WRITEL(ramptr + 8, 0x55555555); + ret |= WRITEL(ramptr + 12, 0x55555555); + ret |= WRITEL(ramptr + 16, 0xaaaaaaaa); + ret |= WRITEL(ramptr + 20, 0xaaaaaaaa); + ret |= READL(ramptr + 4, &t1); + + if (t1 != 0xcdef0123) { + *bw = 32; + ret |= SETIREGOR(SISSR, 0x15, 0x10); + } + } + + } else { + + *chab = 0; *bw = 64; /* default: cha, bw = 64 */ + + done = 0; + + if (t1 == 0x456789ab) { + if (t0 == 0x01234567) { + *chab = 0; *bw = 64; + done = 1; + } + } else { + if (t0 == 0x01234567) { + *chab = 0; *bw = 32; + ret |= SETIREG(SISSR, 0x14, 0x00); + done = 1; + } + } + + if (!done) { + ret |= SETIREG(SISSR, 0x14, 0x03); + ret |= sisusb_triggersr16(sisusb, ramtype); + + ret |= WRITEL(ramptr + 0, 0x01234567); + ret |= WRITEL(ramptr + 4, 0x456789ab); + ret |= WRITEL(ramptr + 8, 0x89abcdef); + ret |= WRITEL(ramptr + 12, 0xcdef0123); + ret |= WRITEL(ramptr + 16, 0x55555555); + ret |= WRITEL(ramptr + 20, 0x55555555); + ret |= WRITEL(ramptr + 24, 0xffffffff); + ret |= WRITEL(ramptr + 28, 0xffffffff); + ret |= READL(ramptr + 0, &t0); + ret |= READL(ramptr + 4, &t1); + + if (t1 == 0x456789ab) { + if (t0 == 0x01234567) { + *chab = 1; *bw = 64; + return ret; + } /* else error */ + } else { + if (t0 == 0x01234567) { + *chab = 1; *bw = 32; + ret |= SETIREG(SISSR, 0x14, 0x01); + } /* else error */ + } + } + } + return ret; +} + +static int +sisusb_verify_mclk(struct sisusb_usb_data *sisusb) +{ + int ret = 0; + u32 ramptr = SISUSB_PCI_MEMBASE; + u8 tmp1, tmp2, i, j; + + ret |= WRITEB(ramptr, 0xaa); + ret |= WRITEB(ramptr + 16, 0x55); + ret |= READB(ramptr, &tmp1); + ret |= READB(ramptr + 16, &tmp2); + if ((tmp1 != 0xaa) || (tmp2 != 0x55)) { + for (i = 0, j = 16; i < 2; i++, j += 16) { + ret |= GETIREG(SISSR, 0x21, &tmp1); + ret |= SETIREGAND(SISSR, 0x21, (tmp1 & 0xfb)); + ret |= SETIREGOR(SISSR, 0x3c, 0x01); /* not on 330 */ + ret |= SETIREGAND(SISSR, 0x3c, 0xfe); /* not on 330 */ + ret |= SETIREG(SISSR, 0x21, tmp1); + ret |= WRITEB(ramptr + 16 + j, j); + ret |= READB(ramptr + 16 + j, &tmp1); + if (tmp1 == j) { + ret |= WRITEB(ramptr + j, j); + break; + } + } + } + return ret; +} + +static int +sisusb_set_rank(struct sisusb_usb_data *sisusb, int *iret, int index, + u8 rankno, u8 chab, const u8 dramtype[][5], + int bw) +{ + int ret = 0, ranksize; + u8 tmp; + + *iret = 0; + + if ((rankno == 2) && (dramtype[index][0] == 2)) + return ret; + + ranksize = dramtype[index][3] / 2 * bw / 32; + + if ((ranksize * rankno) > 128) + return ret; + + tmp = 0; + while ((ranksize >>= 1) > 0) tmp += 0x10; + tmp |= ((rankno - 1) << 2); + tmp |= ((bw / 64) & 0x02); + tmp |= (chab & 0x01); + + ret = SETIREG(SISSR, 0x14, tmp); + ret |= sisusb_triggersr16(sisusb, 0); /* sic! */ + + *iret = 1; + + return ret; +} + +static int +sisusb_check_rbc(struct sisusb_usb_data *sisusb, int *iret, u32 inc, int testn) +{ + int ret = 0, i; + u32 j, tmp; + + *iret = 0; + + for (i = 0, j = 0; i < testn; i++) { + ret |= WRITEL(sisusb->vrambase + j, j); + j += inc; + } + + for (i = 0, j = 0; i < testn; i++) { + ret |= READL(sisusb->vrambase + j, &tmp); + if (tmp != j) return ret; + j += inc; + } + + *iret = 1; + return ret; +} + +static int +sisusb_check_ranks(struct sisusb_usb_data *sisusb, int *iret, int rankno, + int idx, int bw, const u8 rtype[][5]) +{ + int ret = 0, i, i2ret; + u32 inc; + + *iret = 0; + + for (i = rankno; i >= 1; i--) { + inc = 1 << (rtype[idx][2] + + rtype[idx][1] + + rtype[idx][0] + + bw / 64 + i); + ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 2); + if (!i2ret) + return ret; + } + + inc = 1 << (rtype[idx][2] + bw / 64 + 2); + ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 4); + if (!i2ret) + return ret; + + inc = 1 << (10 + bw / 64); + ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 2); + if (!i2ret) + return ret; + + *iret = 1; + return ret; +} + +static int +sisusb_get_sdram_size(struct sisusb_usb_data *sisusb, int *iret, int bw, + int chab) +{ + int ret = 0, i2ret = 0, i, j; + static const u8 sdramtype[13][5] = { + { 2, 12, 9, 64, 0x35 }, + { 1, 13, 9, 64, 0x44 }, + { 2, 12, 8, 32, 0x31 }, + { 2, 11, 9, 32, 0x25 }, + { 1, 12, 9, 32, 0x34 }, + { 1, 13, 8, 32, 0x40 }, + { 2, 11, 8, 16, 0x21 }, + { 1, 12, 8, 16, 0x30 }, + { 1, 11, 9, 16, 0x24 }, + { 1, 11, 8, 8, 0x20 }, + { 2, 9, 8, 4, 0x01 }, + { 1, 10, 8, 4, 0x10 }, + { 1, 9, 8, 2, 0x00 } + }; + + *iret = 1; /* error */ + + for (i = 0; i < 13; i++) { + ret |= SETIREGANDOR(SISSR, 0x13, 0x80, sdramtype[i][4]); + for (j = 2; j > 0; j--) { + ret |= sisusb_set_rank(sisusb, &i2ret, i, j, + chab, sdramtype, bw); + if (!i2ret) + continue; + + ret |= sisusb_check_ranks(sisusb, &i2ret, j, i, + bw, sdramtype); + if (i2ret) { + *iret = 0; /* ram size found */ + return ret; + } + } + } + + return ret; +} + +static int +sisusb_setup_screen(struct sisusb_usb_data *sisusb, int clrall, int drwfr) +{ + int ret = 0; + u32 address; + int i, length, modex, modey, bpp; + + modex = 640; modey = 480; bpp = 2; + + address = sisusb->vrambase; /* Clear video ram */ + + if (clrall) + length = sisusb->vramsize; + else + length = modex * bpp * modey; + + ret = sisusb_clear_vram(sisusb, address, length); + + if (!ret && drwfr) { + for (i = 0; i < modex; i++) { + address = sisusb->vrambase + (i * bpp); + ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM, + address, 0xf100); + address += (modex * (modey-1) * bpp); + ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM, + address, 0xf100); + } + for (i = 0; i < modey; i++) { + address = sisusb->vrambase + ((i * modex) * bpp); + ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM, + address, 0xf100); + address += ((modex - 1) * bpp); + ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM, + address, 0xf100); + } + } + + return ret; +} + +static int +sisusb_set_default_mode(struct sisusb_usb_data *sisusb, int touchengines) +{ + int ret = 0, i, j, modex, modey, bpp, du; + u8 sr31, cr63, tmp8; + static const char attrdata[] = { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x01,0x00,0x00,0x00 + }; + static const char crtcrdata[] = { + 0x5f,0x4f,0x50,0x82,0x54,0x80,0x0b,0x3e, + 0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00, + 0xea,0x8c,0xdf,0x28,0x40,0xe7,0x04,0xa3, + 0xff + }; + static const char grcdata[] = { + 0x00,0x00,0x00,0x00,0x00,0x40,0x05,0x0f, + 0xff + }; + static const char crtcdata[] = { + 0x5f,0x4f,0x4f,0x83,0x55,0x81,0x0b,0x3e, + 0xe9,0x8b,0xdf,0xe8,0x0c,0x00,0x00,0x05, + 0x00 + }; + + modex = 640; modey = 480; bpp = 2; + + GETIREG(SISSR, 0x31, &sr31); + GETIREG(SISCR, 0x63, &cr63); + SETIREGOR(SISSR, 0x01, 0x20); + SETIREG(SISCR, 0x63, cr63 & 0xbf); + SETIREGOR(SISCR, 0x17, 0x80); + SETIREGOR(SISSR, 0x1f, 0x04); + SETIREGAND(SISSR, 0x07, 0xfb); + SETIREG(SISSR, 0x00, 0x03); /* seq */ + SETIREG(SISSR, 0x01, 0x21); + SETIREG(SISSR, 0x02, 0x0f); + SETIREG(SISSR, 0x03, 0x00); + SETIREG(SISSR, 0x04, 0x0e); + SETREG(SISMISCW,0x23); /* misc */ + for (i = 0; i <= 0x18; i++) { /* crtc */ + SETIREG(SISCR, i, crtcrdata[i]); + } + for (i = 0; i <= 0x13; i++) { /* att */ + GETREG(SISINPSTAT, &tmp8); + SETREG(SISAR, i); + SETREG(SISAR, attrdata[i]); + } + GETREG(SISINPSTAT, &tmp8); + SETREG(SISAR, 0x14); + SETREG(SISAR, 0x00); + GETREG(SISINPSTAT, &tmp8); + SETREG(SISAR, 0x20); + GETREG(SISINPSTAT, &tmp8); + for (i = 0; i <= 0x08; i++) { /* grc */ + SETIREG(SISGR, i, grcdata[i]); + } + SETIREGAND(SISGR, 0x05, 0xbf); + for (i = 0x0A; i <= 0x0E; i++) { /* clr ext */ + SETIREG(SISSR, i, 0x00); + } + SETIREGAND(SISSR, 0x37, 0xfe); + SETREG(SISMISCW, 0xef); /* sync */ + SETIREG(SISCR, 0x11, 0x00); /* crtc */ + for (j = 0x00, i = 0; i <= 7; i++, j++) { + SETIREG(SISCR, j, crtcdata[i]); + } + for (j = 0x10; i <= 10; i++, j++) { + SETIREG(SISCR, j, crtcdata[i]); + } + for (j = 0x15; i <= 12; i++, j++) { + SETIREG(SISCR, j, crtcdata[i]); + } + for (j = 0x0A; i <= 15; i++, j++) { + SETIREG(SISSR, j, crtcdata[i]); + } + SETIREG(SISSR, 0x0E, (crtcdata[16] & 0xE0)); + SETIREGANDOR(SISCR, 0x09, 0x5f, ((crtcdata[16] & 0x01) << 5)); + SETIREG(SISCR, 0x14, 0x4f); + du = (modex / 16) * (bpp * 2); /* offset/pitch */ + if (modex % 16) du += bpp; + SETIREGANDOR(SISSR, 0x0e, 0xf0, ((du >> 8) & 0x0f)); + SETIREG(SISCR, 0x13, (du & 0xff)); + du <<= 5; + tmp8 = du >> 8; + if (du & 0xff) tmp8++; + SETIREG(SISSR, 0x10, tmp8); + SETIREG(SISSR, 0x31, 0x00); /* VCLK */ + SETIREG(SISSR, 0x2b, 0x1b); + SETIREG(SISSR, 0x2c, 0xe1); + SETIREG(SISSR, 0x2d, 0x01); + SETIREGAND(SISSR, 0x3d, 0xfe); /* FIFO */ + SETIREG(SISSR, 0x08, 0xae); + SETIREGAND(SISSR, 0x09, 0xf0); + SETIREG(SISSR, 0x08, 0x34); + SETIREGOR(SISSR, 0x3d, 0x01); + SETIREGAND(SISSR, 0x1f, 0x3f); /* mode regs */ + SETIREGANDOR(SISSR, 0x06, 0xc0, 0x0a); + SETIREG(SISCR, 0x19, 0x00); + SETIREGAND(SISCR, 0x1a, 0xfc); + SETIREGAND(SISSR, 0x0f, 0xb7); + SETIREGAND(SISSR, 0x31, 0xfb); + SETIREGANDOR(SISSR, 0x21, 0x1f, 0xa0); + SETIREGAND(SISSR, 0x32, 0xf3); + SETIREGANDOR(SISSR, 0x07, 0xf8, 0x03); + SETIREG(SISCR, 0x52, 0x6c); + + SETIREG(SISCR, 0x0d, 0x00); /* adjust frame */ + SETIREG(SISCR, 0x0c, 0x00); + SETIREG(SISSR, 0x0d, 0x00); + SETIREGAND(SISSR, 0x37, 0xfe); + + SETIREG(SISCR, 0x32, 0x20); + SETIREGAND(SISSR, 0x01, 0xdf); /* enable display */ + SETIREG(SISCR, 0x63, (cr63 & 0xbf)); + SETIREG(SISSR, 0x31, (sr31 & 0xfb)); + + if (touchengines) { + SETIREG(SISSR, 0x20, 0xa1); /* enable engines */ + SETIREGOR(SISSR, 0x1e, 0x5a); + + SETIREG(SISSR, 0x26, 0x01); /* disable cmdqueue */ + SETIREG(SISSR, 0x27, 0x1f); + SETIREG(SISSR, 0x26, 0x00); + } + + SETIREG(SISCR, 0x34, 0x44); /* we just set std mode #44 */ + + return ret; +} + +static int +sisusb_init_gfxcore(struct sisusb_usb_data *sisusb) +{ + int ret = 0, i, j, bw, chab, iret, retry = 3; + u8 tmp8, ramtype; + u32 tmp32; + static const char mclktable[] = { + 0x3b, 0x22, 0x01, 143, + 0x3b, 0x22, 0x01, 143, + 0x3b, 0x22, 0x01, 143, + 0x3b, 0x22, 0x01, 143 + }; + static const char eclktable[] = { + 0x3b, 0x22, 0x01, 143, + 0x3b, 0x22, 0x01, 143, + 0x3b, 0x22, 0x01, 143, + 0x3b, 0x22, 0x01, 143 + }; + static const char ramtypetable1[] = { + 0x00, 0x04, 0x60, 0x60, + 0x0f, 0x0f, 0x1f, 0x1f, + 0xba, 0xba, 0xba, 0xba, + 0xa9, 0xa9, 0xac, 0xac, + 0xa0, 0xa0, 0xa0, 0xa8, + 0x00, 0x00, 0x02, 0x02, + 0x30, 0x30, 0x40, 0x40 + }; + static const char ramtypetable2[] = { + 0x77, 0x77, 0x44, 0x44, + 0x77, 0x77, 0x44, 0x44, + 0x00, 0x00, 0x00, 0x00, + 0x5b, 0x5b, 0xab, 0xab, + 0x00, 0x00, 0xf0, 0xf8 + }; + + while (retry--) { + + /* Enable VGA */ + ret = GETREG(SISVGAEN, &tmp8); + ret |= SETREG(SISVGAEN, (tmp8 | 0x01)); + + /* Enable GPU access to VRAM */ + ret |= GETREG(SISMISCR, &tmp8); + ret |= SETREG(SISMISCW, (tmp8 | 0x01)); + + if (ret) continue; + + /* Reset registers */ + ret |= SETIREGAND(SISCR, 0x5b, 0xdf); + ret |= SETIREG(SISSR, 0x05, 0x86); + ret |= SETIREGOR(SISSR, 0x20, 0x01); + + ret |= SETREG(SISMISCW, 0x67); + + for (i = 0x06; i <= 0x1f; i++) { + ret |= SETIREG(SISSR, i, 0x00); + } + for (i = 0x21; i <= 0x27; i++) { + ret |= SETIREG(SISSR, i, 0x00); + } + for (i = 0x31; i <= 0x3d; i++) { + ret |= SETIREG(SISSR, i, 0x00); + } + for (i = 0x12; i <= 0x1b; i++) { + ret |= SETIREG(SISSR, i, 0x00); + } + for (i = 0x79; i <= 0x7c; i++) { + ret |= SETIREG(SISCR, i, 0x00); + } + + if (ret) continue; + + ret |= SETIREG(SISCR, 0x63, 0x80); + + ret |= GETIREG(SISSR, 0x3a, &ramtype); + ramtype &= 0x03; + + ret |= SETIREG(SISSR, 0x28, mclktable[ramtype * 4]); + ret |= SETIREG(SISSR, 0x29, mclktable[(ramtype * 4) + 1]); + ret |= SETIREG(SISSR, 0x2a, mclktable[(ramtype * 4) + 2]); + + ret |= SETIREG(SISSR, 0x2e, eclktable[ramtype * 4]); + ret |= SETIREG(SISSR, 0x2f, eclktable[(ramtype * 4) + 1]); + ret |= SETIREG(SISSR, 0x30, eclktable[(ramtype * 4) + 2]); + + ret |= SETIREG(SISSR, 0x07, 0x18); + ret |= SETIREG(SISSR, 0x11, 0x0f); + + if (ret) continue; + + for (i = 0x15, j = 0; i <= 0x1b; i++, j++) { + ret |= SETIREG(SISSR, i, ramtypetable1[(j*4) + ramtype]); + } + for (i = 0x40, j = 0; i <= 0x44; i++, j++) { + ret |= SETIREG(SISCR, i, ramtypetable2[(j*4) + ramtype]); + } + + ret |= SETIREG(SISCR, 0x49, 0xaa); + + ret |= SETIREG(SISSR, 0x1f, 0x00); + ret |= SETIREG(SISSR, 0x20, 0xa0); + ret |= SETIREG(SISSR, 0x23, 0xf6); + ret |= SETIREG(SISSR, 0x24, 0x0d); + ret |= SETIREG(SISSR, 0x25, 0x33); + + ret |= SETIREG(SISSR, 0x11, 0x0f); + + ret |= SETIREGOR(SISPART1, 0x2f, 0x01); + + ret |= SETIREGAND(SISCAP, 0x3f, 0xef); + + if (ret) continue; + + ret |= SETIREG(SISPART1, 0x00, 0x00); + + ret |= GETIREG(SISSR, 0x13, &tmp8); + tmp8 >>= 4; + + ret |= SETIREG(SISPART1, 0x02, 0x00); + ret |= SETIREG(SISPART1, 0x2e, 0x08); + + ret |= sisusb_read_pci_config(sisusb, 0x50, &tmp32); + tmp32 &= 0x00f00000; + tmp8 = (tmp32 == 0x100000) ? 0x33 : 0x03; + ret |= SETIREG(SISSR, 0x25, tmp8); + tmp8 = (tmp32 == 0x100000) ? 0xaa : 0x88; + ret |= SETIREG(SISCR, 0x49, tmp8); + + ret |= SETIREG(SISSR, 0x27, 0x1f); + ret |= SETIREG(SISSR, 0x31, 0x00); + ret |= SETIREG(SISSR, 0x32, 0x11); + ret |= SETIREG(SISSR, 0x33, 0x00); + + if (ret) continue; + + ret |= SETIREG(SISCR, 0x83, 0x00); + + ret |= sisusb_set_default_mode(sisusb, 0); + + ret |= SETIREGAND(SISSR, 0x21, 0xdf); + ret |= SETIREGOR(SISSR, 0x01, 0x20); + ret |= SETIREGOR(SISSR, 0x16, 0x0f); + + ret |= sisusb_triggersr16(sisusb, ramtype); + + /* Disable refresh */ + ret |= SETIREGAND(SISSR, 0x17, 0xf8); + ret |= SETIREGOR(SISSR, 0x19, 0x03); + + ret |= sisusb_getbuswidth(sisusb, &bw, &chab); + ret |= sisusb_verify_mclk(sisusb); + + if (ramtype <= 1) { + ret |= sisusb_get_sdram_size(sisusb, &iret, bw, chab); + if (iret) { + printk(KERN_ERR "sisusbvga[%d]: RAM size " + "detection failed, " + "assuming 8MB video RAM\n", + sisusb->minor); + ret |= SETIREG(SISSR,0x14,0x31); + /* TODO */ + } + } else { + printk(KERN_ERR "sisusbvga[%d]: DDR RAM device found, " + "assuming 8MB video RAM\n", + sisusb->minor); + ret |= SETIREG(SISSR,0x14,0x31); + /* *** TODO *** */ + } + + /* Enable refresh */ + ret |= SETIREG(SISSR, 0x16, ramtypetable1[4 + ramtype]); + ret |= SETIREG(SISSR, 0x17, ramtypetable1[8 + ramtype]); + ret |= SETIREG(SISSR, 0x19, ramtypetable1[16 + ramtype]); + + ret |= SETIREGOR(SISSR, 0x21, 0x20); + + ret |= SETIREG(SISSR, 0x22, 0xfb); + ret |= SETIREG(SISSR, 0x21, 0xa5); + + if (ret == 0) + break; + } + + return ret; +} + +#undef SETREG +#undef GETREG +#undef SETIREG +#undef GETIREG +#undef SETIREGOR +#undef SETIREGAND +#undef SETIREGANDOR +#undef READL +#undef WRITEL + +static void +sisusb_get_ramconfig(struct sisusb_usb_data *sisusb) +{ + u8 tmp8, tmp82, ramtype; + int bw = 0; + char *ramtypetext1 = NULL; + const char *ramtypetext2[] = { "SDR SDRAM", "SDR SGRAM", + "DDR SDRAM", "DDR SGRAM" }; + static const int busSDR[4] = {64, 64, 128, 128}; + static const int busDDR[4] = {32, 32, 64, 64}; + static const int busDDRA[4] = {64+32, 64+32 , (64+32)*2, (64+32)*2}; + + sisusb_getidxreg(sisusb, SISSR, 0x14, &tmp8); + sisusb_getidxreg(sisusb, SISSR, 0x15, &tmp82); + sisusb_getidxreg(sisusb, SISSR, 0x3a, &ramtype); + sisusb->vramsize = (1 << ((tmp8 & 0xf0) >> 4)) * 1024 * 1024; + ramtype &= 0x03; + switch ((tmp8 >> 2) & 0x03) { + case 0: ramtypetext1 = "1 ch/1 r"; + if (tmp82 & 0x10) { + bw = 32; + } else { + bw = busSDR[(tmp8 & 0x03)]; + } + break; + case 1: ramtypetext1 = "1 ch/2 r"; + sisusb->vramsize <<= 1; + bw = busSDR[(tmp8 & 0x03)]; + break; + case 2: ramtypetext1 = "asymmeric"; + sisusb->vramsize += sisusb->vramsize/2; + bw = busDDRA[(tmp8 & 0x03)]; + break; + case 3: ramtypetext1 = "2 channel"; + sisusb->vramsize <<= 1; + bw = busDDR[(tmp8 & 0x03)]; + break; + } + + printk(KERN_INFO "sisusbvga[%d]: %dMB %s %s, bus width %d\n", + sisusb->minor, (sisusb->vramsize >> 20), ramtypetext1, + ramtypetext2[ramtype], bw); +} + +static int +sisusb_do_init_gfxdevice(struct sisusb_usb_data *sisusb) +{ + struct sisusb_packet packet; + int ret; + u32 tmp32; + + /* Do some magic */ + packet.header = 0x001f; + packet.address = 0x00000324; + packet.data = 0x00000004; + ret = sisusb_send_bridge_packet(sisusb, 10, &packet, 0); + + packet.header = 0x001f; + packet.address = 0x00000364; + packet.data = 0x00000004; + ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0); + + packet.header = 0x001f; + packet.address = 0x00000384; + packet.data = 0x00000004; + ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0); + + packet.header = 0x001f; + packet.address = 0x00000100; + packet.data = 0x00000700; + ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0); + + packet.header = 0x000f; + packet.address = 0x00000004; + ret |= sisusb_send_bridge_packet(sisusb, 6, &packet, 0); + packet.data |= 0x17; + ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0); + + /* Init BAR 0 (VRAM) */ + ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32); + ret |= sisusb_write_pci_config(sisusb, 0x10, 0xfffffff0); + ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32); + tmp32 &= 0x0f; + tmp32 |= SISUSB_PCI_MEMBASE; + ret |= sisusb_write_pci_config(sisusb, 0x10, tmp32); + + /* Init BAR 1 (MMIO) */ + ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32); + ret |= sisusb_write_pci_config(sisusb, 0x14, 0xfffffff0); + ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32); + tmp32 &= 0x0f; + tmp32 |= SISUSB_PCI_MMIOBASE; + ret |= sisusb_write_pci_config(sisusb, 0x14, tmp32); + + /* Init BAR 2 (i/o ports) */ + ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32); + ret |= sisusb_write_pci_config(sisusb, 0x18, 0xfffffff0); + ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32); + tmp32 &= 0x0f; + tmp32 |= SISUSB_PCI_IOPORTBASE; + ret |= sisusb_write_pci_config(sisusb, 0x18, tmp32); + + /* Enable memory and i/o access */ + ret |= sisusb_read_pci_config(sisusb, 0x04, &tmp32); + tmp32 |= 0x3; + ret |= sisusb_write_pci_config(sisusb, 0x04, tmp32); + + if (ret == 0) { + /* Some further magic */ + packet.header = 0x001f; + packet.address = 0x00000050; + packet.data = 0x000000ff; + ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0); + } + + return ret; +} + +/* Initialize the graphics device (return 0 on success) + * This initializes the net2280 as well as the PCI registers + * of the graphics board. + */ + +static int +sisusb_init_gfxdevice(struct sisusb_usb_data *sisusb, int initscreen) +{ + int ret = 0, test = 0; + u32 tmp32; + + if (sisusb->devinit == 1) { + /* Read PCI BARs and see if they have been set up */ + ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32); + if (ret) return ret; + if ((tmp32 & 0xfffffff0) == SISUSB_PCI_MEMBASE) test++; + + ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32); + if (ret) return ret; + if ((tmp32 & 0xfffffff0) == SISUSB_PCI_MMIOBASE) test++; + + ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32); + if (ret) return ret; + if ((tmp32 & 0xfffffff0) == SISUSB_PCI_IOPORTBASE) test++; + } + + /* No? So reset the device */ + if ((sisusb->devinit == 0) || (test != 3)) { + + ret |= sisusb_do_init_gfxdevice(sisusb); + + if (ret == 0) + sisusb->devinit = 1; + + } + + if (sisusb->devinit) { + /* Initialize the graphics core */ + if (sisusb_init_gfxcore(sisusb) == 0) { + sisusb->gfxinit = 1; + sisusb_get_ramconfig(sisusb); + ret |= sisusb_set_default_mode(sisusb, 1); + ret |= sisusb_setup_screen(sisusb, 1, initscreen); + } + } + + return ret; +} + +/* fops */ + +int +sisusb_open(struct inode *inode, struct file *file) +{ + struct sisusb_usb_data *sisusb; + struct usb_interface *interface; + int subminor = iminor(inode); + + down(&disconnect_sem); + + if (!(interface = usb_find_interface(&sisusb_driver, subminor))) { + printk(KERN_ERR "sisusb[%d]: Failed to find interface\n", + subminor); + up(&disconnect_sem); + return -ENODEV; + } + + if (!(sisusb = usb_get_intfdata(interface))) { + up(&disconnect_sem); + return -ENODEV; + } + + down(&sisusb->lock); + + if (!sisusb->present || !sisusb->ready) { + up(&sisusb->lock); + up(&disconnect_sem); + return -ENODEV; + } + + if (sisusb->isopen) { + up(&sisusb->lock); + up(&disconnect_sem); + return -EBUSY; + } + + if (!sisusb->devinit) { + if (sisusb->sisusb_dev->speed == USB_SPEED_HIGH) { + if (sisusb_init_gfxdevice(sisusb, 0)) { + up(&sisusb->lock); + up(&disconnect_sem); + printk(KERN_ERR + "sisusbvga[%d]: Failed to initialize " + "device\n", + sisusb->minor); + return -EIO; + } + } else { + up(&sisusb->lock); + up(&disconnect_sem); + printk(KERN_ERR + "sisusbvga[%d]: Device not attached to " + "USB 2.0 hub\n", + sisusb->minor); + return -EIO; + } + } + + /* increment usage count for the device */ + kref_get(&sisusb->kref); + + sisusb->isopen = 1; + + file->private_data = sisusb; + + up(&sisusb->lock); + + up(&disconnect_sem); + + printk(KERN_DEBUG "sisusbvga[%d]: opened", sisusb->minor); + + return 0; +} + +static void +sisusb_delete(struct kref *kref) +{ + struct sisusb_usb_data *sisusb = to_sisusb_dev(kref); + + if (!sisusb) + return; + + if (sisusb->sisusb_dev) + usb_put_dev(sisusb->sisusb_dev); + + sisusb->sisusb_dev = NULL; + sisusb_free_buffers(sisusb); + sisusb_free_urbs(sisusb); + kfree(sisusb); +} + +int +sisusb_release(struct inode *inode, struct file *file) +{ + struct sisusb_usb_data *sisusb; + int myminor; + + down(&disconnect_sem); + + if (!(sisusb = (struct sisusb_usb_data *)file->private_data)) { + up(&disconnect_sem); + return -ENODEV; + } + + down(&sisusb->lock); + + if (sisusb->present) { + /* Wait for all URBs to finish if device still present */ + if (!sisusb_wait_all_out_complete(sisusb)) + sisusb_kill_all_busy(sisusb); + } + + myminor = sisusb->minor; + + sisusb->isopen = 0; + file->private_data = NULL; + + up(&sisusb->lock); + + /* decrement the usage count on our device */ + kref_put(&sisusb->kref, sisusb_delete); + + up(&disconnect_sem); + + printk(KERN_DEBUG "sisusbvga[%d]: released", myminor); + + return 0; +} + +ssize_t +sisusb_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) +{ + struct sisusb_usb_data *sisusb; + ssize_t bytes_read = 0; + int errno = 0; + u8 buf8; + u16 buf16; + u32 buf32, address; + + if (!(sisusb = (struct sisusb_usb_data *)file->private_data)) + return -ENODEV; + + down(&sisusb->lock); + + /* Sanity check */ + if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) { + up(&sisusb->lock); + return -ENODEV; + } + + if ((*ppos) >= SISUSB_PCI_PSEUDO_IOPORTBASE && + (*ppos) < SISUSB_PCI_PSEUDO_IOPORTBASE + 128) { + + address = (*ppos) - + SISUSB_PCI_PSEUDO_IOPORTBASE + + SISUSB_PCI_IOPORTBASE; + + /* Read i/o ports + * Byte, word and long(32) can be read. As this + * emulates inX instructions, the data returned is + * in machine-endianness. + */ + switch (count) { + + case 1: + if (sisusb_read_memio_byte(sisusb, + SISUSB_TYPE_IO, + address, &buf8)) + errno = -EIO; + else if (put_user(buf8, (u8 __user *)buffer)) + errno = -EFAULT; + else + bytes_read = 1; + + break; + + case 2: + if (sisusb_read_memio_word(sisusb, + SISUSB_TYPE_IO, + address, &buf16)) + errno = -EIO; + else if (put_user(buf16, (u16 __user *)buffer)) + errno = -EFAULT; + else + bytes_read = 2; + + break; + + case 4: + if (sisusb_read_memio_long(sisusb, + SISUSB_TYPE_IO, + address, &buf32)) + errno = -EIO; + else if (put_user(buf32, (u32 __user *)buffer)) + errno = -EFAULT; + else + bytes_read = 4; + + break; + + default: + errno = -EIO; + + } + + } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MEMBASE && + (*ppos) < SISUSB_PCI_PSEUDO_MEMBASE + sisusb->vramsize) { + + address = (*ppos) - + SISUSB_PCI_PSEUDO_MEMBASE + + SISUSB_PCI_MEMBASE; + + /* Read video ram + * Remember: Data delivered is never endian-corrected + */ + errno = sisusb_read_mem_bulk(sisusb, address, + NULL, count, buffer, &bytes_read); + + if (bytes_read) + errno = bytes_read; + + } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MMIOBASE && + (*ppos) < SISUSB_PCI_PSEUDO_MMIOBASE + SISUSB_PCI_MMIOSIZE) { + + address = (*ppos) - + SISUSB_PCI_PSEUDO_MMIOBASE + + SISUSB_PCI_MMIOBASE; + + /* Read MMIO + * Remember: Data delivered is never endian-corrected + */ + errno = sisusb_read_mem_bulk(sisusb, address, + NULL, count, buffer, &bytes_read); + + if (bytes_read) + errno = bytes_read; + + } else if ((*ppos) >= SISUSB_PCI_PSEUDO_PCIBASE && + (*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE + 0x5c) { + + if (count != 4) { + up(&sisusb->lock); + return -EINVAL; + } + + address = (*ppos) - SISUSB_PCI_PSEUDO_PCIBASE; + + /* Read PCI config register + * Return value delivered in machine endianness. + */ + if (sisusb_read_pci_config(sisusb, address, &buf32)) + errno = -EIO; + else if (put_user(buf32, (u32 __user *)buffer)) + errno = -EFAULT; + else + bytes_read = 4; + + } else { + + errno = -EBADFD; + + } + + (*ppos) += bytes_read; + + up(&sisusb->lock); + + return errno ? errno : bytes_read; +} + +ssize_t +sisusb_write(struct file *file, const char __user *buffer, size_t count, + loff_t *ppos) +{ + struct sisusb_usb_data *sisusb; + int errno = 0; + ssize_t bytes_written = 0; + u8 buf8; + u16 buf16; + u32 buf32, address; + + if (!(sisusb = (struct sisusb_usb_data *)file->private_data)) + return -ENODEV; + + down(&sisusb->lock); + + /* Sanity check */ + if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) { + up(&sisusb->lock); + return -ENODEV; + } + + if ((*ppos) >= SISUSB_PCI_PSEUDO_IOPORTBASE && + (*ppos) < SISUSB_PCI_PSEUDO_IOPORTBASE + 128) { + + address = (*ppos) - + SISUSB_PCI_PSEUDO_IOPORTBASE + + SISUSB_PCI_IOPORTBASE; + + /* Write i/o ports + * Byte, word and long(32) can be written. As this + * emulates outX instructions, the data is expected + * in machine-endianness. + */ + switch (count) { + + case 1: + if (get_user(buf8, (u8 __user *)buffer)) + errno = -EFAULT; + else if (sisusb_write_memio_byte(sisusb, + SISUSB_TYPE_IO, + address, buf8)) + errno = -EIO; + else + bytes_written = 1; + + break; + + case 2: + if (get_user(buf16, (u16 __user *)buffer)) + errno = -EFAULT; + else if (sisusb_write_memio_word(sisusb, + SISUSB_TYPE_IO, + address, buf16)) + errno = -EIO; + else + bytes_written = 2; + + break; + + case 4: + if (get_user(buf32, (u32 __user *)buffer)) + errno = -EFAULT; + else if (sisusb_write_memio_long(sisusb, + SISUSB_TYPE_IO, + address, buf32)) + errno = -EIO; + else + bytes_written = 4; + + break; + + default: + errno = -EIO; + } + + } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MEMBASE && + (*ppos) < SISUSB_PCI_PSEUDO_MEMBASE + sisusb->vramsize) { + + address = (*ppos) - + SISUSB_PCI_PSEUDO_MEMBASE + + SISUSB_PCI_MEMBASE; + + /* Write video ram. + * Buffer is copied 1:1, therefore, on big-endian + * machines, the data must be swapped by userland + * in advance (if applicable; no swapping in 8bpp + * mode or if YUV data is being transferred). + */ + errno = sisusb_write_mem_bulk(sisusb, address, NULL, + count, buffer, 0, &bytes_written); + + if (bytes_written) + errno = bytes_written; + + } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MMIOBASE && + (*ppos) < SISUSB_PCI_PSEUDO_MMIOBASE + SISUSB_PCI_MMIOSIZE) { + + address = (*ppos) - + SISUSB_PCI_PSEUDO_MMIOBASE + + SISUSB_PCI_MMIOBASE; + + /* Write MMIO. + * Buffer is copied 1:1, therefore, on big-endian + * machines, the data must be swapped by userland + * in advance. + */ + errno = sisusb_write_mem_bulk(sisusb, address, NULL, + count, buffer, 0, &bytes_written); + + if (bytes_written) + errno = bytes_written; + + } else if ((*ppos) >= SISUSB_PCI_PSEUDO_PCIBASE && + (*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE + SISUSB_PCI_PCONFSIZE) { + + if (count != 4) { + up(&sisusb->lock); + return -EINVAL; + } + + address = (*ppos) - SISUSB_PCI_PSEUDO_PCIBASE; + + /* Write PCI config register. + * Given value expected in machine endianness. + */ + if (get_user(buf32, (u32 __user *)buffer)) + errno = -EFAULT; + else if (sisusb_write_pci_config(sisusb, address, buf32)) + errno = -EIO; + else + bytes_written = 4; + + + } else { + + /* Error */ + errno = -EBADFD; + + } + + (*ppos) += bytes_written; + + up(&sisusb->lock); + + return errno ? errno : bytes_written; +} + +static loff_t +sisusb_lseek(struct file *file, loff_t offset, int orig) +{ + struct sisusb_usb_data *sisusb; + loff_t ret; + + if (!(sisusb = (struct sisusb_usb_data *)file->private_data)) + return -ENODEV; + + down(&sisusb->lock); + + /* Sanity check */ + if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) { + up(&sisusb->lock); + return -ENODEV; + } + + switch (orig) { + case 0: + file->f_pos = offset; + ret = file->f_pos; + /* never negative, no force_successful_syscall needed */ + break; + case 1: + file->f_pos += offset; + ret = file->f_pos; + /* never negative, no force_successful_syscall needed */ + break; + default: + /* seeking relative to "end of file" is not supported */ + ret = -EINVAL; + } + + up(&sisusb->lock); + return ret; +} + +static int +sisusb_handle_command(struct sisusb_usb_data *sisusb, struct sisusb_command *y, + unsigned long arg) +{ + int retval, port, length; + u32 address; + + port = y->data3 - + SISUSB_PCI_PSEUDO_IOPORTBASE + + SISUSB_PCI_IOPORTBASE; + + switch (y->operation) { + case SUCMD_GET: + retval = sisusb_getidxreg(sisusb, port, + y->data0, &y->data1); + if (!retval) { + if (copy_to_user((void __user *)arg, y, + sizeof(*y))) + retval = -EFAULT; + } + break; + + case SUCMD_SET: + retval = sisusb_setidxreg(sisusb, port, + y->data0, y->data1); + break; + + case SUCMD_SETOR: + retval = sisusb_setidxregor(sisusb, port, + y->data0, y->data1); + break; + + case SUCMD_SETAND: + retval = sisusb_setidxregand(sisusb, port, + y->data0, y->data1); + break; + + case SUCMD_SETANDOR: + retval = sisusb_setidxregandor(sisusb, port, + y->data0, y->data1, y->data2); + break; + + case SUCMD_SETMASK: + retval = sisusb_setidxregmask(sisusb, port, + y->data0, y->data1, y->data2); + break; + + case SUCMD_CLRSCR: + length = (y->data0 << 16) | (y->data1 << 8) | y->data2; + address = y->data3 - + SISUSB_PCI_PSEUDO_MEMBASE + + SISUSB_PCI_MEMBASE; + retval = sisusb_clear_vram(sisusb, address, length); + break; + + default: + retval = -EINVAL; + } + + if(retval > 0) + retval = -EIO; + + return retval; +} + +static int +sisusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct sisusb_usb_data *sisusb; + struct sisusb_info x; + struct sisusb_command y; + int retval = 0; + u32 __user *argp = (u32 __user *)arg; + + if (!(sisusb = (struct sisusb_usb_data *)file->private_data)) + return -ENODEV; + + down(&sisusb->lock); + + /* Sanity check */ + if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) { + retval = -ENODEV; + goto err_out; + } + + switch (cmd) { + + case SISUSB_GET_CONFIG_SIZE: + + if (put_user(sizeof(x), argp)) + retval = -EFAULT; + + break; + + case SISUSB_GET_CONFIG: + + x.sisusb_id = SISUSB_ID; + x.sisusb_version = SISUSB_VERSION; + x.sisusb_revision = SISUSB_REVISION; + x.sisusb_patchlevel = SISUSB_PATCHLEVEL; + x.sisusb_gfxinit = sisusb->gfxinit; + x.sisusb_vrambase = SISUSB_PCI_PSEUDO_MEMBASE; + x.sisusb_mmiobase = SISUSB_PCI_PSEUDO_MMIOBASE; + x.sisusb_iobase = SISUSB_PCI_PSEUDO_IOPORTBASE; + x.sisusb_pcibase = SISUSB_PCI_PSEUDO_PCIBASE; + x.sisusb_vramsize = sisusb->vramsize; + x.sisusb_minor = sisusb->minor; + + if (copy_to_user((void __user *)arg, &x, sizeof(x))) + retval = -EFAULT; + + break; + + case SISUSB_COMMAND: + + if (copy_from_user(&y, (void __user *)arg, sizeof(y))) + retval = -EFAULT; + else + retval = sisusb_handle_command(sisusb, &y, arg); + + break; + + default: + retval = -EINVAL; + break; + } + +err_out: + up(&sisusb->lock); + return retval; +} + +#ifdef SISUSB_NEW_CONFIG_COMPAT +static long +sisusb_compat_ioctl(struct file *f, unsigned int cmd, unsigned long arg) +{ + long retval; + + switch (cmd) { + case SISUSB_GET_CONFIG_SIZE: + case SISUSB_GET_CONFIG: + case SISUSB_COMMAND: + lock_kernel(); + retval = sisusb_ioctl(f->f_dentry->d_inode, f, cmd, arg); + unlock_kernel(); + return retval; + + default: + return -ENOIOCTLCMD; + } +} +#endif + +static struct file_operations usb_sisusb_fops = { + .owner = THIS_MODULE, + .open = sisusb_open, + .release = sisusb_release, + .read = sisusb_read, + .write = sisusb_write, + .llseek = sisusb_lseek, +#ifdef SISUSB_NEW_CONFIG_COMPAT + .compat_ioctl = sisusb_compat_ioctl, +#endif + .ioctl = sisusb_ioctl +}; + +static struct usb_class_driver usb_sisusb_class = { + .name = "usb/sisusbvga%d", + .fops = &usb_sisusb_fops, + .mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + .minor_base = SISUSB_MINOR +}; + +static int sisusb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct sisusb_usb_data *sisusb; + int retval = 0, i; + const char *memfail = + KERN_ERR + "sisusbvga[%d]: Failed to allocate memory for %s buffer\n"; + + printk(KERN_INFO "sisusb: USB2VGA dongle found at address %d\n", + dev->devnum); + + /* Allocate memory for our private */ + if (!(sisusb = kmalloc(sizeof(*sisusb), GFP_KERNEL))) { + printk(KERN_ERR + "sisusb: Failed to allocate memory for private data\n"); + return -ENOMEM; + } + memset(sisusb, 0, sizeof(*sisusb)); + kref_init(&sisusb->kref); + + init_MUTEX(&(sisusb->lock)); + + /* Register device */ + if ((retval = usb_register_dev(intf, &usb_sisusb_class))) { + printk(KERN_ERR + "sisusb: Failed to get a minor for device %d\n", + dev->devnum); + retval = -ENODEV; + goto error_1; + } + + sisusb->sisusb_dev = dev; + sisusb->minor = intf->minor; + sisusb->vrambase = SISUSB_PCI_MEMBASE; + sisusb->mmiobase = SISUSB_PCI_MMIOBASE; + sisusb->mmiosize = SISUSB_PCI_MMIOSIZE; + sisusb->ioportbase = SISUSB_PCI_IOPORTBASE; + /* Everything else is zero */ + + /* Allocate buffers */ + sisusb->ibufsize = SISUSB_IBUF_SIZE; + if (!(sisusb->ibuf = usb_buffer_alloc(dev, SISUSB_IBUF_SIZE, + GFP_KERNEL, &sisusb->transfer_dma_in))) { + printk(memfail, "input", sisusb->minor); + retval = -ENOMEM; + goto error_2; + } + + sisusb->numobufs = 0; + sisusb->obufsize = SISUSB_OBUF_SIZE; + for (i = 0; i < NUMOBUFS; i++) { + if (!(sisusb->obuf[i] = usb_buffer_alloc(dev, SISUSB_OBUF_SIZE, + GFP_KERNEL, + &sisusb->transfer_dma_out[i]))) { + if (i == 0) { + printk(memfail, "output", sisusb->minor); + retval = -ENOMEM; + goto error_3; + } + break; + } else + sisusb->numobufs++; + + } + + /* Allocate URBs */ + if (!(sisusb->sisurbin = usb_alloc_urb(0, GFP_KERNEL))) { + printk(KERN_ERR + "sisusbvga[%d]: Failed to allocate URBs\n", + sisusb->minor); + retval = -ENOMEM; + goto error_3; + } + sisusb->completein = 1; + + for (i = 0; i < sisusb->numobufs; i++) { + if (!(sisusb->sisurbout[i] = usb_alloc_urb(0, GFP_KERNEL))) { + printk(KERN_ERR + "sisusbvga[%d]: Failed to allocate URBs\n", + sisusb->minor); + retval = -ENOMEM; + goto error_4; + } + sisusb->urbout_context[i].sisusb = (void *)sisusb; + sisusb->urbout_context[i].urbindex = i; + sisusb->urbstatus[i] = 0; + } + + printk(KERN_INFO "sisusbvga[%d]: Allocated %d output buffers\n", + sisusb->minor, sisusb->numobufs); + + /* Do remaining init stuff */ + + init_waitqueue_head(&sisusb->wait_q); + + usb_set_intfdata(intf, sisusb); + +#ifdef SISUSB_OLD_CONFIG_COMPAT + { + int ret; + /* Our ioctls are all "32/64bit compatible" */ + ret = register_ioctl32_conversion(SISUSB_GET_CONFIG_SIZE, NULL); + ret |= register_ioctl32_conversion(SISUSB_GET_CONFIG, NULL); + ret |= register_ioctl32_conversion(SISUSB_COMMAND, NULL); + if (ret) + printk(KERN_ERR + "sisusbvga[%d]: Error registering ioctl32 " + "translations\n", + sisusb->minor); + else + sisusb->ioctl32registered = 1; + + } +#endif + + sisusb->present = 1; + + if (dev->speed == USB_SPEED_HIGH) { + if (sisusb_init_gfxdevice(sisusb, 1)) + printk(KERN_ERR + "sisusbvga[%d]: Failed to early " + "initialize device\n", + sisusb->minor); + + } else + printk(KERN_INFO + "sisusbvga[%d]: Not attached to USB 2.0 hub, " + "deferring init\n", + sisusb->minor); + + sisusb->ready = 1; + + return 0; + +error_4: + sisusb_free_urbs(sisusb); +error_3: + sisusb_free_buffers(sisusb); +error_2: + usb_deregister_dev(intf, &usb_sisusb_class); +error_1: + kfree(sisusb); + return retval; +} + +static void sisusb_disconnect(struct usb_interface *intf) +{ + struct sisusb_usb_data *sisusb; + int minor; + + down(&disconnect_sem); + + /* This should *not* happen */ + if (!(sisusb = usb_get_intfdata(intf))) { + up(&disconnect_sem); + return; + } + + down(&sisusb->lock); + + /* Wait for all URBs to complete and kill them in case (MUST do) */ + if (!sisusb_wait_all_out_complete(sisusb)) + sisusb_kill_all_busy(sisusb); + + minor = sisusb->minor; + + usb_set_intfdata(intf, NULL); + + usb_deregister_dev(intf, &usb_sisusb_class); + +#ifdef SISUSB_OLD_CONFIG_COMPAT + if (sisusb->ioctl32registered) { + int ret; + sisusb->ioctl32registered = 0; + ret = unregister_ioctl32_conversion(SISUSB_GET_CONFIG_SIZE); + ret |= unregister_ioctl32_conversion(SISUSB_GET_CONFIG); + ret |= unregister_ioctl32_conversion(SISUSB_COMMAND); + if (ret) { + printk(KERN_ERR + "sisusbvga[%d]: Error unregistering " + "ioctl32 translations\n", + minor); + } + } +#endif + + sisusb->present = 0; + sisusb->ready = 0; + + up(&sisusb->lock); + + /* decrement our usage count */ + kref_put(&sisusb->kref, sisusb_delete); + + up(&disconnect_sem); + + printk(KERN_INFO "sisusbvga[%d]: Disconnected\n", minor); +} + +static struct usb_device_id sisusb_table [] = { + { USB_DEVICE(0x0711, 0x0900) }, + { } +}; + +MODULE_DEVICE_TABLE (usb, sisusb_table); + +static struct usb_driver sisusb_driver = { + .owner = THIS_MODULE, + .name = "sisusb", + .probe = sisusb_probe, + .disconnect = sisusb_disconnect, + .id_table = sisusb_table +}; + +static int __init usb_sisusb_init(void) +{ + int retval; + + if (!(retval = usb_register(&sisusb_driver))) { + printk(KERN_INFO "sisusb: Driver version %d.%d.%d\n", + SISUSB_VERSION, SISUSB_REVISION, SISUSB_PATCHLEVEL); + printk(KERN_INFO + "sisusb: Copyright (C) 2005 Thomas Winischhofer\n"); + } + + return retval; +} + +static void __exit usb_sisusb_exit(void) +{ + usb_deregister(&sisusb_driver); +} + +module_init(usb_sisusb_init); +module_exit(usb_sisusb_exit); + +MODULE_AUTHOR("Thomas Winischhofer "); +MODULE_DESCRIPTION("sisusb - Driver for Net2280/SiS315-based USB2VGA dongles"); +MODULE_LICENSE("GPL"); + diff -Nru a/drivers/usb/misc/sisusbvga/sisusb.h b/drivers/usb/misc/sisusbvga/sisusb.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/usb/misc/sisusbvga/sisusb.h 2005-01-23 22:34:17 -08:00 @@ -0,0 +1,278 @@ +/* + * sisusb - usb kernel driver for Net2280/SiS315 based USB2VGA dongles + * + * Copyright (C) 2005 by Thomas Winischhofer, Vienna, Austria + * + * If distributed as part of the Linux kernel, this code is licensed under the + * terms of the GPL v2. + * + * Otherwise, the following license terms apply: + * + * * Redistribution and use in source and binary forms, with or without + * * modification, are permitted provided that the following conditions + * * are met: + * * 1) Redistributions of source code must retain the above copyright + * * notice, this list of conditions and the following disclaimer. + * * 2) Redistributions in binary form must reproduce the above copyright + * * notice, this list of conditions and the following disclaimer in the + * * documentation and/or other materials provided with the distribution. + * * 3) The name of the author may not be used to endorse or promote products + * * derived from this software without specific prior written permission. + * * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED OR + * * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Author: Thomas Winischhofer + * + */ + +#ifndef _SISUSB_H_ +#define _SISUSB_H_ + +#ifdef CONFIG_COMPAT +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,10) +#include +#define SISUSB_OLD_CONFIG_COMPAT +#else +#define SISUSB_NEW_CONFIG_COMPAT +#endif +#endif + +/* Version Information */ + +#define SISUSB_VERSION 0 +#define SISUSB_REVISION 0 +#define SISUSB_PATCHLEVEL 6 + +/* USB related */ + +#define SISUSB_MINOR 64 /* FIXME */ + +/* Size of the sisusb input/output buffers */ +#define SISUSB_IBUF_SIZE 0x01000 +#define SISUSB_OBUF_SIZE 0x10000 /* fixed */ + +#define NUMOBUFS 8 /* max number of output buffers/output URBs */ + +/* About endianness: + * + * 1) I/O ports, PCI config registers. The read/write() + * calls emulate inX/outX. Hence, the data is + * expected/delivered in machine endiannes by this + * driver. + * 2) Video memory. The data is copied 1:1. There is + * no swapping. Ever. This means for userland that + * the data has to be prepared properly. (Hint: + * think graphics data format, command queue, + * hardware cursor.) + * 3) MMIO. Data is copied 1:1. MMIO must be swapped + * properly by userland. + * + */ + +#ifdef __BIG_ENDIAN +#define SISUSB_CORRECT_ENDIANNESS_PACKET(p) \ + do { \ + p->header = cpu_to_le16(p->header); \ + p->address = cpu_to_le32(p->address); \ + p->address = cpu_to_le32(p->data); \ + } while(0) +#else +#define SISUSB_CORRECT_ENDIANNESS_PACKET(p) +#endif + +struct sisusb_usb_data; + +struct sisusb_urb_context { /* urb->context for outbound bulk URBs */ + struct sisusb_usb_data *sisusb; + int urbindex; + int *actual_length; +}; + +struct sisusb_usb_data { + struct usb_device *sisusb_dev; + struct usb_interface *interface; + struct kref kref; + wait_queue_head_t wait_q; /* for syncind and timeouts */ + struct semaphore lock; /* general race avoidance */ + unsigned int ifnum; /* interface number of the USB device */ + int minor; /* minor (for logging clarity) */ + int isopen; /* !=0 if open */ + int present; /* !=0 if device is present on the bus */ + int ready; /* !=0 if device is ready for userland */ +#ifdef SISUSB_OLD_CONFIG_COMPAT + int ioctl32registered; +#endif + int numobufs; /* number of obufs = number of out urbs */ + char *obuf[NUMOBUFS], *ibuf; /* transfer buffers */ + int obufsize, ibufsize; + dma_addr_t transfer_dma_out[NUMOBUFS]; + dma_addr_t transfer_dma_in; + struct urb *sisurbout[NUMOBUFS]; + struct urb *sisurbin; + unsigned char urbstatus[NUMOBUFS]; + unsigned char completein; + struct sisusb_urb_context urbout_context[NUMOBUFS]; + unsigned long flagb0; + unsigned long vrambase; /* framebuffer base */ + unsigned int vramsize; /* framebuffer size (bytes) */ + unsigned long mmiobase; + unsigned int mmiosize; + unsigned long ioportbase; + unsigned char devinit; /* device initialized? */ + unsigned char gfxinit; /* graphics core initialized? */ + unsigned short chipid, chipvendor; + unsigned short chiprevision; +}; + +#define to_sisusb_dev(d) container_of(d, struct sisusb_usb_data, kref) + +/* USB transport related */ + +/* urbstatus */ +#define SU_URB_BUSY 1 +#define SU_URB_ALLOC 2 + +/* Endpoints */ + +#define SISUSB_EP_GFX_IN 0x0e /* gfx std packet out(0e)/in(8e) */ +#define SISUSB_EP_GFX_OUT 0x0e + +#define SISUSB_EP_GFX_BULK_OUT 0x01 /* gfx mem bulk out/in */ +#define SISUSB_EP_GFX_BULK_IN 0x02 /* ? 2 is "OUT" ? */ + +#define SISUSB_EP_GFX_LBULK_OUT 0x03 /* gfx large mem bulk out */ + +#define SISUSB_EP_UNKNOWN_04 0x04 /* ? 4 is "OUT" ? - unused */ + +#define SISUSB_EP_BRIDGE_IN 0x0d /* Net2280 out(0d)/in(8d) */ +#define SISUSB_EP_BRIDGE_OUT 0x0d + +#define SISUSB_TYPE_MEM 0 +#define SISUSB_TYPE_IO 1 + +struct sisusb_packet { + unsigned short header; + u32 address; + u32 data; +} __attribute__((__packed__)); + +#define CLEARPACKET(packet) memset(packet, 0, 10) + +/* PCI bridge related */ + +#define SISUSB_PCI_MEMBASE 0xd0000000 +#define SISUSB_PCI_MMIOBASE 0xe4000000 +#define SISUSB_PCI_IOPORTBASE 0x0000d000 + +#define SISUSB_PCI_PSEUDO_MEMBASE 0x10000000 +#define SISUSB_PCI_PSEUDO_MMIOBASE 0x20000000 +#define SISUSB_PCI_PSEUDO_IOPORTBASE 0x0000d000 +#define SISUSB_PCI_PSEUDO_PCIBASE 0x00010000 + +#define SISUSB_PCI_MMIOSIZE (128*1024) +#define SISUSB_PCI_PCONFSIZE 0x5c + +/* graphics core related */ + +#define AROFFSET 0x40 +#define ARROFFSET 0x41 +#define GROFFSET 0x4e +#define SROFFSET 0x44 +#define CROFFSET 0x54 +#define MISCROFFSET 0x4c +#define MISCWOFFSET 0x42 +#define INPUTSTATOFFSET 0x5A +#define PART1OFFSET 0x04 +#define PART2OFFSET 0x10 +#define PART3OFFSET 0x12 +#define PART4OFFSET 0x14 +#define PART5OFFSET 0x16 +#define CAPTUREOFFSET 0x00 +#define VIDEOOFFSET 0x02 +#define COLREGOFFSET 0x48 +#define PELMASKOFFSET 0x46 +#define VGAENABLE 0x43 + +#define SISAR SISUSB_PCI_IOPORTBASE + AROFFSET +#define SISARR SISUSB_PCI_IOPORTBASE + ARROFFSET +#define SISGR SISUSB_PCI_IOPORTBASE + GROFFSET +#define SISSR SISUSB_PCI_IOPORTBASE + SROFFSET +#define SISCR SISUSB_PCI_IOPORTBASE + CROFFSET +#define SISMISCR SISUSB_PCI_IOPORTBASE + MISCROFFSET +#define SISMISCW SISUSB_PCI_IOPORTBASE + MISCWOFFSET +#define SISINPSTAT SISUSB_PCI_IOPORTBASE + INPUTSTATOFFSET +#define SISPART1 SISUSB_PCI_IOPORTBASE + PART1OFFSET +#define SISPART2 SISUSB_PCI_IOPORTBASE + PART2OFFSET +#define SISPART3 SISUSB_PCI_IOPORTBASE + PART3OFFSET +#define SISPART4 SISUSB_PCI_IOPORTBASE + PART4OFFSET +#define SISPART5 SISUSB_PCI_IOPORTBASE + PART5OFFSET +#define SISCAP SISUSB_PCI_IOPORTBASE + CAPTUREOFFSET +#define SISVID SISUSB_PCI_IOPORTBASE + VIDEOOFFSET +#define SISCOLIDXR SISUSB_PCI_IOPORTBASE + COLREGOFFSET - 1 +#define SISCOLIDX SISUSB_PCI_IOPORTBASE + COLREGOFFSET +#define SISCOLDATA SISUSB_PCI_IOPORTBASE + COLREGOFFSET + 1 +#define SISCOL2IDX SISPART5 +#define SISCOL2DATA SISPART5 + 1 +#define SISPEL SISUSB_PCI_IOPORTBASE + PELMASKOFFSET +#define SISVGAEN SISUSB_PCI_IOPORTBASE + VGAENABLE +#define SISDACA SISCOLIDX +#define SISDACD SISCOLDATA + +/* ioctl related */ + +/* Structure argument for SISUSB_GET_INFO ioctl */ +struct sisusb_info { + __u32 sisusb_id; /* for identifying sisusb */ +#define SISUSB_ID 0x53495355 /* Identify myself with 'SISU' */ + __u8 sisusb_version; + __u8 sisusb_revision; + __u8 sisusb_patchlevel; + __u8 sisusb_gfxinit; /* graphics core initialized? */ + + __u32 sisusb_vrambase; + __u32 sisusb_mmiobase; + __u32 sisusb_iobase; + __u32 sisusb_pcibase; + + __u32 sisusb_vramsize; /* framebuffer size in bytes */ + + __u32 sisusb_minor; + + __u32 sisusb_fbdevactive; /* != 0 if framebuffer device active */ + + __u8 sisusb_reserved[32]; /* for future use */ +}; + +struct sisusb_command { + __u8 operation; /* see below */ + __u8 data0; /* operation dependent */ + __u8 data1; /* operation dependent */ + __u8 data2; /* operation dependent */ + __u32 data3; /* operation dependent */ + __u32 data4; /* for future use */ +}; + +#define SUCMD_GET 0x01 /* for all: data0 = index, data3 = port */ +#define SUCMD_SET 0x02 /* data1 = value */ +#define SUCMD_SETOR 0x03 /* data1 = or */ +#define SUCMD_SETAND 0x04 /* data1 = and */ +#define SUCMD_SETANDOR 0x05 /* data1 = and, data2 = or */ +#define SUCMD_SETMASK 0x06 /* data1 = data, data2 = mask */ + +#define SUCMD_CLRSCR 0x07 /* data0:1:2 = length, data3 = address */ + +#define SISUSB_COMMAND _IOWR(0xF3,0x3D,struct sisusb_command) +#define SISUSB_GET_CONFIG_SIZE _IOR(0xF3,0x3E,__u32) +#define SISUSB_GET_CONFIG _IOR(0xF3,0x3F,struct sisusb_info) + +#endif /* SISUSB_H */ + diff -Nru a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c --- a/drivers/usb/serial/cypress_m8.c 2005-01-23 22:34:17 -08:00 +++ b/drivers/usb/serial/cypress_m8.c 2005-01-23 22:34:17 -08:00 @@ -57,9 +57,10 @@ #include #include #include -#include #include #include +#include +#include #include "usb-serial.h" #include "cypress_m8.h" @@ -1013,8 +1014,7 @@ cypress_serial_control(port, baud_mask, data_bits, stop_bits, parity_enable, parity_type, 0, CYPRESS_SET_CONFIG); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(50*HZ/1000); /* give some time between change and read (50ms) */ + msleep(50); /* give some time between change and read (50ms) */ /* we perform a CYPRESS_GET_CONFIG so that the current settings are filled into the private structure * this should confirm that all is working if it returns what we just set */ diff -Nru a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c --- a/drivers/usb/serial/ftdi_sio.c 2005-01-23 22:34:17 -08:00 +++ b/drivers/usb/serial/ftdi_sio.c 2005-01-23 22:34:17 -08:00 @@ -296,6 +296,7 @@ { USB_DEVICE_VER(FTDI_VID, FTDI_IRTRANS_PID, 0, 0x3ff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_PID, 0, 0x3ff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_ALT_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_ALT_ALT_PID, 0, 0x3ff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_RELAIS_PID, 0, 0x3ff) }, { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_IOBOARD_PID) }, { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_MINI_IOBOARD_PID) }, @@ -381,6 +382,7 @@ { USB_DEVICE_VER(FTDI_VID, FTDI_IRTRANS_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_ALT_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_ALT_ALT_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_RELAIS_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_XF_632_PID, 0x400, 0xffff) }, @@ -515,6 +517,7 @@ { USB_DEVICE(FTDI_VID, FTDI_SIO_PID) }, { USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) }, { USB_DEVICE(FTDI_VID, FTDI_8U232AM_ALT_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_8U232AM_ALT_ALT_PID) }, { USB_DEVICE(FTDI_VID, FTDI_8U2232C_PID) }, { USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) }, { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_IOBOARD_PID) }, @@ -1140,7 +1143,7 @@ goto check_and_exit; } - if ((new_serial.baud_base != priv->baud_base) || + if ((new_serial.baud_base != priv->baud_base) && (new_serial.baud_base < 9600)) return -EINVAL; diff -Nru a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h --- a/drivers/usb/serial/ftdi_sio.h 2005-01-23 22:34:17 -08:00 +++ b/drivers/usb/serial/ftdi_sio.h 2005-01-23 22:34:17 -08:00 @@ -26,6 +26,7 @@ #define FTDI_SIO_PID 0x8372 /* Product Id SIO application of 8U100AX */ #define FTDI_8U232AM_PID 0x6001 /* Similar device to SIO above */ #define FTDI_8U232AM_ALT_PID 0x6006 /* FTDI's alternate PID for above */ +#define FTDI_8U232AM_ALT_ALT_PID 0xf3c0 /* FTDI's second alternate PID for above */ #define FTDI_8U2232C_PID 0x6010 /* Dual channel device */ #define FTDI_RELAIS_PID 0xFA10 /* Relais device from Rudolf Gugler */ #define FTDI_NF_RIC_VID 0x0DCD /* Vendor Id */ diff -Nru a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c --- a/drivers/usb/storage/protocol.c 2005-01-23 22:34:17 -08:00 +++ b/drivers/usb/storage/protocol.c 2005-01-23 22:34:17 -08:00 @@ -54,39 +54,6 @@ #include "transport.h" /*********************************************************************** - * Helper routines - ***********************************************************************/ - -/* - * Fix-up the return data from a READ CAPACITY command. My Feiya reader - * returns a value that is 1 too large. - */ -static void fix_read_capacity(struct scsi_cmnd *srb) -{ - unsigned int index, offset; - __be32 c; - unsigned long capacity; - - /* verify that it's a READ CAPACITY command */ - if (srb->cmnd[0] != READ_CAPACITY) - return; - - index = offset = 0; - if (usb_stor_access_xfer_buf((unsigned char *) &c, 4, srb, - &index, &offset, FROM_XFER_BUF) != 4) - return; - - capacity = be32_to_cpu(c); - US_DEBUGP("US: Fixing capacity: from %ld to %ld\n", - capacity+1, capacity); - c = cpu_to_be32(capacity - 1); - - index = offset = 0; - usb_stor_access_xfer_buf((unsigned char *) &c, 4, srb, - &index, &offset, TO_XFER_BUF); -} - -/*********************************************************************** * Protocol routines ***********************************************************************/ @@ -174,12 +141,6 @@ { /* send the command to the transport layer */ usb_stor_invoke_transport(srb, us); - - if (srb->result == SAM_STAT_GOOD) { - /* Fix the READ CAPACITY result if necessary */ - if (us->flags & US_FL_FIX_CAPACITY) - fix_read_capacity(srb); - } } /*********************************************************************** diff -Nru a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c --- a/drivers/usb/storage/scsiglue.c 2005-01-23 22:34:17 -08:00 +++ b/drivers/usb/storage/scsiglue.c 2005-01-23 22:34:17 -08:00 @@ -149,6 +149,11 @@ sdev->skip_ms_page_3f = 1; #endif + /* Some disks return the total number blocks in response + * to READ CAPACITY rather than the highest block number. + * If this device makes that mistake, tell the sd driver. */ + if (us->flags & US_FL_FIX_CAPACITY) + sdev->fix_capacity = 1; } else { /* Non-disk-type devices don't need to blacklist any pages diff -Nru a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c --- a/drivers/usb/storage/transport.c 2005-01-23 22:34:17 -08:00 +++ b/drivers/usb/storage/transport.c 2005-01-23 22:34:17 -08:00 @@ -1066,8 +1066,7 @@ /* try to compute the actual residue, based on how much data * was really transferred and what the device tells us */ if (residue) { - if (!(us->flags & US_FL_IGNORE_RESIDUE) || - srb->sc_data_direction == DMA_TO_DEVICE) { + if (!(us->flags & US_FL_IGNORE_RESIDUE)) { residue = min(residue, transfer_length); srb->resid = max(srb->resid, (int) residue); } diff -Nru a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h --- a/drivers/usb/storage/unusual_devs.h 2005-01-23 22:34:17 -08:00 +++ b/drivers/usb/storage/unusual_devs.h 2005-01-23 22:34:17 -08:00 @@ -574,13 +574,11 @@ US_SC_SCSI, US_PR_CB, NULL, US_FL_SINGLE_LUN ), -#if !defined(CONFIG_BLK_DEV_UB) && !defined(CONFIG_BLK_DEV_UB_MODULE) UNUSUAL_DEV( 0x0781, 0x0002, 0x0009, 0x0009, "Sandisk", "ImageMate SDDR-31", US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_IGNORE_SER ), -#endif UNUSUAL_DEV( 0x0781, 0x0100, 0x0100, 0x0100, "Sandisk", diff -Nru a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c --- a/drivers/usb/storage/usb.c 2005-01-23 22:34:17 -08:00 +++ b/drivers/usb/storage/usb.c 2005-01-23 22:34:17 -08:00 @@ -144,9 +144,7 @@ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_QIC, US_PR_BULK) }, { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_UFI, US_PR_BULK) }, { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8070, US_PR_BULK) }, -#if !defined(CONFIG_BLK_DEV_UB) && !defined(CONFIG_BLK_DEV_UB_MODULE) { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_BULK) }, -#endif /* Terminating entry */ { } @@ -220,10 +218,8 @@ .useTransport = US_PR_BULK}, { .useProtocol = US_SC_8070, .useTransport = US_PR_BULK}, -#if !defined(CONFIG_BLK_DEV_UB) && !defined(CONFIG_BLK_DEV_UB_MODULE) { .useProtocol = US_SC_SCSI, .useTransport = US_PR_BULK}, -#endif /* Terminating entry */ { NULL }