Commit 54d285d9 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge kroah.com:/home/linux/BK/bleed-2.6

into kroah.com:/home/linux/BK/usb-2.6
parents d00ea9a1 0e8f510d
......@@ -2672,9 +2672,9 @@ S: 70110 Kuopio
S: Finland
N: Luca Risolia
E: luca_ing@libero.it
D: V4L driver for W996[87]CF JPEG USB Dual Mode Camera Chip
S: Via Libertà 41/a
E: luca.risolia@studio.unibo.it
D: V4L driver for W996[87]CF JPEG USB Dual Mode Camera Chips
S: Via Liberta' 41/A
S: Osio Sotto, 24046, Bergamo
S: Italy
......
W996[87]CF JPEG USB Dual Mode Camera Chip driver for Linux 2.6
==============================================================
W996[87]CF JPEG USB Dual Mode Camera Chip
Driver for Linux 2.6 (basic version)
=========================================
- Documentation -
......@@ -11,15 +12,16 @@ Index
2. License
3. Overview
4. Supported devices
5. Kernel configuration and third-part module compilation
5. Module dependencies
6. Module loading
7. Module paramaters
8. Credits
8. Contact information
9. Credits
1. Copyright
============
Copyright (C) 2002 2003 by Luca Risolia <luca_ing@libero.it>
Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it>
2. License
......@@ -43,23 +45,25 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
===========
This driver supports the video streaming capabilities of the devices mounting
Winbond W9967CF and Winbond W9968CF JPEG USB Dual Mode Camera Chips, when they
are being commanded by USB.
are being commanded by USB. OV681 based cameras should be supported as well.
The driver relies on the Video4Linux, USB and I2C core modules of the Linux
kernel, version 2.6.0 or greater, and is not compatible in any way with
previous versions. It has been designed to run properly on SMP systems
as well. At the moment, an additional module, "ovcamchip", is mandatory; it
provides support for some OmniVision CMOS sensors connected to the W996[87]CF
chips.
The driver is split into two modules: the basic one, "w9968cf", is needed for
The driver is divided into two modules: the basic one, "w9968cf", is needed for
the supported devices to work; the second one, "w9968cf-vpp", is an optional
module, which provides some useful video post-processing functions like video
decoding, up-scaling and colour conversions. These routines can't be included
into official kernels for performance purposes. Once the driver is installed,
decoding, up-scaling and colour conversions. Once the driver is installed,
every time an application tries to open a recognized device, "w9968cf" checks
the presence of the "w9968cf-vpp" module and loads it automatically by default.
Please keep in mind that official kernels do NOT include the second module for
performance purposes. However it is always recommended to download and install
the latest and complete release of the driver, replacing the existing one, if
present: it will be still even possible not to load the "w9968cf-vpp" module at
all, if you ever want to.
The latest and full-featured version of the W996[87]CF driver can be found at:
http://go.lamarinapunto.com/ . Please refer to the documentation included in
that package, if you are going to use it.
Up to 32 cameras can be handled at the same time. They can be connected and
disconnected from the host many times without turning off the computer, if
your system supports the hotplug facility.
......@@ -67,18 +71,21 @@ your system supports the hotplug facility.
To change the default settings for each camera, many paramaters can be passed
through command line when the module is loaded into memory.
The latest and full featured version of the W996[87]CF driver can be found at:
http://go.lamarinapunto.com/
The driver relies on the Video4Linux, USB and I2C core modules of the official
Linux kernels. It has been designed to run properly on SMP systems as well.
At the moment, an additional module, "ovcamchip", is mandatory; it provides
support for some OmniVision CMOS sensors connected to the W996[87]CF chips.
The "ovcamchip" module is part of the OV511 driver, version 2.25, which can be
The "ovcamchip" module is part of the OV511 driver, version 2.27, which can be
downloaded from internet:
http://alpha.dyndns.org/ov511/
To know how to patch, compile and load it, read the paragraphs below.
To know how to compile it, read the documentation included in the OV511
package.
4. Supported devices
====================
At the moment, known W996[87]CF based devices are:
At the moment, known W996[87]CF and OV681 based devices are:
- Aroma Digi Pen ADG-5000 Refurbished
- AVerTV USB
- Creative Labs Video Blaster WebCam Go
......@@ -87,27 +94,26 @@ At the moment, known W996[87]CF based devices are:
- Ezonics EZ-802 EZMega Cam
- OPCOM Digi Pen VGA Dual Mode Pen Camera
If you know any other W996[87]CF based cameras, please contact me.
If you know any other W996[87]CF or OV681 based cameras, please contact me.
The list above does NOT imply that all those devices work with this driver: up
until now only webcams that have a CMOS sensor supported by the "ovcamchip"
module work.
For a list of supported CMOS sensors, please visit the module author homepage:
http://alpha.dyndns.org/ov511/
For a list of supported CMOS sensors, please visit the author's homepage on
this module: http://alpha.dyndns.org/ov511/
Possible external microcontrollers of those webcams are not supported: this
means that still images can't be downloaded from the device memory.
means that still images cannot be downloaded from the device memory.
Furthermore, it's worth to note that I was only able to run tests on my
"Creative Labs Video Blaster WebCam Go". Donations of other models, for
additional testing and full support, would be much appreciated.
5. Kernel configuration and third-part module compilation
=========================================================
As noted above, kernel 2.6.0 is the minimum for this driver; for it to work
properly, the driver needs kernel support for Video4Linux, USB and I2C, and a
third-part module for the CMOS sensor.
5. Module dependencies
======================
For it to work properly, the driver needs kernel support for Video4Linux,
USB and I2C, and a third-party module for the CMOS sensor.
The following options of the kernel configuration file must be enabled and
corresponding modules must be compiled:
......@@ -126,7 +132,7 @@ The I2C core module can be compiled statically in the kernel as well.
#
CONFIG_USB=m
In addition, depending on the hardware being used, just one of the modules
In addition, depending on the hardware being used, only one of the modules
below is necessary:
# USB Host Controller Drivers
......@@ -137,31 +143,16 @@ below is necessary:
Also, make sure "Enforce bandwidth allocation" is NOT enabled.
And finally:
# USB Multimedia devices
#
CONFIG_USB_W9968CF=m
The last module we need is "ovcamchip.o". To obtain it, you have to download
the OV511 driver, version 2.25 - don't use other versions - which is available
at http://alpha.dyndns.org/ov511/ . Then you have to download the latest
version of the full featured W996[87]CF driver, which contains a patch for the
"ovcamchip" module; it is available at http://go.lamarinapunto.com .
Once you have obtained the packages, decompress, patch and compile the
"ovcamchip" module. In other words:
[user@localhost home]$ tar xvzf w9968cf-x.x.tar.gz
[user@localhost home]$ tar xvjf ov511-2.25.tar.bz2
[user@localhost home]$ cd ov511-2.25
[user@localhost ov511-2.25]$ patch -p1 < \
/path/to/w9968cf-x.x/ov511-2.25.patch
[user@localhost ov511-2.25]$ make
It's worth to note that the full featured version of the W996[87]CF driver
can also be installed overwriting the one in the kernel; in this case, read the
documentation included in the package.
If everything went well, the W996[87]CF driver can be immediatly used (see next
paragraph).
the OV511 package, version 2.27 - don't use other versions - and compile it
according to its documentation.
The package is available at http://alpha.dyndns.org/ov511/ .
6. Module loading
......@@ -169,7 +160,7 @@ paragraph).
To use the driver, it is necessary to load the "w9968cf" module into memory
after every other module required.
For example, loading can be done this way, as root:
Loading can be done this way, from root:
[root@localhost home]# modprobe usbcore
[root@localhost home]# modprobe i2c-core
......@@ -191,11 +182,10 @@ explanation about them and which syntax to use, it is recommended to run the
7. Module paramaters
====================
Module paramaters are listed below:
-------------------------------------------------------------------------------
Name: vppmod_load
Type: int
Type: bool
Syntax: <0|1>
Description: Automatic 'w9968cf-vpp' module loading: 0 disabled, 1 enabled.
If enabled, every time an application attempts to open a
......@@ -219,7 +209,7 @@ Syntax: <-1|n[,...]>
Description: Specify V4L minor mode number.
-1 = use next available
n = use minor number n
You can specify 32 cameras this way.
You can specify up to 32 cameras this way.
For example:
video_nr=-1,2,-1 would assign minor number 2 to the second
recognized camera and use auto for the first one and for every
......@@ -236,22 +226,22 @@ Default: 1023
Name: max_buffers
Type: int array (min = 0, max = 32)
Syntax: <n[,...]>
Description: Only for advanced users.
Description: For advanced users.
Specify the maximum number of video frame buffers to allocate
for each device, from 2 to 32.
Default: 2
-------------------------------------------------------------------------------
Name: double_buffer
Type: int array (min = 0, max = 32)
Type: bool array (min = 0, max = 32)
Syntax: <0|1[,...]>
Description: Hardware double buffering: 0 disabled, 1 enabled.
It should be enabled if you want smooth video output: if you
obtain out of sync. video, disable it at all, or try to
obtain out of sync. video, disable it, or try to
decrease the 'clockdiv' module paramater value.
Default: 1 for every device.
-------------------------------------------------------------------------------
Name: clamping
Type: int array (min = 0, max = 32)
Type: bool array (min = 0, max = 32)
Syntax: <0|1[,...]>
Description: Video data clamping: 0 disabled, 1 enabled.
Default: 0 for every device.
......@@ -266,13 +256,13 @@ Description: Video filter type.
Default: 0 for every device.
-------------------------------------------------------------------------------
Name: largeview
Type: int array (min = 0, max = 32)
Type: bool array (min = 0, max = 32)
Syntax: <0|1[,...]>
Description: Large view: 0 disabled, 1 enabled.
Default: 1 for every device.
-------------------------------------------------------------------------------
Name: upscaling
Type: int array (min = 0, max = 32)
Type: bool array (min = 0, max = 32)
Syntax: <0|1[,...]>
Description: Software scaling (for non-compressed video only):
0 disabled, 1 enabled.
......@@ -319,7 +309,7 @@ Default: 0 for every device. Initial palette is 9 (UYVY).
Note: If 'w9968cf-vpp' is not loaded, this paramater is set to 9.
-------------------------------------------------------------------------------
Name: force_rgb
Type: int array (min = 0, max = 32)
Type: bool array (min = 0, max = 32)
Syntax: <0|1[,...]>
Description: Read RGB video data instead of BGR:
1 = use RGB component ordering.
......@@ -328,28 +318,28 @@ Description: Read RGB video data instead of BGR:
Default: 0 for every device.
-------------------------------------------------------------------------------
Name: autobright
Type: long array (min = 0, max = 32)
Type: bool array (min = 0, max = 32)
Syntax: <0|1[,...]>
Description: CMOS sensor automatically changes brightness:
0 = no, 1 = yes
Default: 0 for every device.
-------------------------------------------------------------------------------
Name: autoexp
Type: long array (min = 0, max = 32)
Type: bool array (min = 0, max = 32)
Syntax: <0|1[,...]>
Description: CMOS sensor automatically changes exposure:
0 = no, 1 = yes
Default: 1 for every device.
-------------------------------------------------------------------------------
Name: lightfreq
Type: long array (min = 0, max = 32)
Type: int array (min = 0, max = 32)
Syntax: <50|60[,...]>
Description: Light frequency in Hz:
50 for European and Asian lighting, 60 for American lighting.
Default: 50 for every device.
-------------------------------------------------------------------------------
Name: bandingfilter
Type: long array (min = 0, max = 32)
Type: bool array (min = 0, max = 32)
Syntax: <0|1[,...]>
Description: Banding filter to reduce effects of fluorescent
lighting:
......@@ -359,7 +349,7 @@ Description: Banding filter to reduce effects of fluorescent
Default: 0 for every device.
-------------------------------------------------------------------------------
Name: clockdiv
Type: long array (min = 0, max = 32)
Type: int array (min = 0, max = 32)
Syntax: <-1|n[,...]>
Description: Force pixel clock divisor to a specific value (for experts):
n may vary from 0 to 127.
......@@ -368,21 +358,21 @@ Description: Force pixel clock divisor to a specific value (for experts):
Default: -1 for every device.
-------------------------------------------------------------------------------
Name: backlight
Type: long array (min = 0, max = 32)
Type: bool array (min = 0, max = 32)
Syntax: <0|1[,...]>
Description: Objects are lit from behind:
0 = no, 1 = yes
Default: 0 for every device.
-------------------------------------------------------------------------------
Name: mirror
Type: long array (min = 0, max = 32)
Type: bool array (min = 0, max = 32)
Syntax: <0|1[,...]>
Description: Reverse image horizontally:
0 = no, 1 = yes
Default: 0 for every device.
-------------------------------------------------------------------------------
Name: sensor_mono
Type: long array (min = 0, max = 32)
Name: monochrome
Type: bool array (min = 0, max = 32)
Syntax: <0|1[,...]>
Description: The CMOS sensor is monochrome:
0 = no, 1 = yes
......@@ -423,7 +413,7 @@ Name: debug
Type: int
Syntax: <n>
Description: Debugging information level, from 0 to 6:
0 = none (be cautious)
0 = none (use carefully)
1 = critical errors
2 = significant informations
3 = configuration or general messages
......@@ -435,7 +425,7 @@ Description: Debugging information level, from 0 to 6:
Default: 2
-------------------------------------------------------------------------------
Name: specific_debug
Type: int
Type: bool
Syntax: <0|1>
Description: Enable or disable specific debugging messages:
0 = print messages concerning every level <= 'debug' level.
......@@ -444,7 +434,16 @@ Default: 0
-------------------------------------------------------------------------------
8. Credits
8. Contact information
======================
I may be contacted by e-mail at <luca.risolia@studio.unibo.it>.
I can accept GPG/PGP encrypted e-mail. My GPG key ID is 'FCE635A4'.
My public 1024-bit key should be available at your keyserver; the fingerprint
is: '88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4'.
9. Credits
==========
The development would not have proceed much further without having looked at
the source code of other drivers and without the help of several persons; in
......@@ -456,8 +455,6 @@ particular:
- memory management code has been copied from the bttv driver by Ralph Metzler,
Marcus Metzler and Gerd Knorr;
- the low-level I2C read function has been written by Frédéric Jouault, who
also gave me commented logs about sniffed USB traffic taken from another
driver for another system;
- the low-level I2C read function has been written by Frederic Jouault;
- the low-level I2C fast write function has been written by Piotr Czerczak;
- the low-level I2C fast write function has been written by Piotr Czerczak.
......@@ -2252,7 +2252,7 @@ S: Maintained
USB W996[87]CF DRIVER
P: Luca Risolia
M: luca_ing@libero.it
M: luca.risolia@studio.unibo.it
L: linux-usb-devel@lists.sourceforge.net
W: http://go.lamarinapunto.com
S: Maintained
......
......@@ -54,6 +54,7 @@ obj-$(CONFIG_USB_AUERSWALD) += misc/
obj-$(CONFIG_USB_BRLVGER) += misc/
obj-$(CONFIG_USB_EMI26) += misc/
obj-$(CONFIG_USB_LCD) += misc/
obj-$(CONFIG_USB_LEGOTOWER) += misc/
obj-$(CONFIG_USB_RIO500) += misc/
obj-$(CONFIG_USB_SPEEDTOUCH) += misc/
obj-$(CONFIG_USB_TEST) += misc/
......
......@@ -127,6 +127,8 @@ void hcd_buffer_free (
struct usb_hcd *hcd = bus->hcpriv;
int i;
if (!addr)
return;
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
if (size <= pool_max [i]) {
pci_pool_free (hcd->pool [i], addr, dma);
......
......@@ -23,28 +23,29 @@
#include "usb.h"
/* Active configuration fields */
#define usb_actconfig_show(field, format_string) \
static ssize_t \
show_##field (struct device *dev, char *buf) \
#define usb_actconfig_show(field, multiplier, format_string) \
static ssize_t show_##field (struct device *dev, char *buf) \
{ \
struct usb_device *udev; \
\
udev = to_usb_device (dev); \
if (udev->actconfig) \
return sprintf (buf, format_string, udev->actconfig->desc.field); \
else return 0; \
if (udev->actconfig) \
return sprintf (buf, format_string, \
udev->actconfig->desc.field * multiplier); \
else \
return 0; \
} \
#define usb_actconfig_attr(field, format_string) \
usb_actconfig_show(field,format_string) \
#define usb_actconfig_attr(field, multiplier, format_string) \
usb_actconfig_show(field, multiplier, format_string) \
static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
usb_actconfig_attr (bNumInterfaces, "%2d\n")
usb_actconfig_attr (bmAttributes, "%2x\n")
usb_actconfig_attr (bMaxPower, "%3dmA\n")
usb_actconfig_attr (bNumInterfaces, 1, "%2d\n")
usb_actconfig_attr (bmAttributes, 1, "%2x\n")
usb_actconfig_attr (bMaxPower, 2, "%3dmA\n")
/* configuration value is always present, and r/w */
usb_actconfig_show(bConfigurationValue,"%u\n");
usb_actconfig_show(bConfigurationValue, 1, "%u\n");
static ssize_t
set_bConfigurationValue (struct device *dev, const char *buf, size_t count)
......@@ -62,55 +63,25 @@ static DEVICE_ATTR(bConfigurationValue, S_IRUGO | S_IWUSR,
show_bConfigurationValue, set_bConfigurationValue);
/* String fields */
static ssize_t show_product (struct device *dev, char *buf)
{
struct usb_device *udev;
int len;
udev = to_usb_device (dev);
len = usb_string(udev, udev->descriptor.iProduct, buf, PAGE_SIZE);
if (len < 0)
return 0;
buf[len] = '\n';
buf[len+1] = 0;
return len+1;
}
static DEVICE_ATTR(product,S_IRUGO,show_product,NULL);
static ssize_t
show_manufacturer (struct device *dev, char *buf)
{
struct usb_device *udev;
int len;
udev = to_usb_device (dev);
len = usb_string(udev, udev->descriptor.iManufacturer, buf, PAGE_SIZE);
if (len < 0)
return 0;
buf[len] = '\n';
buf[len+1] = 0;
return len+1;
}
static DEVICE_ATTR(manufacturer,S_IRUGO,show_manufacturer,NULL);
static ssize_t
show_serial (struct device *dev, char *buf)
{
struct usb_device *udev;
int len;
udev = to_usb_device (dev);
#define usb_string_attr(name, field) \
static ssize_t show_##name(struct device *dev, char *buf) \
{ \
struct usb_device *udev; \
int len; \
\
udev = to_usb_device (dev); \
len = usb_string(udev, udev->descriptor.field, buf, PAGE_SIZE); \
if (len < 0) \
return 0; \
buf[len] = '\n'; \
buf[len+1] = 0; \
return len+1; \
} \
static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL);
len = usb_string(udev, udev->descriptor.iSerialNumber, buf, PAGE_SIZE);
if (len < 0)
return 0;
buf[len] = '\n';
buf[len+1] = 0;
return len+1;
}
static DEVICE_ATTR(serial,S_IRUGO,show_serial,NULL);
usb_string_attr(product, iProduct);
usb_string_attr(manufacturer, iManufacturer);
usb_string_attr(serial, iSerialNumber);
static ssize_t
show_speed (struct device *dev, char *buf)
......
......@@ -268,7 +268,7 @@ int usb_submit_urb(struct urb *urb, int mem_flags)
/* "high bandwidth" mode, 1-3 packets/uframe? */
if (dev->speed == USB_SPEED_HIGH) {
int mult = 1 + ((max >> 11) & 0x03);
max &= 0x03ff;
max &= 0x07ff;
max *= mult;
}
......
......@@ -177,6 +177,27 @@ config USB_GADGETFS_PXA2XX
depends on USB_GADGETFS && USB_PXA2XX
default y
config USB_G_SERIAL
tristate "serial Gadget"
depends on USB_GADGET && (USB_DUMMY_HCD || USB_NET2280 || USB_PXA2XX || USB_SA1100)
config USB_G_SERIAL_NET2280
bool
# for now, treat the "dummy" hcd as if it were a net2280
depends on USB_G_SERIAL && (USB_NET2280 || USB_DUMMY_HCD)
default y
config USB_G_SERIAL_PXA2XX
bool
depends on USB_G_SERIAL && USB_PXA2XX
default y
config USB_G_SERIAL_SA1100
bool
depends on USB_G_SERIAL && USB_SA1100
default y
endchoice
# endmenuconfig
......@@ -8,9 +8,10 @@ obj-$(CONFIG_USB_NET2280) += net2280.o
#
g_zero-objs := zero.o usbstring.o
g_ether-objs := ether.o usbstring.o
g_serial-objs := serial.o usbstring.o
gadgetfs-objs := inode.o usbstring.o
obj-$(CONFIG_USB_ZERO) += g_zero.o
obj-$(CONFIG_USB_ETH) += g_ether.o
obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o
obj-$(CONFIG_USB_G_SERIAL) += g_serial.o
/*
* Toshiba TC86C001 ("Goku-S") USB Device Controller driver
*
* Copyright (C) 2000-2002 Lineo
* by Stuart Lynne, Tom Rushworth, and Bruce Balden
* Copyright (C) 2002 Toshiba Corporation
* Copyright (C) 2003 MontaVista Software (source@mvista.com)
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
/*
* This device has ep0 and three semi-configurable bulk/interrupt endpoints.
*
* - Endpoint numbering is fixed: ep{1,2,3}-bulk
* - Gadget drivers can choose ep maxpacket (8/16/32/64)
* - Gadget drivers can choose direction (IN, OUT)
* - DMA works with ep1 (OUT transfers) and ep2 (IN transfers).
*/
#undef DEBUG
// #define VERBOSE /* extra debug messages (success too) */
// #define USB_TRACE /* packet-level success messages */
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
#include <linux/device.h>
#include <linux/usb_ch9.h>
#include <linux/usb_gadget.h>
#include <asm/byteorder.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/unaligned.h>
#include "goku_udc.h"
#define DRIVER_DESC "TC86C001 USB Device Controller"
#define DRIVER_VERSION "30-Oct 2003"
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
static const char driver_name [] = "goku_udc";
static const char driver_desc [] = DRIVER_DESC;
MODULE_AUTHOR("source@mvista.com");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
/*
* IN dma behaves ok under testing, though the IN-dma abort paths don't
* seem to behave quite as expected. Used by default.
*
* OUT dma documents design problems handling the common "short packet"
* transfer termination policy; it couldn't enabled by default, even
* if the OUT-dma abort problems had a resolution.
*/
static unsigned use_dma = 1;
#if 0
//#include <linux/moduleparam.h>
/* "modprobe goku_udc use_dma=1" etc
* 0 to disable dma
* 1 to use IN dma only (normal operation)
* 2 to use IN and OUT dma
*/
module_param(use_dma, uint, S_IRUGO);
#endif
/*-------------------------------------------------------------------------*/
static void nuke(struct goku_ep *, int status);
static inline void
command(struct goku_udc_regs *regs, int command, unsigned epnum)
{
writel(COMMAND_EP(epnum) | command, &regs->Command);
udelay(300);
}
static int
goku_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
{
struct goku_udc *dev;
struct goku_ep *ep;
u32 mode;
u16 max;
unsigned long flags;
ep = container_of(_ep, struct goku_ep, ep);
if (!_ep || !desc || ep->desc
|| desc->bDescriptorType != USB_DT_ENDPOINT)
return -EINVAL;
dev = ep->dev;
if (ep == &dev->ep[0])
return -EINVAL;
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
return -ESHUTDOWN;
if (ep->num != (desc->bEndpointAddress & 0x0f))
return -EINVAL;
switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
case USB_ENDPOINT_XFER_BULK:
case USB_ENDPOINT_XFER_INT:
break;
default:
return -EINVAL;
}
if ((readl(ep->reg_status) & EPxSTATUS_EP_MASK)
!= EPxSTATUS_EP_INVALID)
return -EBUSY;
/* enabling the no-toggle interrupt mode would need an api hook */
mode = 0;
max = le16_to_cpu(get_unaligned(&desc->wMaxPacketSize));
switch (max) {
case 64: mode++;
case 32: mode++;
case 16: mode++;
case 8: mode <<= 3;
break;
default:
return -EINVAL;
}
mode |= 2 << 1; /* bulk, or intr-with-toggle */
/* ep1/ep2 dma direction is chosen early; it works in the other
* direction, with pio. be cautious with out-dma.
*/
ep->is_in = (USB_DIR_IN & desc->bEndpointAddress) != 0;
if (ep->is_in) {
mode |= 1;
ep->dma = (use_dma != 0) && (ep->num == UDC_MSTRD_ENDPOINT);
} else {
ep->dma = (use_dma == 2) && (ep->num == UDC_MSTWR_ENDPOINT);
if (ep->dma)
DBG(dev, "%s out-dma hides short packets\n",
ep->ep.name);
}
spin_lock_irqsave(&ep->dev->lock, flags);
/* ep1 and ep2 can do double buffering and/or dma */
if (ep->num < 3) {
struct goku_udc_regs *regs = ep->dev->regs;
u32 tmp;
/* double buffer except (for now) with pio in */
tmp = ((ep->dma || !ep->is_in)
? 0x10 /* double buffered */
: 0x11 /* single buffer */
) << ep->num;
tmp |= readl(&regs->EPxSingle);
writel(tmp, &regs->EPxSingle);
tmp = (ep->dma ? 0x10/*dma*/ : 0x11/*pio*/) << ep->num;
tmp |= readl(&regs->EPxBCS);
writel(tmp, &regs->EPxBCS);
}
writel(mode, ep->reg_mode);
command(ep->dev->regs, COMMAND_RESET, ep->num);
ep->ep.maxpacket = max;
ep->stopped = 0;
ep->desc = desc;
spin_unlock_irqrestore(&ep->dev->lock, flags);
DBG(dev, "enable %s %s %s maxpacket %u\n", ep->ep.name,
ep->is_in ? "IN" : "OUT",
ep->dma ? "dma" : "pio",
max);
return 0;
}
static void ep_reset(struct goku_udc_regs *regs, struct goku_ep *ep)
{
struct goku_udc *dev = ep->dev;
if (regs) {
command(regs, COMMAND_INVALID, ep->num);
if (ep->num) {
if (ep->num == UDC_MSTWR_ENDPOINT)
dev->int_enable &= ~(INT_MSTWREND
|INT_MSTWRTMOUT);
else if (ep->num == UDC_MSTRD_ENDPOINT)
dev->int_enable &= ~INT_MSTRDEND;
dev->int_enable &= ~INT_EPxDATASET (ep->num);
} else
dev->int_enable &= ~INT_EP0;
writel(dev->int_enable, &regs->int_enable);
readl(&regs->int_enable);
if (ep->num < 3) {
struct goku_udc_regs *regs = ep->dev->regs;
u32 tmp;
tmp = readl(&regs->EPxSingle);
tmp &= ~(0x11 << ep->num);
writel(tmp, &regs->EPxSingle);
tmp = readl(&regs->EPxBCS);
tmp &= ~(0x11 << ep->num);
writel(tmp, &regs->EPxBCS);
}
/* reset dma in case we're still using it */
if (ep->dma) {
u32 master;
master = readl(&regs->dma_master) & MST_RW_BITS;
if (ep->num == UDC_MSTWR_ENDPOINT) {
master &= ~MST_W_BITS;
master |= MST_WR_RESET;
} else {
master &= ~MST_R_BITS;
master |= MST_RD_RESET;
}
writel(master, &regs->dma_master);
}
}
ep->ep.maxpacket = MAX_FIFO_SIZE;
ep->desc = 0;
ep->stopped = 1;
ep->irqs = 0;
ep->dma = 0;
}
static int goku_ep_disable(struct usb_ep *_ep)
{
struct goku_ep *ep;
struct goku_udc *dev;
unsigned long flags;
ep = container_of(_ep, struct goku_ep, ep);
if (!_ep || !ep->desc)
return -ENODEV;
dev = ep->dev;
if (dev->ep0state == EP0_SUSPEND)
return -EBUSY;
VDBG(dev, "disable %s\n", _ep->name);
spin_lock_irqsave(&dev->lock, flags);
nuke(ep, -ESHUTDOWN);
ep_reset(dev->regs, ep);
spin_unlock_irqrestore(&dev->lock, flags);
return 0;
}
/*-------------------------------------------------------------------------*/
static struct usb_request *
goku_alloc_request(struct usb_ep *_ep, int gfp_flags)
{
struct goku_request *req;
if (!_ep)
return 0;
req = kmalloc(sizeof *req, gfp_flags);
if (!req)
return 0;
memset(req, 0, sizeof *req);
req->req.dma = DMA_ADDR_INVALID;
INIT_LIST_HEAD(&req->queue);
return &req->req;
}
static void
goku_free_request(struct usb_ep *_ep, struct usb_request *_req)
{
struct goku_request *req;
if (!_ep || !_req)
return;
req = container_of(_req, struct goku_request, req);
WARN_ON(!list_empty(&req->queue));
kfree(req);
}
/*-------------------------------------------------------------------------*/
#undef USE_KMALLOC
/* many common platforms have dma-coherent caches, which means that it's
* safe to use kmalloc() memory for all i/o buffers without using any
* cache flushing calls. (unless you're trying to share cache lines
* between dma and non-dma activities, which is a slow idea in any case.)
*
* other platforms need more care, with 2.6 having a moderately general
* solution except for the common "buffer is smaller than a page" case.
*/
#if defined(CONFIG_X86)
#define USE_KMALLOC
#elif defined(CONFIG_MIPS) && !defined(CONFIG_NONCOHERENT_IO)
#define USE_KMALLOC
#elif defined(CONFIG_PPC) && !defined(CONFIG_NOT_COHERENT_CACHE)
#define USE_KMALLOC
#endif
/* allocating buffers this way eliminates dma mapping overhead, which
* on some platforms will mean eliminating a per-io buffer copy. with
* some kinds of system caches, further tweaks may still be needed.
*/
static void *
goku_alloc_buffer(struct usb_ep *_ep, unsigned bytes,
dma_addr_t *dma, int gfp_flags)
{
void *retval;
struct goku_ep *ep;
ep = container_of(_ep, struct goku_ep, ep);
if (!_ep)
return 0;
*dma = DMA_ADDR_INVALID;
#if defined(USE_KMALLOC)
retval = kmalloc(bytes, gfp_flags);
if (retval)
*dma = virt_to_phys(retval);
#else
if (ep->dma) {
/* the main problem with this call is that it wastes memory
* on typical 1/N page allocations: it allocates 1-N pages.
*/
#warning Using dma_alloc_coherent even with buffers smaller than a page.
retval = dma_alloc_coherent(&ep->dev->pdev->dev,
bytes, dma, gfp_flags);
} else
retval = kmalloc(bytes, gfp_flags);
#endif
return retval;
}
static void
goku_free_buffer(struct usb_ep *_ep, void *buf, dma_addr_t dma, unsigned bytes)
{
/* free memory into the right allocator */
#ifndef USE_KMALLOC
if (dma != DMA_ADDR_INVALID) {
struct goku_ep *ep;
ep = container_of(_ep, struct goku_ep, ep);
if (!_ep)
return;
dma_free_coherent(&ep->dev->pdev->dev, bytes, buf, dma);
} else
#endif
kfree (buf);
}
/*-------------------------------------------------------------------------*/
static void
done(struct goku_ep *ep, struct goku_request *req, int status)
{
struct goku_udc *dev;
unsigned stopped = ep->stopped;
list_del_init(&req->queue);
if (likely(req->req.status == -EINPROGRESS))
req->req.status = status;
else
status = req->req.status;
dev = ep->dev;
if (req->mapped) {
pci_unmap_single(dev->pdev, req->req.dma, req->req.length,
ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
req->req.dma = DMA_ADDR_INVALID;
req->mapped = 0;
}
#ifndef USB_TRACE
if (status && status != -ESHUTDOWN)
#endif
VDBG(dev, "complete %s req %p stat %d len %u/%u\n",
ep->ep.name, &req->req, status,
req->req.actual, req->req.length);
/* don't modify queue heads during completion callback */
ep->stopped = 1;
spin_unlock(&dev->lock);
req->req.complete(&ep->ep, &req->req);
spin_lock(&dev->lock);
ep->stopped = stopped;
}
/*-------------------------------------------------------------------------*/
static inline int
write_packet(u32 *fifo, u8 *buf, struct goku_request *req, unsigned max)
{
unsigned length, count;
length = min(req->req.length - req->req.actual, max);
req->req.actual += length;
count = length;
while (likely(count--))
writel(*buf++, fifo);
return length;
}
// return: 0 = still running, 1 = completed, negative = errno
static int write_fifo(struct goku_ep *ep, struct goku_request *req)
{
struct goku_udc *dev = ep->dev;
u32 tmp;
u8 *buf;
unsigned count;
int is_last;
tmp = readl(&dev->regs->DataSet);
buf = req->req.buf + req->req.actual;
prefetch(buf);
dev = ep->dev;
if (unlikely(ep->num == 0 && dev->ep0state != EP0_IN))
return -EL2HLT;
/* NOTE: just single-buffered PIO-IN for now. */
if (unlikely((tmp & DATASET_A(ep->num)) != 0))
return 0;
/* clear our "packet available" irq */
if (ep->num != 0)
writel(~INT_EPxDATASET(ep->num), &dev->regs->int_status);
count = write_packet(ep->reg_fifo, buf, req, ep->ep.maxpacket);
/* last packet often short (sometimes a zlp, especially on ep0) */
if (unlikely(count != ep->ep.maxpacket)) {
writel(~(1<<ep->num), &dev->regs->EOP);
if (ep->num == 0) {
dev->ep[0].stopped = 1;
dev->ep0state = EP0_STATUS;
}
is_last = 1;
} else {
if (likely(req->req.length != req->req.actual)
|| req->req.zero)
is_last = 0;
else
is_last = 1;
}
#if 0 /* printk seemed to trash is_last...*/
//#ifdef USB_TRACE
VDBG(dev, "wrote %s %u bytes%s IN %u left %p\n",
ep->ep.name, count, is_last ? "/last" : "",
req->req.length - req->req.actual, req);
#endif
/* requests complete when all IN data is in the FIFO,
* or sometimes later, if a zlp was needed.
*/
if (is_last) {
done(ep, req, 0);
return 1;
}
return 0;
}
static int read_fifo(struct goku_ep *ep, struct goku_request *req)
{
struct goku_udc_regs *regs;
u32 size, set;
u8 *buf;
unsigned bufferspace, is_short, dbuff;
regs = ep->dev->regs;
top:
buf = req->req.buf + req->req.actual;
prefetchw(buf);
if (unlikely(ep->num == 0 && ep->dev->ep0state != EP0_OUT))
return -EL2HLT;
dbuff = (ep->num == 1 || ep->num == 2);
do {
/* ack dataset irq matching the status we'll handle */
if (ep->num != 0)
writel(~INT_EPxDATASET(ep->num), &regs->int_status);
set = readl(&regs->DataSet) & DATASET_AB(ep->num);
size = readl(&regs->EPxSizeLA[ep->num]);
bufferspace = req->req.length - req->req.actual;
/* usually do nothing without an OUT packet */
if (likely(ep->num != 0 || bufferspace != 0)) {
if (unlikely(set == 0))
break;
/* use ep1/ep2 double-buffering for OUT */
if (!(size & PACKET_ACTIVE))
size = readl(&regs->EPxSizeLB[ep->num]);
if (!(size & PACKET_ACTIVE)) // "can't happen"
break;
size &= DATASIZE; /* EPxSizeH == 0 */
/* ep0out no-out-data case for set_config, etc */
} else
size = 0;
/* read all bytes from this packet */
req->req.actual += size;
is_short = (size < ep->ep.maxpacket);
#ifdef USB_TRACE
VDBG(ep->dev, "read %s %u bytes%s OUT req %p %u/%u\n",
ep->ep.name, size, is_short ? "/S" : "",
req, req->req.actual, req->req.length);
#endif
while (likely(size-- != 0)) {
u8 byte = (u8) readl(ep->reg_fifo);
if (unlikely(bufferspace == 0)) {
/* this happens when the driver's buffer
* is smaller than what the host sent.
* discard the extra data in this packet.
*/
if (req->req.status != -EOVERFLOW)
DBG(ep->dev, "%s overflow %u\n",
ep->ep.name, size);
req->req.status = -EOVERFLOW;
} else {
*buf++ = byte;
bufferspace--;
}
}
/* completion */
if (unlikely(is_short || req->req.actual == req->req.length)) {
if (unlikely(ep->num == 0)) {
/* non-control endpoints now usable? */
if (ep->dev->req_config)
writel(ep->dev->configured
? USBSTATE_CONFIGURED
: 0,
&regs->UsbState);
/* ep0out status stage */
writel(~(1<<0), &regs->EOP);
ep->stopped = 1;
ep->dev->ep0state = EP0_STATUS;
}
done(ep, req, 0);
/* empty the second buffer asap */
if (dbuff && !list_empty(&ep->queue)) {
req = list_entry(ep->queue.next,
struct goku_request, queue);
goto top;
}
return 1;
}
} while (dbuff);
return 0;
}
static inline void
pio_irq_enable(struct goku_udc *dev, struct goku_udc_regs *regs, int epnum)
{
dev->int_enable |= INT_EPxDATASET (epnum);
writel(dev->int_enable, &regs->int_enable);
/* write may still be posted */
}
static inline void
pio_irq_disable(struct goku_udc *dev, struct goku_udc_regs *regs, int epnum)
{
dev->int_enable &= ~INT_EPxDATASET (epnum);
writel(dev->int_enable, &regs->int_enable);
/* write may still be posted */
}
static inline void
pio_advance(struct goku_ep *ep)
{
struct goku_request *req;
if (unlikely(list_empty (&ep->queue)))
return;
req = list_entry(ep->queue.next, struct goku_request, queue);
(ep->is_in ? write_fifo : read_fifo)(ep, req);
}
/*-------------------------------------------------------------------------*/
// return: 0 = q running, 1 = q stopped, negative = errno
static int start_dma(struct goku_ep *ep, struct goku_request *req)
{
struct goku_udc_regs *regs = ep->dev->regs;
u32 master;
u32 start = req->req.dma;
u32 end = start + req->req.length - 1;
master = readl(&regs->dma_master) & MST_RW_BITS;
/* re-init the bits affecting IN dma; careful with zlps */
if (likely(ep->is_in)) {
if (unlikely(master & MST_RD_ENA)) {
DBG (ep->dev, "start, IN active dma %03x!!\n",
master);
// return -EL2HLT;
}
writel(end, &regs->in_dma_end);
writel(start, &regs->in_dma_start);
master &= ~MST_R_BITS;
if (unlikely(req->req.length == 0))
master = MST_RD_ENA | MST_RD_EOPB;
else if ((req->req.length % ep->ep.maxpacket) != 0
|| req->req.zero)
master = MST_RD_ENA | MST_EOPB_ENA;
else
master = MST_RD_ENA | MST_EOPB_DIS;
ep->dev->int_enable |= INT_MSTRDEND;
/* Goku DMA-OUT merges short packets, which plays poorly with
* protocols where short packets mark the transfer boundaries.
* The chip supports a nonstandard policy with INT_MSTWRTMOUT,
* ending transfers after 3 SOFs; we don't turn it on.
*/
} else {
if (unlikely(master & MST_WR_ENA)) {
DBG (ep->dev, "start, OUT active dma %03x!!\n",
master);
// return -EL2HLT;
}
writel(end, &regs->out_dma_end);
writel(start, &regs->out_dma_start);
master &= ~MST_W_BITS;
master |= MST_WR_ENA | MST_TIMEOUT_DIS;
ep->dev->int_enable |= INT_MSTWREND|INT_MSTWRTMOUT;
}
writel(master, &regs->dma_master);
writel(ep->dev->int_enable, &regs->int_enable);
return 0;
}
static void dma_advance(struct goku_udc *dev, struct goku_ep *ep)
{
struct goku_request *req;
struct goku_udc_regs *regs = ep->dev->regs;
u32 master;
master = readl(&regs->dma_master);
if (unlikely(list_empty(&ep->queue))) {
stop:
if (ep->is_in)
dev->int_enable &= ~INT_MSTRDEND;
else
dev->int_enable &= ~(INT_MSTWREND|INT_MSTWRTMOUT);
writel(dev->int_enable, &regs->int_enable);
return;
}
req = list_entry(ep->queue.next, struct goku_request, queue);
/* normal hw dma completion (not abort) */
if (likely(ep->is_in)) {
if (unlikely(master & MST_RD_ENA))
return;
req->req.actual = readl(&regs->in_dma_current);
} else {
if (unlikely(master & MST_WR_ENA))
return;
/* hardware merges short packets, and also hides packet
* overruns. a partial packet MAY be in the fifo here.
*/
req->req.actual = readl(&regs->out_dma_current);
}
req->req.actual -= req->req.dma;
req->req.actual++;
#ifdef USB_TRACE
VDBG(dev, "done %s %s dma, %u/%u bytes, req %p\n",
ep->ep.name, ep->is_in ? "IN" : "OUT",
req->req.actual, req->req.length, req);
#endif
done(ep, req, 0);
if (list_empty(&ep->queue))
goto stop;
req = list_entry(ep->queue.next, struct goku_request, queue);
(void) start_dma(ep, req);
}
static void abort_dma(struct goku_ep *ep, int status)
{
struct goku_udc_regs *regs = ep->dev->regs;
struct goku_request *req;
u32 curr, master;
/* NAK future host requests, hoping the implicit delay lets the
* dma engine finish reading (or writing) its latest packet and
* empty the dma buffer (up to 16 bytes).
*
* This avoids needing to clean up a partial packet in the fifo;
* we can't do that for IN without side effects to HALT and TOGGLE.
*/
command(regs, COMMAND_FIFO_DISABLE, ep->num);
req = list_entry(ep->queue.next, struct goku_request, queue);
master = readl(&regs->dma_master) & MST_RW_BITS;
/* FIXME using these resets isn't usably documented. this may
* not work unless it's followed by disabling the endpoint.
*
* FIXME the OUT reset path doesn't even behave consistently.
*/
if (ep->is_in) {
if (unlikely((readl(&regs->dma_master) & MST_RD_ENA) == 0))
goto finished;
curr = readl(&regs->in_dma_current);
writel(curr, &regs->in_dma_end);
writel(curr, &regs->in_dma_start);
master &= ~MST_R_BITS;
master |= MST_RD_RESET;
writel(master, &regs->dma_master);
if (readl(&regs->dma_master) & MST_RD_ENA)
DBG(ep->dev, "IN dma active after reset!\n");
} else {
if (unlikely((readl(&regs->dma_master) & MST_WR_ENA) == 0))
goto finished;
curr = readl(&regs->out_dma_current);
writel(curr, &regs->out_dma_end);
writel(curr, &regs->out_dma_start);
master &= ~MST_W_BITS;
master |= MST_WR_RESET;
writel(master, &regs->dma_master);
if (readl(&regs->dma_master) & MST_WR_ENA)
DBG(ep->dev, "OUT dma active after reset!\n");
}
req->req.actual = (curr - req->req.dma) + 1;
req->req.status = status;
VDBG(ep->dev, "%s %s %s %d/%d\n", __FUNCTION__, ep->ep.name,
ep->is_in ? "IN" : "OUT",
req->req.actual, req->req.length);
command(regs, COMMAND_FIFO_ENABLE, ep->num);
return;
finished:
/* dma already completed; no abort needed */
command(regs, COMMAND_FIFO_ENABLE, ep->num);
req->req.actual = req->req.length;
req->req.status = 0;
}
/*-------------------------------------------------------------------------*/
static int
goku_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags)
{
struct goku_request *req;
struct goku_ep *ep;
struct goku_udc *dev;
unsigned long flags;
int status;
/* always require a cpu-view buffer so pio works */
req = container_of(_req, struct goku_request, req);
if (unlikely(!_req || !_req->complete
|| !_req->buf || !list_empty(&req->queue)))
return -EINVAL;
ep = container_of(_ep, struct goku_ep, ep);
if (unlikely(!_ep || (!ep->desc && ep->num != 0)))
return -EINVAL;
dev = ep->dev;
if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN))
return -ESHUTDOWN;
/* can't touch registers when suspended */
if (dev->ep0state == EP0_SUSPEND)
return -EBUSY;
/* set up dma mapping in case the caller didn't */
if (ep->dma && _req->dma == DMA_ADDR_INVALID) {
_req->dma = pci_map_single(dev->pdev, _req->buf, _req->length,
ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
req->mapped = 1;
}
#ifdef USB_TRACE
VDBG(dev, "%s queue req %p, len %u buf %p\n",
_ep->name, _req, _req->length, _req->buf);
#endif
spin_lock_irqsave(&dev->lock, flags);
_req->status = -EINPROGRESS;
_req->actual = 0;
/* for ep0 IN without premature status, zlp is required and
* writing EOP starts the status stage (OUT).
*/
if (unlikely(ep->num == 0 && ep->is_in))
_req->zero = 1;
/* kickstart this i/o queue? */
status = 0;
if (list_empty(&ep->queue) && likely(!ep->stopped)) {
/* dma: done after dma completion IRQ (or error)
* pio: done after last fifo operation
*/
if (ep->dma)
status = start_dma(ep, req);
else
status = (ep->is_in ? write_fifo : read_fifo)(ep, req);
if (unlikely(status != 0)) {
if (status > 0)
status = 0;
req = 0;
}
} /* else pio or dma irq handler advances the queue. */
if (likely(req != 0))
list_add_tail(&req->queue, &ep->queue);
if (likely(!list_empty(&ep->queue))
&& likely(ep->num != 0)
&& !ep->dma
&& !(dev->int_enable & INT_EPxDATASET (ep->num)))
pio_irq_enable(dev, dev->regs, ep->num);
spin_unlock_irqrestore(&dev->lock, flags);
/* pci writes may still be posted */
return status;
}
/* dequeue ALL requests */
static void nuke(struct goku_ep *ep, int status)
{
struct goku_request *req;
ep->stopped = 1;
if (list_empty(&ep->queue))
return;
if (ep->dma)
abort_dma(ep, status);
while (!list_empty(&ep->queue)) {
req = list_entry(ep->queue.next, struct goku_request, queue);
done(ep, req, status);
}
}
/* dequeue JUST ONE request */
static int goku_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct goku_request *req;
struct goku_ep *ep;
struct goku_udc *dev;
unsigned long flags;
ep = container_of(_ep, struct goku_ep, ep);
if (!_ep || !_req || (!ep->desc && ep->num != 0))
return -EINVAL;
dev = ep->dev;
if (!dev->driver)
return -ESHUTDOWN;
/* we can't touch (dma) registers when suspended */
if (dev->ep0state == EP0_SUSPEND)
return -EBUSY;
VDBG(dev, "%s %s %s %s %p\n", __FUNCTION__, _ep->name,
ep->is_in ? "IN" : "OUT",
ep->dma ? "dma" : "pio",
_req);
spin_lock_irqsave(&dev->lock, flags);
/* make sure it's actually queued on this endpoint */
list_for_each_entry (req, &ep->queue, queue) {
if (&req->req == _req)
break;
}
if (&req->req != _req) {
spin_unlock_irqrestore (&dev->lock, flags);
return -EINVAL;
}
if (ep->dma && ep->queue.next == &req->queue && !ep->stopped) {
abort_dma(ep, -ECONNRESET);
done(ep, req, -ECONNRESET);
dma_advance(dev, ep);
} else if (!list_empty(&req->queue))
done(ep, req, -ECONNRESET);
else
req = 0;
spin_unlock_irqrestore(&dev->lock, flags);
return req ? 0 : -EOPNOTSUPP;
}
/*-------------------------------------------------------------------------*/
static void goku_clear_halt(struct goku_ep *ep)
{
// assert (ep->num !=0)
VDBG(ep->dev, "%s clear halt\n", ep->ep.name);
command(ep->dev->regs, COMMAND_SETDATA0, ep->num);
command(ep->dev->regs, COMMAND_STALL_CLEAR, ep->num);
if (ep->stopped) {
ep->stopped = 0;
if (ep->dma) {
struct goku_request *req;
if (list_empty(&ep->queue))
return;
req = list_entry(ep->queue.next, struct goku_request,
queue);
(void) start_dma(ep, req);
} else
pio_advance(ep);
}
}
static int goku_set_halt(struct usb_ep *_ep, int value)
{
struct goku_ep *ep;
unsigned long flags;
int retval = 0;
if (!_ep)
return -ENODEV;
ep = container_of (_ep, struct goku_ep, ep);
if (ep->num == 0) {
if (value) {
ep->dev->ep0state = EP0_STALL;
ep->dev->ep[0].stopped = 1;
} else
return -EINVAL;
/* don't change EPxSTATUS_EP_INVALID to READY */
} else if (!ep->desc) {
DBG(ep->dev, "%s %s inactive?\n", __FUNCTION__, ep->ep.name);
return -EINVAL;
}
spin_lock_irqsave(&ep->dev->lock, flags);
if (!list_empty(&ep->queue))
retval = -EAGAIN;
else if (ep->is_in && value
/* data in (either) packet buffer? */
&& (ep->dev->regs->DataSet & DATASET_AB(ep->num)))
retval = -EAGAIN;
else if (!value)
goku_clear_halt(ep);
else {
ep->stopped = 1;
VDBG(ep->dev, "%s set halt\n", ep->ep.name);
command(ep->dev->regs, COMMAND_STALL, ep->num);
readl(ep->reg_status);
}
spin_unlock_irqrestore(&ep->dev->lock, flags);
return retval;
}
static int goku_fifo_status(struct usb_ep *_ep)
{
struct goku_ep *ep;
struct goku_udc_regs *regs;
u32 size;
if (!_ep)
return -ENODEV;
ep = container_of(_ep, struct goku_ep, ep);
/* size is only reported sanely for OUT */
if (ep->is_in)
return -EOPNOTSUPP;
/* ignores 16-byte dma buffer; SizeH == 0 */
regs = ep->dev->regs;
size = readl(&regs->EPxSizeLA[ep->num]) & DATASIZE;
size += readl(&regs->EPxSizeLB[ep->num]) & DATASIZE;
VDBG(ep->dev, "%s %s %u\n", __FUNCTION__, ep->ep.name, size);
return size;
}
static void goku_fifo_flush(struct usb_ep *_ep)
{
struct goku_ep *ep;
struct goku_udc_regs *regs;
u32 size;
if (!_ep)
return;
ep = container_of(_ep, struct goku_ep, ep);
VDBG(ep->dev, "%s %s\n", __FUNCTION__, ep->ep.name);
/* don't change EPxSTATUS_EP_INVALID to READY */
if (!ep->desc && ep->num != 0) {
DBG(ep->dev, "%s %s inactive?\n", __FUNCTION__, ep->ep.name);
return;
}
regs = ep->dev->regs;
size = readl(&regs->EPxSizeLA[ep->num]);
size &= DATASIZE;
/* Non-desirable behavior: FIFO_CLEAR also clears the
* endpoint halt feature. For OUT, we _could_ just read
* the bytes out (PIO, if !ep->dma); for in, no choice.
*/
if (size)
command(regs, COMMAND_FIFO_CLEAR, ep->num);
}
static struct usb_ep_ops goku_ep_ops = {
.enable = goku_ep_enable,
.disable = goku_ep_disable,
.alloc_request = goku_alloc_request,
.free_request = goku_free_request,
.alloc_buffer = goku_alloc_buffer,
.free_buffer = goku_free_buffer,
.queue = goku_queue,
.dequeue = goku_dequeue,
.set_halt = goku_set_halt,
.fifo_status = goku_fifo_status,
.fifo_flush = goku_fifo_flush,
};
/*-------------------------------------------------------------------------*/
static int goku_get_frame(struct usb_gadget *_gadget)
{
return -EOPNOTSUPP;
}
static const struct usb_gadget_ops goku_ops = {
.get_frame = goku_get_frame,
// no remote wakeup
// not selfpowered
};
/*-------------------------------------------------------------------------*/
static inline char *dmastr(void)
{
if (use_dma == 0)
return "(dma disabled)";
else if (use_dma == 2)
return "(dma IN and OUT)";
else
return "(dma IN)";
}
/* if we're trying to save space, don't bother with this proc file */
#if defined(CONFIG_PROC_FS) && !defined(CONFIG_EMBEDDED)
# define UDC_PROC_FILE
#endif
#ifdef UDC_PROC_FILE
static const char proc_node_name [] = "driver/udc";
#define FOURBITS "%s%s%s%s"
#define EIGHTBITS FOURBITS FOURBITS
static void
dump_intmask(const char *label, u32 mask, char **next, unsigned *size)
{
int t;
/* int_status is the same format ... */
t = snprintf(*next, *size,
"%s %05X =" FOURBITS EIGHTBITS EIGHTBITS "\n",
label, mask,
(mask & INT_PWRDETECT) ? " power" : "",
(mask & INT_SYSERROR) ? " sys" : "",
(mask & INT_MSTRDEND) ? " in-dma" : "",
(mask & INT_MSTWRTMOUT) ? " wrtmo" : "",
(mask & INT_MSTWREND) ? " out-dma" : "",
(mask & INT_MSTWRSET) ? " wrset" : "",
(mask & INT_ERR) ? " err" : "",
(mask & INT_SOF) ? " sof" : "",
(mask & INT_EP3NAK) ? " ep3nak" : "",
(mask & INT_EP2NAK) ? " ep2nak" : "",
(mask & INT_EP1NAK) ? " ep1nak" : "",
(mask & INT_EP3DATASET) ? " ep3" : "",
(mask & INT_EP2DATASET) ? " ep2" : "",
(mask & INT_EP1DATASET) ? " ep1" : "",
(mask & INT_STATUSNAK) ? " ep0snak" : "",
(mask & INT_STATUS) ? " ep0status" : "",
(mask & INT_SETUP) ? " setup" : "",
(mask & INT_ENDPOINT0) ? " ep0" : "",
(mask & INT_USBRESET) ? " reset" : "",
(mask & INT_SUSPEND) ? " suspend" : "");
*size -= t;
*next += t;
}
static int
udc_proc_read(char *buffer, char **start, off_t off, int count,
int *eof, void *_dev)
{
char *buf = buffer;
struct goku_udc *dev = _dev;
struct goku_udc_regs *regs = dev->regs;
char *next = buf;
unsigned size = count;
unsigned long flags;
int i, t, is_usb_connected;
u32 tmp;
if (off != 0)
return 0;
local_irq_save(flags);
/* basic device status */
tmp = readl(&regs->power_detect);
is_usb_connected = tmp & PW_DETECT;
t = snprintf(next, size,
"%s - %s\n"
"%s version: %s %s\n"
"Gadget driver: %s\n"
"Host %s, %s\n"
"\n",
pci_name(dev->pdev), driver_desc,
driver_name, DRIVER_VERSION, dmastr(),
dev->driver ? dev->driver->driver.name : "(none)",
is_usb_connected
? ((tmp & PW_PULLUP) ? "full speed" : "powered")
: "disconnected",
({char *tmp;
switch(dev->ep0state){
case EP0_DISCONNECT: tmp = "ep0_disconnect"; break;
case EP0_IDLE: tmp = "ep0_idle"; break;
case EP0_IN: tmp = "ep0_in"; break;
case EP0_OUT: tmp = "ep0_out"; break;
case EP0_STATUS: tmp = "ep0_status"; break;
case EP0_STALL: tmp = "ep0_stall"; break;
case EP0_SUSPEND: tmp = "ep0_suspend"; break;
default: tmp = "ep0_?"; break;
} tmp; })
);
size -= t;
next += t;
dump_intmask("int_status", readl(&regs->int_status), &next, &size);
dump_intmask("int_enable", readl(&regs->int_enable), &next, &size);
if (!is_usb_connected || !dev->driver || (tmp & PW_PULLUP) == 0)
goto done;
/* registers for (active) device and ep0 */
t = snprintf(next, size, "\nirqs %lu\ndataset %02x "
"single.bcs %02x.%02x state %x addr %u\n",
dev->irqs, readl(&regs->DataSet),
readl(&regs->EPxSingle), readl(&regs->EPxBCS),
readl(&regs->UsbState),
readl(&regs->address));
size -= t;
next += t;
tmp = readl(&regs->dma_master);
t = snprintf(next, size,
"dma %03X =" EIGHTBITS "%s %s\n", tmp,
(tmp & MST_EOPB_DIS) ? " eopb-" : "",
(tmp & MST_EOPB_ENA) ? " eopb+" : "",
(tmp & MST_TIMEOUT_DIS) ? " tmo-" : "",
(tmp & MST_TIMEOUT_ENA) ? " tmo+" : "",
(tmp & MST_RD_EOPB) ? " eopb" : "",
(tmp & MST_RD_RESET) ? " in_reset" : "",
(tmp & MST_WR_RESET) ? " out_reset" : "",
(tmp & MST_RD_ENA) ? " IN" : "",
(tmp & MST_WR_ENA) ? " OUT" : "",
(tmp & MST_CONNECTION)
? "ep1in/ep2out"
: "ep1out/ep2in");
size -= t;
next += t;
/* dump endpoint queues */
for (i = 0; i < 4; i++) {
struct goku_ep *ep = &dev->ep [i];
struct goku_request *req;
int t;
if (i && !ep->desc)
continue;
tmp = readl(ep->reg_status);
t = snprintf(next, size,
"%s %s max %u %s, irqs %lu, "
"status %02x (%s) " FOURBITS "\n",
ep->ep.name,
ep->is_in ? "in" : "out",
ep->ep.maxpacket,
ep->dma ? "dma" : "pio",
ep->irqs,
tmp, ({ char *s;
switch (tmp & EPxSTATUS_EP_MASK) {
case EPxSTATUS_EP_READY:
s = "ready"; break;
case EPxSTATUS_EP_DATAIN:
s = "packet"; break;
case EPxSTATUS_EP_FULL:
s = "full"; break;
case EPxSTATUS_EP_TX_ERR: // host will retry
s = "tx_err"; break;
case EPxSTATUS_EP_RX_ERR:
s = "rx_err"; break;
case EPxSTATUS_EP_BUSY: /* ep0 only */
s = "busy"; break;
case EPxSTATUS_EP_STALL:
s = "stall"; break;
case EPxSTATUS_EP_INVALID: // these "can't happen"
s = "invalid"; break;
default:
s = "?"; break;
}; s; }),
(tmp & EPxSTATUS_TOGGLE) ? "data1" : "data0",
(tmp & EPxSTATUS_SUSPEND) ? " suspend" : "",
(tmp & EPxSTATUS_FIFO_DISABLE) ? " disable" : "",
(tmp & EPxSTATUS_STAGE_ERROR) ? " ep0stat" : ""
);
if (t <= 0 || t > size)
goto done;
size -= t;
next += t;
if (list_empty(&ep->queue)) {
t = snprintf(next, size, "\t(nothing queued)\n");
if (t <= 0 || t > size)
goto done;
size -= t;
next += t;
continue;
}
list_for_each_entry(req, &ep->queue, queue) {
if (ep->dma && req->queue.prev == &ep->queue) {
if (i == UDC_MSTRD_ENDPOINT)
tmp = readl(&regs->in_dma_current);
else
tmp = readl(&regs->out_dma_current);
tmp -= req->req.dma;
tmp++;
} else
tmp = req->req.actual;
t = snprintf(next, size,
"\treq %p len %u/%u buf %p\n",
&req->req, tmp, req->req.length,
req->req.buf);
if (t <= 0 || t > size)
goto done;
size -= t;
next += t;
}
}
done:
local_irq_restore(flags);
*eof = 1;
return count - size;
}
#endif /* UDC_PROC_FILE */
/*-------------------------------------------------------------------------*/
static void udc_reinit (struct goku_udc *dev)
{
static char *names [] = { "ep0", "ep1-bulk", "ep2-bulk", "ep3-bulk" };
unsigned i;
INIT_LIST_HEAD (&dev->gadget.ep_list);
dev->gadget.ep0 = &dev->ep [0].ep;
dev->gadget.speed = USB_SPEED_UNKNOWN;
dev->ep0state = EP0_DISCONNECT;
dev->irqs = 0;
for (i = 0; i < 4; i++) {
struct goku_ep *ep = &dev->ep[i];
ep->num = i;
ep->ep.name = names[i];
ep->reg_fifo = &dev->regs->ep_fifo [i];
ep->reg_status = &dev->regs->ep_status [i];
ep->reg_mode = &dev->regs->ep_mode[i];
ep->ep.ops = &goku_ep_ops;
list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list);
ep->dev = dev;
INIT_LIST_HEAD (&ep->queue);
ep_reset(0, ep);
}
dev->ep[0].reg_mode = 0;
dev->ep[0].ep.maxpacket = MAX_EP0_SIZE;
list_del_init (&dev->ep[0].ep.ep_list);
}
static void udc_reset(struct goku_udc *dev)
{
struct goku_udc_regs *regs = dev->regs;
writel(0, &regs->power_detect);
writel(0, &regs->int_enable);
readl(&regs->int_enable);
dev->int_enable = 0;
/* deassert reset, leave USB D+ at hi-Z (no pullup)
* don't let INT_PWRDETECT sequence begin
*/
udelay(250);
writel(PW_RESETB, &regs->power_detect);
readl(&regs->int_enable);
}
static void ep0_start(struct goku_udc *dev)
{
struct goku_udc_regs *regs = dev->regs;
unsigned i;
VDBG(dev, "%s\n", __FUNCTION__);
udc_reset(dev);
udc_reinit (dev);
//writel(MST_EOPB_ENA | MST_TIMEOUT_ENA, &regs->dma_master);
/* hw handles set_address, set_feature, get_status; maybe more */
writel( G_REQMODE_SET_INTF | G_REQMODE_GET_INTF
| G_REQMODE_SET_CONF | G_REQMODE_GET_CONF
| G_REQMODE_GET_DESC
| G_REQMODE_CLEAR_FEAT
, &regs->reqmode);
for (i = 0; i < 4; i++)
dev->ep[i].irqs = 0;
/* can't modify descriptors after writing UsbReady */
for (i = 0; i < DESC_LEN; i++)
writel(0, &regs->descriptors[i]);
writel(0, &regs->UsbReady);
/* expect ep0 requests when the host drops reset */
writel(PW_RESETB | PW_PULLUP, &regs->power_detect);
dev->int_enable = INT_DEVWIDE | INT_EP0;
writel(dev->int_enable, &dev->regs->int_enable);
readl(&regs->int_enable);
dev->gadget.speed = USB_SPEED_FULL;
dev->ep0state = EP0_IDLE;
}
static void udc_enable(struct goku_udc *dev)
{
/* start enumeration now, or after power detect irq */
if (readl(&dev->regs->power_detect) & PW_DETECT)
ep0_start(dev);
else {
DBG(dev, "%s\n", __FUNCTION__);
dev->int_enable = INT_PWRDETECT;
writel(dev->int_enable, &dev->regs->int_enable);
}
}
/*-------------------------------------------------------------------------*/
/* keeping it simple:
* - one bus driver, initted first;
* - one function driver, initted second
*/
static struct goku_udc *the_controller;
/* when a driver is successfully registered, it will receive
* control requests including set_configuration(), which enables
* non-control requests. then usb traffic follows until a
* disconnect is reported. then a host may connect again, or
* the driver might get unbound.
*/
int usb_gadget_register_driver(struct usb_gadget_driver *driver)
{
struct goku_udc *dev = the_controller;
int retval;
if (!driver
|| driver->speed != USB_SPEED_FULL
|| !driver->bind
|| !driver->unbind
|| !driver->disconnect
|| !driver->setup)
return -EINVAL;
if (!dev)
return -ENODEV;
if (dev->driver)
return -EBUSY;
/* hook up the driver */
driver->driver.bus = 0;
dev->driver = driver;
dev->gadget.dev.driver = &driver->driver;
retval = driver->bind(&dev->gadget);
if (retval) {
DBG(dev, "bind to driver %s --> error %d\n",
driver->driver.name, retval);
dev->driver = 0;
dev->gadget.dev.driver = 0;
return retval;
}
/* then enable host detection and ep0; and we're ready
* for set_configuration as well as eventual disconnect.
*/
udc_enable(dev);
DBG(dev, "registered gadget driver '%s'\n", driver->driver.name);
return 0;
}
EXPORT_SYMBOL(usb_gadget_register_driver);
static void
stop_activity(struct goku_udc *dev, struct usb_gadget_driver *driver)
{
unsigned i;
DBG (dev, "%s\n", __FUNCTION__);
if (dev->gadget.speed == USB_SPEED_UNKNOWN)
driver = 0;
/* disconnect gadget driver after quiesceing hw and the driver */
udc_reset (dev);
for (i = 0; i < 4; i++)
nuke(&dev->ep [i], -ESHUTDOWN);
if (driver) {
spin_unlock(&dev->lock);
driver->disconnect(&dev->gadget);
spin_lock(&dev->lock);
}
if (dev->driver)
udc_enable(dev);
}
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
{
struct goku_udc *dev = the_controller;
unsigned long flags;
if (!dev)
return -ENODEV;
if (!driver || driver != dev->driver)
return -EINVAL;
spin_lock_irqsave(&dev->lock, flags);
dev->driver = 0;
stop_activity(dev, driver);
spin_unlock_irqrestore(&dev->lock, flags);
driver->unbind(&dev->gadget);
DBG(dev, "unregistered driver '%s'\n", driver->driver.name);
return 0;
}
EXPORT_SYMBOL(usb_gadget_unregister_driver);
/*-------------------------------------------------------------------------*/
static void ep0_setup(struct goku_udc *dev)
{
struct goku_udc_regs *regs = dev->regs;
struct usb_ctrlrequest ctrl;
int tmp;
/* read SETUP packet and enter DATA stage */
ctrl.bRequestType = readl(&regs->bRequestType);
ctrl.bRequest = readl(&regs->bRequest);
ctrl.wValue = (readl(&regs->wValueH) << 8) | readl(&regs->wValueL);
ctrl.wIndex = (readl(&regs->wIndexH) << 8) | readl(&regs->wIndexL);
ctrl.wLength = (readl(&regs->wLengthH) << 8) | readl(&regs->wLengthL);
writel(0, &regs->SetupRecv);
nuke(&dev->ep[0], 0);
dev->ep[0].stopped = 0;
if (likely(ctrl.bRequestType & USB_DIR_IN)) {
dev->ep[0].is_in = 1;
dev->ep0state = EP0_IN;
/* detect early status stages */
writel(ICONTROL_STATUSNAK, &dev->regs->IntControl);
} else {
dev->ep[0].is_in = 0;
dev->ep0state = EP0_OUT;
/* NOTE: CLEAR_FEATURE is done in software so that we can
* synchronize transfer restarts after bulk IN stalls. data
* won't even enter the fifo until the halt is cleared.
*/
switch (ctrl.bRequest) {
case USB_REQ_CLEAR_FEATURE:
switch (ctrl.bRequestType) {
case USB_RECIP_ENDPOINT:
tmp = ctrl.wIndex & 0x0f;
/* active endpoint */
if (tmp > 3 || (!dev->ep[tmp].desc && tmp != 0))
goto stall;
if (ctrl.wIndex & USB_DIR_IN) {
if (!dev->ep[tmp].is_in)
goto stall;
} else {
if (dev->ep[tmp].is_in)
goto stall;
}
/* endpoint halt */
if (ctrl.wValue != 0)
goto stall;
if (tmp)
goku_clear_halt(&dev->ep[tmp]);
succeed:
/* start ep0out status stage */
writel(~(1<<0), &regs->EOP);
dev->ep[0].stopped = 1;
dev->ep0state = EP0_STATUS;
return;
case USB_RECIP_DEVICE:
/* device remote wakeup: always clear */
if (ctrl.wValue != 1)
goto stall;
VDBG(dev, "clear dev remote wakeup\n");
goto succeed;
case USB_RECIP_INTERFACE:
goto stall;
default: /* pass to gadget driver */
break;
}
break;
default:
break;
}
}
#ifdef USB_TRACE
VDBG(dev, "SETUP %02x.%02x v%04x i%04x l%04x\n",
ctrl.bRequestType, ctrl.bRequest,
ctrl.wValue, ctrl.wIndex, ctrl.wLength);
#endif
/* hw wants to know when we're configured (or not) */
dev->req_config = (ctrl.bRequest == USB_REQ_SET_CONFIGURATION
&& ctrl.bRequestType == USB_RECIP_DEVICE);
if (unlikely(dev->req_config))
dev->configured = (ctrl.wValue != 0);
/* delegate everything to the gadget driver.
* it may respond after this irq handler returns.
*/
spin_unlock (&dev->lock);
tmp = dev->driver->setup(&dev->gadget, &ctrl);
spin_lock (&dev->lock);
if (unlikely(tmp < 0)) {
stall:
#ifdef USB_TRACE
VDBG(dev, "req %02x.%02x protocol STALL; err %d\n",
ctrl.bRequestType, ctrl.bRequest, tmp);
#endif
command(regs, COMMAND_STALL, 0);
dev->ep[0].stopped = 1;
dev->ep0state = EP0_STALL;
}
/* expect at least one data or status stage irq */
}
#define ACK(irqbit) { \
stat &= ~irqbit; \
writel(~irqbit, &regs->int_status); \
handled = 1; \
}
static irqreturn_t goku_irq(int irq, void *_dev, struct pt_regs *r)
{
struct goku_udc *dev = _dev;
struct goku_udc_regs *regs = dev->regs;
struct goku_ep *ep;
u32 stat, handled = 0;
unsigned i, rescans = 5;
spin_lock(&dev->lock);
rescan:
stat = readl(&regs->int_status) & dev->int_enable;
if (!stat)
goto done;
dev->irqs++;
/* device-wide irqs */
if (unlikely(stat & INT_DEVWIDE)) {
if (stat & INT_SYSERROR) {
ERROR(dev, "system error\n");
stop_activity(dev, dev->driver);
stat = 0;
handled = 1;
// FIXME have a neater way to prevent re-enumeration
dev->driver = 0;
goto done;
}
if (stat & INT_PWRDETECT) {
writel(~stat, &regs->int_status);
if (readl(&dev->regs->power_detect) & PW_DETECT) {
VDBG(dev, "connect\n");
ep0_start(dev);
} else {
DBG(dev, "disconnect\n");
if (dev->gadget.speed == USB_SPEED_FULL)
stop_activity(dev, dev->driver);
dev->ep0state = EP0_DISCONNECT;
dev->int_enable = INT_DEVWIDE;
writel(dev->int_enable, &dev->regs->int_enable);
}
stat = 0;
handled = 1;
goto done;
}
if (stat & INT_SUSPEND) {
ACK(INT_SUSPEND);
if (readl(&regs->ep_status[0]) & EPxSTATUS_SUSPEND) {
switch (dev->ep0state) {
case EP0_DISCONNECT:
case EP0_SUSPEND:
goto pm_next;
default:
break;
}
DBG(dev, "USB suspend\n");
dev->ep0state = EP0_SUSPEND;
if (dev->gadget.speed != USB_SPEED_UNKNOWN
&& dev->driver
&& dev->driver->suspend) {
spin_unlock(&dev->lock);
dev->driver->suspend(&dev->gadget);
spin_lock(&dev->lock);
}
} else {
if (dev->ep0state != EP0_SUSPEND) {
DBG(dev, "bogus USB resume %d\n",
dev->ep0state);
goto pm_next;
}
DBG(dev, "USB resume\n");
dev->ep0state = EP0_IDLE;
if (dev->gadget.speed != USB_SPEED_UNKNOWN
&& dev->driver
&& dev->driver->resume) {
spin_unlock(&dev->lock);
dev->driver->resume(&dev->gadget);
spin_lock(&dev->lock);
}
}
}
pm_next:
if (stat & INT_USBRESET) { /* hub reset done */
ACK(INT_USBRESET);
INFO(dev, "USB reset done, gadget %s\n",
dev->driver->driver.name);
}
// and INT_ERR on some endpoint's crc/bitstuff/... problem
}
/* progress ep0 setup, data, or status stages.
* no transition {EP0_STATUS, EP0_STALL} --> EP0_IDLE; saves irqs
*/
if (stat & INT_SETUP) {
ACK(INT_SETUP);
dev->ep[0].irqs++;
ep0_setup(dev);
}
if (stat & INT_STATUSNAK) {
ACK(INT_STATUSNAK|INT_ENDPOINT0);
if (dev->ep0state == EP0_IN) {
ep = &dev->ep[0];
ep->irqs++;
nuke(ep, 0);
writel(~(1<<0), &regs->EOP);
dev->ep0state = EP0_STATUS;
}
}
if (stat & INT_ENDPOINT0) {
ACK(INT_ENDPOINT0);
ep = &dev->ep[0];
ep->irqs++;
pio_advance(ep);
}
/* dma completion */
if (stat & INT_MSTRDEND) { /* IN */
ACK(INT_MSTRDEND);
ep = &dev->ep[UDC_MSTRD_ENDPOINT];
ep->irqs++;
dma_advance(dev, ep);
}
if (stat & INT_MSTWREND) { /* OUT */
ACK(INT_MSTWREND);
ep = &dev->ep[UDC_MSTWR_ENDPOINT];
ep->irqs++;
dma_advance(dev, ep);
}
if (stat & INT_MSTWRTMOUT) { /* OUT */
ACK(INT_MSTWRTMOUT);
ep = &dev->ep[UDC_MSTWR_ENDPOINT];
ep->irqs++;
ERROR(dev, "%s write timeout ?\n", ep->ep.name);
// reset dma? then dma_advance()
}
/* pio */
for (i = 1; i < 4; i++) {
u32 tmp = INT_EPxDATASET(i);
if (!(stat & tmp))
continue;
ep = &dev->ep[i];
pio_advance(ep);
if (list_empty (&ep->queue))
pio_irq_disable(dev, regs, i);
stat &= ~tmp;
handled = 1;
ep->irqs++;
}
if (rescans--)
goto rescan;
done:
(void)readl(&regs->int_enable);
spin_unlock(&dev->lock);
if (stat)
DBG(dev, "unhandled irq status: %05x (%05x, %05x)\n", stat,
readl(&regs->int_status), dev->int_enable);
return IRQ_RETVAL(handled);
}
#undef ACK
/*-------------------------------------------------------------------------*/
static void gadget_release(struct device *_dev)
{
struct goku_udc *dev = dev_get_drvdata(_dev);
kfree(dev);
}
/* tear down the binding between this driver and the pci device */
static void goku_remove(struct pci_dev *pdev)
{
struct goku_udc *dev = pci_get_drvdata(pdev);
DBG(dev, "%s\n", __FUNCTION__);
/* start with the driver above us */
if (dev->driver) {
/* should have been done already by driver model core */
WARN(dev, "pci remove, driver '%s' is still registered\n",
dev->driver->driver.name);
usb_gadget_unregister_driver(dev->driver);
}
#ifdef UDC_PROC_FILE
remove_proc_entry(proc_node_name, NULL);
#endif
if (dev->regs)
udc_reset(dev);
if (dev->got_irq)
free_irq(pdev->irq, dev);
if (dev->regs)
iounmap(dev->regs);
if (dev->got_region)
release_mem_region(pci_resource_start (pdev, 0),
pci_resource_len (pdev, 0));
if (dev->enabled)
pci_disable_device(pdev);
device_unregister(&dev->gadget.dev);
pci_set_drvdata(pdev, 0);
dev->regs = 0;
the_controller = 0;
INFO(dev, "unbind\n");
}
/* wrap this driver around the specified pci device, but
* don't respond over USB until a gadget driver binds to us.
*/
static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct goku_udc *dev = 0;
unsigned long resource, len;
void *base = 0;
int retval;
char buf [8], *bufp;
/* if you want to support more than one controller in a system,
* usb_gadget_driver_{register,unregister}() must change.
*/
if (the_controller) {
WARN(dev, "ignoring %s\n", pci_name(pdev));
return -EBUSY;
}
if (!pdev->irq) {
printk(KERN_ERR "Check PCI %s IRQ setup!\n", pci_name(pdev));
retval = -ENODEV;
goto done;
}
/* alloc, and start init */
dev = kmalloc (sizeof *dev, SLAB_KERNEL);
if (dev == NULL){
pr_debug("enomem %s\n", pci_name(pdev));
retval = -ENOMEM;
goto done;
}
memset(dev, 0, sizeof *dev);
spin_lock_init(&dev->lock);
dev->pdev = pdev;
dev->gadget.ops = &goku_ops;
/* the "gadget" abstracts/virtualizes the controller */
strcpy(dev->gadget.dev.bus_id, "gadget");
dev->gadget.dev.parent = &pdev->dev;
dev->gadget.dev.dma_mask = pdev->dev.dma_mask;
dev->gadget.dev.release = gadget_release;
dev->gadget.name = driver_name;
/* now all the pci goodies ... */
retval = pci_enable_device(pdev);
if (retval < 0) {
DBG(dev, "can't enable, %d\n", retval);
goto done;
}
dev->enabled = 1;
resource = pci_resource_start(pdev, 0);
len = pci_resource_len(pdev, 0);
if (!request_mem_region(resource, len, driver_name)) {
DBG(dev, "controller already in use\n");
retval = -EBUSY;
goto done;
}
dev->got_region = 1;
base = ioremap_nocache(resource, len);
if (base == NULL) {
DBG(dev, "can't map memory\n");
retval = -EFAULT;
goto done;
}
dev->regs = (struct goku_udc_regs *) base;
pci_set_drvdata(pdev, dev);
INFO(dev, "%s\n", driver_desc);
INFO(dev, "version: " DRIVER_VERSION " %s\n", dmastr());
#ifndef __sparc__
snprintf(buf, sizeof buf, "%d", pdev->irq);
bufp = buf;
#else
bufp = __irq_itoa(pdev->irq);
#endif
INFO(dev, "irq %s, pci mem %p\n", bufp, base);
/* init to known state, then setup irqs */
udc_reset(dev);
udc_reinit (dev);
if (request_irq(pdev->irq, goku_irq, SA_SHIRQ/*|SA_SAMPLE_RANDOM*/,
driver_name, dev) != 0) {
DBG(dev, "request interrupt %s failed\n", bufp);
retval = -EBUSY;
goto done;
}
dev->got_irq = 1;
if (use_dma)
pci_set_master(pdev);
#ifdef UDC_PROC_FILE
create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev);
#endif
/* done */
the_controller = dev;
device_register(&dev->gadget.dev);
return 0;
done:
if (dev)
goku_remove (pdev);
return retval;
}
/*-------------------------------------------------------------------------*/
static struct pci_device_id pci_ids [] = { {
.class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
.class_mask = ~0,
.vendor = 0x102f, /* Toshiba */
.device = 0x0107, /* this UDC */
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
}, { /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE (pci, pci_ids);
static struct pci_driver goku_pci_driver = {
.name = (char *) driver_name,
.id_table = pci_ids,
.probe = goku_probe,
.remove = goku_remove,
/* FIXME add power management support */
};
static int __init init (void)
{
return pci_module_init (&goku_pci_driver);
}
module_init (init);
static void __exit cleanup (void)
{
pci_unregister_driver (&goku_pci_driver);
}
module_exit (cleanup);
/*
* Toshiba TC86C001 ("Goku-S") USB Device Controller driver
*
* Copyright (C) 2000-2002 Lineo
* by Stuart Lynne, Tom Rushworth, and Bruce Balden
* Copyright (C) 2002 Toshiba Corporation
* Copyright (C) 2003 MontaVista Software (source@mvista.com)
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
/*
* PCI BAR 0 points to these registers.
*/
struct goku_udc_regs {
/* irq management */
u32 int_status; /* 0x000 */
u32 int_enable;
#define INT_SUSPEND 0x00001 /* or resume */
#define INT_USBRESET 0x00002
#define INT_ENDPOINT0 0x00004
#define INT_SETUP 0x00008
#define INT_STATUS 0x00010
#define INT_STATUSNAK 0x00020
#define INT_EPxDATASET(n) (0x00020 << (n)) /* 0 < n < 4 */
# define INT_EP1DATASET 0x00040
# define INT_EP2DATASET 0x00080
# define INT_EP3DATASET 0x00100
#define INT_EPnNAK(n) (0x00100 < (n)) /* 0 < n < 4 */
# define INT_EP1NAK 0x00200
# define INT_EP2NAK 0x00400
# define INT_EP3NAK 0x00800
#define INT_SOF 0x01000
#define INT_ERR 0x02000
#define INT_MSTWRSET 0x04000
#define INT_MSTWREND 0x08000
#define INT_MSTWRTMOUT 0x10000
#define INT_MSTRDEND 0x20000
#define INT_SYSERROR 0x40000
#define INT_PWRDETECT 0x80000
#define INT_DEVWIDE (INT_PWRDETECT|INT_SYSERROR/*|INT_ERR*/|INT_USBRESET|INT_SUSPEND)
#define INT_EP0 (INT_SETUP|INT_ENDPOINT0/*|INT_STATUS*/|INT_STATUSNAK)
u32 dma_master;
#define MST_EOPB_DIS 0x0800
#define MST_EOPB_ENA 0x0400
#define MST_TIMEOUT_DIS 0x0200
#define MST_TIMEOUT_ENA 0x0100
#define MST_RD_EOPB 0x0080 /* write-only */
#define MST_RD_RESET 0x0040
#define MST_WR_RESET 0x0020
#define MST_RD_ENA 0x0004 /* 1:start, 0:ignore */
#define MST_WR_ENA 0x0002 /* 1:start, 0:ignore */
#define MST_CONNECTION 0x0001 /* 0 for ep1out/ep2in */
#define MST_R_BITS (MST_EOPB_DIS|MST_EOPB_ENA \
|MST_RD_ENA|MST_RD_RESET)
#define MST_W_BITS (MST_TIMEOUT_DIS|MST_TIMEOUT_ENA \
|MST_WR_ENA|MST_WR_RESET)
#define MST_RW_BITS (MST_R_BITS|MST_W_BITS \
|MST_CONNECTION)
/* these values assume (dma_master & MST_CONNECTION) == 0 */
#define UDC_MSTWR_ENDPOINT 1
#define UDC_MSTRD_ENDPOINT 2
/* dma master write */
u32 out_dma_start;
u32 out_dma_end;
u32 out_dma_current;
/* dma master read */
u32 in_dma_start;
u32 in_dma_end;
u32 in_dma_current;
u32 power_detect;
#define PW_DETECT 0x04
#define PW_RESETB 0x02
#define PW_PULLUP 0x01
u8 _reserved0 [0x1d8];
/* endpoint registers */
u32 ep_fifo [4]; /* 0x200 */
u8 _reserved1 [0x10];
u32 ep_mode [4]; /* only 1-3 valid */
u8 _reserved2 [0x10];
u32 ep_status [4];
#define EPxSTATUS_TOGGLE 0x40
#define EPxSTATUS_SUSPEND 0x20
#define EPxSTATUS_EP_MASK (0x07<<2)
# define EPxSTATUS_EP_READY (0<<2)
# define EPxSTATUS_EP_DATAIN (1<<2)
# define EPxSTATUS_EP_FULL (2<<2)
# define EPxSTATUS_EP_TX_ERR (3<<2)
# define EPxSTATUS_EP_RX_ERR (4<<2)
# define EPxSTATUS_EP_BUSY (5<<2)
# define EPxSTATUS_EP_STALL (6<<2)
# define EPxSTATUS_EP_INVALID (7<<2)
#define EPxSTATUS_FIFO_DISABLE 0x02
#define EPxSTATUS_STAGE_ERROR 0x01
u8 _reserved3 [0x10];
u32 EPxSizeLA[4];
#define PACKET_ACTIVE (1<<7)
#define DATASIZE 0x7f
u8 _reserved3a [0x10];
u32 EPxSizeLB[4]; /* only 1,2 valid */
u8 _reserved3b [0x10];
u32 EPxSizeHA[4]; /* only 1-3 valid */
u8 _reserved3c [0x10];
u32 EPxSizeHB[4]; /* only 1,2 valid */
u8 _reserved4[0x30];
/* SETUP packet contents */
u32 bRequestType; /* 0x300 */
u32 bRequest;
u32 wValueL;
u32 wValueH;
u32 wIndexL;
u32 wIndexH;
u32 wLengthL;
u32 wLengthH;
/* command interaction/handshaking */
u32 SetupRecv; /* 0x320 */
u32 CurrConfig;
u32 StdRequest;
u32 Request;
u32 DataSet;
#define DATASET_A(epnum) (1<<(2*(epnum)))
#define DATASET_B(epnum) (2<<(2*(epnum)))
#define DATASET_AB(epnum) (3<<(2*(epnum)))
u8 _reserved5[4];
u32 UsbState;
#define USBSTATE_CONFIGURED 0x04
#define USBSTATE_ADDRESSED 0x02
#define USBSTATE_DEFAULT 0x01
u32 EOP;
u32 Command; /* 0x340 */
#define COMMAND_SETDATA0 2
#define COMMAND_RESET 3
#define COMMAND_STALL 4
#define COMMAND_INVALID 5
#define COMMAND_FIFO_DISABLE 7
#define COMMAND_FIFO_ENABLE 8
#define COMMAND_INIT_DESCRIPTOR 9
#define COMMAND_FIFO_CLEAR 10 /* also stall */
#define COMMAND_STALL_CLEAR 11
#define COMMAND_EP(n) ((n) << 4)
u32 EPxSingle;
u8 _reserved6[4];
u32 EPxBCS;
u8 _reserved7[8];
u32 IntControl;
#define ICONTROL_STATUSNAK 1
u8 _reserved8[4];
u32 reqmode; // 0x360 standard request mode, low 8 bits
#define G_REQMODE_SET_INTF (1<<7)
#define G_REQMODE_GET_INTF (1<<6)
#define G_REQMODE_SET_CONF (1<<5)
#define G_REQMODE_GET_CONF (1<<4)
#define G_REQMODE_GET_DESC (1<<3)
#define G_REQMODE_SET_FEAT (1<<2)
#define G_REQMODE_CLEAR_FEAT (1<<1)
#define G_REQMODE_GET_STATUS (1<<0)
u32 ReqMode;
u8 _reserved9[0x18];
u32 PortStatus; /* 0x380 */
u8 _reserved10[8];
u32 address;
u32 buff_test;
u8 _reserved11[4];
u32 UsbReady;
u8 _reserved12[4];
u32 SetDescStall; /* 0x3a0 */
u8 _reserved13[0x45c];
/* hardware could handle limited GET_DESCRIPTOR duties */
#define DESC_LEN 0x80
u32 descriptors[DESC_LEN]; /* 0x800 */
u8 _reserved14[0x600];
} __attribute__ ((packed));
#define MAX_FIFO_SIZE 64
#define MAX_EP0_SIZE 8 /* ep0 fifo is bigger, though */
/*-------------------------------------------------------------------------*/
/* DRIVER DATA STRUCTURES and UTILITIES */
struct goku_ep {
struct usb_ep ep;
struct goku_udc *dev;
unsigned long irqs;
unsigned num:8,
dma:1,
is_in:1,
stopped:1;
/* analogous to a host-side qh */
struct list_head queue;
const struct usb_endpoint_descriptor *desc;
u32 *reg_fifo;
u32 *reg_mode;
u32 *reg_status;
};
struct goku_request {
struct usb_request req;
struct list_head queue;
unsigned mapped:1;
};
enum ep0state {
EP0_DISCONNECT, /* no host */
EP0_IDLE, /* between STATUS ack and SETUP report */
EP0_IN, EP0_OUT, /* data stage */
EP0_STATUS, /* status stage */
EP0_STALL, /* data or status stages */
EP0_SUSPEND, /* usb suspend */
};
struct goku_udc {
/* each pci device provides one gadget, several endpoints */
struct usb_gadget gadget;
spinlock_t lock;
struct goku_ep ep[4];
struct usb_gadget_driver *driver;
enum ep0state ep0state;
unsigned got_irq:1,
got_region:1,
req_config:1,
configured:1,
enabled:1;
/* pci state used to access those endpoints */
struct pci_dev *pdev;
struct goku_udc_regs *regs;
u32 int_enable;
/* statistics... */
unsigned long irqs;
};
/*-------------------------------------------------------------------------*/
#define xprintk(dev,level,fmt,args...) \
printk(level "%s %s: " fmt , driver_name , \
pci_name(dev->pdev) , ## args)
#ifdef DEBUG
#define DBG(dev,fmt,args...) \
xprintk(dev , KERN_DEBUG , fmt , ## args)
#else
#define DBG(dev,fmt,args...) \
do { } while (0)
#endif /* DEBUG */
#ifdef VERBOSE
#define VDBG DBG
#else
#define VDBG(dev,fmt,args...) \
do { } while (0)
#endif /* VERBOSE */
#define ERROR(dev,fmt,args...) \
xprintk(dev , KERN_ERR , fmt , ## args)
#define WARN(dev,fmt,args...) \
xprintk(dev , KERN_WARNING , fmt , ## args)
#define INFO(dev,fmt,args...) \
xprintk(dev , KERN_INFO , fmt , ## args)
/*
* g_serial.c -- USB gadget serial driver
*
* Copyright 2003 (C) Al Borchers (alborchers@steinerpoint.com)
*
* This code is based in part on the Gadget Zero driver, which
* is Copyright (C) 2003 by David Brownell, all rights reserved.
*
* This code also borrows from usbserial.c, which is
* Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2000 Peter Berger (pberger@brimson.com)
* Copyright (C) 2000 Al Borchers (alborchers@steinerpoint.com)
*
* This software is distributed under the terms of the GNU General
* Public License ("GPL") as published by the Free Software Foundation,
* either version 2 of that License or (at your option) any later version.
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/uts.h>
#include <linux/version.h>
#include <linux/wait.h>
#include <linux/list.h>
#include <linux/proc_fs.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <asm/byteorder.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/unaligned.h>
#include <asm/uaccess.h>
#include <linux/usb_ch9.h>
#include <linux/usb_gadget.h>
/* Wait Cond */
#define __wait_cond_interruptible(wq, condition, lock, flags, ret) \
do { \
wait_queue_t __wait; \
init_waitqueue_entry(&__wait, current); \
\
add_wait_queue(&wq, &__wait); \
for (;;) { \
set_current_state(TASK_INTERRUPTIBLE); \
if (condition) \
break; \
if (!signal_pending(current)) { \
spin_unlock_irqrestore(lock, flags); \
schedule(); \
spin_lock_irqsave(lock, flags); \
continue; \
} \
ret = -ERESTARTSYS; \
break; \
} \
current->state = TASK_RUNNING; \
remove_wait_queue(&wq, &__wait); \
} while (0)
#define wait_cond_interruptible(wq, condition, lock, flags) \
({ \
int __ret = 0; \
if (!(condition)) \
__wait_cond_interruptible(wq, condition, lock, flags, \
__ret); \
__ret; \
})
#define __wait_cond_interruptible_timeout(wq, condition, lock, flags, \
timeout, ret) \
do { \
signed long __timeout = timeout; \
wait_queue_t __wait; \
init_waitqueue_entry(&__wait, current); \
\
add_wait_queue(&wq, &__wait); \
for (;;) { \
set_current_state(TASK_INTERRUPTIBLE); \
if (__timeout == 0) \
break; \
if (condition) \
break; \
if (!signal_pending(current)) { \
spin_unlock_irqrestore(lock, flags); \
__timeout = schedule_timeout(__timeout); \
spin_lock_irqsave(lock, flags); \
continue; \
} \
ret = -ERESTARTSYS; \
break; \
} \
current->state = TASK_RUNNING; \
remove_wait_queue(&wq, &__wait); \
} while (0)
#define wait_cond_interruptible_timeout(wq, condition, lock, flags, \
timeout) \
({ \
int __ret = 0; \
if (!(condition)) \
__wait_cond_interruptible_timeout(wq, condition, lock, \
flags, timeout, __ret); \
__ret; \
})
/* Defines */
#define GS_VERSION_STR "v0.1"
#define GS_VERSION_NUM 0x0001
#define GS_LONG_NAME "Gadget Serial"
#define GS_SHORT_NAME "g_serial"
#define GS_MAJOR 127
#define GS_MINOR_START 0
#define GS_NUM_PORTS 16
#define GS_VENDOR_ID 0x05F9
#define GS_PRODUCT_ID 0xFFFF
#define GS_NUM_CONFIGS 1
#define GS_NO_CONFIG_ID 0
#define GS_BULK_CONFIG_ID 2
#define GS_NUM_INTERFACES 1
#define GS_INTERFACE_ID 0
#define GS_ALT_INTERFACE_ID 0
#define GS_NUM_ENDPOINTS 2
#define GS_MAX_DESC_LEN 256
#define GS_DEFAULT_READ_Q_SIZE 32
#define GS_DEFAULT_WRITE_Q_SIZE 32
#define GS_DEFAULT_WRITE_BUF_SIZE 8192
#define GS_TMP_BUF_SIZE 8192
#define GS_CLOSE_TIMEOUT 15
/* debug macro */
#if G_SERIAL_DEBUG
static int debug = G_SERIAL_DEBUG;
#define gs_debug(format, arg...) \
do { if (debug) printk(KERN_DEBUG format, ## arg); } while(0)
#define gs_debug_level(level, format, arg...) \
do { if (debug>=level) printk(KERN_DEBUG format, ## arg); } while(0)
#else
#define gs_debug(format, arg...) \
do { } while(0)
#define gs_debug_level(level, format, arg...) \
do { } while(0)
#endif /* G_SERIAL_DEBUG */
/* USB Controllers */
/*
* NetChip 2280, PCI based.
*
* This has half a dozen configurable endpoints, four with dedicated
* DMA channels to manage their FIFOs. It supports high speed.
* Those endpoints can be arranged in any desired configuration.
*/
#ifdef CONFIG_USB_G_SERIAL_NET2280
#define CHIP "net2280"
#define EP0_MAXPACKET 64
static const char EP_OUT_NAME[] = "ep-a";
#define EP_OUT_NUM 2
static const char EP_IN_NAME[] = "ep-b";
#define EP_IN_NUM 2
#define HIGHSPEED
#define SELFPOWER USB_CONFIG_ATT_SELFPOWER
extern int net2280_set_fifo_mode(struct usb_gadget *gadget, int mode);
static inline void hw_optimize(struct usb_gadget *gadget)
{
/* we can have bigger ep-a/ep-b fifos (2KB each, 4 packets
* for highspeed bulk) because we're not using ep-c/ep-d.
*/
net2280_set_fifo_mode (gadget, 1);
}
#endif
/*
* PXA-2xx UDC: widely used in second gen Linux-capable PDAs.
*
* This has fifteen fixed-function full speed endpoints, and it
* can support all USB transfer types.
*
* These supports three or four configurations, with fixed numbers.
* The hardware interprets SET_INTERFACE, net effect is that you
* can't use altsettings or reset the interfaces independently.
* So stick to a single interface.
*/
#ifdef CONFIG_USB_G_SERIAL_PXA2XX
#define CHIP "pxa2xx"
#define EP0_MAXPACKET 16
static const char EP_OUT_NAME[] = "ep12out-bulk";
#define EP_OUT_NUM 12
static const char EP_IN_NAME[] = "ep11in-bulk";
#define EP_IN_NUM 11
#define SELFPOWER USB_CONFIG_ATT_SELFPOWER
/* no hw optimizations to apply */
#define hw_optimize(g) do {} while (0)
#endif
/*
* SA-1100 UDC: widely used in first gen Linux-capable PDAs.
*
* This has only two fixed function endpoints, which can only
* be used for bulk (or interrupt) transfers. (Plus control.)
*
* Since it can't flush its TX fifos without disabling the UDC,
* the current configuration or altsettings can't change except
* in special situations. So this is a case of "choose it right
* during enumeration" ...
*/
#ifdef CONFIG_USB_G_SERIAL_SA1100
#define CHIP "sa1100"
#define EP0_MAXPACKET 8
static const char EP_OUT_NAME[] = "ep1out-bulk";
#define EP_OUT_NUM 1
static const char EP_IN_NAME [] = "ep2in-bulk";
#define EP_IN_NUM 2
#define SELFPOWER USB_CONFIG_ATT_SELFPOWER
/* no hw optimizations to apply */
#define hw_optimize(g) do {} while (0)
#endif
/*
* Toshiba TC86C001 ("Goku-S") UDC
*
* This has three semi-configurable full speed bulk/interrupt endpoints.
*/
#ifdef CONFIG_USB_G_SERIAL_GOKU
#define CHIP "goku"
#define DRIVER_VERSION_NUM 0x0116
#define EP0_MAXPACKET 8
static const char EP_OUT_NAME [] = "ep1-bulk";
#define EP_OUT_NUM 1
static const char EP_IN_NAME [] = "ep2-bulk";
#define EP_IN_NUM 2
#define SELFPOWER USB_CONFIG_ATT_SELFPOWER
/* no hw optimizations to apply */
#define hw_optimize(g) do {} while (0)
#endif
/*
* USB Controller Defaults
*/
#ifndef EP0_MAXPACKET
#error Configure some USB peripheral controller for g_serial!
#endif
#ifndef SELFPOWER
/* default: say we rely on bus power */
#define SELFPOWER 0
/* else value must be USB_CONFIG_ATT_SELFPOWER */
#endif
#ifndef MAX_USB_POWER
/* any hub supports this steady state bus power consumption */
#define MAX_USB_POWER 100 /* mA */
#endif
#ifndef WAKEUP
/* default: this driver won't do remote wakeup */
#define WAKEUP 0
/* else value must be USB_CONFIG_ATT_WAKEUP */
#endif
/* Structures */
struct gs_dev;
/* circular buffer */
struct gs_buf {
unsigned int buf_size;
char *buf_buf;
char *buf_get;
char *buf_put;
};
/* list of requests */
struct gs_req_entry {
struct list_head re_entry;
struct usb_request *re_req;
};
/* the port structure holds info for each port, one for each minor number */
struct gs_port {
struct gs_dev *port_dev; /* pointer to device struct */
struct tty_struct *port_tty; /* pointer to tty struct */
spinlock_t port_lock;
int port_num;
int port_open_count;
int port_in_use; /* open/close in progress */
wait_queue_head_t port_write_wait;/* waiting to write */
struct gs_buf *port_write_buf;
};
/* the device structure holds info for the USB device */
struct gs_dev {
struct usb_gadget *dev_gadget; /* gadget device pointer */
spinlock_t dev_lock; /* lock for set/reset config */
int dev_config; /* configuration number */
struct usb_ep *dev_in_ep; /* address of in endpoint */
struct usb_ep *dev_out_ep; /* address of out endpoint */
struct usb_request *dev_ctrl_req; /* control request */
struct list_head dev_req_list; /* list of write requests */
int dev_sched_port; /* round robin port scheduled */
struct gs_port *dev_port[GS_NUM_PORTS]; /* the ports */
};
/* Functions */
/* module */
static int __init gs_module_init(void);
static void __exit gs_module_exit(void);
/* tty driver */
static int gs_open(struct tty_struct *tty, struct file *file);
static void gs_close(struct tty_struct *tty, struct file *file);
static int gs_write(struct tty_struct *tty, int from_user,
const unsigned char *buf, int count);
static void gs_put_char(struct tty_struct *tty, unsigned char ch);
static void gs_flush_chars(struct tty_struct *tty);
static int gs_write_room(struct tty_struct *tty);
static int gs_chars_in_buffer(struct tty_struct *tty);
static void gs_throttle(struct tty_struct * tty);
static void gs_unthrottle(struct tty_struct * tty);
static void gs_break(struct tty_struct *tty, int break_state);
static int gs_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg);
static void gs_set_termios(struct tty_struct *tty, struct termios *old);
static int gs_send(struct gs_dev *dev);
static int gs_send_packet(struct gs_dev *dev, char *packet,
unsigned int size);
static int gs_recv_packet(struct gs_dev *dev, char *packet,
unsigned int size);
static void gs_read_complete(struct usb_ep *ep, struct usb_request *req);
static void gs_write_complete(struct usb_ep *ep, struct usb_request *req);
/* gadget driver */
static int gs_bind(struct usb_gadget *gadget);
static void gs_unbind(struct usb_gadget *gadget);
static int gs_setup(struct usb_gadget *gadget,
const struct usb_ctrlrequest *ctrl);
static void gs_setup_complete(struct usb_ep *ep, struct usb_request *req);
static void gs_disconnect(struct usb_gadget *gadget);
static int gs_set_config(struct gs_dev *dev, unsigned config);
static void gs_reset_config(struct gs_dev *dev);
static int gs_build_config_desc(u8 *buf, enum usb_device_speed speed,
u8 type, unsigned int index);
static struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned int len,
int kmalloc_flags);
static void gs_free_req(struct usb_ep *ep, struct usb_request *req);
static struct gs_req_entry *gs_alloc_req_entry(struct usb_ep *ep, unsigned len,
int kmalloc_flags);
static void gs_free_req_entry(struct usb_ep *ep, struct gs_req_entry *req);
static int gs_alloc_ports(struct gs_dev *dev, int kmalloc_flags);
static void gs_free_ports(struct gs_dev *dev);
/* circular buffer */
static struct gs_buf *gs_buf_alloc(unsigned int size, int kmalloc_flags);
static void gs_buf_free(struct gs_buf *gb);
static void gs_buf_clear(struct gs_buf *gb);
static unsigned int gs_buf_data_avail(struct gs_buf *gb);
static unsigned int gs_buf_space_avail(struct gs_buf *gb);
static unsigned int gs_buf_put(struct gs_buf *gb, const char *buf,
unsigned int count);
static unsigned int gs_buf_get(struct gs_buf *gb, char *buf,
unsigned int count);
/* Globals */
static struct gs_dev *gs_device;
static struct semaphore gs_open_close_sem[GS_NUM_PORTS];
static unsigned int read_q_size = GS_DEFAULT_READ_Q_SIZE;
static unsigned int write_q_size = GS_DEFAULT_WRITE_Q_SIZE;
static unsigned int write_buf_size = GS_DEFAULT_WRITE_BUF_SIZE;
static unsigned char gs_tmp_buf[GS_TMP_BUF_SIZE];
static struct semaphore gs_tmp_buf_sem;
/* tty driver struct */
static struct tty_operations gs_tty_ops = {
.open = gs_open,
.close = gs_close,
.write = gs_write,
.put_char = gs_put_char,
.flush_chars = gs_flush_chars,
.write_room = gs_write_room,
.ioctl = gs_ioctl,
.set_termios = gs_set_termios,
.throttle = gs_throttle,
.unthrottle = gs_unthrottle,
.break_ctl = gs_break,
.chars_in_buffer = gs_chars_in_buffer,
};
static struct tty_driver *gs_tty_driver;
/* gadget driver struct */
static struct usb_gadget_driver gs_gadget_driver = {
#ifdef HIGHSPEED
.speed = USB_SPEED_HIGH,
#else
.speed = USB_SPEED_FULL,
#endif
.function = GS_LONG_NAME,
.bind = gs_bind,
.unbind = gs_unbind,
.setup = gs_setup,
.disconnect = gs_disconnect,
.driver = {
.name = GS_SHORT_NAME,
/* .shutdown = ... */
/* .suspend = ... */
/* .resume = ... */
},
};
/* USB descriptors */
#define GS_MANUFACTURER_STR_ID 1
#define GS_PRODUCT_STR_ID 2
#define GS_SERIAL_STR_ID 3
#define GS_CONFIG_STR_ID 4
/* static strings, in iso 8859/1 */
static struct usb_string gs_strings[] = {
{ GS_MANUFACTURER_STR_ID, UTS_SYSNAME " " UTS_RELEASE " with " CHIP },
{ GS_PRODUCT_STR_ID, GS_LONG_NAME },
{ GS_SERIAL_STR_ID, "0" },
{ GS_CONFIG_STR_ID, "Bulk" },
{ } /* end of list */
};
static struct usb_gadget_strings gs_string_table = {
.language = 0x0409, /* en-us */
.strings = gs_strings,
};
static const struct usb_device_descriptor gs_device_desc = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = __constant_cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_VENDOR_SPEC,
.bMaxPacketSize0 = EP0_MAXPACKET,
.idVendor = __constant_cpu_to_le16(GS_VENDOR_ID),
.idProduct = __constant_cpu_to_le16(GS_PRODUCT_ID),
.bcdDevice = __constant_cpu_to_le16(GS_VERSION_NUM),
.iManufacturer = GS_MANUFACTURER_STR_ID,
.iProduct = GS_PRODUCT_STR_ID,
.iSerialNumber = GS_SERIAL_STR_ID,
.bNumConfigurations = GS_NUM_CONFIGS,
};
static const struct usb_config_descriptor gs_config_desc = {
.bLength = USB_DT_CONFIG_SIZE,
.bDescriptorType = USB_DT_CONFIG,
/* .wTotalLength set by gs_build_config_desc */
.bNumInterfaces = GS_NUM_INTERFACES,
.bConfigurationValue = GS_BULK_CONFIG_ID,
.iConfiguration = GS_CONFIG_STR_ID,
.bmAttributes = USB_CONFIG_ATT_ONE | SELFPOWER | WAKEUP,
.bMaxPower = (MAX_USB_POWER + 1) / 2,
};
static const struct usb_interface_descriptor gs_interface_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bNumEndpoints = GS_NUM_ENDPOINTS,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.iInterface = GS_CONFIG_STR_ID,
};
static const struct usb_endpoint_descriptor gs_fullspeed_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_IN_NUM | USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = __constant_cpu_to_le16(64),
};
static const struct usb_endpoint_descriptor gs_fullspeed_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_OUT_NUM | USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = __constant_cpu_to_le16(64),
};
static const struct usb_endpoint_descriptor gs_highspeed_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_IN_NUM | USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = __constant_cpu_to_le16(512),
};
static const struct usb_endpoint_descriptor gs_highspeed_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP_OUT_NUM | USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = __constant_cpu_to_le16(512),
};
#ifdef HIGHSPEED
static const struct usb_qualifier_descriptor gs_qualifier_desc = {
.bLength = sizeof(struct usb_qualifier_descriptor),
.bDescriptorType = USB_DT_DEVICE_QUALIFIER,
.bcdUSB = __constant_cpu_to_le16 (0x0200),
.bDeviceClass = USB_CLASS_VENDOR_SPEC,
/* assumes ep0 uses the same value for both speeds ... */
.bMaxPacketSize0 = EP0_MAXPACKET,
.bNumConfigurations = GS_NUM_CONFIGS,
};
#endif
/* Module */
MODULE_DESCRIPTION(GS_LONG_NAME);
MODULE_AUTHOR("Al Borchers");
MODULE_LICENSE("GPL");
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Enable debugging, 0=off, 1=on");
MODULE_PARM(read_q_size, "i");
MODULE_PARM_DESC(read_q_size, "Read request queue size, default=32");
MODULE_PARM(write_q_size, "i");
MODULE_PARM_DESC(write_q_size, "Write request queue size, default=32");
MODULE_PARM(write_buf_size, "i");
MODULE_PARM_DESC(write_buf_size, "Write buffer size, default=8192");
module_init(gs_module_init);
module_exit(gs_module_exit);
/*
* gs_module_init
*
* Register as a USB gadget driver and a tty driver.
*/
static int __init gs_module_init(void)
{
int i;
int retval;
retval = usb_gadget_register_driver(&gs_gadget_driver);
if (retval) {
printk(KERN_ERR "gs_module_init: cannot register gadget driver, ret=%d\n", retval);
return retval;
}
gs_tty_driver = alloc_tty_driver(GS_NUM_PORTS);
if (!gs_tty_driver)
return -ENOMEM;
gs_tty_driver->owner = THIS_MODULE;
gs_tty_driver->driver_name = GS_SHORT_NAME;
gs_tty_driver->name = "ttygs";
gs_tty_driver->devfs_name = "usb/ttygs/";
gs_tty_driver->major = GS_MAJOR;
gs_tty_driver->minor_start = GS_MINOR_START;
gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
gs_tty_driver->subtype = SERIAL_TYPE_NORMAL;
gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
gs_tty_driver->init_termios = tty_std_termios;
gs_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
tty_set_operations(gs_tty_driver, &gs_tty_ops);
for (i=0; i < GS_NUM_PORTS; i++)
sema_init(&gs_open_close_sem[i], 1);
sema_init(&gs_tmp_buf_sem, 1);
retval = tty_register_driver(gs_tty_driver);
if (retval) {
usb_gadget_unregister_driver(&gs_gadget_driver);
put_tty_driver(gs_tty_driver);
printk(KERN_ERR "gs_module_init: cannot register tty driver, ret=%d\n", retval);
return retval;
}
printk(KERN_INFO "gs_module_init: %s %s loaded\n", GS_LONG_NAME, GS_VERSION_STR);
return 0;
}
/*
* gs_module_exit
*
* Unregister as a tty driver and a USB gadget driver.
*/
static void __exit gs_module_exit(void)
{
tty_unregister_driver(gs_tty_driver);
put_tty_driver(gs_tty_driver);
usb_gadget_unregister_driver(&gs_gadget_driver);
printk(KERN_INFO "gs_module_exit: %s %s unloaded\n", GS_LONG_NAME, GS_VERSION_STR);
}
/* TTY Driver */
/*
* gs_open
*/
static int gs_open(struct tty_struct *tty, struct file *file)
{
int port_num;
unsigned long flags;
struct gs_port *port;
struct gs_dev *dev;
struct gs_buf *buf;
struct semaphore *sem;
port_num = tty->index;
gs_debug("gs_open: (%d,%p,%p)\n", port_num, tty, file);
tty->driver_data = NULL;
if (port_num < 0 || port_num >= GS_NUM_PORTS) {
printk(KERN_ERR "gs_open: (%d,%p,%p) invalid port number\n",
port_num, tty, file);
return -ENODEV;
}
dev = gs_device;
if (dev == NULL) {
printk(KERN_ERR "gs_open: (%d,%p,%p) NULL device pointer\n",
port_num, tty, file);
return -ENODEV;
}
sem = &gs_open_close_sem[port_num];
if (down_interruptible(sem)) {
printk(KERN_ERR
"gs_open: (%d,%p,%p) interrupted waiting for semaphore\n",
port_num, tty, file);
return -ERESTARTSYS;
}
spin_lock_irqsave(&dev->dev_lock, flags);
if (dev->dev_config == GS_NO_CONFIG_ID) {
printk(KERN_ERR
"gs_open: (%d,%p,%p) device is not connected\n",
port_num, tty, file);
spin_unlock_irqrestore(&dev->dev_lock, flags);
up(sem);
return -ENODEV;
}
port = dev->dev_port[port_num];
if (port == NULL) {
printk(KERN_ERR "gs_open: (%d,%p,%p) NULL port pointer\n",
port_num, tty, file);
spin_unlock_irqrestore(&dev->dev_lock, flags);
up(sem);
return -ENODEV;
}
spin_lock(&port->port_lock);
spin_unlock(&dev->dev_lock);
if (port->port_dev == NULL) {
printk(KERN_ERR "gs_open: (%d,%p,%p) port disconnected (1)\n",
port_num, tty, file);
spin_unlock_irqrestore(&port->port_lock, flags);
up(sem);
return -EIO;
}
if (port->port_open_count > 0) {
++port->port_open_count;
spin_unlock_irqrestore(&port->port_lock, flags);
gs_debug("gs_open: (%d,%p,%p) already open\n",
port_num, tty, file);
up(sem);
return 0;
}
/* mark port as in use, we can drop port lock and sleep if necessary */
port->port_in_use = 1;
/* allocate write buffer on first open */
if (port->port_write_buf == NULL) {
spin_unlock_irqrestore(&port->port_lock, flags);
buf = gs_buf_alloc(write_buf_size, GFP_KERNEL);
spin_lock_irqsave(&port->port_lock, flags);
/* might have been disconnected while asleep, check */
if (port->port_dev == NULL) {
printk(KERN_ERR
"gs_open: (%d,%p,%p) port disconnected (2)\n",
port_num, tty, file);
port->port_in_use = 0;
spin_unlock_irqrestore(&port->port_lock, flags);
up(sem);
return -EIO;
}
if ((port->port_write_buf=buf) == NULL) {
printk(KERN_ERR "gs_open: (%d,%p,%p) cannot allocate port write buffer\n",
port_num, tty, file);
port->port_in_use = 0;
spin_unlock_irqrestore(&port->port_lock, flags);
up(sem);
return -ENOMEM;
}
}
/* wait for carrier detect (not implemented) */
/* might have been disconnected while asleep, check */
if (port->port_dev == NULL) {
printk(KERN_ERR "gs_open: (%d,%p,%p) port disconnected (3)\n",
port_num, tty, file);
port->port_in_use = 0;
spin_unlock_irqrestore(&port->port_lock, flags);
up(sem);
return -EIO;
}
tty->driver_data = port;
port->port_tty = tty;
port->port_open_count = 1;
port->port_in_use = 0;
spin_unlock_irqrestore(&port->port_lock, flags);
up(sem);
gs_debug("gs_open: (%d,%p,%p) completed\n", port_num, tty, file);
return 0;
}
/*
* gs_close
*/
static void gs_close(struct tty_struct *tty, struct file *file)
{
unsigned long flags;
struct gs_port *port = tty->driver_data;
struct semaphore *sem;
if (port == NULL) {
printk(KERN_ERR "gs_close: NULL port pointer\n");
return;
}
gs_debug("gs_close: (%d,%p,%p)\n", port->port_num, tty, file);
sem = &gs_open_close_sem[port->port_num];
down(sem);
spin_lock_irqsave(&port->port_lock, flags);
if (port->port_open_count == 0) {
printk(KERN_ERR
"gs_close: (%d,%p,%p) port is already closed\n",
port->port_num, tty, file);
spin_unlock_irqrestore(&port->port_lock, flags);
up(sem);
return;
}
if (port->port_open_count > 0) {
--port->port_open_count;
spin_unlock_irqrestore(&port->port_lock, flags);
up(sem);
return;
}
/* free disconnected port on final close */
if (port->port_dev == NULL) {
kfree(port);
spin_unlock_irqrestore(&port->port_lock, flags);
up(sem);
return;
}
/* mark port as closed but in use, we can drop port lock */
/* and sleep if necessary */
port->port_in_use = 1;
port->port_open_count = 0;
/* wait for write buffer to drain, or */
/* at most GS_CLOSE_TIMEOUT seconds */
if (gs_buf_data_avail(port->port_write_buf) > 0) {
wait_cond_interruptible_timeout(port->port_write_wait,
port->port_dev == NULL
|| gs_buf_data_avail(port->port_write_buf) == 0,
&port->port_lock, flags, GS_CLOSE_TIMEOUT * HZ);
}
/* free disconnected port on final close */
/* (might have happened during the above sleep) */
if (port->port_dev == NULL) {
kfree(port);
spin_unlock_irqrestore(&port->port_lock, flags);
up(sem);
return;
}
gs_buf_clear(port->port_write_buf);
tty->driver_data = NULL;
port->port_tty = NULL;
port->port_in_use = 0;
spin_unlock_irqrestore(&port->port_lock, flags);
up(sem);
gs_debug("gs_close: (%d,%p,%p) completed\n",
port->port_num, tty, file);
}
/*
* gs_write
*/
static int gs_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
{
unsigned long flags;
struct gs_port *port = tty->driver_data;
if (port == NULL) {
printk(KERN_ERR "gs_write: NULL port pointer\n");
return -EIO;
}
gs_debug("gs_write: (%d,%p) writing %d bytes\n", port->port_num, tty,
count);
if (count == 0)
return 0;
/* copy from user into tmp buffer, get tmp_buf semaphore */
if (from_user) {
if (count > GS_TMP_BUF_SIZE)
count = GS_TMP_BUF_SIZE;
down(&gs_tmp_buf_sem);
if (copy_from_user(gs_tmp_buf, buf, count) != 0) {
up(&gs_tmp_buf_sem);
printk(KERN_ERR
"gs_write: (%d,%p) cannot copy from user space\n",
port->port_num, tty);
return -EFAULT;
}
buf = gs_tmp_buf;
}
spin_lock_irqsave(&port->port_lock, flags);
if (port->port_dev == NULL) {
printk(KERN_ERR "gs_write: (%d,%p) port is not connected\n",
port->port_num, tty);
spin_unlock_irqrestore(&port->port_lock, flags);
if (from_user)
up(&gs_tmp_buf_sem);
return -EIO;
}
if (port->port_open_count == 0) {
printk(KERN_ERR "gs_write: (%d,%p) port is closed\n",
port->port_num, tty);
spin_unlock_irqrestore(&port->port_lock, flags);
if (from_user)
up(&gs_tmp_buf_sem);
return -EBADF;
}
count = gs_buf_put(port->port_write_buf, buf, count);
spin_unlock_irqrestore(&port->port_lock, flags);
if (from_user)
up(&gs_tmp_buf_sem);
gs_send(gs_device);
gs_debug("gs_write: (%d,%p) wrote %d bytes\n", port->port_num, tty,
count);
return count;
}
/*
* gs_put_char
*/
static void gs_put_char(struct tty_struct *tty, unsigned char ch)
{
unsigned long flags;
struct gs_port *port = tty->driver_data;
if (port == NULL) {
printk(KERN_ERR "gs_put_char: NULL port pointer\n");
return;
}
gs_debug("gs_put_char: (%d,%p) char=0x%x, called from %p, %p, %p\n", port->port_num, tty, ch, __builtin_return_address(0), __builtin_return_address(1), __builtin_return_address(2));
spin_lock_irqsave(&port->port_lock, flags);
if (port->port_dev == NULL) {
printk(KERN_ERR "gs_put_char: (%d,%p) port is not connected\n",
port->port_num, tty);
spin_unlock_irqrestore(&port->port_lock, flags);
return;
}
if (port->port_open_count == 0) {
printk(KERN_ERR "gs_put_char: (%d,%p) port is closed\n",
port->port_num, tty);
spin_unlock_irqrestore(&port->port_lock, flags);
return;
}
gs_buf_put(port->port_write_buf, &ch, 1);
spin_unlock_irqrestore(&port->port_lock, flags);
}
/*
* gs_flush_chars
*/
static void gs_flush_chars(struct tty_struct *tty)
{
unsigned long flags;
struct gs_port *port = tty->driver_data;
if (port == NULL) {
printk(KERN_ERR "gs_flush_chars: NULL port pointer\n");
return;
}
gs_debug("gs_flush_chars: (%d,%p)\n", port->port_num, tty);
spin_lock_irqsave(&port->port_lock, flags);
if (port->port_dev == NULL) {
printk(KERN_ERR
"gs_flush_chars: (%d,%p) port is not connected\n",
port->port_num, tty);
spin_unlock_irqrestore(&port->port_lock, flags);
return;
}
if (port->port_open_count == 0) {
printk(KERN_ERR "gs_flush_chars: (%d,%p) port is closed\n",
port->port_num, tty);
spin_unlock_irqrestore(&port->port_lock, flags);
return;
}
spin_unlock_irqrestore(&port->port_lock, flags);
gs_send(gs_device);
}
/*
* gs_write_room
*/
static int gs_write_room(struct tty_struct *tty)
{
int room = 0;
unsigned long flags;
struct gs_port *port = tty->driver_data;
if (port == NULL)
return 0;
spin_lock_irqsave(&port->port_lock, flags);
if (port->port_dev != NULL && port->port_open_count > 0
&& port->port_write_buf != NULL)
room = gs_buf_space_avail(port->port_write_buf);
spin_unlock_irqrestore(&port->port_lock, flags);
gs_debug("gs_write_room: (%d,%p) room=%d\n",
port->port_num, tty, room);
return room;
}
/*
* gs_chars_in_buffer
*/
static int gs_chars_in_buffer(struct tty_struct *tty)
{
int chars = 0;
unsigned long flags;
struct gs_port *port = tty->driver_data;
if (port == NULL)
return 0;
spin_lock_irqsave(&port->port_lock, flags);
if (port->port_dev != NULL && port->port_open_count > 0
&& port->port_write_buf != NULL)
chars = gs_buf_data_avail(port->port_write_buf);
spin_unlock_irqrestore(&port->port_lock, flags);
gs_debug("gs_chars_in_buffer: (%d,%p) chars=%d\n",
port->port_num, tty, chars);
return chars;
}
/*
* gs_throttle
*/
static void gs_throttle(struct tty_struct *tty)
{
}
/*
* gs_unthrottle
*/
static void gs_unthrottle(struct tty_struct *tty)
{
}
/*
* gs_break
*/
static void gs_break(struct tty_struct *tty, int break_state)
{
}
/*
* gs_ioctl
*/
static int gs_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
{
struct gs_port *port = tty->driver_data;
if (port == NULL) {
printk(KERN_ERR "gs_ioctl: NULL port pointer\n");
return -EIO;
}
gs_debug("gs_ioctl: (%d,%p,%p) cmd=0x%4.4x, arg=%lu\n",
port->port_num, tty, file, cmd, arg);
/* handle ioctls */
/* could not handle ioctl */
return -ENOIOCTLCMD;
}
/*
* gs_set_termios
*/
static void gs_set_termios(struct tty_struct *tty, struct termios *old)
{
}
/*
* gs_send
*
* This function finds available write requests, calls
* gs_send_packet to fill these packets with data, and
* continues until either there are no more write requests
* available or no more data to send. This function is
* run whenever data arrives or write requests are available.
*/
static int gs_send(struct gs_dev *dev)
{
int ret,len;
unsigned long flags;
struct usb_ep *ep;
struct usb_request *req;
struct gs_req_entry *req_entry;
if (dev == NULL) {
printk(KERN_ERR "gs_send: NULL device pointer\n");
return -ENODEV;
}
spin_lock_irqsave(&dev->dev_lock, flags);
ep = dev->dev_in_ep;
while(!list_empty(&dev->dev_req_list)) {
req_entry = list_entry(dev->dev_req_list.next,
struct gs_req_entry, re_entry);
req = req_entry->re_req;
len = gs_send_packet(dev, req->buf, ep->maxpacket);
if (len > 0) {
gs_debug_level(3, "gs_send: len=%d, 0x%2.2x 0x%2.2x 0x%2.2x ...\n", len, *((unsigned char *)req->buf), *((unsigned char *)req->buf+1), *((unsigned char *)req->buf+2));
list_del(&req_entry->re_entry);
req->length = len;
if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) {
printk(KERN_ERR
"gs_send: cannot queue read request, ret=%d\n",
ret);
break;
}
} else {
break;
}
}
spin_unlock_irqrestore(&dev->dev_lock, flags);
return 0;
}
/*
* gs_send_packet
*
* If there is data to send, a packet is built in the given
* buffer and the size is returned. If there is no data to
* send, 0 is returned. If there is any error a negative
* error number is returned.
*
* Called during USB completion routine, on interrupt time.
*
* We assume that disconnect will not happen until all completion
* routines have completed, so we can assume that the dev_port
* array does not change during the lifetime of this function.
*/
static int gs_send_packet(struct gs_dev *dev, char *packet, unsigned int size)
{
unsigned int len;
struct gs_port *port;
/* TEMPORARY -- only port 0 is supported right now */
port = dev->dev_port[0];
if (port == NULL) {
printk(KERN_ERR
"gs_send_packet: port=%d, NULL port pointer\n",
0);
return -EIO;
}
spin_lock(&port->port_lock);
len = gs_buf_data_avail(port->port_write_buf);
if (len < size)
size = len;
if (size == 0) {
spin_unlock(&port->port_lock);
return 0;
}
size = gs_buf_get(port->port_write_buf, packet, size);
wake_up_interruptible(&port->port_tty->write_wait);
spin_unlock(&port->port_lock);
return size;
}
/*
* gs_recv_packet
*
* Called for each USB packet received. Reads the packet
* header and stuffs the data in the appropriate tty buffer.
* Returns 0 if successful, or a negative error number.
*
* Called during USB completion routine, on interrupt time.
*
* We assume that disconnect will not happen until all completion
* routines have completed, so we can assume that the dev_port
* array does not change during the lifetime of this function.
*/
static int gs_recv_packet(struct gs_dev *dev, char *packet, unsigned int size)
{
unsigned int len;
struct gs_port *port;
/* TEMPORARY -- only port 0 is supported right now */
port = dev->dev_port[0];
if (port == NULL) {
printk(KERN_ERR "gs_recv_packet: port=%d, NULL port pointer\n",
port->port_num);
return -EIO;
}
spin_lock(&port->port_lock);
if (port->port_tty == NULL) {
printk(KERN_ERR "gs_recv_packet: port=%d, NULL tty pointer\n",
port->port_num);
spin_unlock(&port->port_lock);
return -EIO;
}
if (port->port_tty->magic != TTY_MAGIC) {
printk(KERN_ERR "gs_recv_packet: port=%d, bad tty magic\n",
port->port_num);
spin_unlock(&port->port_lock);
return -EIO;
}
len = (unsigned int)(TTY_FLIPBUF_SIZE - port->port_tty->flip.count);
if (len < size)
size = len;
if (size > 0) {
memcpy(port->port_tty->flip.char_buf_ptr, packet, size);
port->port_tty->flip.char_buf_ptr += size;
port->port_tty->flip.count += size;
tty_flip_buffer_push(port->port_tty);
wake_up_interruptible(&port->port_tty->read_wait);
}
spin_unlock(&port->port_lock);
return 0;
}
/*
* gs_read_complete
*/
static void gs_read_complete(struct usb_ep *ep, struct usb_request *req)
{
int ret;
struct gs_dev *dev = ep->driver_data;
if (dev == NULL) {
printk(KERN_ERR "gs_read_complete: NULL device pointer\n");
return;
}
switch(req->status) {
case 0:
/* normal completion */
gs_recv_packet(dev, req->buf, req->actual);
requeue:
req->length = ep->maxpacket;
if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) {
printk(KERN_ERR
"gs_read_complete: cannot queue read request, ret=%d\n",
ret);
}
break;
case -ESHUTDOWN:
/* disconnect */
gs_debug("gs_read_complete: shutdown\n");
gs_free_req(ep, req);
break;
default:
/* unexpected */
printk(KERN_ERR
"gs_read_complete: unexpected status error, status=%d\n",
req->status);
goto requeue;
break;
}
}
/*
* gs_write_complete
*/
static void gs_write_complete(struct usb_ep *ep, struct usb_request *req)
{
struct gs_dev *dev = ep->driver_data;
struct gs_req_entry *gs_req = req->context;
if (dev == NULL) {
printk(KERN_ERR "gs_write_complete: NULL device pointer\n");
return;
}
switch(req->status) {
case 0:
/* normal completion */
requeue:
if (gs_req == NULL) {
printk(KERN_ERR
"gs_write_complete: NULL request pointer\n");
return;
}
spin_lock(&dev->dev_lock);
list_add(&gs_req->re_entry, &dev->dev_req_list);
spin_unlock(&dev->dev_lock);
gs_send(dev);
break;
case -ESHUTDOWN:
/* disconnect */
gs_debug("gs_write_complete: shutdown\n");
gs_free_req(ep, req);
break;
default:
printk(KERN_ERR
"gs_write_complete: unexpected status error, status=%d\n",
req->status);
goto requeue;
break;
}
}
/* Gadget Driver */
/*
* gs_bind
*
* Called on module load. Allocates and initializes the device
* structure and a control request.
*/
static int gs_bind(struct usb_gadget *gadget)
{
int ret;
struct gs_dev *dev;
gs_device = dev = kmalloc(sizeof(struct gs_dev), GFP_KERNEL);
if (dev == NULL)
return -ENOMEM;
set_gadget_data(gadget, dev);
memset(dev, 0, sizeof(struct gs_dev));
dev->dev_gadget = gadget;
spin_lock_init(&dev->dev_lock);
INIT_LIST_HEAD(&dev->dev_req_list);
if ((ret=gs_alloc_ports(dev, GFP_KERNEL)) != 0) {
printk(KERN_ERR "gs_bind: cannot allocate ports\n");
gs_unbind(gadget);
return ret;
}
/* preallocate control response and buffer */
dev->dev_ctrl_req = gs_alloc_req(gadget->ep0, GS_MAX_DESC_LEN,
GFP_KERNEL);
if (dev->dev_ctrl_req == NULL) {
gs_unbind(gadget);
return -ENOMEM;
}
dev->dev_ctrl_req->complete = gs_setup_complete;
gadget->ep0->driver_data = dev;
printk(KERN_INFO "gs_bind: %s %s bound\n",
GS_LONG_NAME, GS_VERSION_STR);
return 0;
}
/*
* gs_unbind
*
* Called on module unload. Frees the control request and device
* structure.
*/
static void gs_unbind(struct usb_gadget *gadget)
{
struct gs_dev *dev = get_gadget_data(gadget);
gs_device = NULL;
/* read/write requests already freed, only control request remains */
if (dev != NULL) {
if (dev->dev_ctrl_req != NULL)
gs_free_req(gadget->ep0, dev->dev_ctrl_req);
gs_free_ports(dev);
kfree(dev);
set_gadget_data(gadget, NULL);
}
printk(KERN_INFO "gs_unbind: %s %s unbound\n", GS_LONG_NAME,
GS_VERSION_STR);
}
/*
* gs_setup
*
* Implements all the control endpoint functionality that's not
* handled in hardware or the hardware driver.
*
* Returns the size of the data sent to the host, or a negative
* error number.
*/
static int gs_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
{
int ret = -EOPNOTSUPP;
unsigned int sv_config;
struct gs_dev *dev = get_gadget_data(gadget);
struct usb_request *req = dev->dev_ctrl_req;
switch (ctrl->bRequest) {
case USB_REQ_GET_DESCRIPTOR:
if (ctrl->bRequestType != USB_DIR_IN)
break;
switch (ctrl->wValue >> 8) {
case USB_DT_DEVICE:
ret = min(ctrl->wLength,
(u16)sizeof(struct usb_device_descriptor));
memcpy(req->buf, &gs_device_desc, ret);
break;
#ifdef HIGHSPEED
case USB_DT_DEVICE_QUALIFIER:
ret = min(ctrl->wLength,
(u16)sizeof(struct usb_qualifier_descriptor));
memcpy(req->buf, &gs_qualifier_desc, ret);
break;
case USB_DT_OTHER_SPEED_CONFIG:
#endif /* HIGHSPEED */
case USB_DT_CONFIG:
ret = gs_build_config_desc(req->buf, gadget->speed,
ctrl->wValue >> 8, ctrl->wValue & 0xff);
if (ret >= 0)
ret = min(ctrl->wLength, (u16)ret);
break;
case USB_DT_STRING:
/* wIndex == language code. */
ret = usb_gadget_get_string(&gs_string_table,
ctrl->wValue & 0xff, req->buf);
if (ret >= 0)
ret = min(ctrl->wLength, (u16)ret);
break;
}
break;
case USB_REQ_SET_CONFIGURATION:
if (ctrl->bRequestType != 0)
break;
spin_lock(&dev->dev_lock);
ret = gs_set_config(dev, ctrl->wValue);
spin_unlock(&dev->dev_lock);
break;
case USB_REQ_GET_CONFIGURATION:
if (ctrl->bRequestType != USB_DIR_IN)
break;
*(u8 *)req->buf = dev->dev_config;
ret = min(ctrl->wLength, (u16)1);
break;
case USB_REQ_SET_INTERFACE:
if (ctrl->bRequestType != USB_RECIP_INTERFACE)
break;
spin_lock(&dev->dev_lock);
if (dev->dev_config == GS_BULK_CONFIG_ID
&& ctrl->wIndex == GS_INTERFACE_ID
&& ctrl->wValue == GS_ALT_INTERFACE_ID) {
sv_config = dev->dev_config;
/* since there is only one interface, setting the */
/* interface is equivalent to setting the config */
gs_reset_config(dev);
gs_set_config(dev, sv_config);
ret = 0;
}
spin_unlock(&dev->dev_lock);
break;
case USB_REQ_GET_INTERFACE:
if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
break;
if (dev->dev_config == GS_NO_CONFIG_ID)
break;
if (ctrl->wIndex != GS_INTERFACE_ID) {
ret = -EDOM;
break;
}
*(u8 *)req->buf = GS_ALT_INTERFACE_ID;
ret = min(ctrl->wLength, (u16)1);
break;
default:
printk(KERN_ERR "gs_setup: unknown request, type=%02x, request=%02x, value=%04x, index=%04x, length=%d\n",
ctrl->bRequestType, ctrl->bRequest, ctrl->wValue,
ctrl->wIndex, ctrl->wLength);
break;
}
/* respond with data transfer before status phase? */
if (ret >= 0) {
req->length = ret;
ret = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
if (ret < 0) {
printk(KERN_ERR
"gs_setup: cannot queue response, ret=%d\n",
ret);
req->status = 0;
gs_setup_complete(gadget->ep0, req);
}
}
/* device either stalls (ret < 0) or reports success */
return ret;
}
/*
* gs_setup_complete
*/
static void gs_setup_complete(struct usb_ep *ep, struct usb_request *req)
{
if (req->status || req->actual != req->length) {
printk(KERN_ERR "gs_setup_complete: status error, status=%d, actual=%d, length=%d\n",
req->status, req->actual, req->length);
}
}
/*
* gs_disconnect
*
* Called when the device is disconnected. Frees the closed
* ports and disconnects open ports. Open ports will be freed
* on close. Then reallocates the ports for the next connection.
*/
static void gs_disconnect(struct usb_gadget *gadget)
{
unsigned long flags;
struct gs_dev *dev = get_gadget_data(gadget);
spin_lock_irqsave(&dev->dev_lock, flags);
gs_reset_config(dev);
/* free closed ports and disconnect open ports */
/* (open ports will be freed when closed) */
gs_free_ports(dev);
/* re-allocate ports for the next connection */
if (gs_alloc_ports(dev, GFP_ATOMIC) != 0)
printk(KERN_ERR "gs_disconnect: cannot re-allocate ports\n");
spin_unlock_irqrestore(&dev->dev_lock, flags);
printk(KERN_INFO "gs_disconnect: %s disconnected\n", GS_LONG_NAME);
}
/*
* gs_set_config
*
* Configures the device by enabling device specific
* optimizations, setting up the endpoints, allocating
* read and write requests and queuing read requests.
*
* The device lock must be held when calling this function.
*/
static int gs_set_config(struct gs_dev *dev, unsigned config)
{
int i;
int ret = 0;
struct usb_gadget *gadget = dev->dev_gadget;
struct usb_ep *ep;
struct usb_request *req;
struct gs_req_entry *req_entry;
if (dev == NULL) {
printk(KERN_ERR "gs_set_config: NULL device pointer\n");
return 0;
}
if (config == dev->dev_config)
return 0;
gs_reset_config(dev);
if (config == GS_NO_CONFIG_ID)
return 0;
if (config != GS_BULK_CONFIG_ID)
return -EINVAL;
hw_optimize(gadget);
gadget_for_each_ep(ep, gadget) {
if (strcmp(ep->name, EP_IN_NAME) == 0) {
ret = usb_ep_enable(ep,
gadget->speed == USB_SPEED_HIGH ?
&gs_highspeed_in_desc : &gs_fullspeed_in_desc);
if (ret == 0) {
ep->driver_data = dev;
dev->dev_in_ep = ep;
} else {
printk(KERN_ERR "gs_set_config: cannot enable in endpoint %s, ret=%d\n",
ep->name, ret);
gs_reset_config(dev);
return ret;
}
}
else if (strcmp(ep->name, EP_OUT_NAME) == 0) {
ret = usb_ep_enable(ep,
gadget->speed == USB_SPEED_HIGH ?
&gs_highspeed_out_desc :
&gs_fullspeed_out_desc);
if (ret == 0) {
ep->driver_data = dev;
dev->dev_out_ep = ep;
} else {
printk(KERN_ERR "gs_set_config: cannot enable out endpoint %s, ret=%d\n",
ep->name, ret);
gs_reset_config(dev);
return ret;
}
}
}
if (dev->dev_in_ep == NULL || dev->dev_out_ep == NULL) {
gs_reset_config(dev);
printk(KERN_ERR "gs_set_config: cannot find endpoints\n");
return -ENODEV;
}
/* allocate and queue read requests */
ep = dev->dev_out_ep;
for (i=0; i<read_q_size && ret == 0; i++) {
if ((req=gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC))) {
req->complete = gs_read_complete;
if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) {
printk(KERN_ERR "gs_set_config: cannot queue read request, ret=%d\n",
ret);
}
} else {
gs_reset_config(dev);
printk(KERN_ERR
"gs_set_config: cannot allocate read requests\n");
return -ENOMEM;
}
}
/* allocate write requests, and put on free list */
ep = dev->dev_in_ep;
for (i=0; i<write_q_size; i++) {
if ((req_entry=gs_alloc_req_entry(ep, ep->maxpacket, GFP_ATOMIC))) {
req_entry->re_req->complete = gs_write_complete;
list_add(&req_entry->re_entry, &dev->dev_req_list);
} else {
gs_reset_config(dev);
printk(KERN_ERR
"gs_set_config: cannot allocate write requests\n");
return -ENOMEM;
}
}
dev->dev_config = config;
printk(KERN_INFO "gs_set_config: %s configured for %s speed\n",
GS_LONG_NAME,
gadget->speed == USB_SPEED_HIGH ? "high" : "full");
return 0;
}
/*
* gs_reset_config
*
* Mark the device as not configured, disable all endpoints,
* which forces completion of pending I/O and frees queued
* requests, and free the remaining write requests on the
* free list.
*
* The device lock must be held when calling this function.
*/
static void gs_reset_config(struct gs_dev *dev)
{
struct gs_req_entry *req_entry;
if (dev == NULL) {
printk(KERN_ERR "gs_reset_config: NULL device pointer\n");
return;
}
if (dev->dev_config == GS_NO_CONFIG_ID)
return;
dev->dev_config = GS_NO_CONFIG_ID;
/* free write requests on the free list */
while(!list_empty(&dev->dev_req_list)) {
req_entry = list_entry(dev->dev_req_list.next,
struct gs_req_entry, re_entry);
list_del(&req_entry->re_entry);
gs_free_req_entry(dev->dev_in_ep, req_entry);
}
/* disable endpoints, forcing completion of pending i/o; */
/* completion handlers free their requests in this case */
if (dev->dev_in_ep) {
usb_ep_disable(dev->dev_in_ep);
dev->dev_in_ep = NULL;
}
if (dev->dev_out_ep) {
usb_ep_disable(dev->dev_out_ep);
dev->dev_out_ep = NULL;
}
}
/*
* gs_build_config_desc
*
* Builds a config descriptor in the given buffer and returns the
* length, or a negative error number.
*/
static int gs_build_config_desc(u8 *buf, enum usb_device_speed speed, u8 type, unsigned int index)
{
int high_speed;
int len = USB_DT_CONFIG_SIZE + USB_DT_INTERFACE_SIZE
+ GS_NUM_ENDPOINTS * USB_DT_ENDPOINT_SIZE;
/* only one config */
if (index != 0)
return -EINVAL;
memcpy(buf, &gs_config_desc, USB_DT_CONFIG_SIZE);
((struct usb_config_descriptor *)buf)->bDescriptorType = type;
((struct usb_config_descriptor *)buf)->wTotalLength = __constant_cpu_to_le16(len);
buf += USB_DT_CONFIG_SIZE;
memcpy(buf, &gs_interface_desc, USB_DT_INTERFACE_SIZE);
buf += USB_DT_INTERFACE_SIZE;
/* other speed switches high and full speed */
high_speed = (speed == USB_SPEED_HIGH);
if (type == USB_DT_OTHER_SPEED_CONFIG)
high_speed = !high_speed;
memcpy(buf,
high_speed ? &gs_highspeed_in_desc : &gs_fullspeed_in_desc,
USB_DT_ENDPOINT_SIZE);
buf += USB_DT_ENDPOINT_SIZE;
memcpy(buf,
high_speed ? &gs_highspeed_out_desc : &gs_fullspeed_out_desc,
USB_DT_ENDPOINT_SIZE);
return len;
}
/*
* gs_alloc_req
*
* Allocate a usb_request and its buffer. Returns a pointer to the
* usb_request or NULL if there is an error.
*/
static struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned int len, int kmalloc_flags)
{
struct usb_request *req;
if (ep == NULL)
return NULL;
req = usb_ep_alloc_request(ep, kmalloc_flags);
if (req != NULL) {
req->length = len;
req->buf = usb_ep_alloc_buffer(ep, len, &req->dma,
kmalloc_flags);
if (req->buf == NULL) {
usb_ep_free_request(ep, req);
return NULL;
}
}
return req;
}
/*
* gs_free_req
*
* Free a usb_request and its buffer.
*/
static void gs_free_req(struct usb_ep *ep, struct usb_request *req)
{
if (ep != NULL && req != NULL) {
if (req->buf != NULL)
usb_ep_free_buffer(ep, req->buf, req->dma,
req->length);
usb_ep_free_request(ep, req);
}
}
/*
* gs_alloc_req_entry
*
* Allocates a request and its buffer, using the given
* endpoint, buffer len, and kmalloc flags.
*/
static struct gs_req_entry *gs_alloc_req_entry(struct usb_ep *ep, unsigned len, int kmalloc_flags)
{
struct gs_req_entry *req;
req = kmalloc(sizeof(struct gs_req_entry), kmalloc_flags);
if (req == NULL)
return NULL;
req->re_req = gs_alloc_req(ep, len, kmalloc_flags);
if (req->re_req == NULL) {
kfree(req);
return NULL;
}
req->re_req->context = req;
return req;
}
/*
* gs_free_req_entry
*
* Frees a request and its buffer.
*/
static void gs_free_req_entry(struct usb_ep *ep, struct gs_req_entry *req)
{
if (ep != NULL && req != NULL) {
if (req->re_req != NULL)
gs_free_req(ep, req->re_req);
kfree(req);
}
}
/*
* gs_alloc_ports
*
* Allocate all ports and set the gs_dev struct to point to them.
* Return 0 if successful, or a negative error number.
*
* The device lock is normally held when calling this function.
*/
static int gs_alloc_ports(struct gs_dev *dev, int kmalloc_flags)
{
int i;
struct gs_port *port;
if (dev == NULL)
return -EIO;
for (i=0; i<GS_NUM_PORTS; i++) {
if ((port=(struct gs_port *)kmalloc(sizeof(struct gs_port), kmalloc_flags)) == NULL)
return -ENOMEM;
memset(port, 0, sizeof(struct gs_port));
port->port_dev = dev;
port->port_num = i;
spin_lock_init(&port->port_lock);
init_waitqueue_head(&port->port_write_wait);
dev->dev_port[i] = port;
}
return 0;
}
/*
* gs_free_ports
*
* Free all closed ports. Open ports are disconnected by
* freeing their write buffers, setting their device pointers
* and the pointers to them in the device to NULL. These
* ports will be freed when closed.
*
* The device lock is normally held when calling this function.
*/
static void gs_free_ports(struct gs_dev *dev)
{
int i;
unsigned long flags;
struct gs_port *port;
if (dev == NULL)
return;
for (i=0; i<GS_NUM_PORTS; i++) {
if ((port=dev->dev_port[i]) != NULL) {
dev->dev_port[i] = NULL;
spin_lock_irqsave(&port->port_lock, flags);
if (port->port_write_buf != NULL) {
gs_buf_free(port->port_write_buf);
port->port_write_buf = NULL;
}
if (port->port_open_count > 0 || port->port_in_use) {
port->port_dev = NULL;
wake_up_interruptible(&port->port_write_wait);
wake_up_interruptible(&port->port_tty->read_wait);
wake_up_interruptible(&port->port_tty->write_wait);
} else {
kfree(port);
}
spin_unlock_irqrestore(&port->port_lock, flags);
}
}
}
/* Circular Buffer */
/*
* gs_buf_alloc
*
* Allocate a circular buffer and all associated memory.
*/
static struct gs_buf *gs_buf_alloc(unsigned int size, int kmalloc_flags)
{
struct gs_buf *gb;
if (size == 0)
return NULL;
gb = (struct gs_buf *)kmalloc(sizeof(struct gs_buf), kmalloc_flags);
if (gb == NULL)
return NULL;
gb->buf_buf = kmalloc(size, kmalloc_flags);
if (gb->buf_buf == NULL) {
kfree(gb);
return NULL;
}
gb->buf_size = size;
gb->buf_get = gb->buf_put = gb->buf_buf;
return gb;
}
/*
* gs_buf_free
*
* Free the buffer and all associated memory.
*/
void gs_buf_free(struct gs_buf *gb)
{
if (gb != NULL) {
if (gb->buf_buf != NULL)
kfree(gb->buf_buf);
kfree(gb);
}
}
/*
* gs_buf_clear
*
* Clear out all data in the circular buffer.
*/
void gs_buf_clear(struct gs_buf *gb)
{
if (gb != NULL)
gb->buf_get = gb->buf_put;
/* equivalent to a get of all data available */
}
/*
* gs_buf_data_avail
*
* Return the number of bytes of data available in the circular
* buffer.
*/
unsigned int gs_buf_data_avail(struct gs_buf *gb)
{
if (gb != NULL)
return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size;
else
return 0;
}
/*
* gs_buf_space_avail
*
* Return the number of bytes of space available in the circular
* buffer.
*/
unsigned int gs_buf_space_avail(struct gs_buf *gb)
{
if (gb != NULL)
return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size;
else
return 0;
}
/*
* gs_buf_put
*
* Copy data data from a user buffer and put it into the circular buffer.
* Restrict to the amount of space available.
*
* Return the number of bytes copied.
*/
unsigned int gs_buf_put(struct gs_buf *gb, const char *buf, unsigned int count)
{
unsigned int len;
if (gb == NULL)
return 0;
len = gs_buf_space_avail(gb);
if (count > len)
count = len;
if (count == 0)
return 0;
len = gb->buf_buf + gb->buf_size - gb->buf_put;
if (count > len) {
memcpy(gb->buf_put, buf, len);
memcpy(gb->buf_buf, buf+len, count - len);
gb->buf_put = gb->buf_buf + count - len;
} else {
memcpy(gb->buf_put, buf, count);
if (count < len)
gb->buf_put += count;
else /* count == len */
gb->buf_put = gb->buf_buf;
}
return count;
}
/*
* gs_buf_get
*
* Get data from the circular buffer and copy to the given buffer.
* Restrict to the amount of data available.
*
* Return the number of bytes copied.
*/
unsigned int gs_buf_get(struct gs_buf *gb, char *buf, unsigned int count)
{
unsigned int len;
if (gb == NULL)
return 0;
len = gs_buf_data_avail(gb);
if (count > len)
count = len;
if (count == 0)
return 0;
len = gb->buf_buf + gb->buf_size - gb->buf_get;
if (count > len) {
memcpy(buf, gb->buf_get, len);
memcpy(buf+len, gb->buf_buf, count - len);
gb->buf_get = gb->buf_buf + count - len;
} else {
memcpy(buf, gb->buf_get, count);
if (count < len)
gb->buf_get += count;
else /* count == len */
gb->buf_get = gb->buf_buf;
}
return count;
}
......@@ -63,6 +63,7 @@ static void dbg_hcs_params (struct ehci_hcd *ehci, char *label)
buf[0] = 0;
for (i = 0; i < HCS_N_PORTS (params); i++) {
// FIXME MIPS won't readb() ...
byte = readb (&ehci->caps->portroute[(i>>1)]);
sprintf(tmp, "%d ",
((i & 0x1) ? ((byte)&0xf) : ((byte>>4)&0xf)));
......@@ -115,7 +116,7 @@ static inline void dbg_hcc_params (struct ehci_hcd *ehci, char *label) {}
#ifdef DEBUG
static void __attribute__((__unused__))
dbg_qtd (char *label, struct ehci_hcd *ehci, struct ehci_qtd *qtd)
dbg_qtd (const char *label, struct ehci_hcd *ehci, struct ehci_qtd *qtd)
{
ehci_dbg (ehci, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd,
cpu_to_le32p (&qtd->hw_next),
......@@ -131,7 +132,7 @@ dbg_qtd (char *label, struct ehci_hcd *ehci, struct ehci_qtd *qtd)
}
static void __attribute__((__unused__))
dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
dbg_qh (const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
{
ehci_dbg (ehci, "%s qh %p n%08x info %x %x qtd %x\n", label,
qh, qh->hw_next, qh->hw_info1, qh->hw_info2,
......@@ -139,6 +140,36 @@ dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
dbg_qtd ("overlay", ehci, (struct ehci_qtd *) &qh->hw_qtd_next);
}
static void __attribute__((__unused__))
dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
{
ehci_dbg (ehci, "%s [%d] itd %p, next %08x, urb %p\n",
label, itd->frame, itd, le32_to_cpu(itd->hw_next), itd->urb);
ehci_dbg (ehci,
" trans: %08x %08x %08x %08x %08x %08x %08x %08x\n",
le32_to_cpu(itd->hw_transaction[0]),
le32_to_cpu(itd->hw_transaction[1]),
le32_to_cpu(itd->hw_transaction[2]),
le32_to_cpu(itd->hw_transaction[3]),
le32_to_cpu(itd->hw_transaction[4]),
le32_to_cpu(itd->hw_transaction[5]),
le32_to_cpu(itd->hw_transaction[6]),
le32_to_cpu(itd->hw_transaction[7]));
ehci_dbg (ehci,
" buf: %08x %08x %08x %08x %08x %08x %08x\n",
le32_to_cpu(itd->hw_bufp[0]),
le32_to_cpu(itd->hw_bufp[1]),
le32_to_cpu(itd->hw_bufp[2]),
le32_to_cpu(itd->hw_bufp[3]),
le32_to_cpu(itd->hw_bufp[4]),
le32_to_cpu(itd->hw_bufp[5]),
le32_to_cpu(itd->hw_bufp[6]));
ehci_dbg (ehci, " index: %d %d %d %d %d %d %d %d\n",
itd->index[0], itd->index[1], itd->index[2],
itd->index[3], itd->index[4], itd->index[5],
itd->index[6], itd->index[7]);
}
static int __attribute__((__unused__))
dbg_status_buf (char *buf, unsigned len, char *label, u32 status)
{
......@@ -591,7 +622,7 @@ show_registers (struct class_device *class_dev, char *buf)
spin_lock_irqsave (&ehci->lock, flags);
/* Capability Registers */
i = readw (&ehci->caps->hci_version);
i = HC_VERSION(readl (&ehci->caps->hc_capbase));
temp = snprintf (next, size,
"PCI device %s\nEHCI %x.%02x, hcd state %d (driver " DRIVER_VERSION ")\n",
pci_name(hcd->pdev),
......
......@@ -67,6 +67,9 @@
*
* HISTORY:
*
* 2003-12-29 Rewritten high speed iso transfer support (by Michal Sojka,
* <sojkam@centrum.cz>, updates by DB).
*
* 2002-11-29 Correct handling for hw async_next register.
* 2002-08-06 Handling for bulk and interrupt transfers is mostly shared;
* only scheduling is different, no arbitrary limitations.
......@@ -90,14 +93,16 @@
* 2001-June Works with usb-storage and NEC EHCI on 2.4
*/
#define DRIVER_VERSION "2003-Jun-13"
#define DRIVER_VERSION "2003-Dec-29"
#define DRIVER_AUTHOR "David Brownell"
#define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver"
static const char hcd_name [] = "ehci_hcd";
// #define EHCI_VERBOSE_DEBUG
#undef EHCI_VERBOSE_DEBUG
#undef EHCI_URB_TRACE
// #define have_split_iso
#ifdef DEBUG
......@@ -324,8 +329,8 @@ static int ehci_hc_reset (struct usb_hcd *hcd)
spin_lock_init (&ehci->lock);
ehci->caps = (struct ehci_caps *) hcd->regs;
ehci->regs = (struct ehci_regs *) (hcd->regs +
readb (&ehci->caps->length));
ehci->regs = (struct ehci_regs *) (hcd->regs +
HC_LENGTH (readl (&ehci->caps->hc_capbase)));
dbg_hcs_params (ehci, "reset");
dbg_hcc_params (ehci, "reset");
......@@ -489,7 +494,7 @@ static int ehci_start (struct usb_hcd *hcd)
/* PCI Serial Bus Release Number is at 0x60 offset */
pci_read_config_byte (hcd->pdev, 0x60, &tempbyte);
temp = readw (&ehci->caps->hci_version);
temp = HC_VERSION(readl (&ehci->caps->hc_capbase));
ehci_info (ehci,
"USB %x.%x enabled, EHCI %x.%02x, driver %s\n",
((tempbyte & 0xf0)>>4), (tempbyte & 0x0f),
......@@ -899,10 +904,19 @@ ehci_endpoint_disable (struct usb_hcd *hcd, struct hcd_dev *dev, int ep)
if (!qh)
goto done;
/* endpoints can be iso streams. for now, we don't
* accelerate iso completions ... so spin a while.
*/
if (qh->hw_info1 == 0) {
ehci_vdbg (ehci, "iso delay\n");
goto idle_timeout;
}
if (!HCD_IS_RUNNING (ehci->hcd.state))
qh->qh_state = QH_STATE_IDLE;
switch (qh->qh_state) {
case QH_STATE_UNLINK: /* wait for hw to finish? */
idle_timeout:
spin_unlock_irqrestore (&ehci->lock, flags);
set_current_state (TASK_UNINTERRUPTIBLE);
schedule_timeout (1);
......
......@@ -28,7 +28,7 @@
* - driver buffers, read/written by HC ... single shot DMA mapped
*
* There's also PCI "register" data, which is memory mapped.
* No memory seen by this driver is pagable.
* No memory seen by this driver is pageable.
*/
/*-------------------------------------------------------------------------*/
......@@ -131,6 +131,7 @@ static void qh_put (struct ehci_hcd *ehci, struct ehci_qh *qh)
}
if (qh->dummy)
ehci_qtd_free (ehci, qh->dummy);
usb_put_dev (qh->dev);
pci_pool_free (ehci->qh_pool, qh, qh->qh_dma);
}
......
......@@ -164,7 +164,8 @@ static void qtd_copy_status (
/* if async CSPLIT failed, try cleaning out the TT buffer */
} else if (urb->dev->tt && !usb_pipeint (urb->pipe)
&& QTD_CERR(token) == 0) {
&& ((token & QTD_STS_MMF) != 0
|| QTD_CERR(token) == 0)) {
#ifdef DEBUG
struct usb_device *tt = urb->dev->tt->hub;
dev_dbg (&tt->dev,
......@@ -212,6 +213,16 @@ ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb, struct pt_regs *regs)
}
spin_unlock (&urb->lock);
#ifdef EHCI_URB_TRACE
ehci_dbg (ehci,
"%s %s urb %p ep%d%s status %d len %d/%d\n",
__FUNCTION__, urb->dev->devpath, urb,
usb_pipeendpoint (urb->pipe),
usb_pipein (urb->pipe) ? "in" : "out",
urb->status,
urb->actual_length, urb->transfer_buffer_length);
#endif
/* complete() can reenter this HCD */
spin_unlock (&ehci->lock);
usb_hcd_giveback_urb (&ehci->hcd, urb, regs);
......@@ -640,6 +651,9 @@ qh_make (
qh->period = urb->interval;
}
/* support for tt scheduling */
qh->dev = usb_get_dev (urb->dev);
}
/* using TT? */
......@@ -699,8 +713,6 @@ qh_make (
usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1);
return qh;
}
#undef hb_mult
#undef hb_packet
/*-------------------------------------------------------------------------*/
......@@ -887,10 +899,14 @@ submit_async (
if (usb_pipein (urb->pipe) && !usb_pipecontrol (urb->pipe))
epnum |= 0x10;
ehci_vdbg (ehci, "submit_async urb %p len %d ep%d%s qtd %p [qh %p]\n",
urb, urb->transfer_buffer_length,
epnum & 0x0f, (epnum & 0x10) ? "in" : "out",
#ifdef EHCI_URB_TRACE
ehci_dbg (ehci,
"%s %s urb %p ep%d%s len %d, qtd %p [qh %p]\n",
__FUNCTION__, urb->dev->devpath, urb,
epnum & 0x0f, usb_pipein (urb->pipe) ? "in" : "out",
urb->transfer_buffer_length,
qtd, dev ? dev->ep [epnum] : (void *)~0);
#endif
spin_lock_irqsave (&ehci->lock, flags);
qh = qh_append_tds (ehci, urb, qtd_list, epnum, &dev->ep [epnum]);
......
/*
* Copyright (c) 2001-2002 by David Brownell
* Copyright (c) 2001-2003 by David Brownell
* Copyright (c) 2003 Michal Sojka, for high-speed iso transfers
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
......@@ -27,10 +28,10 @@
* Note that for interrupt transfers, the QH/QTD manipulation is shared
* with the "asynchronous" transaction support (control/bulk transfers).
* The only real difference is in how interrupt transfers are scheduled.
* We get some funky API restrictions from the current URB model, which
* works notably better for reading transfers than for writing. (And
* which accordingly needs to change before it'll work inside devices,
* or with "USB On The Go" additions to USB 2.0 ...)
*
* For ISO, we make an "iso_stream" head to serve the same role as a QH.
* It keeps track of every ITD (or SITD) that's linked, and holds enough
* pre-calculated schedule data to make appending to the queue be quick.
*/
static int ehci_get_frame (struct usb_hcd *hcd);
......@@ -126,9 +127,7 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe)
q = &q->fstn->fstn_next;
break;
case Q_TYPE_ITD:
/* NOTE the "one uframe per itd" policy */
if (q->itd->hw_transaction [uframe] != 0)
usecs += q->itd->usecs;
usecs += q->itd->usecs [uframe];
q = &q->itd->itd_next;
break;
#ifdef have_split_iso
......@@ -520,417 +519,646 @@ intr_complete (
/*-------------------------------------------------------------------------*/
static void
itd_free_list (struct ehci_hcd *ehci, struct urb *urb)
static inline struct ehci_iso_stream *
iso_stream_alloc (int mem_flags)
{
struct ehci_itd *first_itd = urb->hcpriv;
while (!list_empty (&first_itd->itd_list)) {
struct ehci_itd *itd;
itd = list_entry (
first_itd->itd_list.next,
struct ehci_itd, itd_list);
list_del (&itd->itd_list);
pci_pool_free (ehci->itd_pool, itd, itd->itd_dma);
struct ehci_iso_stream *stream;
stream = kmalloc(sizeof *stream, mem_flags);
if (likely (stream != 0)) {
memset (stream, 0, sizeof(*stream));
INIT_LIST_HEAD(&stream->itd_list);
INIT_LIST_HEAD(&stream->free_itd_list);
stream->next_uframe = -1;
stream->refcount = 1;
}
pci_pool_free (ehci->itd_pool, first_itd, first_itd->itd_dma);
urb->hcpriv = 0;
return stream;
}
static int
itd_fill (
struct ehci_hcd *ehci,
struct ehci_itd *itd,
struct urb *urb,
unsigned index, // urb->iso_frame_desc [index]
dma_addr_t dma // mapped transfer buffer
) {
u64 temp;
u32 buf1;
unsigned i, epnum, maxp, multi;
unsigned length;
int is_input;
itd->hw_next = EHCI_LIST_END;
itd->urb = urb;
itd->index = index;
/* tell itd about its transfer buffer, max 2 pages */
length = urb->iso_frame_desc [index].length;
dma += urb->iso_frame_desc [index].offset;
temp = dma & ~0x0fff;
for (i = 0; i < 2; i++) {
itd->hw_bufp [i] = cpu_to_le32 ((u32) temp);
itd->hw_bufp_hi [i] = cpu_to_le32 ((u32)(temp >> 32));
temp += 0x1000;
}
itd->buf_dma = dma;
static inline void
iso_stream_init (
struct ehci_iso_stream *stream,
struct usb_device *dev,
int pipe,
unsigned interval
)
{
u32 buf1;
unsigned epnum, maxp, multi;
int is_input;
long bandwidth;
/*
* this might be a "high bandwidth" highspeed endpoint,
* as encoded in the ep descriptor's maxpacket field
* as encoded in the ep descriptor's wMaxPacket field
*/
epnum = usb_pipeendpoint (urb->pipe);
is_input = usb_pipein (urb->pipe);
epnum = usb_pipeendpoint (pipe);
is_input = usb_pipein (pipe) ? USB_DIR_IN : 0;
if (is_input) {
maxp = urb->dev->epmaxpacketin [epnum];
maxp = dev->epmaxpacketin [epnum];
buf1 = (1 << 11);
} else {
maxp = urb->dev->epmaxpacketout [epnum];
maxp = dev->epmaxpacketout [epnum];
buf1 = 0;
}
buf1 |= (maxp & 0x03ff);
multi = 1;
multi += (maxp >> 11) & 0x03;
maxp &= 0x03ff;
multi = hb_mult(maxp);
maxp = max_packet(maxp);
buf1 |= maxp;
maxp *= multi;
/* transfer can't fit in any uframe? */
if (length < 0 || maxp < length) {
dbg ("BAD iso packet: %d bytes, max %d, urb %p [%d] (of %d)",
length, maxp, urb, index,
urb->iso_frame_desc [index].length);
return -ENOSPC;
stream->dev = (struct hcd_dev *)dev->hcpriv;
stream->bEndpointAddress = is_input | epnum;
stream->interval = interval;
stream->maxp = maxp;
stream->buf0 = cpu_to_le32 ((epnum << 8) | dev->devnum);
stream->buf1 = cpu_to_le32 (buf1);
stream->buf2 = cpu_to_le32 (multi);
/* usbfs wants to report the average usecs per frame tied up
* when transfers on this endpoint are scheduled ...
*/
stream->usecs = HS_USECS_ISO (maxp);
bandwidth = stream->usecs * 8;
bandwidth /= 1 << (interval - 1);
stream->bandwidth = bandwidth;
}
static void
iso_stream_put(struct ehci_hcd *ehci, struct ehci_iso_stream *stream)
{
stream->refcount--;
/* free whenever just a dev->ep reference remains.
* not like a QH -- no persistent state (toggle, halt)
*/
if (stream->refcount == 1) {
int is_in;
// BUG_ON (!list_empty(&stream->itd_list));
while (!list_empty (&stream->free_itd_list)) {
struct ehci_itd *itd;
itd = list_entry (stream->free_itd_list.next,
struct ehci_itd, itd_list);
list_del (&itd->itd_list);
pci_pool_free (ehci->itd_pool, itd, itd->itd_dma);
}
is_in = (stream->bEndpointAddress & USB_DIR_IN) ? 0x10 : 0;
stream->bEndpointAddress &= 0x0f;
stream->dev->ep [is_in + stream->bEndpointAddress] = 0;
if (stream->rescheduled) {
ehci_info (ehci, "ep%d%s-iso rescheduled "
"%lu times in %lu seconds\n",
stream->bEndpointAddress, is_in ? "in" : "out",
stream->rescheduled,
((jiffies - stream->start)/HZ)
);
}
kfree(stream);
}
itd->usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 1, length);
}
/* "plus" info in low order bits of buffer pointers */
itd->hw_bufp [0] |= cpu_to_le32 ((epnum << 8) | urb->dev->devnum);
itd->hw_bufp [1] |= cpu_to_le32 (buf1);
itd->hw_bufp [2] |= cpu_to_le32 (multi);
static inline struct ehci_iso_stream *
iso_stream_get (struct ehci_iso_stream *stream)
{
if (likely (stream != 0))
stream->refcount++;
return stream;
}
static struct ehci_iso_stream *
iso_stream_find (struct ehci_hcd *ehci, struct urb *urb)
{
unsigned epnum;
struct hcd_dev *dev;
struct ehci_iso_stream *stream;
unsigned long flags;
/* figure hw_transaction[] value (it's scheduled later) */
itd->transaction = EHCI_ISOC_ACTIVE;
itd->transaction |= dma & 0x0fff; /* offset; buffer=0 */
if ((index + 1) == urb->number_of_packets)
itd->transaction |= EHCI_ITD_IOC; /* end-of-urb irq */
itd->transaction |= length << 16;
cpu_to_le32s (&itd->transaction);
epnum = usb_pipeendpoint (urb->pipe);
if (usb_pipein(urb->pipe))
epnum += 0x10;
spin_lock_irqsave (&ehci->lock, flags);
dev = (struct hcd_dev *)urb->dev->hcpriv;
stream = dev->ep [epnum];
if (unlikely (stream == 0)) {
stream = iso_stream_alloc(GFP_ATOMIC);
if (likely (stream != 0)) {
/* dev->ep owns the initial refcount */
dev->ep[epnum] = stream;
iso_stream_init(stream, urb->dev, urb->pipe,
urb->interval);
}
/* if dev->ep [epnum] is a QH, info1.maxpacket is nonzero */
} else if (unlikely (stream->hw_info1 != 0)) {
ehci_dbg (ehci, "dev %s ep%d%s, not iso??\n",
urb->dev->devpath, epnum & 0x0f,
(epnum & 0x10) ? "in" : "out");
stream = 0;
}
/* caller guarantees an eventual matching iso_stream_put */
stream = iso_stream_get (stream);
spin_unlock_irqrestore (&ehci->lock, flags);
return stream;
}
/*-------------------------------------------------------------------------*/
static inline struct ehci_itd_sched *
itd_sched_alloc (unsigned packets, int mem_flags)
{
struct ehci_itd_sched *itd_sched;
int size = sizeof *itd_sched;
size += packets * sizeof (struct ehci_iso_uframe);
itd_sched = kmalloc (size, mem_flags);
if (likely (itd_sched != 0)) {
memset(itd_sched, 0, size);
INIT_LIST_HEAD (&itd_sched->itd_list);
}
return itd_sched;
}
static int
itd_sched_init (
struct ehci_itd_sched *itd_sched,
struct ehci_iso_stream *stream,
struct urb *urb
)
{
unsigned i;
dma_addr_t dma = urb->transfer_dma;
/* how many uframes are needed for these transfers */
itd_sched->span = urb->number_of_packets * stream->interval;
/* figure out per-uframe itd fields that we'll need later
* when we fit new itds into the schedule.
*/
for (i = 0; i < urb->number_of_packets; i++) {
struct ehci_iso_uframe *uframe = &itd_sched->packet [i];
unsigned length;
dma_addr_t buf;
u32 trans;
length = urb->iso_frame_desc [i].length;
buf = dma + urb->iso_frame_desc [i].offset;
trans = EHCI_ISOC_ACTIVE;
trans |= buf & 0x0fff;
if (unlikely ((i + 1) == urb->number_of_packets))
trans |= EHCI_ITD_IOC;
trans |= length << 16;
uframe->transaction = cpu_to_le32 (trans);
/* might need to cross a buffer page within a td */
uframe->bufp = (buf & ~(u64)0x0fff);
buf += length;
if (unlikely ((uframe->bufp != (buf & ~(u64)0x0fff))))
uframe->cross = 1;
}
return 0;
}
static void
itd_sched_free (
struct ehci_iso_stream *stream,
struct ehci_itd_sched *itd_sched
)
{
list_splice (&itd_sched->itd_list, &stream->free_itd_list);
kfree (itd_sched);
}
static int
itd_urb_transaction (
struct ehci_iso_stream *stream,
struct ehci_hcd *ehci,
struct urb *urb,
int mem_flags
) {
int frame_index;
struct ehci_itd *first_itd, *itd;
)
{
struct ehci_itd *itd;
int status;
dma_addr_t itd_dma;
int i;
unsigned num_itds;
struct ehci_itd_sched *itd_sched;
itd_sched = itd_sched_alloc (urb->number_of_packets, mem_flags);
if (unlikely (itd_sched == 0))
return -ENOMEM;
status = itd_sched_init (itd_sched, stream, urb);
if (unlikely (status != 0)) {
itd_sched_free (stream, itd_sched);
return status;
}
if (urb->interval < 8)
num_itds = 1 + (itd_sched->span + 7) / 8;
else
num_itds = urb->number_of_packets;
/* allocate/init ITDs */
for (frame_index = 0, first_itd = 0;
frame_index < urb->number_of_packets;
frame_index++) {
itd = pci_pool_alloc (ehci->itd_pool, mem_flags, &itd_dma);
if (!itd) {
status = -ENOMEM;
goto fail;
for (i = 0; i < num_itds; i++) {
/* free_itd_list.next might be cache-hot ... but maybe
* the HC caches it too. avoid that issue for now.
*/
/* prefer previously-allocated itds */
if (likely (!list_empty(&stream->free_itd_list))) {
itd = list_entry (stream->free_itd_list.prev,
struct ehci_itd, itd_list);
list_del (&itd->itd_list);
itd_dma = itd->itd_dma;
} else
itd = pci_pool_alloc (ehci->itd_pool, mem_flags,
&itd_dma);
if (unlikely (0 == itd)) {
itd_sched_free (stream, itd_sched);
return -ENOMEM;
}
memset (itd, 0, sizeof *itd);
itd->itd_dma = itd_dma;
status = itd_fill (ehci, itd, urb, frame_index,
urb->transfer_dma);
if (status != 0)
goto fail;
if (first_itd)
list_add_tail (&itd->itd_list,
&first_itd->itd_list);
else {
INIT_LIST_HEAD (&itd->itd_list);
urb->hcpriv = first_itd = itd;
}
list_add (&itd->itd_list, &itd_sched->itd_list);
}
/* temporarily store schedule info in hcpriv */
urb->hcpriv = itd_sched;
urb->error_count = 0;
return 0;
fail:
if (urb->hcpriv)
itd_free_list (ehci, urb);
return status;
}
/*-------------------------------------------------------------------------*/
static inline void
itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd)
{
/* always prepend ITD/SITD ... only QH tree is order-sensitive */
itd->itd_next = ehci->pshadow [frame];
itd->hw_next = ehci->periodic [frame];
ehci->pshadow [frame].itd = itd;
ehci->periodic [frame] = cpu_to_le32 (itd->itd_dma) | Q_TYPE_ITD;
}
/*
* return zero on success, else -errno
* - start holds first uframe to start scheduling into
* - max is the first uframe it's NOT (!) OK to start scheduling into
* math to be done modulo "mod" (ehci->periodic_size << 3)
* This scheduler plans almost as far into the future as it has actual
* periodic schedule slots. (Affected by TUNE_FLS, which defaults to
* "as small as possible" to be cache-friendlier.) That limits the size
* transfers you can stream reliably; avoid more than 64 msec per urb.
* Also avoid queue depths of less than the system's worst irq latency.
*/
static int get_iso_range (
struct ehci_hcd *ehci,
struct urb *urb,
unsigned *start,
unsigned *max,
unsigned mod
) {
struct list_head *lh;
struct hcd_dev *dev = urb->dev->hcpriv;
int last = -1;
unsigned now, span, end;
span = urb->interval * urb->number_of_packets;
#define SCHEDULE_SLOP 10 /* frames */
/* first see if we know when the next transfer SHOULD happen */
list_for_each (lh, &dev->urb_list) {
struct urb *u;
struct ehci_itd *itd;
unsigned s;
static int
itd_stream_schedule (
struct ehci_hcd *ehci,
struct urb *urb,
struct ehci_iso_stream *stream
)
{
u32 now, start, end, max;
int status;
unsigned mod = ehci->periodic_size << 3;
struct ehci_itd_sched *itd_sched = urb->hcpriv;
u = list_entry (lh, struct urb, urb_list);
if (u == urb || u->pipe != urb->pipe)
continue;
if (u->interval != urb->interval) { /* must not change! */
dbg ("urb %p interval %d ... != %p interval %d",
u, u->interval, urb, urb->interval);
return -EINVAL;
}
/* URB for this endpoint... covers through when? */
itd = urb->hcpriv;
s = itd->uframe + u->interval * u->number_of_packets;
if (last < 0)
last = s;
else {
/*
* So far we can only queue two ISO URBs...
*
* FIXME do interval math, figure out whether
* this URB is "before" or not ... also, handle
* the case where the URB might have completed,
* but hasn't yet been processed.
*/
dbg ("NYET: queue >2 URBs per ISO endpoint");
return -EDOM;
}
if (unlikely (itd_sched->span > (mod - 8 * SCHEDULE_SLOP))) {
ehci_dbg (ehci, "iso request %p too long\n", urb);
status = -EFBIG;
goto fail;
}
/* calculate the legal range [start,max) */
now = readl (&ehci->regs->frame_index) + 1; /* next uframe */
if (!ehci->periodic_sched)
now += 8; /* startup delay */
now %= mod;
end = now + mod;
if (last < 0) {
*start = now + ehci->i_thresh + /* paranoia */ 1;
*max = end - span;
if (*max < *start + 1)
*max = *start + 1;
} else {
*start = last % mod;
*max = (last + 1) % mod;
}
now = readl (&ehci->regs->frame_index) % mod;
/* when's the last uframe this urb could start? */
max = now + mod;
max -= itd_sched->span;
max -= 8 * SCHEDULE_SLOP;
/* explicit start frame? */
if (!(urb->transfer_flags & URB_ISO_ASAP)) {
unsigned temp;
/* sanity check: must be in range */
urb->start_frame %= ehci->periodic_size;
temp = urb->start_frame << 3;
if (temp < *start)
temp += mod;
if (temp > *max)
return -EDOM;
/* use that explicit start frame */
*start = urb->start_frame << 3;
temp += 8;
if (temp < *max)
*max = temp;
/* typical case: reuse current schedule. stream is still active,
* and no gaps from host falling behind (irq delays etc)
*/
if (likely (!list_empty (&stream->itd_list))) {
start = stream->next_uframe;
if (start < now)
start += mod;
if (likely (start < max))
goto ready;
/* two cases:
* (a) we missed some uframes ... can reschedule
* (b) trying to overcommit the schedule
* FIXME (b) should be a hard failure
*/
}
// FIXME minimize wraparound to "now" ... insist max+span
// (and start+span) remains a few frames short of "end"
/* need to schedule; when's the next (u)frame we could start?
* this is bigger than ehci->i_thresh allows; scheduling itself
* isn't free, the slop should handle reasonably slow cpus. it
* can also help high bandwidth if the dma and irq loads don't
* jump until after the queue is primed.
*/
start = SCHEDULE_SLOP * 8 + (now & ~0x07);
end = start;
*max %= ehci->periodic_size;
if ((*start + span) < end)
return 0;
return -EFBIG;
}
ehci_vdbg (ehci, "%s schedule from %d (%d..%d), was %d\n",
__FUNCTION__, now, start, max,
stream->next_uframe);
static int
itd_schedule (struct ehci_hcd *ehci, struct urb *urb)
{
unsigned start, max, i;
int status;
unsigned mod = ehci->periodic_size << 3;
/* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */
for (i = 0; i < urb->number_of_packets; i++) {
urb->iso_frame_desc [i].status = -EINPROGRESS;
urb->iso_frame_desc [i].actual_length = 0;
}
if (likely (max > (start + urb->interval)))
max = start + urb->interval;
if ((status = get_iso_range (ehci, urb, &start, &max, mod)) != 0)
return status;
/* hack: account for itds already scheduled to this endpoint */
if (unlikely (list_empty (&stream->itd_list)))
end = max;
/* within [start..max] find a uframe slot with enough bandwidth */
end %= mod;
do {
unsigned uframe;
unsigned usecs;
struct ehci_itd *itd;
int enough_space = 1;
/* check schedule: enough space? */
itd = urb->hcpriv;
uframe = start;
for (i = 0, uframe = start;
i < urb->number_of_packets;
i++, uframe += urb->interval) {
do {
uframe %= mod;
/* can't commit more than 80% periodic == 100 usec */
if (periodic_usecs (ehci, uframe >> 3, uframe & 0x7)
> (100 - itd->usecs)) {
itd = 0;
> (100 - stream->usecs)) {
enough_space = 0;
break;
}
itd = list_entry (itd->itd_list.next,
struct ehci_itd, itd_list);
}
if (!itd)
continue;
/* that's where we'll schedule this! */
itd = urb->hcpriv;
urb->start_frame = start >> 3;
vdbg ("ISO urb %p (%d packets period %d) starting %d.%d",
urb, urb->number_of_packets, urb->interval,
urb->start_frame, start & 0x7);
for (i = 0, uframe = start, usecs = 0;
i < urb->number_of_packets;
i++, uframe += urb->interval) {
uframe %= mod;
itd->uframe = uframe;
itd->hw_transaction [uframe & 0x07] = itd->transaction;
itd_link (ehci, (uframe >> 3) % ehci->periodic_size,
itd);
wmb ();
usecs += itd->usecs;
/* we know urb->interval is 2^N uframes */
uframe += urb->interval;
} while (uframe != end);
itd = list_entry (itd->itd_list.next,
struct ehci_itd, itd_list);
}
/* update bandwidth utilization records (for usbfs)
*
* FIXME This claims each URB queued to an endpoint, as if
* transfers were concurrent, not sequential. So bandwidth
* typically gets double-billed ... comes from tying it to
* URBs rather than endpoints in the schedule. Luckily we
* don't use this usbfs data for serious decision making.
*/
usecs /= urb->number_of_packets;
usecs /= urb->interval;
usecs >>= 3;
if (usecs < 1)
usecs = 1;
usb_claim_bandwidth (urb->dev, urb, usecs, 1);
/* maybe enable periodic schedule processing */
if (!ehci->periodic_sched++) {
if ((status = enable_periodic (ehci)) != 0) {
// FIXME deschedule right away
err ("itd_schedule, enable = %d", status);
/* (re)schedule it here if there's enough bandwidth */
if (enough_space) {
start %= mod;
if (unlikely (!list_empty (&stream->itd_list))) {
/* host fell behind ... maybe irq latencies
* delayed this request queue for too long.
*/
stream->rescheduled++;
dev_dbg (&urb->dev->dev,
"iso%d%s %d.%d skip %d.%d\n",
stream->bEndpointAddress & 0x0f,
(stream->bEndpointAddress & USB_DIR_IN)
? "in" : "out",
stream->next_uframe >> 3,
stream->next_uframe & 0x7,
start >> 3, start & 0x7);
}
stream->next_uframe = start;
goto ready;
}
return 0;
} while ((start = ++start % mod) != max);
} while (++start < max);
/* no room in the schedule */
dbg ("urb %p, CAN'T SCHEDULE", urb);
return -ENOSPC;
ehci_dbg (ehci, "iso %ssched full %p (now %d end %d max %d)\n",
list_empty (&stream->itd_list) ? "" : "re",
urb, now, end, max);
status = -ENOSPC;
fail:
itd_sched_free (stream, itd_sched);
urb->hcpriv = 0;
return status;
ready:
urb->start_frame = stream->next_uframe;
return 0;
}
/*-------------------------------------------------------------------------*/
static inline void
itd_init (struct ehci_iso_stream *stream, struct ehci_itd *itd)
{
int i;
itd->hw_next = EHCI_LIST_END;
itd->hw_bufp [0] = stream->buf0;
itd->hw_bufp [1] = stream->buf1;
itd->hw_bufp [2] = stream->buf2;
for (i = 0; i < 8; i++)
itd->index[i] = -1;
/* All other fields are filled when scheduling */
}
static inline void
itd_patch (
struct ehci_itd *itd,
struct ehci_itd_sched *itd_sched,
unsigned index,
u16 uframe,
int first
)
{
struct ehci_iso_uframe *uf = &itd_sched->packet [index];
unsigned pg = itd->pg;
// BUG_ON (pg == 6 && uf->cross);
uframe &= 0x07;
itd->index [uframe] = index;
itd->hw_transaction [uframe] = uf->transaction;
itd->hw_transaction [uframe] |= cpu_to_le32 (pg << 12);
itd->hw_bufp [pg] |= cpu_to_le32 (uf->bufp & ~(u32)0);
itd->hw_bufp_hi [pg] |= cpu_to_le32 ((u32)(uf->bufp >> 32));
/* iso_frame_desc[].offset must be strictly increasing */
if (unlikely (!first && uf->cross)) {
u64 bufp = uf->bufp + 4096;
itd->pg = ++pg;
itd->hw_bufp [pg] |= cpu_to_le32 (bufp & ~(u32)0);
itd->hw_bufp_hi [pg] |= cpu_to_le32 ((u32)(bufp >> 32));
}
}
static inline void
itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd)
{
/* always prepend ITD/SITD ... only QH tree is order-sensitive */
itd->itd_next = ehci->pshadow [frame];
itd->hw_next = ehci->periodic [frame];
ehci->pshadow [frame].itd = itd;
itd->frame = frame;
wmb ();
ehci->periodic [frame] = cpu_to_le32 (itd->itd_dma) | Q_TYPE_ITD;
}
/* fit urb's itds into the selected schedule slot; activate as needed */
static int
itd_link_urb (
struct ehci_hcd *ehci,
struct urb *urb,
unsigned mod,
struct ehci_iso_stream *stream
)
{
int packet, first = 1;
unsigned next_uframe, uframe, frame;
struct ehci_itd_sched *itd_sched = urb->hcpriv;
struct ehci_itd *itd;
next_uframe = stream->next_uframe % mod;
if (unlikely (list_empty(&stream->itd_list))) {
hcd_to_bus (&ehci->hcd)->bandwidth_allocated
+= stream->bandwidth;
ehci_vdbg (ehci,
"schedule devp %s ep%d%s-iso period %d start %d.%d\n",
urb->dev->devpath, stream->bEndpointAddress & 0x0f,
(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
urb->interval,
next_uframe >> 3, next_uframe & 0x7);
stream->start = jiffies;
}
hcd_to_bus (&ehci->hcd)->bandwidth_isoc_reqs++;
/* fill iTDs uframe by uframe */
for (packet = 0, itd = 0; packet < urb->number_of_packets; ) {
if (itd == 0) {
/* ASSERT: we have all necessary itds */
// BUG_ON (list_empty (&itd_sched->itd_list));
/* ASSERT: no itds for this endpoint in this uframe */
itd = list_entry (itd_sched->itd_list.next,
struct ehci_itd, itd_list);
list_move_tail (&itd->itd_list, &stream->itd_list);
itd->stream = iso_stream_get (stream);
itd->urb = usb_get_urb (urb);
first = 1;
itd_init (stream, itd);
}
uframe = next_uframe & 0x07;
frame = next_uframe >> 3;
itd->usecs [uframe] = stream->usecs;
itd_patch (itd, itd_sched, packet, uframe, first);
first = 0;
next_uframe += stream->interval;
next_uframe %= mod;
packet++;
/* link completed itds into the schedule */
if (((next_uframe >> 3) != frame)
|| packet == urb->number_of_packets) {
itd_link (ehci, frame % ehci->periodic_size, itd);
itd = 0;
}
}
stream->next_uframe = next_uframe;
/* don't need that schedule data any more */
itd_sched_free (stream, itd_sched);
urb->hcpriv = 0;
if (unlikely (!ehci->periodic_sched++))
return enable_periodic (ehci);
return 0;
}
#define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR)
static unsigned
itd_complete (
struct ehci_hcd *ehci,
struct ehci_itd *itd,
unsigned uframe,
struct pt_regs *regs
) {
struct urb *urb = itd->urb;
struct usb_iso_packet_descriptor *desc;
u32 t;
/* update status for this uframe's transfers */
desc = &urb->iso_frame_desc [itd->index];
t = itd->hw_transaction [uframe];
itd->hw_transaction [uframe] = 0;
if (t & EHCI_ISOC_ACTIVE)
desc->status = -EXDEV;
else if (t & ISO_ERRS) {
urb->error_count++;
if (t & EHCI_ISOC_BUF_ERR)
desc->status = usb_pipein (urb->pipe)
? -ENOSR /* couldn't read */
: -ECOMM; /* couldn't write */
else if (t & EHCI_ISOC_BABBLE)
desc->status = -EOVERFLOW;
else /* (t & EHCI_ISOC_XACTERR) */
desc->status = -EPROTO;
/* HC need not update length with this error */
if (!(t & EHCI_ISOC_BABBLE))
desc->actual_length += EHCI_ITD_LENGTH (t);
} else {
desc->status = 0;
desc->actual_length += EHCI_ITD_LENGTH (t);
unsigned uframe;
int urb_index = -1;
struct ehci_iso_stream *stream = itd->stream;
struct usb_device *dev;
/* for each uframe with a packet */
for (uframe = 0; uframe < 8; uframe++) {
if (likely (itd->index[uframe] == -1))
continue;
urb_index = itd->index[uframe];
desc = &urb->iso_frame_desc [urb_index];
t = le32_to_cpup (&itd->hw_transaction [uframe]);
itd->hw_transaction [uframe] = 0;
/* report transfer status */
if (unlikely (t & ISO_ERRS)) {
urb->error_count++;
if (t & EHCI_ISOC_BUF_ERR)
desc->status = usb_pipein (urb->pipe)
? -ENOSR /* hc couldn't read */
: -ECOMM; /* hc couldn't write */
else if (t & EHCI_ISOC_BABBLE)
desc->status = -EOVERFLOW;
else /* (t & EHCI_ISOC_XACTERR) */
desc->status = -EPROTO;
/* HC need not update length with this error */
if (!(t & EHCI_ISOC_BABBLE))
desc->actual_length = EHCI_ITD_LENGTH (t);
} else if (likely ((t & EHCI_ISOC_ACTIVE) == 0)) {
desc->status = 0;
desc->actual_length = EHCI_ITD_LENGTH (t);
}
}
vdbg ("itd %p urb %p packet %d/%d trans %x status %d len %d",
itd, urb, itd->index + 1, urb->number_of_packets,
t, desc->status, desc->actual_length);
usb_put_urb (urb);
itd->urb = 0;
itd->stream = 0;
list_move (&itd->itd_list, &stream->free_itd_list);
iso_stream_put (ehci, stream);
/* handle completion now? */
if ((itd->index + 1) != urb->number_of_packets)
if (likely ((urb_index + 1) != urb->number_of_packets))
return 0;
/*
* Always give the urb back to the driver ... expect it to submit
* a new urb (or resubmit this), and to have another already queued
* when un-interrupted transfers are needed.
*
* NOTE that for now we don't accelerate ISO unlinks; they just
* happen according to the current schedule. Means a delay of
* up to about a second (max).
/* ASSERT: it's really the last itd for this urb
list_for_each_entry (itd, &stream->itd_list, itd_list)
BUG_ON (itd->urb == urb);
*/
itd_free_list (ehci, urb);
if (urb->status == -EINPROGRESS)
urb->status = 0;
/* complete() can reenter this HCD */
spin_unlock (&ehci->lock);
usb_hcd_giveback_urb (&ehci->hcd, urb, regs);
spin_lock (&ehci->lock);
/* give urb back to the driver ... can be out-of-order */
dev = usb_get_dev (urb->dev);
ehci_urb_done (ehci, urb, regs);
urb = 0;
/* defer stopping schedule; completion can submit */
ehci->periodic_sched--;
if (!ehci->periodic_sched)
if (unlikely (!ehci->periodic_sched))
(void) disable_periodic (ehci);
hcd_to_bus (&ehci->hcd)->bandwidth_isoc_reqs--;
if (unlikely (list_empty (&stream->itd_list))) {
hcd_to_bus (&ehci->hcd)->bandwidth_allocated
-= stream->bandwidth;
ehci_vdbg (ehci,
"deschedule devp %s ep%d%s-iso\n",
dev->devpath, stream->bEndpointAddress & 0x0f,
(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
}
iso_stream_put (ehci, stream);
usb_put_dev (dev);
return 1;
}
......@@ -939,23 +1167,50 @@ itd_complete (
static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags)
{
int status;
unsigned long flags;
int status = -EINVAL;
unsigned long flags;
struct ehci_iso_stream *stream;
dbg ("itd_submit urb %p", urb);
/* Get iso_stream head */
stream = iso_stream_find (ehci, urb);
if (unlikely (stream == 0)) {
ehci_dbg (ehci, "can't get iso stream\n");
return -ENOMEM;
}
if (unlikely (urb->interval != stream->interval)) {
ehci_dbg (ehci, "can't change iso interval %d --> %d\n",
stream->interval, urb->interval);
goto done;
}
#ifdef EHCI_URB_TRACE
ehci_dbg (ehci,
"%s %s urb %p ep%d%s len %d, %d pkts %d uframes [%p]\n",
__FUNCTION__, urb->dev->devpath, urb,
usb_pipeendpoint (urb->pipe),
usb_pipein (urb->pipe) ? "in" : "out",
urb->transfer_buffer_length,
urb->number_of_packets, urb->interval,
stream);
#endif
/* allocate ITDs w/o locking anything */
status = itd_urb_transaction (ehci, urb, mem_flags);
if (status < 0)
return status;
status = itd_urb_transaction (stream, ehci, urb, mem_flags);
if (unlikely (status < 0)) {
ehci_dbg (ehci, "can't init itds\n");
goto done;
}
/* schedule ... need to lock */
spin_lock_irqsave (&ehci->lock, flags);
status = itd_schedule (ehci, urb);
status = itd_stream_schedule (ehci, urb, stream);
if (likely (status == 0))
itd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
spin_unlock_irqrestore (&ehci->lock, flags);
if (status < 0)
itd_free_list (ehci, urb);
done:
if (unlikely (status < 0))
iso_stream_put (ehci, stream);
return status;
}
......@@ -986,24 +1241,23 @@ scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs)
* When running, scan from last scan point up to "now"
* else clean up by scanning everything that's left.
* Touches as few pages as possible: cache-friendly.
* Don't scan ISO entries more than once, though.
*/
frame = ehci->next_uframe >> 3;
now_uframe = ehci->next_uframe;
if (HCD_IS_RUNNING (ehci->hcd.state))
now_uframe = readl (&ehci->regs->frame_index);
clock = readl (&ehci->regs->frame_index) % mod;
else
now_uframe = (frame << 3) - 1;
now_uframe %= mod;
clock = now_uframe >> 3;
clock = now_uframe + mod - 1;
for (;;) {
union ehci_shadow q, *q_p;
u32 type, *hw_p;
unsigned uframes;
frame = now_uframe >> 3;
restart:
/* scan schedule to _before_ current frame index */
if (frame == clock)
if ((frame == (clock >> 3))
&& HCD_IS_RUNNING (ehci->hcd.state))
uframes = now_uframe & 0x07;
else
uframes = 8;
......@@ -1043,34 +1297,31 @@ scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs)
case Q_TYPE_ITD:
last = (q.itd->hw_next == EHCI_LIST_END);
/* Unlink each (S)ITD we see, since the ISO
* URB model forces constant rescheduling.
* That complicates sharing uframes in ITDs,
* and means we need to skip uframes the HC
* hasn't yet processed.
*/
for (uf = 0; uf < uframes; uf++) {
if (q.itd->hw_transaction [uf] != 0) {
temp = q;
*q_p = q.itd->itd_next;
*hw_p = q.itd->hw_next;
type = Q_NEXT_TYPE (*hw_p);
/* might free q.itd ... */
count += itd_complete (ehci,
temp.itd, uf, regs);
break;
}
}
/* we might skip this ITD's uframe ... */
if (uf == uframes) {
/* skip itds for later in the frame */
rmb ();
for (uf = uframes; uf < 8; uf++) {
if (0 == (q.itd->hw_transaction [uf]
& ISO_ACTIVE))
continue;
q_p = &q.itd->itd_next;
hw_p = &q.itd->hw_next;
type = Q_NEXT_TYPE (q.itd->hw_next);
q = *q_p;
break;
}
if (uf != 8)
break;
q = *q_p;
break;
/* this one's ready ... HC won't cache the
* pointer for much longer, if at all.
*/
*q_p = q.itd->itd_next;
*hw_p = q.itd->hw_next;
wmb();
/* always rescan here; simpler */
count += itd_complete (ehci, q.itd, regs);
goto restart;
#ifdef have_split_iso
case Q_TYPE_SITD:
last = (q.sitd->hw_next == EHCI_LIST_END);
......@@ -1104,7 +1355,7 @@ scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs)
// FIXME: likewise assumes HC doesn't halt mid-scan
if (frame == clock) {
if (now_uframe == clock) {
unsigned now;
if (!HCD_IS_RUNNING (ehci->hcd.state))
......@@ -1115,9 +1366,13 @@ scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs)
break;
/* rescan the rest of this frame, then ... */
now_uframe = now;
clock = now_uframe >> 3;
} else
frame = (frame + 1) % ehci->periodic_size;
clock = now;
} else {
/* FIXME sometimes we can scan the next frame
* right away, not always inching up on it ...
*/
now_uframe++;
now_uframe %= mod;
}
}
}
......@@ -153,9 +153,12 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
/* Section 2.2 Host Controller Capability Registers */
struct ehci_caps {
u8 length; /* CAPLENGTH - size of this struct */
u8 reserved; /* offset 0x1 */
u16 hci_version; /* HCIVERSION - offset 0x2 */
/* these fields are specified as 8 and 16 bit registers,
* but some hosts can't perform 8 or 16 bit PCI accesses.
*/
u32 hc_capbase;
#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */
#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */
u32 hcs_params; /* HCSPARAMS - offset 0x4 */
#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */
#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */
......@@ -378,11 +381,68 @@ struct ehci_qh {
unsigned short period; /* polling interval */
unsigned short start; /* where polling starts */
#define NO_FRAME ((unsigned short)~0) /* pick new start */
struct usb_device *dev; /* access to TT */
} __attribute__ ((aligned (32)));
/*-------------------------------------------------------------------------*/
/* description of one iso highspeed transaction (up to 3 KB data) */
struct ehci_iso_uframe {
/* These will be copied to iTD when scheduling */
u64 bufp; /* itd->hw_bufp{,_hi}[pg] |= */
u32 transaction; /* itd->hw_transaction[i] |= */
u8 cross; /* buf crosses pages */
};
/* temporary schedule data for highspeed packets from iso urbs
* each packet is one uframe's usb transactions, in some itd,
* beginning at stream->next_uframe
*/
struct ehci_itd_sched {
struct list_head itd_list;
unsigned span;
struct ehci_iso_uframe packet [0];
};
/*
* ehci_iso_stream - groups all (s)itds for this endpoint.
* acts like a qh would, if EHCI had them for ISO.
*/
struct ehci_iso_stream {
/* first two fields match QH, but info1 == 0 */
u32 hw_next;
u32 hw_info1;
u32 refcount;
u8 bEndpointAddress;
struct list_head itd_list; /* queued itds */
struct list_head free_itd_list; /* list of unused itds */
struct hcd_dev *dev;
/* output of (re)scheduling */
unsigned long start; /* jiffies */
unsigned long rescheduled;
int next_uframe;
/* the rest is derived from the endpoint descriptor,
* trusting urb->interval == (1 << (epdesc->bInterval - 1)),
* including the extra info for hw_bufp[0..2]
*/
u8 interval;
u8 usecs;
u16 maxp;
unsigned bandwidth;
/* This is used to initialize iTD's hw_bufp fields */
u32 buf0;
u32 buf1;
u32 buf2;
/* ... sITD won't use buf[012], and needs TT access ... */
};
/*-------------------------------------------------------------------------*/
/*
* EHCI Specification 0.95 Section 3.3
* Fig 3-4 "Isochronous Transaction Descriptor (iTD)"
......@@ -397,9 +457,11 @@ struct ehci_itd {
#define EHCI_ISOC_BUF_ERR (1<<30) /* Data buffer error */
#define EHCI_ISOC_BABBLE (1<<29) /* babble detected */
#define EHCI_ISOC_XACTERR (1<<28) /* XactErr - transaction error */
#define EHCI_ITD_LENGTH(tok) (((tok)>>16) & 0x7fff)
#define EHCI_ITD_LENGTH(tok) (((tok)>>16) & 0x0fff)
#define EHCI_ITD_IOC (1 << 15) /* interrupt on complete */
#define ISO_ACTIVE __constant_cpu_to_le32(EHCI_ISOC_ACTIVE)
u32 hw_bufp [7]; /* see EHCI 3.3.3 */
u32 hw_bufp_hi [7]; /* Appendix B */
......@@ -408,14 +470,14 @@ struct ehci_itd {
union ehci_shadow itd_next; /* ptr to periodic q entry */
struct urb *urb;
struct list_head itd_list; /* list of urb frames' itds */
dma_addr_t buf_dma; /* frame's buffer address */
/* for now, only one hw_transaction per itd */
u32 transaction;
u16 index; /* in urb->iso_frame_desc */
u16 uframe; /* in periodic schedule */
u16 usecs;
struct ehci_iso_stream *stream; /* endpoint's queue */
struct list_head itd_list; /* list of stream's itds */
/* any/all hw_transactions here may be used by that urb */
unsigned frame; /* where scheduled */
unsigned pg;
unsigned index[8]; /* in urb->iso_frame_desc */
u8 usecs[8];
} __attribute__ ((aligned (32)));
/*-------------------------------------------------------------------------*/
......
......@@ -54,7 +54,11 @@
#define UPDATE_PULSE_AWAKE (1<<2)
#define UPDATE_PULSE_MODE (1<<3)
#define POWERMATE_PAYLOAD_SIZE 3
/* at least two versions of the hardware exist, with differing payload
sizes. the first three bytes always contain the "interesting" data in
the relevant format. */
#define POWERMATE_PAYLOAD_SIZE_MAX 6
#define POWERMATE_PAYLOAD_SIZE_MIN 3
struct powermate_device {
signed char *data;
dma_addr_t data_dma;
......@@ -269,7 +273,7 @@ static int powermate_input_event(struct input_dev *dev, unsigned int type, unsig
static int powermate_alloc_buffers(struct usb_device *udev, struct powermate_device *pm)
{
pm->data = usb_buffer_alloc(udev, POWERMATE_PAYLOAD_SIZE,
pm->data = usb_buffer_alloc(udev, POWERMATE_PAYLOAD_SIZE_MAX,
SLAB_ATOMIC, &pm->data_dma);
if (!pm->data)
return -1;
......@@ -284,7 +288,7 @@ static int powermate_alloc_buffers(struct usb_device *udev, struct powermate_dev
static void powermate_free_buffers(struct usb_device *udev, struct powermate_device *pm)
{
if (pm->data)
usb_buffer_free(udev, POWERMATE_PAYLOAD_SIZE,
usb_buffer_free(udev, POWERMATE_PAYLOAD_SIZE_MAX,
pm->data, pm->data_dma);
if (pm->configcr)
usb_buffer_free(udev, sizeof(*(pm->configcr)),
......@@ -347,12 +351,14 @@ static int powermate_probe(struct usb_interface *intf, const struct usb_device_i
pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
if (maxp != POWERMATE_PAYLOAD_SIZE)
printk("powermate: Expected payload of %d bytes, found %d bytes!\n", POWERMATE_PAYLOAD_SIZE, maxp);
if(maxp < POWERMATE_PAYLOAD_SIZE_MIN || maxp > POWERMATE_PAYLOAD_SIZE_MAX){
printk("powermate: Expected payload of %d--%d bytes, found %d bytes!\n",
POWERMATE_PAYLOAD_SIZE_MIN, POWERMATE_PAYLOAD_SIZE_MAX, maxp);
maxp = POWERMATE_PAYLOAD_SIZE_MAX;
}
usb_fill_int_urb(pm->irq, udev, pipe, pm->data,
POWERMATE_PAYLOAD_SIZE, powermate_irq,
maxp, powermate_irq,
pm, endpoint->bInterval);
pm->irq->transfer_dma = pm->data_dma;
pm->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
......
......@@ -182,24 +182,21 @@ config USB_W9968CF
tristate "USB W996[87]CF JPEG Dual Mode Camera support"
depends on USB && VIDEO_DEV && I2C
---help---
Say Y here if you want support for cameras based on
Say Y here if you want support for cameras based on OV681 or
Winbond W9967CF/W9968CF JPEG USB Dual Mode Camera Chips.
This driver has an optional plugin, which is distributed as a
separate module only (released under GPL). It contains code that
allows you to use higher resolutions and framerates, and can't
be included into the official Linux kernel for performance
purposes.
At the moment the driver needs a third-part module for the CMOS
separate module only (released under GPL). It allows to use higher
resolutions and framerates, but cannot be included in the official
Linux kernel for performance purposes.
At the moment the driver needs a third-party module for the CMOS
sensors, which is available on internet: it is recommended to read
<file:Documentation/usb/w9968cf.txt> for more informations and for
a list of supported cameras.
This driver uses the Video For Linux and the I2C APIs.
You must say Y or M to both "Video For Linux" and
"I2C Support" to use this driver.
Information on this API and pointers to "v4l" programs may be found
on the WWW at <http://roadrunner.swansea.uk.linux.org/v4l.shtml>.
This driver uses the Video For Linux and the I2C APIs. You must say
Y or M to both "Video For Linux" and "I2C Support" to use this
driver.
This code is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you want).
......
/***************************************************************************
* Video4Linux driver for W996[87]CF JPEG USB Dual Mode Camera Chip. *
* *
* Copyright (C) 2002 2003 by Luca Risolia <luca_ing@libero.it> *
* Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
* - Memory management code from bttv driver by Ralph Metzler, *
* Marcus Metzler and Gerd Knorr. *
* - I2C interface to kernel, high-level CMOS sensor control routines and *
* some symbolic names from OV511 driver by Mark W. McClelland. *
* - Low-level I2C fast write function by Piotr Czerczak. *
* - Low-level I2C read function by Frédéric Jouault. *
* - Low-level I2C read function by Frederic Jouault. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
......@@ -27,6 +27,7 @@
#include <linux/version.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
......@@ -48,81 +49,86 @@
/****************************************************************************
* Modules paramaters *
* Module macros and paramaters *
****************************************************************************/
static u8 vppmod_load = W9968CF_VPPMOD_LOAD;
static u8 simcams = W9968CF_SIMCAMS;
static int video_nr[]={[0 ... W9968CF_MAX_DEVICES-1] = -1}; /* -1=first free */
static u16 packet_size[] = {[0 ... W9968CF_MAX_DEVICES-1]=W9968CF_PACKET_SIZE};
static u8 max_buffers[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BUFFERS};
static u8 double_buffer[] = {[0 ... W9968CF_MAX_DEVICES-1] =
W9968CF_DOUBLE_BUFFER};
static u8 clamping[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLAMPING};
static u8 filter_type[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_FILTER_TYPE};
static u8 largeview[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_LARGEVIEW};
static u8 decompression[] = {[0 ... W9968CF_MAX_DEVICES-1] =
W9968CF_DECOMPRESSION};
static u8 upscaling[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_UPSCALING};
static u8 force_palette[] = {[0 ... W9968CF_MAX_DEVICES-1] = 0};
static u8 force_rgb[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_FORCE_RGB};
static u8 autobright[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOBRIGHT};
static u8 autoexp[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOEXP};
static u8 lightfreq[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_LIGHTFREQ};
static u8 bandingfilter[] = {[0 ... W9968CF_MAX_DEVICES-1]=
W9968CF_BANDINGFILTER};
static int clockdiv[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLOCKDIV};
static u8 backlight[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BACKLIGHT};
static u8 mirror[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_MIRROR};
static u8 sensor_mono[] = {[0 ... W9968CF_MAX_DEVICES-1]=W9968CF_SENSOR_MONO};
static u16 brightness[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BRIGHTNESS};
static u16 hue[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_HUE};
static u16 colour[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_COLOUR};
static u16 contrast[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CONTRAST};
static u16 whiteness[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_WHITENESS};
MODULE_AUTHOR(W9968CF_MODULE_AUTHOR" "W9968CF_AUTHOR_EMAIL);
MODULE_DESCRIPTION(W9968CF_MODULE_NAME" "W9968CF_MODULE_VERSION);
MODULE_LICENSE(W9968CF_MODULE_LICENSE);
MODULE_SUPPORTED_DEVICE("Video");
static int vppmod_load = W9968CF_VPPMOD_LOAD;
static unsigned short simcams = W9968CF_SIMCAMS;
static short video_nr[]={[0 ... W9968CF_MAX_DEVICES-1] = -1}; /*-1=first free*/
static unsigned int packet_size[] = {[0 ... W9968CF_MAX_DEVICES-1] =
W9968CF_PACKET_SIZE};
static unsigned short max_buffers[] = {[0 ... W9968CF_MAX_DEVICES-1] =
W9968CF_BUFFERS};
static int double_buffer[] = {[0 ... W9968CF_MAX_DEVICES-1] =
W9968CF_DOUBLE_BUFFER};
static int clamping[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLAMPING};
static unsigned short filter_type[]= {[0 ... W9968CF_MAX_DEVICES-1] =
W9968CF_FILTER_TYPE};
static int largeview[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_LARGEVIEW};
static unsigned short decompression[] = {[0 ... W9968CF_MAX_DEVICES-1] =
W9968CF_DECOMPRESSION};
static int upscaling[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_UPSCALING};
static unsigned short force_palette[] = {[0 ... W9968CF_MAX_DEVICES-1] = 0};
static int force_rgb[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_FORCE_RGB};
static int autobright[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOBRIGHT};
static int autoexp[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOEXP};
static unsigned short lightfreq[] = {[0 ... W9968CF_MAX_DEVICES-1] =
W9968CF_LIGHTFREQ};
static int bandingfilter[] = {[0 ... W9968CF_MAX_DEVICES-1]=
W9968CF_BANDINGFILTER};
static short clockdiv[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLOCKDIV};
static int backlight[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BACKLIGHT};
static int mirror[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_MIRROR};
static int monochrome[] = {[0 ... W9968CF_MAX_DEVICES-1]=W9968CF_MONOCHROME};
static unsigned int brightness[] = {[0 ... W9968CF_MAX_DEVICES-1] =
W9968CF_BRIGHTNESS};
static unsigned int hue[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_HUE};
static unsigned int colour[]={[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_COLOUR};
static unsigned int contrast[] = {[0 ... W9968CF_MAX_DEVICES-1] =
W9968CF_CONTRAST};
static unsigned int whiteness[] = {[0 ... W9968CF_MAX_DEVICES-1] =
W9968CF_WHITENESS};
#ifdef W9968CF_DEBUG
static u8 debug = W9968CF_DEBUG_LEVEL;
static u8 specific_debug = W9968CF_SPECIFIC_DEBUG;
static unsigned short debug = W9968CF_DEBUG_LEVEL;
static int specific_debug = W9968CF_SPECIFIC_DEBUG;
#endif
MODULE_AUTHOR("Luca Risolia <luca_ing@libero.it>");
MODULE_DESCRIPTION("Video4Linux driver for "
"W996[87]CF JPEG USB Dual Mode Camera Chip");
MODULE_SUPPORTED_DEVICE("Video");
MODULE_LICENSE("GPL");
MODULE_PARM(vppmod_load, "i");
MODULE_PARM(simcams, "i");
MODULE_PARM(video_nr, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
MODULE_PARM(packet_size, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
MODULE_PARM(max_buffers, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
MODULE_PARM(double_buffer, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
MODULE_PARM(clamping, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
MODULE_PARM(filter_type, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
MODULE_PARM(largeview, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
MODULE_PARM(decompression, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
MODULE_PARM(upscaling, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
MODULE_PARM(force_palette, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
MODULE_PARM(force_rgb, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
MODULE_PARM(autobright, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
MODULE_PARM(autoexp, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
MODULE_PARM(lightfreq, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
MODULE_PARM(bandingfilter, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
MODULE_PARM(clockdiv, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
MODULE_PARM(backlight, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
MODULE_PARM(mirror, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
MODULE_PARM(sensor_mono, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
MODULE_PARM(brightness, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
MODULE_PARM(hue, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
MODULE_PARM(colour, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
MODULE_PARM(contrast, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
MODULE_PARM(whiteness, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
static unsigned int param_nv[23]; /* number of values per paramater */
module_param(vppmod_load, bool, 0444);
module_param(simcams, ushort, 0444);
module_param_array(video_nr, short, param_nv[0], 0444);
module_param_array(packet_size, uint, param_nv[1], 0444);
module_param_array(max_buffers, ushort, param_nv[2], 0444);
module_param_array(double_buffer, bool, param_nv[3], 0444);
module_param_array(clamping, bool, param_nv[4], 0444);
module_param_array(filter_type, ushort, param_nv[5], 0444);
module_param_array(largeview, bool, param_nv[6], 0444);
module_param_array(decompression, ushort, param_nv[7], 0444);
module_param_array(upscaling, bool, param_nv[8], 0444);
module_param_array(force_palette, ushort, param_nv[9], 0444);
module_param_array(force_rgb, ushort, param_nv[10], 0444);
module_param_array(autobright, bool, param_nv[11], 0444);
module_param_array(autoexp, bool, param_nv[12], 0444);
module_param_array(lightfreq, ushort, param_nv[13], 0444);
module_param_array(bandingfilter, bool, param_nv[14], 0444);
module_param_array(clockdiv, short, param_nv[15], 0444);
module_param_array(backlight, bool, param_nv[16], 0444);
module_param_array(mirror, bool, param_nv[17], 0444);
module_param_array(monochrome, bool, param_nv[18], 0444);
module_param_array(brightness, uint, param_nv[19], 0444);
module_param_array(hue, uint, param_nv[20], 0444);
module_param_array(colour, uint, param_nv[21], 0444);
module_param_array(contrast, uint, param_nv[22], 0444);
module_param_array(whiteness, uint, param_nv[23], 0444);
#ifdef W9968CF_DEBUG
MODULE_PARM(debug, "i");
MODULE_PARM(specific_debug, "i");
module_param(debug, ushort, 0444);
module_param(specific_debug, bool, 0444);
#endif
MODULE_PARM_DESC(vppmod_load,
......@@ -146,7 +152,7 @@ MODULE_PARM_DESC(video_nr,
"\n<-1|n[,...]> Specify V4L minor mode number."
"\n -1 = use next available (default)"
"\n n = use minor number n (integer >= 0)"
"\nYou can specify " __MODULE_STRING(W9968CF_MAX_DEVICES)
"\nYou can specify up to "__MODULE_STRING(W9968CF_MAX_DEVICES)
" cameras this way."
"\nFor example:"
"\nvideo_nr=-1,2,-1 would assign minor number 2 to"
......@@ -160,7 +166,7 @@ MODULE_PARM_DESC(packet_size,
"(default is "__MODULE_STRING(W9968CF_PACKET_SIZE)")."
"\n");
MODULE_PARM_DESC(max_buffers,
"\n<n[,...]> Only for advanced users."
"\n<n[,...]> For advanced users."
"\nSpecify the maximum number of video frame buffers"
"\nto allocate for each device, from 2 to "
__MODULE_STRING(W9968CF_MAX_BUFFERS)
......@@ -203,7 +209,8 @@ MODULE_PARM_DESC(upscaling,
"\nDefault value is "__MODULE_STRING(W9968CF_UPSCALING)
" for every device."
"\nIf 'w9968cf-vpp' is not loaded, this paramater is"
" set to 0.");
" set to 0."
"\n");
MODULE_PARM_DESC(decompression,
"\n<0|1|2[,...]> Software video decompression:"
"\n- 0 disables decompression (doesn't allow formats needing"
......@@ -218,7 +225,8 @@ MODULE_PARM_DESC(decompression,
"\nDefault value is "__MODULE_STRING(W9968CF_DECOMPRESSION)
" for every device."
"\nIf 'w9968cf-vpp' is not loaded, forcing decompression is "
"\nnot allowed; in this case this paramater is set to 2.");
"\nnot allowed; in this case this paramater is set to 2."
"\n");
MODULE_PARM_DESC(force_palette,
"\n<0"
"|" __MODULE_STRING(VIDEO_PALETTE_UYVY)
......@@ -252,7 +260,8 @@ MODULE_PARM_DESC(force_palette,
"\nInitial palette is "
__MODULE_STRING(W9968CF_PALETTE_DECOMP_ON)"."
"\nIf 'w9968cf-vpp' is not loaded, this paramater is"
" set to 9 (UYVY).");
" set to 9 (UYVY)."
"\n");
MODULE_PARM_DESC(force_rgb,
"\n<0|1[,...]> Read RGB video data instead of BGR:"
"\n 1 = use RGB component ordering."
......@@ -311,10 +320,11 @@ MODULE_PARM_DESC(mirror,
"\nDefault value is "__MODULE_STRING(W9968CF_MIRROR)
" for every device."
"\n");
MODULE_PARM_DESC(sensor_mono,
"\n<0|1[,...]> The OV CMOS sensor is monochrome:"
MODULE_PARM_DESC(monochrome,
"\n<0|1[,...]> Use OV CMOS sensor as monochrome sensor:"
"\n 0 = no, 1 = yes"
"\nDefault value is "__MODULE_STRING(W9968CF_SENSOR_MONO)
"\nNot all the sensors support monochrome color."
"\nDefault value is "__MODULE_STRING(W9968CF_MONOCHROME)
" for every device."
"\n");
MODULE_PARM_DESC(brightness,
......@@ -346,7 +356,7 @@ MODULE_PARM_DESC(whiteness,
#ifdef W9968CF_DEBUG
MODULE_PARM_DESC(debug,
"\n<n> Debugging information level, from 0 to 6:"
"\n0 = none (be cautious)"
"\n0 = none (use carefully)"
"\n1 = critical errors"
"\n2 = significant informations"
"\n3 = configuration or general messages"
......@@ -380,13 +390,12 @@ static int w9968cf_open(struct inode*, struct file*);
static int w9968cf_release(struct inode*, struct file*);
static ssize_t w9968cf_read(struct file*, char*, size_t, loff_t*);
static int w9968cf_mmap(struct file*, struct vm_area_struct*);
static int w9968cf_ioctl(struct inode*, struct file*,
unsigned int, unsigned long);
static int w9968cf_do_ioctl(struct w9968cf_device*, unsigned int, void*);
static int w9968cf_ioctl(struct inode*, struct file*, unsigned, unsigned long);
static int w9968cf_v4l_ioctl(struct inode*, struct file*, unsigned int, void*);
/* USB-specific */
static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs);
static int w9968cf_start_transfer(struct w9968cf_device*);
static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs);
static int w9968cf_stop_transfer(struct w9968cf_device*);
static int w9968cf_write_reg(struct w9968cf_device*, u16 value, u16 index);
static int w9968cf_read_reg(struct w9968cf_device*, u16 index);
......@@ -402,6 +411,7 @@ static int w9968cf_smbus_write_byte(struct w9968cf_device*, u8 v);
static int w9968cf_smbus_read_byte(struct w9968cf_device*, u8* v);
static int w9968cf_smbus_write_ack(struct w9968cf_device*);
static int w9968cf_smbus_read_ack(struct w9968cf_device*);
static int w9968cf_smbus_refresh_bus(struct w9968cf_device*);
static int w9968cf_i2c_adap_read_byte(struct w9968cf_device* cam,
u16 address, u8* value);
static int w9968cf_i2c_adap_read_byte_data(struct w9968cf_device*, u16 address,
......@@ -436,12 +446,11 @@ static int w9968cf_sensor_set_control(struct w9968cf_device*,int cid,int val);
static int w9968cf_sensor_get_control(struct w9968cf_device*,int cid,int *val);
static inline int w9968cf_sensor_cmd(struct w9968cf_device*,
unsigned int cmd, void *arg);
static void w9968cf_sensor_configure(struct w9968cf_device*);
static int w9968cf_sensor_change_settings(struct w9968cf_device*);
static int w9968cf_sensor_get_picture(struct w9968cf_device*,
struct video_picture*);
static int w9968cf_sensor_set_picture(struct w9968cf_device*,
struct video_picture pict);
static int w9968cf_sensor_init(struct w9968cf_device*);
static int w9968cf_sensor_update_settings(struct w9968cf_device*);
static int w9968cf_sensor_get_picture(struct w9968cf_device*);
static int w9968cf_sensor_update_picture(struct w9968cf_device*,
struct video_picture pict);
/* Other helper functions */
static void w9968cf_configure_camera(struct w9968cf_device*,struct usb_device*,
......@@ -587,8 +596,6 @@ static struct w9968cf_symbolic_list urb_errlist[] = {
* Memory management functions *
****************************************************************************/
/* Shameless copy from bttv-driver.c */
/* Here we want the physical address of the memory.
This is used when initializing the contents of the area. */
static inline unsigned long kvirt_to_pa(unsigned long adr)
......@@ -639,7 +646,6 @@ static void rvfree(void* mem, unsigned long size)
}
vfree(mem);
}
/* End of shameless copy */
/*--------------------------------------------------------------------------
......@@ -820,9 +826,9 @@ static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs)
}
for (i = 0; i < urb->number_of_packets; i++) {
len = urb->iso_frame_desc[i].actual_length;
len = urb->iso_frame_desc[i].actual_length;
status = urb->iso_frame_desc[i].status;
pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer;
pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer;
if (status && len != 0) {
DBG(4, "URB failed, error in data packet "
......@@ -923,7 +929,6 @@ static int w9968cf_start_transfer(struct w9968cf_device* cam)
for (i = 0; i < W9968CF_URBS; i++) {
urb = usb_alloc_urb(W9968CF_ISO_PACKETS, GFP_KERNEL);
cam->urb[i] = urb;
if (!urb) {
for (j = 0; j < i; j++)
usb_free_urb(cam->urb[j]);
......@@ -953,7 +958,7 @@ static int w9968cf_start_transfer(struct w9968cf_device* cam)
t_size = (w*h*d)/16;
err = w9968cf_write_reg(cam, 0xbf17, 0x00); /* reset everything */
err = w9968cf_write_reg(cam, 0xbf17, 0x00); /* reset everything */
err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* normal operation */
/* Transfer size */
......@@ -1026,11 +1031,12 @@ static int w9968cf_stop_transfer(struct w9968cf_device* cam)
spin_unlock_irqrestore(&cam->urb_lock, lock_flags);
for (i = W9968CF_URBS-1; i >= 0; i--)
if (cam->urb[i])
if (cam->urb[i]) {
if (!usb_unlink_urb(cam->urb[i])) {
usb_free_urb(cam->urb[i]);
cam->urb[i] = NULL;
}
}
if (cam->disconnected)
goto exit;
......@@ -1086,8 +1092,7 @@ static int w9968cf_read_reg(struct w9968cf_device* cam, u16 index)
res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 1,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, index, (void*)buff,
2, W9968CF_USB_CTRL_TIMEOUT);
0, index, buff, 2, W9968CF_USB_CTRL_TIMEOUT);
if (res < 0)
DBG(4, "Failed to read a register "
......@@ -1099,7 +1104,7 @@ static int w9968cf_read_reg(struct w9968cf_device* cam, u16 index)
/*--------------------------------------------------------------------------
Write data to the fast serial bus registers.
Write 64-bit data to the fast serial bus registers.
Return 0 on success, -1 otherwise.
--------------------------------------------------------------------------*/
static int w9968cf_write_fsb(struct w9968cf_device* cam, u16* data)
......@@ -1112,8 +1117,7 @@ static int w9968cf_write_fsb(struct w9968cf_device* cam, u16* data)
res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0,
USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
value, 0x06, (void*)data, 6,
W9968CF_USB_CTRL_TIMEOUT);
value, 0x06, data, 6, W9968CF_USB_CTRL_TIMEOUT);
if (res < 0)
DBG(4, "Failed to write the FSB registers "
......@@ -1275,6 +1279,22 @@ static int w9968cf_smbus_read_ack(struct w9968cf_device* cam)
}
/* This seems to refresh the communication through the serial bus */
static int w9968cf_smbus_refresh_bus(struct w9968cf_device* cam)
{
int err = 0, j;
for (j = 1; j <= 10; j++) {
err = w9968cf_write_reg(cam, 0x0020, 0x01);
err += w9968cf_write_reg(cam, 0x0000, 0x01);
if (err)
break;
}
return err;
}
/* SMBus protocol: S Addr Wr [A] Subaddr [A] Value [A] P */
static int
w9968cf_i2c_adap_fastwrite_byte_data(struct w9968cf_device* cam,
......@@ -1283,8 +1303,10 @@ w9968cf_i2c_adap_fastwrite_byte_data(struct w9968cf_device* cam,
u16* data = cam->data_buffer;
int err = 0;
/* Enable SBUS outputs */
err += w9968cf_write_reg(cam, 0x0020, 0x01);
err += w9968cf_smbus_refresh_bus(cam);
/* Enable SBUS outputs */
err += w9968cf_write_sb(cam, 0x0020);
data[0] = 0x082f | ((address & 0x80) ? 0x1500 : 0x0);
data[0] |= (address & 0x40) ? 0x4000 : 0x0;
......@@ -1328,7 +1350,7 @@ w9968cf_i2c_adap_fastwrite_byte_data(struct w9968cf_device* cam,
err += w9968cf_write_fsb(cam, data);
/* Disable SBUS outputs */
err += w9968cf_write_reg(cam, 0x0000, 0x01);
err += w9968cf_write_sb(cam, 0x0000);
if (!err)
DBG(5, "I2C write byte data done, addr.0x%04X, subaddr.0x%02X "
......@@ -1365,7 +1387,7 @@ w9968cf_i2c_adap_read_byte_data(struct w9968cf_device* cam,
err += w9968cf_smbus_read_byte(cam, value);
err += w9968cf_smbus_write_ack(cam);
err += w9968cf_smbus_stop(cam);
/* Serial data disable */
err += w9968cf_write_sb(cam, 0x0000);
......@@ -1434,8 +1456,8 @@ w9968cf_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
int size, union i2c_smbus_data *data)
{
struct w9968cf_device* cam = i2c_get_adapdata(adapter);
u8 i, j;
int rc = 0, err = 0;
u8 i;
int err = 0;
switch (addr) {
case OV6xx0_SID:
......@@ -1451,31 +1473,26 @@ w9968cf_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
addr <<= 1;
if (read_write == I2C_SMBUS_WRITE)
rc = w9968cf_i2c_adap_write_byte(cam, addr, command);
err = w9968cf_i2c_adap_write_byte(cam, addr, command);
else if (read_write == I2C_SMBUS_READ)
rc = w9968cf_i2c_adap_read_byte(cam,addr, &data->byte);
err = w9968cf_i2c_adap_read_byte(cam,addr,&data->byte);
} else if (size == I2C_SMBUS_BYTE_DATA) {
addr <<= 1;
if (read_write == I2C_SMBUS_WRITE)
rc = w9968cf_i2c_adap_fastwrite_byte_data(cam, addr,
err = w9968cf_i2c_adap_fastwrite_byte_data(cam, addr,
command, data->byte);
else if (read_write == I2C_SMBUS_READ) {
for (i = 1; i <= W9968CF_I2C_RW_RETRIES; i++) {
rc = w9968cf_i2c_adap_read_byte_data(cam, addr,
err = w9968cf_i2c_adap_read_byte_data(cam,addr,
command, &data->byte);
if (rc < 0) {
/* Work around: this seems to wake up
the EEPROM from the stall state */
for (j = 0; j <= 10; j++) {
err += w9968cf_write_sb(cam,0x0020);
err += w9968cf_write_sb(cam,0x0000);
if (err)
break;
if (err) {
if (w9968cf_smbus_refresh_bus(cam)) {
err = -EIO;
break;
}
}
else
} else
break;
}
......@@ -1487,11 +1504,7 @@ w9968cf_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
return -EINVAL;
}
/* This works around a bug in the I2C core */
if (rc > 0)
rc = 0;
return rc;
return err;
}
......@@ -1507,41 +1520,22 @@ static int w9968cf_i2c_attach_inform(struct i2c_client* client)
{
struct w9968cf_device* cam = i2c_get_adapdata(client->adapter);
const char* clientname = i2c_clientname(client);
int id = client->driver->id;
int id = client->driver->id, err = 0;
if (id == I2C_DRIVERID_OVCAMCHIP) {
int rc = 0;
cam->sensor_client = client;
rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_INITIALIZE,
&cam->sensor_mono);
if (rc < 0) {
DBG(1, "CMOS sensor initialization failed (rc=%d)",rc);
cam->sensor_client = NULL;
return rc;
}
if (w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_Q_SUBTYPE,
&cam->sensor) < 0)
rc = -EIO;
else if (client->addr==OV7xx0_SID || client->addr==OV6xx0_SID)
w9968cf_sensor_configure(cam);
else
rc = -EINVAL;
if (rc < 0) {
err = w9968cf_sensor_init(cam);
if (err) {
cam->sensor_client = NULL;
cam->sensor = CC_UNKNOWN;
return rc;
return err;
}
} else {
DBG(4, "Rejected client [%s] with [%s]",
} else {
DBG(4, "Rejected client [%s] with driver [%s]",
clientname, client->driver->name)
return -1;
return -EINVAL;
}
DBG(2, "I2C attach client [%s] with [%s]",
DBG(5, "I2C attach client [%s] with driver [%s]",
clientname, client->driver->name)
return 0;
......@@ -1557,7 +1551,7 @@ static int w9968cf_i2c_detach_inform(struct i2c_client* client)
cam->sensor_client = NULL;
}
DBG(2, "I2C detach [%s]", clientname)
DBG(5, "I2C detach client [%s]", clientname)
return 0;
}
......@@ -1573,7 +1567,7 @@ w9968cf_i2c_control(struct i2c_adapter* adapter, unsigned int cmd,
static int w9968cf_i2c_init(struct w9968cf_device* cam)
{
int rc = 0;
int err = 0;
static struct i2c_algorithm algo = {
.name = "W996[87]CF algorithm",
......@@ -1596,15 +1590,15 @@ static int w9968cf_i2c_init(struct w9968cf_device* cam)
strcpy(cam->i2c_adapter.name, "w9968cf");
i2c_set_adapdata(&cam->i2c_adapter, cam);
DBG(6, "Registering I2C bus with kernel...")
DBG(6, "Registering I2C adapter with kernel...")
rc = i2c_add_adapter(&cam->i2c_adapter);
if (rc)
DBG(5, "Failed to register the I2C bus.")
err = i2c_add_adapter(&cam->i2c_adapter);
if (err)
DBG(1, "Failed to register the I2C adapter.")
else
DBG(5, "I2C bus registered.")
DBG(5, "I2C adapter registered.")
return rc;
return err;
}
......@@ -1644,7 +1638,7 @@ static int w9968cf_turn_on_led(struct w9968cf_device* cam)
--------------------------------------------------------------------------*/
static int w9968cf_init_chip(struct w9968cf_device* cam)
{
int err = 0, rc = 0;
int err = 0;
err += w9968cf_write_reg(cam, 0xff00, 0x00); /* power off */
err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* power on */
......@@ -1679,22 +1673,11 @@ static int w9968cf_init_chip(struct w9968cf_device* cam)
err += w9968cf_set_window(cam, cam->window);
if (err)
goto error;
rc = w9968cf_sensor_change_settings(cam);
if (rc)
goto error;
DBG(5, "Chip successfully initialized.");
return 0;
error:
DBG(1, "Chip initialization failed.")
if (err)
return err;
DBG(1, "Chip initialization failed.")
else
return rc;
DBG(5, "Chip successfully initialized.")
return err;
}
......@@ -1706,7 +1689,7 @@ static int
w9968cf_set_picture(struct w9968cf_device* cam, struct video_picture pict)
{
u16 fmt, hw_depth, hw_palette, reg_v = 0x0000;
int err = 0, rc = 0;
int err = 0;
/* Make sure we are using a valid depth */
pict.depth = w9968cf_valid_depth(pict.palette);
......@@ -1767,12 +1750,10 @@ w9968cf_set_picture(struct w9968cf_device* cam, struct video_picture pict)
else if (cam->filter_type == 2)
reg_v |= 0x000c;
err = w9968cf_write_reg(cam, reg_v, 0x16);
if (err)
if ((err = w9968cf_write_reg(cam, reg_v, 0x16)))
goto error;
rc = w9968cf_sensor_set_picture(cam, pict);
if (rc)
if ((err = w9968cf_sensor_update_picture(cam, pict)))
goto error;
/* If all went well, update the device data structure */
......@@ -1791,10 +1772,7 @@ w9968cf_set_picture(struct w9968cf_device* cam, struct video_picture pict)
error:
DBG(1, "Failed to change picture settings.")
if (err)
return err;
else
return rc;
return err;
}
......@@ -1809,7 +1787,7 @@ w9968cf_set_window(struct w9968cf_device* cam, struct video_window win)
u16 x, y, w, h, scx, scy, cw, ch, ax, ay;
unsigned long fw, fh;
struct ovcamchip_window s_win;
int err=0, rc=0;
int err = 0;
/* Work around to avoid FP arithmetics */
#define __SC(x) ((x) << 10)
......@@ -1853,8 +1831,8 @@ w9968cf_set_window(struct w9968cf_device* cam, struct video_window win)
ch = h;
}
/* Setup the sensor window */
s_win.format = SENSOR_FORMAT;
/* Setup the window of the sensor */
s_win.format = VIDEO_PALETTE_UYVY;
s_win.width = cam->maxwidth;
s_win.height = cam->maxheight;
s_win.quarter = 0; /* full progressive video */
......@@ -1883,7 +1861,7 @@ w9968cf_set_window(struct w9968cf_device* cam, struct video_window win)
s_win.clockdiv = W9968CF_DEF_CLOCKDIVISOR;
}
/* We have to scale win.x and win.y offsets */
/* We have to scale win.x and win.y offsets */
if ( (cam->largeview && !(cam->vpp_flag & VPP_UPSCALE))
|| (cam->vpp_flag & VPP_UPSCALE) ) {
ax = __SC(win.x)/fw;
......@@ -1914,7 +1892,7 @@ w9968cf_set_window(struct w9968cf_device* cam, struct video_window win)
y = ay + s_win.y;
/* Go ! */
if ((rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_MODE, &s_win)))
if ((err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_MODE, &s_win)))
goto error;
err += w9968cf_write_reg(cam, scx + x, 0x10);
......@@ -1959,10 +1937,7 @@ w9968cf_set_window(struct w9968cf_device* cam, struct video_window win)
error:
DBG(1, "Failed to change the capture area size.")
if (err)
return err;
else
return rc;
return err;
}
......@@ -2177,173 +2152,192 @@ static int
w9968cf_sensor_set_control(struct w9968cf_device* cam, int cid, int val)
{
struct ovcamchip_control ctl;
int rc;
int err;
ctl.id = cid;
ctl.value = val;
rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_CTRL, &ctl);
err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_CTRL, &ctl);
return rc;
return err;
}
static int
w9968cf_sensor_get_control(struct w9968cf_device* cam, int cid, int *val)
w9968cf_sensor_get_control(struct w9968cf_device* cam, int cid, int* val)
{
struct ovcamchip_control ctl;
int rc;
int err;
ctl.id = cid;
rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_G_CTRL, &ctl);
if (rc >= 0)
err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_G_CTRL, &ctl);
if (!err)
*val = ctl.value;
return rc;
return err;
}
static inline int
w9968cf_sensor_cmd(struct w9968cf_device* cam, unsigned int cmd, void *arg)
w9968cf_sensor_cmd(struct w9968cf_device* cam, unsigned int cmd, void* arg)
{
struct i2c_client* c = cam->sensor_client;
int rc = 0;
DBG(6, "Executing CMOS sensor command...")
if (c && c->driver->command)
return c->driver->command(cam->sensor_client, cmd, arg);
else
if (c->driver->command) {
rc = c->driver->command(cam->sensor_client, cmd, arg);
/* The I2C driver returns -EPERM on non-supported controls */
return (rc < 0 && rc != -EPERM) ? rc : 0;
} else
return -ENODEV;
}
/*--------------------------------------------------------------------------
Change some settings of the CMOS sensor.
Returns: 0 for success, a negative number otherwise.
Update some settings of the CMOS sensor.
Returns: 0 on success, a negative number otherwise.
--------------------------------------------------------------------------*/
static int w9968cf_sensor_change_settings(struct w9968cf_device* cam)
static int w9968cf_sensor_update_settings(struct w9968cf_device* cam)
{
int rc;
int err = 0;
/* Auto brightness */
rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOBRIGHT,
cam->auto_brt);
if (SENSOR_FATAL_ERROR(rc))
return rc;
err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOBRIGHT,
cam->auto_brt);
if (err)
return err;
/* Auto exposure */
rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOEXP,
cam->auto_exp);
if (SENSOR_FATAL_ERROR(rc))
return rc;
err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOEXP,
cam->auto_exp);
if (err)
return err;
/* Banding filter */
rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BANDFILT,
cam->bandfilt);
if (SENSOR_FATAL_ERROR(rc))
return rc;
err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BANDFILT,
cam->bandfilt);
if (err)
return err;
/* Light frequency */
rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_FREQ,
cam->lightfreq);
if (SENSOR_FATAL_ERROR(rc))
return rc;
err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_FREQ,
cam->lightfreq);
if (err)
return err;
/* Back light */
rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BACKLIGHT,
cam->backlight);
if (SENSOR_FATAL_ERROR(rc))
return rc;
err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BACKLIGHT,
cam->backlight);
if (err)
return err;
/* Mirror */
rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_MIRROR,
cam->mirror);
if (SENSOR_FATAL_ERROR(rc))
return rc;
err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_MIRROR,
cam->mirror);
if (err)
return err;
return 0;
}
/*--------------------------------------------------------------------------
Get some current picture settings from the CMOS sensor.
Returns: 0 for success, a negative number otherwise.
Get some current picture settings from the CMOS sensor and update the
internal 'picture' structure of the camera.
Returns: 0 on success, a negative number otherwise.
--------------------------------------------------------------------------*/
static int
w9968cf_sensor_get_picture(struct w9968cf_device* cam,
struct video_picture* pict)
static int w9968cf_sensor_get_picture(struct w9968cf_device* cam)
{
int rc, v;
/* Don't return error if a setting is unsupported, or rest of settings
will not be performed */
rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_CONT, &v);
if (SENSOR_FATAL_ERROR(rc))
return rc;
pict->contrast = v;
int err, v;
rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_BRIGHT, &v);
if (SENSOR_FATAL_ERROR(rc))
return rc;
pict->brightness = v;
err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_CONT, &v);
if (err)
return err;
cam->picture.contrast = v;
rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_SAT, &v);
if (SENSOR_FATAL_ERROR(rc))
return rc;
pict->colour = v;
err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_BRIGHT, &v);
if (err)
return err;
cam->picture.brightness = v;
rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_HUE, &v);
if (SENSOR_FATAL_ERROR(rc))
return rc;
pict->hue = v;
err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_SAT, &v);
if (err)
return err;
cam->picture.colour = v;
pict->whiteness = W9968CF_WHITENESS; /* to do! */
err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_HUE, &v);
if (err)
return err;
cam->picture.hue = v;
DBG(5, "Got picture settings from the CMOS sensor.")
PDBGG("Brightness, contrast, hue, colour, whiteness are "
"%d,%d,%d,%d,%d.", pict->brightness, pict->contrast,
pict->hue, pict->colour, pict->whiteness)
"%d,%d,%d,%d,%d.", cam->picture.brightness,cam->picture.contrast,
cam->picture.hue, cam->picture.colour, cam->picture.whiteness)
return 0;
}
/*--------------------------------------------------------------------------
Change picture settings of the CMOS sensor.
Returns: 0 for success, a negative number otherwise.
Update picture settings of the CMOS sensor.
Returns: 0 on success, a negative number otherwise.
--------------------------------------------------------------------------*/
static int
w9968cf_sensor_set_picture(struct w9968cf_device* cam,
struct video_picture pict)
w9968cf_sensor_update_picture(struct w9968cf_device* cam,
struct video_picture pict)
{
int rc;
rc = w9968cf_sensor_set_control(cam,OVCAMCHIP_CID_CONT, pict.contrast);
if (SENSOR_FATAL_ERROR(rc))
return rc;
int err = 0;
if (!cam->auto_brt) {
rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BRIGHT,
pict.brightness);
if (SENSOR_FATAL_ERROR(rc))
return rc;
if ((!cam->sensor_initialized)
|| pict.contrast != cam->picture.contrast) {
err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_CONT,
pict.contrast);
if (err)
goto fail;
DBG(4, "Contrast changed from %d to %d.",
cam->picture.contrast, pict.contrast)
cam->picture.contrast = pict.contrast;
}
rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_SAT, pict.colour);
if (SENSOR_FATAL_ERROR(rc))
return rc;
if (((!cam->sensor_initialized) ||
pict.brightness != cam->picture.brightness) && (!cam->auto_brt)) {
err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BRIGHT,
pict.brightness);
if (err)
goto fail;
DBG(4, "Brightness changed from %d to %d.",
cam->picture.brightness, pict.brightness)
cam->picture.brightness = pict.brightness;
}
rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_HUE, pict.hue);
if (SENSOR_FATAL_ERROR(rc))
return rc;
if ((!cam->sensor_initialized) || pict.colour != cam->picture.colour) {
err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_SAT,
pict.colour);
if (err)
goto fail;
DBG(4, "Colour changed from %d to %d.",
cam->picture.colour, pict.colour)
cam->picture.colour = pict.colour;
}
PDBGG("Brightness, contrast, hue, colour, whiteness are "
"%d,%d,%d,%d,%d.", pict.brightness, pict.contrast,
pict.hue, pict.colour, pict.whiteness)
if ((!cam->sensor_initialized) || pict.hue != cam->picture.hue) {
err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_HUE,
pict.hue);
if (err)
goto fail;
DBG(4, "Hue changed from %d to %d.",
cam->picture.hue, pict.hue)
cam->picture.hue = pict.hue;
}
return 0;
fail:
DBG(4, "Failed to change sensor picture setting.")
return err;
}
......@@ -2353,12 +2347,22 @@ w9968cf_sensor_set_picture(struct w9968cf_device* cam,
****************************************************************************/
/*--------------------------------------------------------------------------
This function is called when the CMOS sensor is detected.
This function is called when a supported CMOS sensor is detected.
Return 0 if the initialization succeeds, a negative number otherwise.
--------------------------------------------------------------------------*/
static void w9968cf_sensor_configure(struct w9968cf_device* cam)
static int w9968cf_sensor_init(struct w9968cf_device* cam)
{
/* NOTE: Make sure width and height are a multiple of 16 */
int err = 0;
if ((err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_INITIALIZE,
&cam->monochrome)))
goto error;
if ((err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_Q_SUBTYPE,
&cam->sensor)))
goto error;
/* NOTE: Make sure width and height are a multiple of 16 */
switch (cam->sensor_client->addr) {
case OV6xx0_SID:
cam->maxwidth = 352;
......@@ -2372,6 +2376,10 @@ static void w9968cf_sensor_configure(struct w9968cf_device* cam)
cam->minwidth = 64;
cam->minheight = 48;
break;
default:
DBG(1, "Not supported CMOS sensor detected for %s.",
symbolic(camlist, cam->id))
return -EINVAL;
}
/* These values depend on the ones in the ovxxx0.c sources */
......@@ -2390,7 +2398,24 @@ static void w9968cf_sensor_configure(struct w9968cf_device* cam)
cam->hs_polarity = 0;
}
DBG(5, "CMOS sensor %s configured.", symbolic(senlist, cam->sensor))
if ((err = w9968cf_sensor_update_settings(cam)))
goto error;
if ((err = w9968cf_sensor_update_picture(cam, cam->picture)))
goto error;
cam->sensor_initialized = 1;
DBG(2, "%s CMOS sensor initialized.", symbolic(senlist, cam->sensor))
return 0;
error:
cam->sensor_initialized = 0;
cam->sensor = CC_UNKNOWN;
DBG(1, "CMOS sensor initialization failed for %s (/dev/video%d). "
"Try to detach and attach this device again.",
symbolic(camlist, cam->id), cam->v4ldev->minor)
return err;
}
......@@ -2415,13 +2440,7 @@ w9968cf_configure_camera(struct w9968cf_device* cam,
cam->usbdev = udev;
cam->id = mod_id;
cam->sensor = CC_UNKNOWN;
strcpy(cam->v4ldev.name, symbolic(camlist, mod_id));
cam->v4ldev.type = VID_TYPE_CAPTURE | VID_TYPE_SCALES;
cam->v4ldev.hardware = VID_HARDWARE_W9968CF;
cam->v4ldev.fops = &w9968cf_fops;
cam->v4ldev.priv = (void*)cam;
cam->v4ldev.minor = video_nr[dev_nr];
cam->sensor_initialized = 0;
/* Calculate the alternate setting number (from 1 to 16)
according to the 'packet_size' module parameter */
......@@ -2433,66 +2452,66 @@ w9968cf_configure_camera(struct w9968cf_device* cam,
cam->max_buffers = (max_buffers[dev_nr] < 2 ||
max_buffers[dev_nr] > W9968CF_MAX_BUFFERS)
? W9968CF_BUFFERS : max_buffers[dev_nr];
? W9968CF_BUFFERS : (u8)max_buffers[dev_nr];
cam->double_buffer = (double_buffer[dev_nr] == 0 ||
double_buffer[dev_nr] == 1)
? double_buffer[dev_nr] : W9968CF_DOUBLE_BUFFER;
? (u8)double_buffer[dev_nr]:W9968CF_DOUBLE_BUFFER;
cam->clamping = (clamping[dev_nr] == 0 || clamping[dev_nr] == 1)
? clamping[dev_nr] : W9968CF_CLAMPING;
? (u8)clamping[dev_nr] : W9968CF_CLAMPING;
cam->filter_type = (filter_type[dev_nr] == 0 ||
filter_type[dev_nr] == 1 ||
filter_type[dev_nr] == 2)
? filter_type[dev_nr] : W9968CF_FILTER_TYPE;
? (u8)filter_type[dev_nr] : W9968CF_FILTER_TYPE;
cam->capture = 1;
cam->largeview = (largeview[dev_nr] == 0 || largeview[dev_nr] == 1)
? largeview[dev_nr] : W9968CF_LARGEVIEW;
? (u8)largeview[dev_nr] : W9968CF_LARGEVIEW;
cam->decompression = (decompression[dev_nr] == 0 ||
decompression[dev_nr] == 1 ||
decompression[dev_nr] == 2)
? decompression[dev_nr] : W9968CF_DECOMPRESSION;
? (u8)decompression[dev_nr]:W9968CF_DECOMPRESSION;
cam->upscaling = (upscaling[dev_nr] == 0 ||
upscaling[dev_nr] == 1)
? upscaling[dev_nr] : W9968CF_UPSCALING;
? (u8)upscaling[dev_nr] : W9968CF_UPSCALING;
cam->auto_brt = (autobright[dev_nr] == 0 || autobright[dev_nr] == 1)
? autobright[dev_nr] : W9968CF_AUTOBRIGHT;
? (u8)autobright[dev_nr] : W9968CF_AUTOBRIGHT;
cam->auto_exp = (autoexp[dev_nr] == 0 || autoexp[dev_nr] == 1)
? autoexp[dev_nr] : W9968CF_AUTOEXP;
? (u8)autoexp[dev_nr] : W9968CF_AUTOEXP;
cam->lightfreq = (lightfreq[dev_nr] == 50 || lightfreq[dev_nr] == 60)
? lightfreq[dev_nr] : W9968CF_LIGHTFREQ;
? (u8)lightfreq[dev_nr] : W9968CF_LIGHTFREQ;
cam->bandfilt = (bandingfilter[dev_nr] == 0 ||
bandingfilter[dev_nr] == 1)
? bandingfilter[dev_nr] : W9968CF_BANDINGFILTER;
? (u8)bandingfilter[dev_nr] : W9968CF_BANDINGFILTER;
cam->backlight = (backlight[dev_nr] == 0 || backlight[dev_nr] == 1)
? backlight[dev_nr] : W9968CF_BACKLIGHT;
? (u8)backlight[dev_nr] : W9968CF_BACKLIGHT;
cam->clockdiv = (clockdiv[dev_nr] == -1 || clockdiv[dev_nr] >= 0)
? clockdiv[dev_nr] : W9968CF_CLOCKDIV;
? (s8)clockdiv[dev_nr] : W9968CF_CLOCKDIV;
cam->mirror = (mirror[dev_nr] == 0 || mirror[dev_nr] == 1)
? mirror[dev_nr] : W9968CF_MIRROR;
cam->sensor_mono = (sensor_mono[dev_nr]==0 || sensor_mono[dev_nr]==1)
? sensor_mono[dev_nr] : W9968CF_SENSOR_MONO;
cam->picture.brightness = brightness[dev_nr];
cam->picture.hue = hue[dev_nr];
cam->picture.colour = colour[dev_nr];
cam->picture.contrast = contrast[dev_nr];
cam->picture.whiteness = whiteness[dev_nr];
if (w9968cf_valid_palette(force_palette[dev_nr])) {
cam->picture.palette = force_palette[dev_nr];
? (u8)mirror[dev_nr] : W9968CF_MIRROR;
cam->monochrome = (monochrome[dev_nr] == 0 || monochrome[dev_nr] == 1)
? monochrome[dev_nr] : W9968CF_MONOCHROME;
cam->picture.brightness = (u16)brightness[dev_nr];
cam->picture.hue = (u16)hue[dev_nr];
cam->picture.colour = (u16)colour[dev_nr];
cam->picture.contrast = (u16)contrast[dev_nr];
cam->picture.whiteness = (u16)whiteness[dev_nr];
if (w9968cf_valid_palette((u16)force_palette[dev_nr])) {
cam->picture.palette = (u16)force_palette[dev_nr];
cam->force_palette = 1;
} else {
cam->force_palette = 0;
......@@ -2505,7 +2524,7 @@ w9968cf_configure_camera(struct w9968cf_device* cam,
}
cam->force_rgb = (force_rgb[dev_nr] == 0 || force_rgb[dev_nr] == 1)
? force_rgb[dev_nr] : W9968CF_FORCE_RGB;
? (u8)force_rgb[dev_nr] : W9968CF_FORCE_RGB;
cam->window.x = 0;
cam->window.y = 0;
......@@ -2610,7 +2629,7 @@ w9968cf_configure_camera(struct w9968cf_device* cam,
else
DBG(3, "- Clock divisor: %d", cam->clockdiv)
if (cam->sensor_mono)
if (cam->monochrome)
DBG(3, "- CMOS sensor used as monochrome.")
else
DBG(3, "- CMOS sensor not used as monochrome.")
......@@ -2626,9 +2645,9 @@ static void w9968cf_release_resources(struct w9968cf_device* cam)
{
down(&w9968cf_devlist_sem);
DBG(2, "V4L device deregistered: /dev/video%d", cam->v4ldev.minor)
DBG(2, "V4L device deregistered: /dev/video%d", cam->v4ldev->minor)
video_unregister_device(&cam->v4ldev);
video_unregister_device(cam->v4ldev);
list_del(&cam->v4llist);
i2c_del_adapter(&cam->i2c_adapter);
w9968cf_deallocate_memory(cam);
......@@ -2648,24 +2667,25 @@ static void w9968cf_release_resources(struct w9968cf_device* cam)
static int w9968cf_open(struct inode* inode, struct file* filp)
{
struct w9968cf_device* cam =
(struct w9968cf_device*)video_devdata(filp)->priv;
struct w9968cf_device* cam;
int err;
cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp));
down(&cam->dev_sem);
if (cam->sensor == CC_UNKNOWN) {
DBG(2, "No supported CMOS sensor has been detected by the "
"'ovcamchip' module for the %s (/dev/video%d). Make "
"sure it is loaded *before* the 'w9968cf' module.",
symbolic(camlist, cam->id),cam->v4ldev.minor)
symbolic(camlist, cam->id), cam->v4ldev->minor)
up(&cam->dev_sem);
return -ENODEV;
}
if (cam->users) {
DBG(2, "%s (/dev/video%d) has been already occupied by '%s'.",
symbolic(camlist, cam->id),cam->v4ldev.minor, cam->command)
symbolic(camlist, cam->id),cam->v4ldev->minor,cam->command)
if ((filp->f_flags & O_NONBLOCK)||(filp->f_flags & O_NDELAY)) {
up(&cam->dev_sem);
return -EWOULDBLOCK;
......@@ -2680,8 +2700,8 @@ static int w9968cf_open(struct inode* inode, struct file* filp)
down(&cam->dev_sem);
}
DBG(5, "Opening the %s, /dev/video%d ...",
symbolic(camlist, cam->id), cam->v4ldev.minor)
DBG(5, "Opening '%s', /dev/video%d ...",
symbolic(camlist, cam->id), cam->v4ldev->minor)
cam->streaming = 0;
cam->misconfigured = 0;
......@@ -2698,7 +2718,7 @@ static int w9968cf_open(struct inode* inode, struct file* filp)
if ((err = w9968cf_start_transfer(cam)))
goto deallocate_memory;
filp->private_data = (void*)cam;
filp->private_data = cam;
cam->users++;
strcpy(cam->command, current->comm);
......@@ -2720,8 +2740,9 @@ static int w9968cf_open(struct inode* inode, struct file* filp)
static int w9968cf_release(struct inode* inode, struct file* filp)
{
struct w9968cf_device* cam =
(struct w9968cf_device*)video_devdata(filp)->priv;
struct w9968cf_device* cam;
cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp));
down(&cam->dev_sem); /* prevent disconnect() to be called */
......@@ -2749,11 +2770,12 @@ static int w9968cf_release(struct inode* inode, struct file* filp)
static ssize_t
w9968cf_read(struct file* filp, char* buf, size_t count, loff_t* f_pos)
{
struct w9968cf_device* cam =
(struct w9968cf_device*)video_devdata(filp)->priv;
struct w9968cf_device* cam;
struct w9968cf_frame_t* fr;
int err = 0;
cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp));
if (filp->f_flags & O_NONBLOCK)
return -EWOULDBLOCK;
......@@ -2817,9 +2839,8 @@ w9968cf_read(struct file* filp, char* buf, size_t count, loff_t* f_pos)
static int w9968cf_mmap(struct file* filp, struct vm_area_struct *vma)
{
struct w9968cf_device* cam =
(struct w9968cf_device*)video_devdata(filp)->priv;
struct w9968cf_device* cam = (struct w9968cf_device*)
video_get_drvdata(video_devdata(filp));
unsigned long vsize = vma->vm_end - vma->vm_start,
psize = cam->nbuffers * w9968cf_get_max_bufsize(cam),
start = vma->vm_start,
......@@ -2860,10 +2881,11 @@ static int
w9968cf_ioctl(struct inode* inode, struct file* filp,
unsigned int cmd, unsigned long arg)
{
struct w9968cf_device* cam =
(struct w9968cf_device*)video_devdata(filp)->priv;
struct w9968cf_device* cam;
int err;
cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp));
if (down_interruptible(&cam->fileop_sem))
return -ERESTARTSYS;
......@@ -2879,7 +2901,7 @@ w9968cf_ioctl(struct inode* inode, struct file* filp,
return -EIO;
}
err = w9968cf_do_ioctl(cam, cmd, (void*)arg);
err = w9968cf_v4l_ioctl(inode, filp, cmd, (void* )arg);
up(&cam->fileop_sem);
return err;
......@@ -2887,8 +2909,10 @@ w9968cf_ioctl(struct inode* inode, struct file* filp,
static int
w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg)
w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
unsigned int cmd, void* arg)
{
struct w9968cf_device* cam;
const char* v4l1_ioctls[] = {
"?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER",
"GPICT", "SPICT", "CCAPTURE", "GWIN", "SWIN", "GFBUF",
......@@ -2900,7 +2924,9 @@ w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg)
#define V4L1_IOCTL(cmd) \
((_IOC_NR((cmd)) < sizeof(v4l1_ioctls)/sizeof(char*)) ? \
v4l1_ioctls[_IOC_NR((cmd))] : "???")
v4l1_ioctls[_IOC_NR((cmd))] : "?")
cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp));
switch (cmd) {
......@@ -2914,7 +2940,7 @@ w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg)
.minheight = cam->minheight,
};
sprintf(cap.name, "W996[87]CF USB Camera #%d",
cam->v4ldev.minor);
cam->v4ldev->minor);
cap.maxwidth = (cam->upscaling && w9968cf_vppmod_present)
? W9968CF_MAX_WIDTH : cam->maxwidth;
cap.maxheight = (cam->upscaling && w9968cf_vppmod_present)
......@@ -2965,15 +2991,10 @@ w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg)
case VIDIOCGPICT: /* get image properties of the picture */
{
struct video_picture pict;
if (w9968cf_sensor_get_picture(cam, &pict))
if (w9968cf_sensor_get_picture(cam))
return -EIO;
pict.depth = cam->picture.depth;
pict.palette = cam->picture.palette;
if (copy_to_user(arg, &pict, sizeof(pict)))
if (copy_to_user(arg, &cam->picture, sizeof(cam->picture)))
return -EFAULT;
DBG(5, "VIDIOCGPICT successfully called.")
......@@ -3002,13 +3023,6 @@ w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg)
return -EINVAL;
}
if (pict.depth != w9968cf_valid_depth(pict.palette)) {
DBG(4, "Depth %d bpp is not supported for %s palette. "
"VIDIOCSPICT failed.",
pict.depth, symbolic(v4l1_plist, pict.palette))
return -EINVAL;
}
if (!cam->force_palette) {
if (cam->decompression == 0) {
if (w9968cf_need_decompression(pict.palette)) {
......@@ -3027,9 +3041,15 @@ w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg)
}
}
if (pict.palette != cam->picture.palette ||
pict.depth != cam->picture.depth)
{
if (pict.depth != w9968cf_valid_depth(pict.palette)) {
DBG(4, "Requested depth %d bpp is not valid for %s "
"palette: ignored and changed to %d bpp.",
pict.depth, symbolic(v4l1_plist, pict.palette),
w9968cf_valid_depth(pict.palette))
pict.depth = w9968cf_valid_depth(pict.palette);
}
if (pict.palette != cam->picture.palette) {
if(*cam->requested_frame
|| cam->frame_current->queued) {
err = wait_event_interruptible
......@@ -3052,15 +3072,9 @@ w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg)
if (w9968cf_start_transfer(cam))
goto ioctl_fail;
} else if ( ((pict.brightness != cam->picture.brightness) &&
(!cam->auto_brt)) ||
pict.hue != cam->picture.hue ||
pict.colour != cam->picture.colour ||
pict.contrast != cam->picture.contrast ||
pict.whiteness != cam->picture.whiteness ) {
if (w9968cf_sensor_set_picture(cam, pict))
return -EIO;
}
} else if (w9968cf_sensor_update_picture(cam, pict))
return -EIO;
DBG(5, "VIDIOCSPICT successfully called.")
return 0;
......@@ -3092,7 +3106,6 @@ w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg)
win.y != cam->window.y ||
win.width != cam->window.width ||
win.height != cam->window.height) {
if(*cam->requested_frame
|| cam->frame_current->queued) {
err = wait_event_interruptible
......@@ -3203,12 +3216,12 @@ w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg)
}
}
if (w9968cf_adjust_window_size(cam, (u16*)&mmap.width,
(u16*)&mmap.height)) {
if ((err = w9968cf_adjust_window_size(cam, (u16*)&mmap.width,
(u16*)&mmap.height))) {
DBG(4, "Resolution not supported (%dx%d). "
"VIDIOCMCAPTURE failed.",
mmap.width, mmap.height)
return -EINVAL;
return err;
}
fr = &cam->frame[mmap.frame];
......@@ -3276,10 +3289,13 @@ w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg)
case VIDIOCSYNC: /* wait until the capture of a frame is finished */
{
unsigned int f_num = *((unsigned int *) arg);
unsigned int f_num;
struct w9968cf_frame_t* fr;
int err = 0;
if (copy_from_user(&f_num, arg, sizeof(f_num)))
return -EFAULT;
if (f_num >= cam->nbuffers) {
DBG(4, "Invalid frame number (%d). "
"VIDIOCMCAPTURE failed.", f_num)
......@@ -3323,7 +3339,7 @@ w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg)
case VIDIOCGUNIT:/* report the unit numbers of the associated devices*/
{
struct video_unit unit = {
.video = cam->v4ldev.minor,
.video = cam->v4ldev->minor,
.vbi = VIDEO_NO_UNIT,
.radio = VIDEO_NO_UNIT,
.audio = VIDEO_NO_UNIT,
......@@ -3342,9 +3358,8 @@ w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg)
case VIDIOCGFBUF:
{
struct video_buffer* buffer = (struct video_buffer*)arg;
memset(buffer, 0, sizeof(struct video_buffer));
if (clear_user(arg, sizeof(struct video_buffer)))
return -EFAULT;
DBG(5, "VIDIOCGFBUF successfully called.")
return 0;
......@@ -3470,10 +3485,6 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
else
return -ENODEV;
/* We don't handle multi-config cameras */
if (udev->descriptor.bNumConfigurations != 1)
return -ENODEV;
DBG(2, "%s detected.", symbolic(camlist, mod_id))
if (simcams > W9968CF_MAX_DEVICES)
......@@ -3491,14 +3502,6 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
return -EPERM;
}
err = usb_set_configuration(udev, 1);
err += usb_set_interface(udev, 0, 0);
if (err) {
DBG(1, "Device configuration failed.")
return -EIO;
}
cam = (struct w9968cf_device*)
kmalloc(sizeof(struct w9968cf_device), GFP_KERNEL);
......@@ -3530,10 +3533,24 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
}
memset(cam->data_buffer, 0, 8);
/* Set some basic constants */
w9968cf_configure_camera(cam, udev, mod_id, dev_nr);
/* Register the V4L device */
cam->v4ldev = video_device_alloc();
if (!cam->v4ldev) {
DBG(1, "Could not allocate memory for a V4L structure.")
err = -ENOMEM;
goto fail;
}
err = video_register_device(&cam->v4ldev, VFL_TYPE_GRABBER,
strcpy(cam->v4ldev->name, symbolic(camlist, mod_id));
cam->v4ldev->owner = THIS_MODULE;
cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES;
cam->v4ldev->hardware = VID_HARDWARE_W9968CF;
cam->v4ldev->fops = &w9968cf_fops;
cam->v4ldev->minor = video_nr[dev_nr];
cam->v4ldev->release = video_device_release;
video_set_drvdata(cam->v4ldev, cam);
err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER,
video_nr[dev_nr]);
if (err) {
DBG(1, "V4L device registration failed.")
......@@ -3544,13 +3561,15 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
goto fail;
}
DBG(2, "V4L device registered as /dev/video%d", cam->v4ldev.minor)
DBG(2, "V4L device registered as /dev/video%d.", cam->v4ldev->minor)
/* Ok, add a new entry into the list of V4L registered devices */
/* Set some basic constants */
w9968cf_configure_camera(cam, udev, mod_id, dev_nr);
/* Add a new entry into the list of V4L registered devices */
down(&w9968cf_devlist_sem);
list_add(&cam->v4llist, &w9968cf_dev_list);
up(&w9968cf_devlist_sem);
dev_nr = (dev_nr < W9968CF_MAX_DEVICES-1) ? dev_nr+1 : 0;
w9968cf_turn_on_led(cam);
......@@ -3559,8 +3578,7 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
up(&cam->dev_sem);
dev_set_drvdata(&intf->dev, (void*)cam);
usb_set_intfdata(intf, cam);
return 0;
fail: /* Free unused memory */
......@@ -3569,6 +3587,8 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
kfree(cam->control_buffer);
if (cam->data_buffer)
kfree(cam->data_buffer);
if (cam->v4ldev)
video_device_release(cam->v4ldev);
up(&cam->dev_sem);
kfree(cam);
}
......@@ -3579,9 +3599,7 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
static void w9968cf_usb_disconnect(struct usb_interface* intf)
{
struct w9968cf_device* cam =
(struct w9968cf_device*)dev_get_drvdata(&intf->dev);
dev_set_drvdata(&intf->dev, NULL);
(struct w9968cf_device*)usb_get_intfdata(intf);
if (cam) {
/* Prevent concurrent accesses to data */
......@@ -3599,7 +3617,7 @@ static void w9968cf_usb_disconnect(struct usb_interface* intf)
DBG(2, "The device is open (/dev/video%d)! "
"Process name: %s. Deregistration and memory "
"deallocation are deferred on close.",
cam->v4ldev.minor, cam->command)
cam->v4ldev->minor, cam->command)
cam->misconfigured = 1;
......@@ -3613,6 +3631,8 @@ static void w9968cf_usb_disconnect(struct usb_interface* intf)
if (!cam->users)
kfree(cam);
}
usb_set_intfdata(intf, NULL);
}
......
/***************************************************************************
* Video4Linux driver for W996[87]CF JPEG USB Dual Mode Camera Chip. *
* *
* Copyright (C) 2002 2003 by Luca Risolia <luca_ing@libero.it> *
* Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
......@@ -28,6 +28,7 @@
#include <linux/list.h>
#include <linux/wait.h>
#include <linux/config.h>
#include <linux/param.h>
#include <asm/semaphore.h>
#include <asm/types.h>
......@@ -105,7 +106,7 @@ static const struct w9968cf_format w9968cf_formatlist[] = {
#define W9968CF_LARGEVIEW 1 /* 0 disable, 1 enable */
#define W9968CF_UPSCALING 0 /* 0 disable, 1 enable */
#define W9968CF_SENSOR_MONO 0 /* 0 not monochrome, 1 monochrome sensor */
#define W9968CF_MONOCHROME 0 /* 0 not monochrome, 1 monochrome sensor */
#define W9968CF_BRIGHTNESS 31000 /* from 0 to 65535 */
#define W9968CF_HUE 32768 /* from 0 to 65535 */
#define W9968CF_COLOUR 32768 /* from 0 to 65535 */
......@@ -129,9 +130,10 @@ static const struct w9968cf_format w9968cf_formatlist[] = {
#define W9968CF_MODULE_NAME "V4L driver for W996[87]CF JPEG USB " \
"Dual Mode Camera Chip"
#define W9968CF_MODULE_VERSION "v1.22"
#define W9968CF_MODULE_AUTHOR "(C) 2002 2003 Luca Risolia"
#define W9968CF_AUTHOR_EMAIL "<luca_ing@libero.it>"
#define W9968CF_MODULE_VERSION "v1.25-basic"
#define W9968CF_MODULE_AUTHOR "(C) 2002-2004 Luca Risolia"
#define W9968CF_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
#define W9968CF_MODULE_LICENSE "GPL"
static u8 w9968cf_vppmod_present; /* status flag: yes=1, no=0 */
......@@ -171,6 +173,7 @@ enum w9968cf_frame_status {
};
struct w9968cf_frame_t {
#define W9968CF_HW_BUF_SIZE 640*480*2 /* buf.size of original frames */
void* buffer;
u32 length;
enum w9968cf_frame_status status;
......@@ -194,8 +197,8 @@ struct semaphore w9968cf_devlist_sem; /* semaphore for list traversal */
struct w9968cf_device {
enum w9968cf_model_id id; /* private device identifier */
struct video_device v4ldev; /* V4L structure */
struct list_head v4llist; /* entry of the list of V4L cameras */
struct video_device* v4ldev; /* -> V4L structure */
struct list_head v4llist; /* entry of the list of V4L cameras */
struct usb_device* usbdev; /* -> main USB structure */
struct urb* urb[W9968CF_URBS]; /* -> USB request block structs */
......@@ -207,9 +210,9 @@ struct w9968cf_device {
struct w9968cf_frame_t frame_tmp; /* temporary frame */
struct w9968cf_frame_t* frame_current; /* -> frame being grabbed */
struct w9968cf_frame_t* requested_frame[W9968CF_MAX_BUFFERS];
void* vpp_buffer; /* -> helper buffer for post-processing routines */
void* vpp_buffer; /*-> helper buf.for video post-processing routines */
u8 max_buffers, /* number of requested buffers */
u8 max_buffers, /* number of requested buffers */
force_palette, /* yes=1/no=0 */
force_rgb, /* read RGB instead of BGR, yes=1, no=0 */
double_buffer, /* hardware double buffering yes=1/no=0 */
......@@ -220,15 +223,17 @@ struct w9968cf_device {
decompression, /* 0=disabled, 1=forced, 2=allowed */
upscaling; /* software image scaling, 0=enabled, 1=disabled */
struct video_picture picture; /* current window settings */
struct video_window window; /* current picture settings */
struct video_picture picture; /* current picture settings */
struct video_window window; /* current window settings */
u16 hw_depth, /* depth (used by the chip) */
hw_palette, /* palette (used by the chip) */
hw_width, /* width (used by the chip) */
hw_height, /* height (used by the chip) */
hs_polarity, /* 0=negative sync pulse, 1=positive sync pulse */
vs_polarity; /* 0=negative sync pulse, 1=positive sync pulse */
vs_polarity, /* 0=negative sync pulse, 1=positive sync pulse */
start_cropx, /* pixels from HS inactive edge to 1st cropped pixel*/
start_cropy; /* pixels from VS incative edge to 1st cropped pixel*/
enum w9968cf_vpp_flag vpp_flag; /* post-processing routines in use */
......@@ -239,16 +244,15 @@ struct w9968cf_device {
users, /* flag: number of users holding the device */
streaming; /* flag: yes=1, no=0 */
int sensor; /* type of image CMOS sensor chip (CC_*) */
/* Determined by CMOS sensor type */
u16 maxwidth,
maxheight,
minwidth,
minheight,
start_cropx,
start_cropy;
u8 sensor_initialized; /* flag: yes=1, no=0 */
/* Determined by CMOS sensor type: */
int sensor, /* type of image sensor chip (CC_*) */
monochrome; /* CMOS sensor is (probably) monochrome */
u16 maxwidth, /* maximum width supported by the CMOS sensor */
maxheight, /* maximum height supported by the CMOS sensor */
minwidth, /* minimum width supported by the CMOS sensor */
minheight; /* minimum height supported by the CMOS sensor */
u8 auto_brt, /* auto brightness enabled flag */
auto_exp, /* auto exposure enabled flag */
backlight, /* backlight exposure algorithm flag */
......@@ -256,7 +260,6 @@ struct w9968cf_device {
lightfreq, /* power (lighting) frequency */
bandfilt; /* banding filter enabled flag */
s8 clockdiv; /* clock divisor */
int sensor_mono; /* CMOS sensor is (probably) monochrome */
/* I2C interface to kernel */
struct i2c_adapter i2c_adapter;
......@@ -271,14 +274,9 @@ struct w9968cf_device {
wait_queue_head_t open, wait_queue;
};
#define W9968CF_HW_BUF_SIZE 640*480*2 /* buf. size for original video frames */
#define SENSOR_FORMAT VIDEO_PALETTE_UYVY
#define SENSOR_FATAL_ERROR(rc) ((rc) < 0 && (rc) != -EPERM)
/****************************************************************************
* Macros and other constants *
* Macros for debugging *
****************************************************************************/
#undef DBG
......@@ -294,7 +292,7 @@ if ( ((specific_debug) && (debug == (level))) || \
else if ((level) == 4) \
warn(fmt, ## args); \
else if ((level) >= 5) \
info("[%s,%d] " fmt, \
info("[%s:%d] " fmt, \
__PRETTY_FUNCTION__, __LINE__ , ## args); \
} \
}
......@@ -305,7 +303,7 @@ if ( ((specific_debug) && (debug == (level))) || \
#undef PDBG
#undef PDBGG
#define PDBG(fmt, args...) info("[%s, %d] "fmt, \
#define PDBG(fmt, args...) info("[%s:%d] "fmt, \
__PRETTY_FUNCTION__, __LINE__ , ## args);
#define PDBGG(fmt, args...) do {;} while(0); /* nothing: it's a placeholder */
......
/***************************************************************************
* Video decoder for the W996[87]CF driver for Linux. *
* *
* Copyright (C) 2003 by Luca Risolia <luca_ing@libero.it> *
* Copyright (C) 2003 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
......
/***************************************************************************
* Various definitions for compatibility with external modules. *
* Various definitions for compatibility with OVCAMCHIP external module. *
* This file is part of the W996[87]CF driver for Linux. *
* *
* Copyright (C) 2002 2003 by Luca Risolia <luca_ing@libero.it> *
* The definitions have been taken from the OVCAMCHIP module written by *
* Mark McClelland. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
......@@ -27,10 +28,8 @@
#include <asm/ioctl.h>
#include <asm/types.h>
/* The following values have been copied from the "ovcamchip" module. */
#ifndef I2C_DRIVERID_OVCAMCHIP
# define I2C_DRIVERID_OVCAMCHIP 0xf00f
# define I2C_DRIVERID_OVCAMCHIP 0xf00f
#endif
/* Controls */
......@@ -77,10 +76,10 @@ struct ovcamchip_window {
int width;
int height;
int format;
int quarter; /* Scale width and height down 2x */
int quarter; /* Scale width and height down 2x */
/* This stuff will be removed eventually */
int clockdiv; /* Clock divisor setting */
int clockdiv; /* Clock divisor setting */
};
/* Commands.
......
......@@ -4,9 +4,22 @@
comment "USB Miscellaneous drivers"
depends on USB
config USB_EMI62
tristate "EMI 6|2m USB Audio interface support"
---help---
This driver loads firmware to Emagic EMI 6|2m low latency USB
Audio and Midi interface.
After firmware load the device is handled with standard linux
USB Audio driver.
This code is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you want).
The module will be called audio. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt>.
config USB_EMI26
tristate "EMI 2|6 USB Audio interface support"
depends on USB_AUDIO
---help---
This driver loads firmware to Emagic EMI 2|6 low latency USB
Audio interface.
......@@ -93,6 +106,16 @@ config USB_LCD
To compile this driver as a module, choose M here: the
module will be called usblcd.
config USB_LED
tristate "USB LED driver support"
depends on USB
help
Say Y here if you want to connect an USBLED device to your
computer's USB port.
To compile this driver as a module, choose M here: the
module will be called usbled.
config USB_SPEEDTOUCH
tristate "Alcatel Speedtouch USB support"
depends on USB && ATM
......
......@@ -5,11 +5,13 @@
obj-$(CONFIG_USB_AUERSWALD) += auerswald.o
obj-$(CONFIG_USB_BRLVGER) += brlvger.o
obj-$(CONFIG_USB_EMI62) += emi62.o
obj-$(CONFIG_USB_EMI26) += emi26.o
obj-$(CONFIG_USB_LCD) += usblcd.o
obj-$(CONFIG_USB_LED) += usbled.o
obj-$(CONFIG_USB_LEGOTOWER) += legousbtower.o
obj-$(CONFIG_USB_RIO500) += rio500.o
obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o
obj-$(CONFIG_USB_TEST) += usbtest.o
obj-$(CONFIG_USB_TIGL) += tiglusb.o
obj-$(CONFIG_USB_USS720) += uss720.o
obj-$(CONFIG_USB_LEGOTOWER) += legousbtower.o
/*
* Emagic EMI 2|6 usb audio interface firmware loader.
* Copyright (C) 2002
* Tapio Laxstrm (tapio.laxstrom@iptime.fi)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, as published by
* the Free Software Foundation, version 2.
*
* $Id: emi62.c,v 1.15 2002/04/23 06:13:59 tapio Exp $
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/usb.h>
#define MAX_INTEL_HEX_RECORD_LENGTH 16
typedef struct _INTEL_HEX_RECORD
{
__u32 length;
__u32 address;
__u32 type;
__u8 data[MAX_INTEL_HEX_RECORD_LENGTH];
} INTEL_HEX_RECORD, *PINTEL_HEX_RECORD;
/* include firmware (variables)*/
/* FIXME: This is quick and dirty solution! */
#define SPDIF /* if you want SPDIF comment next line */
//#undef SPDIF /* if you want MIDI uncomment this line */
#ifdef SPDIF
# include "emi62_fw_s.h" /* spdif fw */
#else
# include "emi62_fw_m.h" /* midi fw */
#endif
#define EMI62_VENDOR_ID 0x086a /* Emagic Soft-und Hardware GmBH */
#define EMI62_PRODUCT_ID 0x0110 /* EMI 6|2m without firmware */
#define ANCHOR_LOAD_INTERNAL 0xA0 /* Vendor specific request code for Anchor Upload/Download (This one is implemented in the core) */
#define ANCHOR_LOAD_EXTERNAL 0xA3 /* This command is not implemented in the core. Requires firmware */
#define ANCHOR_LOAD_FPGA 0xA5 /* This command is not implemented in the core. Requires firmware. Emagic extension */
#define MAX_INTERNAL_ADDRESS 0x1B3F /* This is the highest internal RAM address for the AN2131Q */
#define CPUCS_REG 0x7F92 /* EZ-USB Control and Status Register. Bit 0 controls 8051 reset */
#define INTERNAL_RAM(address) (address <= MAX_INTERNAL_ADDRESS)
static int emi62_writememory( struct usb_device *dev, int address, unsigned char *data, int length, __u8 bRequest);
static int emi62_set_reset(struct usb_device *dev, unsigned char reset_bit);
static int emi62_load_firmware (struct usb_device *dev);
static int emi62_probe(struct usb_interface *intf, const struct usb_device_id *id);
static void emi62_disconnect(struct usb_interface *intf);
static int __init emi62_init (void);
static void __exit emi62_exit (void);
/* thanks to drivers/usb/serial/keyspan_pda.c code */
static int emi62_writememory (struct usb_device *dev, int address, unsigned char *data, int length, __u8 request)
{
int result;
unsigned char *buffer = kmalloc (length, GFP_KERNEL);
if (!buffer) {
err("emi62: kmalloc(%d) failed.", length);
return -ENOMEM;
}
memcpy (buffer, data, length);
/* Note: usb_control_msg returns negative value on error or length of the
* data that was written! */
result = usb_control_msg (dev, usb_sndctrlpipe(dev, 0), request, 0x40, address, 0, buffer, length, 300);
kfree (buffer);
return result;
}
/* thanks to drivers/usb/serial/keyspan_pda.c code */
static int emi62_set_reset (struct usb_device *dev, unsigned char reset_bit)
{
int response;
info("%s - %d", __FUNCTION__, reset_bit);
response = emi62_writememory (dev, CPUCS_REG, &reset_bit, 1, 0xa0);
if (response < 0) {
err("emi62: set_reset (%d) failed", reset_bit);
}
return response;
}
#define FW_LOAD_SIZE 1023
static int emi62_load_firmware (struct usb_device *dev)
{
int err;
int i;
int pos = 0; /* Position in hex record */
__u32 addr; /* Address to write */
__u8 *buf;
dev_dbg(&dev->dev, "load_firmware\n");
buf = kmalloc(FW_LOAD_SIZE, GFP_KERNEL);
if (!buf) {
err( "%s - error loading firmware: error = %d", __FUNCTION__, -ENOMEM);
err = -ENOMEM;
goto wraperr;
}
/* Assert reset (stop the CPU in the EMI) */
err = emi62_set_reset(dev,1);
if (err < 0) {
err("%s - error loading firmware: error = %d", __FUNCTION__, err);
goto wraperr;
}
/* 1. We need to put the loader for the FPGA into the EZ-USB */
for (i=0; g_emi62_loader[i].type == 0; i++) {
err = emi62_writememory(dev, g_emi62_loader[i].address, g_emi62_loader[i].data, g_emi62_loader[i].length, ANCHOR_LOAD_INTERNAL);
if (err < 0) {
err("%s - error loading firmware: error = %d", __FUNCTION__, err);
goto wraperr;
}
}
/* De-assert reset (let the CPU run) */
err = emi62_set_reset(dev,0);
/* 2. We upload the FPGA firmware into the EMI
* Note: collect up to 1023 (yes!) bytes and send them with
* a single request. This is _much_ faster! */
do {
i = 0;
addr = g_emi62bs[pos].address;
/* intel hex records are terminated with type 0 element */
while ((g_emi62bs[pos].type == 0) && (i + g_emi62bs[pos].length < FW_LOAD_SIZE)) {
memcpy(buf + i, g_emi62bs[pos].data, g_emi62bs[pos].length);
i += g_emi62bs[pos].length;
pos++;
}
err = emi62_writememory(dev, addr, buf, i, ANCHOR_LOAD_FPGA);
if (err < 0) {
err("%s - error loading firmware: error = %d", __FUNCTION__, err);
goto wraperr;
}
} while (i > 0);
/* Assert reset (stop the CPU in the EMI) */
err = emi62_set_reset(dev,1);
if (err < 0) {
err("%s - error loading firmware: error = %d", __FUNCTION__, err);
goto wraperr;
}
/* 3. We need to put the loader for the firmware into the EZ-USB (again...) */
for (i=0; g_emi62_loader[i].type == 0; i++) {
err = emi62_writememory(dev, g_emi62_loader[i].address, g_emi62_loader[i].data, g_emi62_loader[i].length, ANCHOR_LOAD_INTERNAL);
if (err < 0) {
err("%s - error loading firmware: error = %d", __FUNCTION__, err);
goto wraperr;
}
}
/* De-assert reset (let the CPU run) */
err = emi62_set_reset(dev,0);
if (err < 0) {
err("%s - error loading firmware: error = %d", __FUNCTION__, err);
goto wraperr;
}
/* 4. We put the part of the firmware that lies in the external RAM into the EZ-USB */
/* FIXME: quick and dirty ifdefs */
#ifdef SPDIF
for (i=0; g_HexSpdifFw62[i].type == 0; i++) {
if (!INTERNAL_RAM(g_HexSpdifFw62[i].address)) {
err = emi62_writememory(dev, g_HexSpdifFw62[i].address, g_HexSpdifFw62[i].data, g_HexSpdifFw62[i].length, ANCHOR_LOAD_EXTERNAL);
if (err < 0) {
err("%s - error loading firmware: error = %d", __FUNCTION__, err);
goto wraperr;
}
}
}
#else /* MIDI */
for (i=0; g_HexMidiFw62[i].type == 0; i++) {
if (!INTERNAL_RAM(g_HexMidiFw62[i].address)) {
err = emi62_writememory(dev, g_HexMidiFw62[i].address, g_HexMidiFw62[i].data, g_HexMidiFw62[i].length, ANCHOR_LOAD_EXTERNAL);
if (err < 0) {
err("%s - error loading firmware: error = %d\n", __FUNCTION__, err);
goto wraperr;
return err;
}
}
}
#endif
/* Assert reset (stop the CPU in the EMI) */
err = emi62_set_reset(dev,1);
if (err < 0) {
err("%s - error loading firmware: error = %d", __FUNCTION__, err);
goto wraperr;
}
/* FIXME: quick and dirty ifdefs */
#ifdef SPDIF
for (i=0; g_HexSpdifFw62[i].type == 0; i++) {
if (INTERNAL_RAM(g_HexSpdifFw62[i].address)) {
err = emi62_writememory(dev, g_HexSpdifFw62[i].address, g_HexSpdifFw62[i].data, g_HexSpdifFw62[i].length, ANCHOR_LOAD_INTERNAL);
if (err < 0) {
err("%s - error loading firmware: error = %d", __FUNCTION__, err);
goto wraperr;
}
}
}
#else /* MIDI */
for (i=0; g_HexMidiFw62[i].type == 0; i++) {
if (INTERNAL_RAM(g_HexMidiFw62[i].address)) {
err = emi62_writememory(dev, g_HexMidiFw62[i].address, g_HexMidiFw62[i].data, g_HexMidiFw62[i].length, ANCHOR_LOAD_INTERNAL);
if (err < 0) {
err("%s - error loading firmware: error = %d\n", __FUNCTION__, err);
goto wraperr;
}
}
}
#endif
/* De-assert reset (let the CPU run) */
err = emi62_set_reset(dev,0);
if (err < 0) {
err("%s - error loading firmware: error = %d", __FUNCTION__, err);
goto wraperr;
}
/* return 1 to fail the driver inialization
* and give real driver change to load */
return 1;
wraperr:
kfree(buf);
dev_err(&dev->dev, "Error\n");
return err;
}
static __devinitdata struct usb_device_id id_table [] = {
{ USB_DEVICE(EMI62_VENDOR_ID, EMI62_PRODUCT_ID) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, id_table);
static int emi62_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
dev_dbg(&intf->dev, "emi62_probe\n");
info("%s start", __FUNCTION__);
if((dev->descriptor.idVendor == EMI62_VENDOR_ID) && (dev->descriptor.idProduct == EMI62_PRODUCT_ID)) {
emi62_load_firmware(dev);
}
/* do not return the driver context, let real audio driver do that */
return -EIO;
}
static void emi62_disconnect(struct usb_interface *intf)
{
}
struct usb_driver emi62_driver = {
.owner = THIS_MODULE,
.name = "emi62 - firmware loader",
.probe = emi62_probe,
.disconnect = emi62_disconnect,
.id_table = id_table,
};
static int __init emi62_init (void)
{
int retval;
retval = usb_register (&emi62_driver);
if (retval)
printk(KERN_ERR "adi-emi: registration failed\n");
return retval;
}
static void __exit emi62_exit (void)
{
usb_deregister (&emi62_driver);
}
module_init(emi62_init);
module_exit(emi62_exit);
MODULE_AUTHOR("tapio laxstrm");
MODULE_DESCRIPTION("Emagic EMI 6|2m firmware loader.");
MODULE_LICENSE("GPL");
/* vi:ai:syntax=c:sw=8:ts=8:tw=80
*/
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* USB LED driver - 1.1
*
* Copyright (C) 2004 Greg Kroah-Hartman (greg@kroah.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2.
*
*/
#include <linux/config.h>
#ifdef CONFIG_USB_DEBUG
#define DEBUG 1
#endif
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/usb.h>
#define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com"
#define DRIVER_DESC "USB LED Driver"
#define VENDOR_ID 0x0fc5
#define PRODUCT_ID 0x1223
/* table of devices that work with this driver */
static struct usb_device_id id_table [] = {
{ USB_DEVICE(VENDOR_ID, PRODUCT_ID) },
{ },
};
MODULE_DEVICE_TABLE (usb, id_table);
struct usb_led {
struct usb_device * udev;
unsigned char blue;
unsigned char red;
unsigned char green;
};
#define BLUE 0x04
#define RED 0x02
#define GREEN 0x01
static void change_color(struct usb_led *led)
{
int retval;
unsigned char color = 0x07;
unsigned char *buffer;
buffer = kmalloc(8, GFP_KERNEL);
if (!buffer) {
dev_err(&led->udev->dev, "out of memory\n");
return;
}
if (led->blue)
color &= ~(BLUE);
if (led->red)
color &= ~(RED);
if (led->green)
color &= ~(GREEN);
dev_dbg(&led->udev->dev,
"blue = %d, red = %d, green = %d, color = %.2x\n",
led->blue, led->red, led->green, color);
retval = usb_control_msg(led->udev,
usb_sndctrlpipe(led->udev, 0),
0x12,
0xc8,
(0x02 * 0x100) + 0x0a,
(0x00 * 0x100) + color,
buffer,
8,
2 * HZ);
if (retval)
dev_dbg(&led->udev->dev, "retval = %d\n", retval);
kfree(buffer);
}
#define show_set(value) \
static ssize_t show_##value(struct device *dev, char *buf) \
{ \
struct usb_interface *intf = to_usb_interface(dev); \
struct usb_led *led = usb_get_intfdata(intf); \
\
return sprintf(buf, "%d\n", led->value); \
} \
static ssize_t set_##value(struct device *dev, const char *buf, size_t count) \
{ \
struct usb_interface *intf = to_usb_interface(dev); \
struct usb_led *led = usb_get_intfdata(intf); \
int temp = simple_strtoul(buf, NULL, 10); \
\
led->value = temp; \
change_color(led); \
return count; \
} \
static DEVICE_ATTR(value, S_IWUGO | S_IRUGO, show_##value, set_##value);
show_set(blue);
show_set(red);
show_set(green);
static int led_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(interface);
struct usb_led *dev = NULL;
int retval = -ENOMEM;
dev = kmalloc(sizeof(struct usb_led), GFP_KERNEL);
if (dev == NULL) {
dev_err(&interface->dev, "Out of memory\n");
goto error;
}
memset (dev, 0x00, sizeof (*dev));
dev->udev = usb_get_dev(udev);
usb_set_intfdata (interface, dev);
device_create_file(&interface->dev, &dev_attr_blue);
device_create_file(&interface->dev, &dev_attr_red);
device_create_file(&interface->dev, &dev_attr_green);
dev_info(&interface->dev, "USB LED device now attached\n");
return 0;
error:
kfree(dev);
return retval;
}
static void led_disconnect(struct usb_interface *interface)
{
struct usb_led *dev;
dev = usb_get_intfdata (interface);
usb_set_intfdata (interface, NULL);
device_remove_file(&interface->dev, &dev_attr_blue);
device_remove_file(&interface->dev, &dev_attr_red);
device_remove_file(&interface->dev, &dev_attr_green);
usb_put_dev(dev->udev);
kfree(dev);
dev_info(&interface->dev, "USB LED now disconnected\n");
}
static struct usb_driver led_driver = {
.owner = THIS_MODULE,
.name = "usbled",
.probe = led_probe,
.disconnect = led_disconnect,
.id_table = id_table,
};
static int __init usb_led_init(void)
{
int retval = 0;
retval = usb_register(&led_driver);
if (retval)
err("usb_register failed. Error number %d", retval);
return retval;
}
static void __exit usb_led_exit(void)
{
usb_deregister(&led_driver);
}
module_init (usb_led_init);
module_exit (usb_led_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
......@@ -302,6 +302,9 @@ MODULE_PARM_DESC (msg_level, "Initial message level (default = 1)");
static struct ethtool_ops usbnet_ethtool_ops;
static void usbnet_get_drvinfo (struct net_device *, struct ethtool_drvinfo *);
static u32 usbnet_get_link (struct net_device *);
static u32 usbnet_get_msglevel (struct net_device *);
static void usbnet_set_msglevel (struct net_device *, u32);
/* mostly for PDA style devices, which are always connected if present */
static int always_connected (struct usbnet *dev)
......@@ -658,6 +661,21 @@ static int ax8817x_set_settings(struct net_device *net, struct ethtool_cmd *cmd)
return mii_ethtool_sset(&dev->mii,cmd);
}
/* We need to override some ethtool_ops so we require our
own structure so we don't interfere with other usbnet
devices that may be connected at the same time. */
static struct ethtool_ops ax8817x_ethtool_ops = {
.get_drvinfo = ax8817x_get_drvinfo,
.get_link = ax8817x_get_link,
.get_msglevel = usbnet_get_msglevel,
.set_msglevel = usbnet_set_msglevel,
.get_wol = ax8817x_get_wol,
.set_wol = ax8817x_set_wol,
.get_eeprom = ax8817x_get_eeprom,
.get_settings = ax8817x_get_settings,
.set_settings = ax8817x_set_settings,
};
static int ax8817x_bind(struct usbnet *dev, struct usb_interface *intf)
{
int ret;
......@@ -744,14 +762,7 @@ static int ax8817x_bind(struct usbnet *dev, struct usb_interface *intf)
}
dev->net->set_multicast_list = ax8817x_set_multicast;
usbnet_ethtool_ops.get_drvinfo = &ax8817x_get_drvinfo;
usbnet_ethtool_ops.get_link = &ax8817x_get_link;
usbnet_ethtool_ops.get_wol = &ax8817x_get_wol;
usbnet_ethtool_ops.set_wol = &ax8817x_set_wol;
usbnet_ethtool_ops.get_eeprom = &ax8817x_get_eeprom;
usbnet_ethtool_ops.get_settings = &ax8817x_get_settings;
usbnet_ethtool_ops.set_settings = &ax8817x_set_settings;
dev->net->ethtool_ops = &ax8817x_ethtool_ops;
return 0;
}
......
......@@ -36,6 +36,8 @@
#include <asm/uaccess.h>
#include <linux/usb.h>
#define CYBERJACK_LOCAL_BUF_SIZE 32
#ifdef CONFIG_USB_SERIAL_DEBUG
static int debug = 1;
#else
......@@ -62,6 +64,7 @@ static int cyberjack_open (struct usb_serial_port *port, struct file *filp);
static void cyberjack_close (struct usb_serial_port *port, struct file *filp);
static int cyberjack_write (struct usb_serial_port *port, int from_user,
const unsigned char *buf, int count);
static int cyberjack_write_room( struct usb_serial_port *port );
static void cyberjack_read_int_callback (struct urb *urb, struct pt_regs *regs);
static void cyberjack_read_bulk_callback (struct urb *urb, struct pt_regs *regs);
static void cyberjack_write_bulk_callback (struct urb *urb, struct pt_regs *regs);
......@@ -95,6 +98,7 @@ static struct usb_serial_device_type cyberjack_device = {
.open = cyberjack_open,
.close = cyberjack_close,
.write = cyberjack_write,
.write_room = cyberjack_write_room,
.read_int_callback = cyberjack_read_int_callback,
.read_bulk_callback = cyberjack_read_bulk_callback,
.write_bulk_callback = cyberjack_write_bulk_callback,
......@@ -156,6 +160,11 @@ static int cyberjack_open (struct usb_serial_port *port, struct file *filp)
dbg("%s - port %d", __FUNCTION__, port->number);
dbg("%s - usb_clear_halt", __FUNCTION__ );
usb_clear_halt(port->serial->dev, port->write_urb->pipe);
usb_clear_halt(port->serial->dev, port->read_urb->pipe);
usb_clear_halt(port->serial->dev, port->interrupt_in_urb->pipe);
/* force low_latency on so that our tty_push actually forces
* the data through, otherwise it is scheduled, and with high
* data rates (like with OHCI) data can get lost.
......@@ -192,6 +201,10 @@ static void cyberjack_close (struct usb_serial_port *port, struct file *filp)
usb_unlink_urb (port->write_urb);
usb_unlink_urb (port->read_urb);
usb_unlink_urb (port->interrupt_in_urb);
dbg("%s - usb_clear_halt", __FUNCTION__ );
usb_clear_halt(port->serial->dev, port->write_urb->pipe);
usb_clear_halt(port->serial->dev, port->read_urb->pipe);
usb_clear_halt(port->serial->dev, port->interrupt_in_urb->pipe);
}
}
......@@ -202,6 +215,7 @@ static int cyberjack_write (struct usb_serial_port *port, int from_user, const u
unsigned long flags;
int result;
int wrexpected;
unsigned char localbuf[CYBERJACK_LOCAL_BUF_SIZE]; /* Buffer for collecting data to write */
dbg("%s - port %d", __FUNCTION__, port->number);
dbg("%s - from_user %d", __FUNCTION__, from_user);
......@@ -218,22 +232,29 @@ static int cyberjack_write (struct usb_serial_port *port, int from_user, const u
spin_lock_irqsave(&priv->lock, flags);
if( (count+priv->wrfilled)>sizeof(priv->wrbuf) ) {
if( (count+priv->wrfilled)>sizeof(priv->wrbuf) ||
(count>sizeof(localbuf)) ) {
/* To much data for buffer. Reset buffer. */
priv->wrfilled=0;
spin_unlock_irqrestore(&priv->lock, flags);
return (0);
}
spin_unlock_irqrestore(&priv->lock, flags);
/* Copy data */
if (from_user) {
if (copy_from_user(priv->wrbuf+priv->wrfilled, buf, count)) {
spin_unlock_irqrestore(&priv->lock, flags);
if (copy_from_user(localbuf, buf, count)) {
return -EFAULT;
}
} else {
memcpy (priv->wrbuf+priv->wrfilled, buf, count);
memcpy (localbuf, buf, count);
}
spin_lock_irqsave(&priv->lock, flags);
memcpy (priv->wrbuf+priv->wrfilled, localbuf, count);
usb_serial_debug_data (__FILE__, __FUNCTION__, count,
priv->wrbuf+priv->wrfilled);
priv->wrfilled += count;
......@@ -291,6 +312,11 @@ static int cyberjack_write (struct usb_serial_port *port, int from_user, const u
return (count);
}
static int cyberjack_write_room( struct usb_serial_port *port )
{
return CYBERJACK_LOCAL_BUF_SIZE;
}
static void cyberjack_read_int_callback( struct urb *urb, struct pt_regs *regs )
{
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
......
......@@ -372,6 +372,7 @@ static struct usb_device_id id_table_FT232BM [] = {
{ USB_DEVICE_VER(FTDI_VID, FTDI_MTXORB_5_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(FTDI_VID, FTDI_MTXORB_6_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(FTDI_VID, FTDI_PERLE_ULTRAPORT_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(FTDI_VID, FTDI_PIEGROUP_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2101_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2102_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2103_PID, 0x400, 0xffff) },
......@@ -576,6 +577,8 @@ static int ftdi_chars_in_buffer (struct usb_serial_port *port);
static void ftdi_write_bulk_callback (struct urb *urb, struct pt_regs *regs);
static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs);
static void ftdi_set_termios (struct usb_serial_port *port, struct termios * old);
static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file);
static int ftdi_tiocmset (struct usb_serial_port *port, struct file * file, unsigned int set, unsigned int clear);
static int ftdi_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg);
static void ftdi_break_ctl (struct usb_serial_port *port, int break_state );
static void ftdi_throttle (struct usb_serial_port *port);
......@@ -603,6 +606,8 @@ static struct usb_serial_device_type ftdi_SIO_device = {
.chars_in_buffer = ftdi_chars_in_buffer,
.read_bulk_callback = ftdi_read_bulk_callback,
.write_bulk_callback = ftdi_write_bulk_callback,
.tiocmget = ftdi_tiocmget,
.tiocmset = ftdi_tiocmset,
.ioctl = ftdi_ioctl,
.set_termios = ftdi_set_termios,
.break_ctl = ftdi_break_ctl,
......@@ -627,6 +632,8 @@ static struct usb_serial_device_type ftdi_8U232AM_device = {
.chars_in_buffer = ftdi_chars_in_buffer,
.read_bulk_callback = ftdi_read_bulk_callback,
.write_bulk_callback = ftdi_write_bulk_callback,
.tiocmget = ftdi_tiocmget,
.tiocmset = ftdi_tiocmset,
.ioctl = ftdi_ioctl,
.set_termios = ftdi_set_termios,
.break_ctl = ftdi_break_ctl,
......@@ -651,6 +658,8 @@ static struct usb_serial_device_type ftdi_FT232BM_device = {
.chars_in_buffer = ftdi_chars_in_buffer,
.read_bulk_callback = ftdi_read_bulk_callback,
.write_bulk_callback = ftdi_write_bulk_callback,
.tiocmget = ftdi_tiocmget,
.tiocmset = ftdi_tiocmset,
.ioctl = ftdi_ioctl,
.set_termios = ftdi_set_termios,
.break_ctl = ftdi_break_ctl,
......@@ -675,6 +684,8 @@ static struct usb_serial_device_type ftdi_USB_UIRT_device = {
.chars_in_buffer = ftdi_chars_in_buffer,
.read_bulk_callback = ftdi_read_bulk_callback,
.write_bulk_callback = ftdi_write_bulk_callback,
.tiocmget = ftdi_tiocmget,
.tiocmset = ftdi_tiocmset,
.ioctl = ftdi_ioctl,
.set_termios = ftdi_set_termios,
.break_ctl = ftdi_break_ctl,
......@@ -701,6 +712,8 @@ static struct usb_serial_device_type ftdi_HE_TIRA1_device = {
.chars_in_buffer = ftdi_chars_in_buffer,
.read_bulk_callback = ftdi_read_bulk_callback,
.write_bulk_callback = ftdi_write_bulk_callback,
.tiocmget = ftdi_tiocmget,
.tiocmset = ftdi_tiocmset,
.ioctl = ftdi_ioctl,
.set_termios = ftdi_set_termios,
.break_ctl = ftdi_break_ctl,
......@@ -1808,13 +1821,92 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct termios *old_
} /* ftdi_termios */
static int ftdi_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg)
static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file)
{
struct usb_serial *serial = port->serial;
struct ftdi_private *priv = usb_get_serial_port_data(port);
unsigned char buf[2];
int ret;
dbg("%s TIOCMGET", __FUNCTION__);
switch (priv->chip_type) {
case SIO:
/* Request the status from the device */
if ((ret = usb_control_msg(serial->dev,
usb_rcvctrlpipe(serial->dev, 0),
FTDI_SIO_GET_MODEM_STATUS_REQUEST,
FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
0, 0,
buf, 1, WDR_TIMEOUT)) < 0 ) {
err("%s Could not get modem status of device - err: %d", __FUNCTION__,
ret);
return(ret);
}
break;
case FT8U232AM:
case FT232BM:
/* the 8U232AM returns a two byte value (the sio is a 1 byte value) - in the same
format as the data returned from the in point */
if ((ret = usb_control_msg(serial->dev,
usb_rcvctrlpipe(serial->dev, 0),
FTDI_SIO_GET_MODEM_STATUS_REQUEST,
FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
0, 0,
buf, 2, WDR_TIMEOUT)) < 0 ) {
err("%s Could not get modem status of device - err: %d", __FUNCTION__,
ret);
return(ret);
}
break;
default:
return -EFAULT;
break;
}
return (buf[0] & FTDI_SIO_DSR_MASK ? TIOCM_DSR : 0) |
(buf[0] & FTDI_SIO_CTS_MASK ? TIOCM_CTS : 0) |
(buf[0] & FTDI_SIO_RI_MASK ? TIOCM_RI : 0) |
(buf[0] & FTDI_SIO_RLSD_MASK ? TIOCM_CD : 0) |
priv->last_dtr_rts;
}
static int ftdi_tiocmset(struct usb_serial_port *port, struct file * file, unsigned int set, unsigned int clear)
{
int ret;
if (set & TIOCM_DTR){
if ((ret = set_dtr(port, HIGH)) < 0) {
err("Urb to set DTR failed");
return(ret);
}
}
if (set & TIOCM_RTS) {
if ((ret = set_rts(port, HIGH)) < 0){
err("Urb to set RTS failed");
return(ret);
}
}
if (clear & TIOCM_DTR){
if ((ret = set_dtr(port, LOW)) < 0){
err("Urb to unset DTR failed");
return(ret);
}
}
if (clear & TIOCM_RTS) {
if ((ret = set_rts(port, LOW)) < 0){
err("Urb to unset RTS failed");
return(ret);
}
}
return(0);
}
static int ftdi_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg)
{
struct ftdi_private *priv = usb_get_serial_port_data(port);
__u16 urb_value=0; /* Will hold the new flags */
char buf[2];
int ret, mask;
dbg("%s cmd 0x%04x", __FUNCTION__, cmd);
......@@ -1822,67 +1914,6 @@ static int ftdi_ioctl (struct usb_serial_port *port, struct file * file, unsigne
/* Based on code from acm.c and others */
switch (cmd) {
case TIOCMGET:
dbg("%s TIOCMGET", __FUNCTION__);
switch (priv->chip_type) {
case SIO:
/* Request the status from the device */
if ((ret = usb_control_msg(serial->dev,
usb_rcvctrlpipe(serial->dev, 0),
FTDI_SIO_GET_MODEM_STATUS_REQUEST,
FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
0, 0,
buf, 1, WDR_TIMEOUT)) < 0 ) {
err("%s Could not get modem status of device - err: %d", __FUNCTION__,
ret);
return(ret);
}
break;
case FT8U232AM:
case FT232BM:
/* the 8U232AM returns a two byte value (the sio is a 1 byte value) - in the same
format as the data returned from the in point */
if ((ret = usb_control_msg(serial->dev,
usb_rcvctrlpipe(serial->dev, 0),
FTDI_SIO_GET_MODEM_STATUS_REQUEST,
FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
0, 0,
buf, 2, WDR_TIMEOUT)) < 0 ) {
err("%s Could not get modem status of device - err: %d", __FUNCTION__,
ret);
return(ret);
}
break;
default:
return -EFAULT;
break;
}
return put_user((buf[0] & FTDI_SIO_DSR_MASK ? TIOCM_DSR : 0) |
(buf[0] & FTDI_SIO_CTS_MASK ? TIOCM_CTS : 0) |
(buf[0] & FTDI_SIO_RI_MASK ? TIOCM_RI : 0) |
(buf[0] & FTDI_SIO_RLSD_MASK ? TIOCM_CD : 0) |
priv->last_dtr_rts,
(unsigned long *) arg);
break;
case TIOCMSET: /* Turns on and off the lines as specified by the mask */
dbg("%s TIOCMSET", __FUNCTION__);
if (get_user(mask, (unsigned long *) arg))
return -EFAULT;
urb_value = ((mask & TIOCM_DTR) ? HIGH : LOW);
if ((ret = set_dtr(port, urb_value)) < 0){
err("Error from DTR set urb (TIOCMSET)");
return(ret);
}
urb_value = ((mask & TIOCM_RTS) ? HIGH : LOW);
if ((ret = set_rts(port, urb_value)) < 0){
err("Error from RTS set urb (TIOCMSET)");
return(ret);
}
return(0);
break;
case TIOCMBIS: /* turns on (Sets) the lines as specified by the mask */
dbg("%s TIOCMBIS", __FUNCTION__);
if (get_user(mask, (unsigned long *) arg))
......
......@@ -145,6 +145,9 @@
/* Note: OCT US101 is also rebadged as Dick Smith Electronics (NZ) XH6381 */
#define OCT_US101_PID 0x0421 /* OCT US101 USB to RS-232 */
/* an infrared receiver for user access control with IR tags */
#define FTDI_PIEGROUP_PID 0xF208 /* Product Id */
/*
* Protego product ids
*/
......
......@@ -567,7 +567,9 @@ static void ir_set_termios (struct usb_serial_port *port, struct termios *old_te
case B115200: ir_baud = SPEED_115200; break;
case B576000: ir_baud = SPEED_576000; break;
case B1152000: ir_baud = SPEED_1152000; break;
#ifdef B4000000
case B4000000: ir_baud = SPEED_4000000; break;
#endif
}
if (xbof == -1) {
......
......@@ -79,6 +79,7 @@ static struct usb_device_id id_table [] = {
{ USB_DEVICE(TRIPP_VENDOR_ID, TRIPP_PRODUCT_ID) },
{ USB_DEVICE(RADIOSHACK_VENDOR_ID, RADIOSHACK_PRODUCT_ID) },
{ USB_DEVICE(DCU10_VENDOR_ID, DCU10_PRODUCT_ID) },
{ USB_DEVICE(SITECOM_VENDOR_ID, SITECOM_PRODUCT_ID) },
{ } /* Terminating entry */
};
......
......@@ -38,3 +38,6 @@
#define DCU10_VENDOR_ID 0x0731
#define DCU10_PRODUCT_ID 0x0528
#define SITECOM_VENDOR_ID 0x6189
#define SITECOM_PRODUCT_ID 0x2068
......@@ -235,6 +235,8 @@ static struct usb_device_id id_table [] = {
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NZ90V_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_TJ25_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SCH_I330_ID),
.driver_info = (kernel_ulong_t)&palm_os_4_probe },
{ USB_DEVICE(GARMIN_VENDOR_ID, GARMIN_IQUE_3600_ID),
......@@ -269,6 +271,7 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NX60_ID) },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NZ90V_ID) },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_UX50_ID) },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_TJ25_ID) },
{ USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SCH_I330_ID) },
{ USB_DEVICE(GARMIN_VENDOR_ID, GARMIN_IQUE_3600_ID) },
{ }, /* optional parameter entry */
......
......@@ -42,6 +42,7 @@
#define SONY_CLIE_NX60_ID 0x00DA
#define SONY_CLIE_NZ90V_ID 0x00E9
#define SONY_CLIE_UX50_ID 0x0144
#define SONY_CLIE_TJ25_ID 0x0169
#define SAMSUNG_VENDOR_ID 0x04E8
#define SAMSUNG_SCH_I330_ID 0x8001
......
......@@ -76,6 +76,7 @@
#include <linux/module.h>
#include <linux/spinlock.h>
#include <asm/uaccess.h>
#include <asm/termbits.h>
#include <linux/usb.h>
#include <linux/serial_reg.h>
#include <linux/serial.h>
......
......@@ -46,8 +46,8 @@
*
* This driver supports reading and writing. If you're truly paranoid,
* however, you can force the driver into a write-protected state by setting
* the WP enable bits in datafab_handle_mode_sense(). Basically this means
* setting mode_param_header[3] = 0x80.
* the WP enable bits in datafab_handle_mode_sense(). See the comments
* in that routine.
*/
#include "transport.h"
......@@ -89,11 +89,10 @@ datafab_bulk_write(struct us_data *us, unsigned char *data, unsigned int len) {
static int datafab_read_data(struct us_data *us,
struct datafab_info *info,
u32 sector,
u32 sectors,
unsigned char *buffer,
int use_sg)
u32 sectors)
{
unsigned char *command = us->iobuf;
unsigned char *buffer;
unsigned char thistime;
unsigned int totallen, alloclen;
int len, result;
......@@ -116,18 +115,13 @@ static int datafab_read_data(struct us_data *us,
totallen = sectors * info->ssize;
// Since we don't read more than 64 KB at a time, we have to create
// a bounce buffer if the transfer uses scatter-gather. We will
// move the data a piece at a time between the bounce buffer and
// the actual transfer buffer. If we're not using scatter-gather,
// we can simply update the transfer buffer pointer to get the
// same effect.
// a bounce buffer and move the data a piece at a time between the
// bounce buffer and the actual transfer buffer.
alloclen = min(totallen, 65536u);
if (use_sg) {
buffer = kmalloc(alloclen, GFP_NOIO);
if (buffer == NULL)
return USB_STOR_TRANSPORT_ERROR;
}
buffer = kmalloc(alloclen, GFP_NOIO);
if (buffer == NULL)
return USB_STOR_TRANSPORT_ERROR;
do {
// loop, never allocate or transfer more than 64k at once
......@@ -157,24 +151,19 @@ static int datafab_read_data(struct us_data *us,
if (result != USB_STOR_XFER_GOOD)
goto leave;
// Store the data (s-g) or update the pointer (!s-g)
if (use_sg)
usb_stor_access_xfer_buf(buffer, len, us->srb,
&sg_idx, &sg_offset, TO_XFER_BUF);
else
buffer += len;
// Store the data in the transfer buffer
usb_stor_access_xfer_buf(buffer, len, us->srb,
&sg_idx, &sg_offset, TO_XFER_BUF);
sector += thistime;
totallen -= len;
} while (totallen > 0);
if (use_sg)
kfree(buffer);
kfree(buffer);
return USB_STOR_TRANSPORT_GOOD;
leave:
if (use_sg)
kfree(buffer);
kfree(buffer);
return USB_STOR_TRANSPORT_ERROR;
}
......@@ -182,12 +171,11 @@ static int datafab_read_data(struct us_data *us,
static int datafab_write_data(struct us_data *us,
struct datafab_info *info,
u32 sector,
u32 sectors,
unsigned char *buffer,
int use_sg)
u32 sectors)
{
unsigned char *command = us->iobuf;
unsigned char *reply = us->iobuf;
unsigned char *buffer;
unsigned char thistime;
unsigned int totallen, alloclen;
int len, result;
......@@ -210,18 +198,13 @@ static int datafab_write_data(struct us_data *us,
totallen = sectors * info->ssize;
// Since we don't write more than 64 KB at a time, we have to create
// a bounce buffer if the transfer uses scatter-gather. We will
// move the data a piece at a time between the bounce buffer and
// the actual transfer buffer. If we're not using scatter-gather,
// we can simply update the transfer buffer pointer to get the
// same effect.
// a bounce buffer and move the data a piece at a time between the
// bounce buffer and the actual transfer buffer.
alloclen = min(totallen, 65536u);
if (use_sg) {
buffer = kmalloc(alloclen, GFP_NOIO);
if (buffer == NULL)
return USB_STOR_TRANSPORT_ERROR;
}
buffer = kmalloc(alloclen, GFP_NOIO);
if (buffer == NULL)
return USB_STOR_TRANSPORT_ERROR;
do {
// loop, never allocate or transfer more than 64k at once
......@@ -230,10 +213,9 @@ static int datafab_write_data(struct us_data *us,
len = min(totallen, alloclen);
thistime = (len / info->ssize) & 0xff;
// Get the data from the transfer buffer (s-g)
if (use_sg)
usb_stor_access_xfer_buf(buffer, len, us->srb,
&sg_idx, &sg_offset, FROM_XFER_BUF);
// Get the data from the transfer buffer
usb_stor_access_xfer_buf(buffer, len, us->srb,
&sg_idx, &sg_offset, FROM_XFER_BUF);
command[0] = 0;
command[1] = thistime;
......@@ -269,21 +251,15 @@ static int datafab_write_data(struct us_data *us,
goto leave;
}
// Update the transfer buffer pointer (!s-g)
if (!use_sg)
buffer += len;
sector += thistime;
totallen -= len;
} while (totallen > 0);
if (use_sg)
kfree(buffer);
kfree(buffer);
return USB_STOR_TRANSPORT_GOOD;
leave:
if (use_sg)
kfree(buffer);
kfree(buffer);
return USB_STOR_TRANSPORT_ERROR;
}
......@@ -413,38 +389,30 @@ static int datafab_id_device(struct us_data *us,
static int datafab_handle_mode_sense(struct us_data *us,
Scsi_Cmnd * srb,
unsigned char *ptr,
int sense_6)
{
unsigned char mode_param_header[8] = {
0, 0, 0, 0, 0, 0, 0, 0
};
unsigned char rw_err_page[12] = {
static unsigned char rw_err_page[12] = {
0x1, 0xA, 0x21, 1, 0, 0, 0, 0, 1, 0, 0, 0
};
unsigned char cache_page[12] = {
static unsigned char cache_page[12] = {
0x8, 0xA, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
unsigned char rbac_page[12] = {
static unsigned char rbac_page[12] = {
0x1B, 0xA, 0, 0x81, 0, 0, 0, 0, 0, 0, 0, 0
};
unsigned char timer_page[8] = {
static unsigned char timer_page[8] = {
0x1C, 0x6, 0, 0, 0, 0
};
unsigned char pc, page_code;
unsigned short total_len = 0;
unsigned short param_len, i = 0;
unsigned int i = 0;
struct datafab_info *info = (struct datafab_info *) (us->extra);
unsigned char *ptr = us->iobuf;
// most of this stuff is just a hack to get things working. the
// datafab reader doesn't present a SCSI interface so we
// fudge the SCSI commands...
//
if (sense_6)
param_len = srb->cmnd[4];
else
param_len = ((u16) (srb->cmnd[7]) >> 8) | ((u16) (srb->cmnd[8]));
pc = srb->cmnd[2] >> 6;
page_code = srb->cmnd[2] & 0x3F;
......@@ -463,66 +431,44 @@ static int datafab_handle_mode_sense(struct us_data *us,
break;
}
mode_param_header[3] = 0x80; // write enable
memset(ptr, 0, 8);
if (sense_6) {
ptr[2] = 0x00; // WP enable: 0x80
i = 4;
} else {
ptr[3] = 0x00; // WP enable: 0x80
i = 8;
}
switch (page_code) {
case 0x0:
default:
// vendor-specific mode
return USB_STOR_TRANSPORT_ERROR;
info->sense_key = 0x05;
info->sense_asc = 0x24;
info->sense_ascq = 0x00;
return USB_STOR_TRANSPORT_FAILED;
case 0x1:
total_len = sizeof(rw_err_page);
mode_param_header[0] = total_len >> 8;
mode_param_header[1] = total_len & 0xFF;
mode_param_header[3] = 0x00; // WP enable: 0x80
memcpy(ptr, mode_param_header, sizeof(mode_param_header));
i += sizeof(mode_param_header);
memcpy(ptr + i, rw_err_page, sizeof(rw_err_page));
i += sizeof(rw_err_page);
break;
case 0x8:
total_len = sizeof(cache_page);
mode_param_header[0] = total_len >> 8;
mode_param_header[1] = total_len & 0xFF;
mode_param_header[3] = 0x00; // WP enable: 0x80
memcpy(ptr, mode_param_header, sizeof(mode_param_header));
i += sizeof(mode_param_header);
memcpy(ptr + i, cache_page, sizeof(cache_page));
i += sizeof(cache_page);
break;
case 0x1B:
total_len = sizeof(rbac_page);
mode_param_header[0] = total_len >> 8;
mode_param_header[1] = total_len & 0xFF;
mode_param_header[3] = 0x00; // WP enable: 0x80
memcpy(ptr, mode_param_header, sizeof(mode_param_header));
i += sizeof(mode_param_header);
memcpy(ptr + i, rbac_page, sizeof(rbac_page));
i += sizeof(rbac_page);
break;
case 0x1C:
total_len = sizeof(timer_page);
mode_param_header[0] = total_len >> 8;
mode_param_header[1] = total_len & 0xFF;
mode_param_header[3] = 0x00; // WP enable: 0x80
memcpy(ptr, mode_param_header, sizeof(mode_param_header));
i += sizeof(mode_param_header);
memcpy(ptr + i, timer_page, sizeof(timer_page));
i += sizeof(timer_page);
break;
case 0x3F: // retrieve all pages
total_len = sizeof(timer_page) + sizeof(rbac_page) +
sizeof(cache_page) + sizeof(rw_err_page);
mode_param_header[0] = total_len >> 8;
mode_param_header[1] = total_len & 0xFF;
mode_param_header[3] = 0x00; // WP enable
memcpy(ptr, mode_param_header, sizeof(mode_param_header));
i += sizeof(mode_param_header);
memcpy(ptr + i, timer_page, sizeof(timer_page));
i += sizeof(timer_page);
memcpy(ptr + i, rbac_page, sizeof(rbac_page));
......@@ -530,9 +476,16 @@ static int datafab_handle_mode_sense(struct us_data *us,
memcpy(ptr + i, cache_page, sizeof(cache_page));
i += sizeof(cache_page);
memcpy(ptr + i, rw_err_page, sizeof(rw_err_page));
i += sizeof(rw_err_page);
break;
}
if (sense_6)
ptr[0] = i - 1;
else
((u16 *) ptr)[0] = cpu_to_be16(i - 2);
usb_stor_set_xfer_buf(ptr, i, srb);
return USB_STOR_TRANSPORT_GOOD;
}
......@@ -550,8 +503,8 @@ int datafab_transport(Scsi_Cmnd * srb, struct us_data *us)
struct datafab_info *info;
int rc;
unsigned long block, blocks;
unsigned char *ptr = NULL;
unsigned char inquiry_reply[36] = {
unsigned char *ptr = us->iobuf;
static unsigned char inquiry_reply[8] = {
0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00
};
......@@ -568,12 +521,11 @@ int datafab_transport(Scsi_Cmnd * srb, struct us_data *us)
}
info = (struct datafab_info *) (us->extra);
ptr = (unsigned char *) srb->request_buffer;
if (srb->cmnd[0] == INQUIRY) {
US_DEBUGP("datafab_transport: INQUIRY. Returning bogus response");
memset( inquiry_reply + 8, 0, 28 );
fill_inquiry_response(us, inquiry_reply, 36);
memcpy(ptr, inquiry_reply, sizeof(inquiry_reply));
fill_inquiry_response(us, ptr, 36);
return USB_STOR_TRANSPORT_GOOD;
}
......@@ -588,15 +540,9 @@ int datafab_transport(Scsi_Cmnd * srb, struct us_data *us)
// build the reply
//
ptr[0] = (info->sectors >> 24) & 0xFF;
ptr[1] = (info->sectors >> 16) & 0xFF;
ptr[2] = (info->sectors >> 8) & 0xFF;
ptr[3] = (info->sectors) & 0xFF;
ptr[4] = (info->ssize >> 24) & 0xFF;
ptr[5] = (info->ssize >> 16) & 0xFF;
ptr[6] = (info->ssize >> 8) & 0xFF;
ptr[7] = (info->ssize) & 0xFF;
((u32 *) ptr)[0] = cpu_to_be32(info->sectors);
((u32 *) ptr)[1] = cpu_to_be32(info->ssize);
usb_stor_set_xfer_buf(ptr, 8, srb);
return USB_STOR_TRANSPORT_GOOD;
}
......@@ -615,7 +561,7 @@ int datafab_transport(Scsi_Cmnd * srb, struct us_data *us)
blocks = ((u32)(srb->cmnd[7]) << 8) | ((u32)(srb->cmnd[8]));
US_DEBUGP("datafab_transport: READ_10: read block 0x%04lx count %ld\n", block, blocks);
return datafab_read_data(us, info, block, blocks, ptr, srb->use_sg);
return datafab_read_data(us, info, block, blocks);
}
if (srb->cmnd[0] == READ_12) {
......@@ -628,7 +574,7 @@ int datafab_transport(Scsi_Cmnd * srb, struct us_data *us)
((u32)(srb->cmnd[8]) << 8) | ((u32)(srb->cmnd[9]));
US_DEBUGP("datafab_transport: READ_12: read block 0x%04lx count %ld\n", block, blocks);
return datafab_read_data(us, info, block, blocks, ptr, srb->use_sg);
return datafab_read_data(us, info, block, blocks);
}
if (srb->cmnd[0] == WRITE_10) {
......@@ -638,7 +584,7 @@ int datafab_transport(Scsi_Cmnd * srb, struct us_data *us)
blocks = ((u32)(srb->cmnd[7]) << 8) | ((u32)(srb->cmnd[8]));
US_DEBUGP("datafab_transport: WRITE_10: write block 0x%04lx count %ld\n", block, blocks);
return datafab_write_data(us, info, block, blocks, ptr, srb->use_sg);
return datafab_write_data(us, info, block, blocks);
}
if (srb->cmnd[0] == WRITE_12) {
......@@ -651,7 +597,7 @@ int datafab_transport(Scsi_Cmnd * srb, struct us_data *us)
((u32)(srb->cmnd[8]) << 8) | ((u32)(srb->cmnd[9]));
US_DEBUGP("datafab_transport: WRITE_12: write block 0x%04lx count %ld\n", block, blocks);
return datafab_write_data(us, info, block, blocks, ptr, srb->use_sg);
return datafab_write_data(us, info, block, blocks);
}
if (srb->cmnd[0] == TEST_UNIT_READY) {
......@@ -666,23 +612,25 @@ int datafab_transport(Scsi_Cmnd * srb, struct us_data *us)
// we can set the correct sense data. so far though it hasn't been
// necessary
//
memset(ptr, 0, 18);
ptr[0] = 0xF0;
ptr[2] = info->sense_key;
ptr[7] = 11;
ptr[12] = info->sense_asc;
ptr[13] = info->sense_ascq;
usb_stor_set_xfer_buf(ptr, 18, srb);
return USB_STOR_TRANSPORT_GOOD;
}
if (srb->cmnd[0] == MODE_SENSE) {
US_DEBUGP("datafab_transport: MODE_SENSE_6 detected\n");
return datafab_handle_mode_sense(us, srb, ptr, TRUE);
return datafab_handle_mode_sense(us, srb, TRUE);
}
if (srb->cmnd[0] == MODE_SENSE_10) {
US_DEBUGP("datafab_transport: MODE_SENSE_10 detected\n");
return datafab_handle_mode_sense(us, srb, ptr, FALSE);
return datafab_handle_mode_sense(us, srb, FALSE);
}
if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) {
......@@ -711,5 +659,8 @@ int datafab_transport(Scsi_Cmnd * srb, struct us_data *us)
US_DEBUGP("datafab_transport: Gah! Unknown command: %d (0x%x)\n",
srb->cmnd[0], srb->cmnd[0]);
return USB_STOR_TRANSPORT_ERROR;
info->sense_key = 0x05;
info->sense_asc = 0x20;
info->sense_ascq = 0x00;
return USB_STOR_TRANSPORT_FAILED;
}
......@@ -43,8 +43,8 @@
*
* This driver supports reading and writing. If you're truly paranoid,
* however, you can force the driver into a write-protected state by setting
* the WP enable bits in jumpshot_handle_mode_sense. Basically this means
* setting mode_param_header[3] = 0x80.
* the WP enable bits in jumpshot_handle_mode_sense. See the comments
* in that routine.
*/
#include "transport.h"
......@@ -109,11 +109,10 @@ static int jumpshot_get_status(struct us_data *us)
static int jumpshot_read_data(struct us_data *us,
struct jumpshot_info *info,
u32 sector,
u32 sectors,
unsigned char *buffer,
int use_sg)
u32 sectors)
{
unsigned char *command = us->iobuf;
unsigned char *buffer;
unsigned char thistime;
unsigned int totallen, alloclen;
int len, result;
......@@ -130,18 +129,13 @@ static int jumpshot_read_data(struct us_data *us,
totallen = sectors * info->ssize;
// Since we don't read more than 64 KB at a time, we have to create
// a bounce buffer if the transfer uses scatter-gather. We will
// move the data a piece at a time between the bounce buffer and
// the actual transfer buffer. If we're not using scatter-gather,
// we can simply update the transfer buffer pointer to get the
// same effect.
// a bounce buffer and move the data a piece at a time between the
// bounce buffer and the actual transfer buffer.
alloclen = min(totallen, 65536u);
if (use_sg) {
buffer = kmalloc(alloclen, GFP_NOIO);
if (buffer == NULL)
return USB_STOR_TRANSPORT_ERROR;
}
buffer = kmalloc(alloclen, GFP_NOIO);
if (buffer == NULL)
return USB_STOR_TRANSPORT_ERROR;
do {
// loop, never allocate or transfer more than 64k at once
......@@ -171,24 +165,19 @@ static int jumpshot_read_data(struct us_data *us,
US_DEBUGP("jumpshot_read_data: %d bytes\n", len);
// Store the data (s-g) or update the pointer (!s-g)
if (use_sg)
usb_stor_access_xfer_buf(buffer, len, us->srb,
&sg_idx, &sg_offset, TO_XFER_BUF);
else
buffer += len;
// Store the data in the transfer buffer
usb_stor_access_xfer_buf(buffer, len, us->srb,
&sg_idx, &sg_offset, TO_XFER_BUF);
sector += thistime;
totallen -= len;
} while (totallen > 0);
if (use_sg)
kfree(buffer);
kfree(buffer);
return USB_STOR_TRANSPORT_GOOD;
leave:
if (use_sg)
kfree(buffer);
kfree(buffer);
return USB_STOR_TRANSPORT_ERROR;
}
......@@ -196,11 +185,10 @@ static int jumpshot_read_data(struct us_data *us,
static int jumpshot_write_data(struct us_data *us,
struct jumpshot_info *info,
u32 sector,
u32 sectors,
unsigned char *buffer,
int use_sg)
u32 sectors)
{
unsigned char *command = us->iobuf;
unsigned char *buffer;
unsigned char thistime;
unsigned int totallen, alloclen;
int len, result, waitcount;
......@@ -217,18 +205,13 @@ static int jumpshot_write_data(struct us_data *us,
totallen = sectors * info->ssize;
// Since we don't write more than 64 KB at a time, we have to create
// a bounce buffer if the transfer uses scatter-gather. We will
// move the data a piece at a time between the bounce buffer and
// the actual transfer buffer. If we're not using scatter-gather,
// we can simply update the transfer buffer pointer to get the
// same effect.
// a bounce buffer and move the data a piece at a time between the
// bounce buffer and the actual transfer buffer.
alloclen = min(totallen, 65536u);
if (use_sg) {
buffer = kmalloc(alloclen, GFP_NOIO);
if (buffer == NULL)
return USB_STOR_TRANSPORT_ERROR;
}
buffer = kmalloc(alloclen, GFP_NOIO);
if (buffer == NULL)
return USB_STOR_TRANSPORT_ERROR;
do {
// loop, never allocate or transfer more than 64k at once
......@@ -237,10 +220,9 @@ static int jumpshot_write_data(struct us_data *us,
len = min(totallen, alloclen);
thistime = (len / info->ssize) & 0xff;
// Get the data from the transfer buffer (s-g)
if (use_sg)
usb_stor_access_xfer_buf(buffer, len, us->srb,
&sg_idx, &sg_offset, FROM_XFER_BUF);
// Get the data from the transfer buffer
usb_stor_access_xfer_buf(buffer, len, us->srb,
&sg_idx, &sg_offset, FROM_XFER_BUF);
command[0] = 0;
command[1] = thistime;
......@@ -278,21 +260,15 @@ static int jumpshot_write_data(struct us_data *us,
if (result != USB_STOR_TRANSPORT_GOOD)
US_DEBUGP("jumpshot_write_data: Gah! Waitcount = 10. Bad write!?\n");
// Update the transfer buffer pointer (!s-g)
if (!use_sg)
buffer += len;
sector += thistime;
totallen -= len;
} while (totallen > 0);
if (use_sg)
kfree(buffer);
kfree(buffer);
return result;
leave:
if (use_sg)
kfree(buffer);
kfree(buffer);
return USB_STOR_TRANSPORT_ERROR;
}
......@@ -344,34 +320,24 @@ static int jumpshot_id_device(struct us_data *us,
static int jumpshot_handle_mode_sense(struct us_data *us,
Scsi_Cmnd * srb,
unsigned char *ptr,
int sense_6)
{
unsigned char mode_param_header[8] = {
0, 0, 0, 0, 0, 0, 0, 0
};
unsigned char rw_err_page[12] = {
static unsigned char rw_err_page[12] = {
0x1, 0xA, 0x21, 1, 0, 0, 0, 0, 1, 0, 0, 0
};
unsigned char cache_page[12] = {
static unsigned char cache_page[12] = {
0x8, 0xA, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
unsigned char rbac_page[12] = {
static unsigned char rbac_page[12] = {
0x1B, 0xA, 0, 0x81, 0, 0, 0, 0, 0, 0, 0, 0
};
unsigned char timer_page[8] = {
static unsigned char timer_page[8] = {
0x1C, 0x6, 0, 0, 0, 0
};
unsigned char pc, page_code;
unsigned short total_len = 0;
unsigned short param_len, i = 0;
if (sense_6)
param_len = srb->cmnd[4];
else
param_len = ((u32) (srb->cmnd[7]) >> 8) | ((u32) (srb->cmnd[8]));
unsigned int i = 0;
struct jumpshot_info *info = (struct jumpshot_info *) (us->extra);
unsigned char *ptr = us->iobuf;
pc = srb->cmnd[2] >> 6;
page_code = srb->cmnd[2] & 0x3F;
......@@ -391,66 +357,44 @@ static int jumpshot_handle_mode_sense(struct us_data *us,
break;
}
mode_param_header[3] = 0x80; // write enable
memset(ptr, 0, 8);
if (sense_6) {
ptr[2] = 0x00; // WP enable: 0x80
i = 4;
} else {
ptr[3] = 0x00; // WP enable: 0x80
i = 8;
}
switch (page_code) {
case 0x0:
// vendor-specific mode
return USB_STOR_TRANSPORT_ERROR;
info->sense_key = 0x05;
info->sense_asc = 0x24;
info->sense_ascq = 0x00;
return USB_STOR_TRANSPORT_FAILED;
case 0x1:
total_len = sizeof(rw_err_page);
mode_param_header[0] = total_len >> 8;
mode_param_header[1] = total_len & 0xFF;
mode_param_header[3] = 0x00; // WP enable: 0x80
memcpy(ptr, mode_param_header, sizeof(mode_param_header));
i += sizeof(mode_param_header);
memcpy(ptr + i, rw_err_page, sizeof(rw_err_page));
i += sizeof(rw_err_page);
break;
case 0x8:
total_len = sizeof(cache_page);
mode_param_header[0] = total_len >> 8;
mode_param_header[1] = total_len & 0xFF;
mode_param_header[3] = 0x00; // WP enable: 0x80
memcpy(ptr, mode_param_header, sizeof(mode_param_header));
i += sizeof(mode_param_header);
memcpy(ptr + i, cache_page, sizeof(cache_page));
i += sizeof(cache_page);
break;
case 0x1B:
total_len = sizeof(rbac_page);
mode_param_header[0] = total_len >> 8;
mode_param_header[1] = total_len & 0xFF;
mode_param_header[3] = 0x00; // WP enable: 0x80
memcpy(ptr, mode_param_header, sizeof(mode_param_header));
i += sizeof(mode_param_header);
memcpy(ptr + i, rbac_page, sizeof(rbac_page));
i += sizeof(rbac_page);
break;
case 0x1C:
total_len = sizeof(timer_page);
mode_param_header[0] = total_len >> 8;
mode_param_header[1] = total_len & 0xFF;
mode_param_header[3] = 0x00; // WP enable: 0x80
memcpy(ptr, mode_param_header, sizeof(mode_param_header));
i += sizeof(mode_param_header);
memcpy(ptr + i, timer_page, sizeof(timer_page));
i += sizeof(timer_page);
break;
case 0x3F:
total_len = sizeof(timer_page) + sizeof(rbac_page) +
sizeof(cache_page) + sizeof(rw_err_page);
mode_param_header[0] = total_len >> 8;
mode_param_header[1] = total_len & 0xFF;
mode_param_header[3] = 0x00; // WP enable: 0x80
memcpy(ptr, mode_param_header, sizeof(mode_param_header));
i += sizeof(mode_param_header);
memcpy(ptr + i, timer_page, sizeof(timer_page));
i += sizeof(timer_page);
memcpy(ptr + i, rbac_page, sizeof(rbac_page));
......@@ -458,9 +402,16 @@ static int jumpshot_handle_mode_sense(struct us_data *us,
memcpy(ptr + i, cache_page, sizeof(cache_page));
i += sizeof(cache_page);
memcpy(ptr + i, rw_err_page, sizeof(rw_err_page));
i += sizeof(rw_err_page);
break;
}
if (sense_6)
ptr[0] = i - 1;
else
((u16 *) ptr)[0] = cpu_to_be16(i - 2);
usb_stor_set_xfer_buf(ptr, i, srb);
return USB_STOR_TRANSPORT_GOOD;
}
......@@ -480,8 +431,8 @@ int jumpshot_transport(Scsi_Cmnd * srb, struct us_data *us)
struct jumpshot_info *info;
int rc;
unsigned long block, blocks;
unsigned char *ptr = NULL;
unsigned char inquiry_response[36] = {
unsigned char *ptr = us->iobuf;
static unsigned char inquiry_response[8] = {
0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00
};
......@@ -496,12 +447,11 @@ int jumpshot_transport(Scsi_Cmnd * srb, struct us_data *us)
}
info = (struct jumpshot_info *) (us->extra);
ptr = (unsigned char *) srb->request_buffer;
if (srb->cmnd[0] == INQUIRY) {
US_DEBUGP("jumpshot_transport: INQUIRY. Returning bogus response.\n");
memset(inquiry_response + 8, 0, 28);
fill_inquiry_response(us, inquiry_response, 36);
memcpy(ptr, inquiry_response, sizeof(inquiry_response));
fill_inquiry_response(us, ptr, 36);
return USB_STOR_TRANSPORT_GOOD;
}
......@@ -521,15 +471,9 @@ int jumpshot_transport(Scsi_Cmnd * srb, struct us_data *us)
// build the reply
//
ptr[0] = (info->sectors >> 24) & 0xFF;
ptr[1] = (info->sectors >> 16) & 0xFF;
ptr[2] = (info->sectors >> 8) & 0xFF;
ptr[3] = (info->sectors) & 0xFF;
ptr[4] = (info->ssize >> 24) & 0xFF;
ptr[5] = (info->ssize >> 16) & 0xFF;
ptr[6] = (info->ssize >> 8) & 0xFF;
ptr[7] = (info->ssize) & 0xFF;
((u32 *) ptr)[0] = cpu_to_be32(info->sectors);
((u32 *) ptr)[1] = cpu_to_be32(info->ssize);
usb_stor_set_xfer_buf(ptr, 8, srb);
return USB_STOR_TRANSPORT_GOOD;
}
......@@ -546,7 +490,7 @@ int jumpshot_transport(Scsi_Cmnd * srb, struct us_data *us)
blocks = ((u32)(srb->cmnd[7]) << 8) | ((u32)(srb->cmnd[8]));
US_DEBUGP("jumpshot_transport: READ_10: read block 0x%04lx count %ld\n", block, blocks);
return jumpshot_read_data(us, info, block, blocks, ptr, srb->use_sg);
return jumpshot_read_data(us, info, block, blocks);
}
if (srb->cmnd[0] == READ_12) {
......@@ -559,7 +503,7 @@ int jumpshot_transport(Scsi_Cmnd * srb, struct us_data *us)
((u32)(srb->cmnd[8]) << 8) | ((u32)(srb->cmnd[9]));
US_DEBUGP("jumpshot_transport: READ_12: read block 0x%04lx count %ld\n", block, blocks);
return jumpshot_read_data(us, info, block, blocks, ptr, srb->use_sg);
return jumpshot_read_data(us, info, block, blocks);
}
if (srb->cmnd[0] == WRITE_10) {
......@@ -569,7 +513,7 @@ int jumpshot_transport(Scsi_Cmnd * srb, struct us_data *us)
blocks = ((u32)(srb->cmnd[7]) << 8) | ((u32)(srb->cmnd[8]));
US_DEBUGP("jumpshot_transport: WRITE_10: write block 0x%04lx count %ld\n", block, blocks);
return jumpshot_write_data(us, info, block, blocks, ptr, srb->use_sg);
return jumpshot_write_data(us, info, block, blocks);
}
if (srb->cmnd[0] == WRITE_12) {
......@@ -582,7 +526,7 @@ int jumpshot_transport(Scsi_Cmnd * srb, struct us_data *us)
((u32)(srb->cmnd[8]) << 8) | ((u32)(srb->cmnd[9]));
US_DEBUGP("jumpshot_transport: WRITE_12: write block 0x%04lx count %ld\n", block, blocks);
return jumpshot_write_data(us, info, block, blocks, ptr, srb->use_sg);
return jumpshot_write_data(us, info, block, blocks);
}
......@@ -592,25 +536,27 @@ int jumpshot_transport(Scsi_Cmnd * srb, struct us_data *us)
}
if (srb->cmnd[0] == REQUEST_SENSE) {
US_DEBUGP("jumpshot_transport: REQUEST_SENSE. Returning NO SENSE for now\n");
US_DEBUGP("jumpshot_transport: REQUEST_SENSE.\n");
memset(ptr, 0, 18);
ptr[0] = 0xF0;
ptr[2] = info->sense_key;
ptr[7] = 11;
ptr[12] = info->sense_asc;
ptr[13] = info->sense_ascq;
usb_stor_set_xfer_buf(ptr, 18, srb);
return USB_STOR_TRANSPORT_GOOD;
}
if (srb->cmnd[0] == MODE_SENSE) {
US_DEBUGP("jumpshot_transport: MODE_SENSE_6 detected\n");
return jumpshot_handle_mode_sense(us, srb, ptr, TRUE);
return jumpshot_handle_mode_sense(us, srb, TRUE);
}
if (srb->cmnd[0] == MODE_SENSE_10) {
US_DEBUGP("jumpshot_transport: MODE_SENSE_10 detected\n");
return jumpshot_handle_mode_sense(us, srb, ptr, FALSE);
return jumpshot_handle_mode_sense(us, srb, FALSE);
}
if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) {
......@@ -639,5 +585,8 @@ int jumpshot_transport(Scsi_Cmnd * srb, struct us_data *us)
US_DEBUGP("jumpshot_transport: Gah! Unknown command: %d (0x%x)\n",
srb->cmnd[0], srb->cmnd[0]);
return USB_STOR_TRANSPORT_ERROR;
info->sense_key = 0x05;
info->sense_asc = 0x20;
info->sense_ascq = 0x00;
return USB_STOR_TRANSPORT_FAILED;
}
......@@ -65,6 +65,16 @@ static const char* host_info(struct Scsi_Host *host)
static int slave_configure (struct scsi_device *sdev)
{
/* Scatter-gather buffers (all but the last) must have a length
* divisible by the bulk maxpacket size. Otherwise a data packet
* would end up being short, causing a premature end to the data
* transfer. Since high-speed bulk pipes have a maxpacket size
* of 512, we'll use that as the scsi device queue's DMA alignment
* mask. Guaranteeing proper alignment of the first buffer will
* have the desired effect because, except at the beginning and
* the end, scatter-gather buffers follow page boundaries. */
blk_queue_dma_alignment(sdev->request_queue, (512 - 1));
/* this is to satisify the compiler, tho I don't think the
* return code is ever checked anywhere. */
return 0;
......@@ -227,13 +237,14 @@ static int bus_reset( Scsi_Cmnd *srb )
#undef SPRINTF
#define SPRINTF(args...) \
do { if (pos < buffer+length) pos += sprintf(pos, ## args); } while (0)
#define DO_FLAG(a) \
do { if (us->flags & US_FL_##a) pos += sprintf(pos, " " #a); } while(0)
static int proc_info (struct Scsi_Host *hostptr, char *buffer, char **start, off_t offset,
int length, int inout)
{
struct us_data *us;
char *pos = buffer;
unsigned long f;
/* if someone is sending us data, just throw it away */
if (inout)
......@@ -256,17 +267,14 @@ static int proc_info (struct Scsi_Host *hostptr, char *buffer, char **start, off
/* show the device flags */
if (pos < buffer + length) {
pos += sprintf(pos, " Quirks:");
f = us->flags;
#define DO_FLAG(a) if (f & US_FL_##a) pos += sprintf(pos, " " #a)
DO_FLAG(SINGLE_LUN);
DO_FLAG(SCM_MULT_TARG);
DO_FLAG(FIX_INQUIRY);
DO_FLAG(FIX_CAPACITY);
#undef DO_FLAG
*(pos++) = '\n';
}
}
/*
* Calculate start of next buffer, and return value.
......@@ -281,6 +289,53 @@ static int proc_info (struct Scsi_Host *hostptr, char *buffer, char **start, off
return (length);
}
/***********************************************************************
* Sysfs interface
***********************************************************************/
/* Output routine for the sysfs info file */
static ssize_t show_info(struct device *dev, char *buffer)
{
char *pos = buffer;
const int length = PAGE_SIZE;
struct scsi_device *sdev = to_scsi_device(dev);
struct us_data *us = (struct us_data*)sdev->host->hostdata[0];
/* print the controller name */
SPRINTF(" Host scsi%d: usb-storage\n", sdev->host->host_no);
/* print product, vendor, and serial number strings */
SPRINTF(" Vendor: %s\n", us->vendor);
SPRINTF(" Product: %s\n", us->product);
SPRINTF("Serial Number: %s\n", us->serial);
/* show the protocol and transport */
SPRINTF(" Protocol: %s\n", us->protocol_name);
SPRINTF(" Transport: %s\n", us->transport_name);
/* show the device flags */
if (pos < buffer + length) {
pos += sprintf(pos, " Quirks:");
DO_FLAG(SINGLE_LUN);
DO_FLAG(SCM_MULT_TARG);
DO_FLAG(FIX_INQUIRY);
DO_FLAG(FIX_CAPACITY);
*(pos++) = '\n';
}
return (pos - buffer);
}
static DEVICE_ATTR(info, S_IRUGO, show_info, NULL);
static struct device_attribute *sysfs_device_attr_list[] = {
&dev_attr_info,
NULL,
};
/*
* this defines our host template, with which we'll allocate hosts
*/
......@@ -324,6 +379,9 @@ struct scsi_host_template usb_stor_host_template = {
/* emulated HBA */
.emulated = TRUE,
/* sysfs device attributes */
.sdev_attrs = sysfs_device_attr_list,
/* modify scsi_device bits on probe */
.flags = (BLIST_MS_SKIP_PAGE_08 | BLIST_MS_SKIP_PAGE_3F |
BLIST_USE_10_BYTE_MS),
......
......@@ -662,31 +662,24 @@ sddr09_read_status(struct us_data *us, unsigned char *status) {
static int
sddr09_read_data(struct us_data *us,
unsigned long address,
unsigned int sectors,
unsigned char *buffer,
int use_sg) {
unsigned int sectors) {
struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra;
unsigned char *buffer;
unsigned int lba, maxlba, pba;
unsigned int page, pages;
unsigned int len, index, offset;
int result;
// Since we only read in one block at a time, we have to create
// a bounce buffer if the transfer uses scatter-gather. We will
// move the data a piece at a time between the bounce buffer and
// the actual transfer buffer. If we're not using scatter-gather,
// we can simply update the transfer buffer pointer to get the
// same effect.
if (use_sg) {
len = min(sectors, (unsigned int) info->blocksize) *
info->pagesize;
buffer = kmalloc(len, GFP_NOIO);
if (buffer == NULL) {
printk("sddr09_read_data: Out of memory\n");
return USB_STOR_TRANSPORT_ERROR;
}
// a bounce buffer and move the data a piece at a time between the
// bounce buffer and the actual transfer buffer.
len = min(sectors, (unsigned int) info->blocksize) * info->pagesize;
buffer = kmalloc(len, GFP_NOIO);
if (buffer == NULL) {
printk("sddr09_read_data: Out of memory\n");
return USB_STOR_TRANSPORT_ERROR;
}
// Figure out the initial LBA and page
......@@ -743,21 +736,16 @@ sddr09_read_data(struct us_data *us,
break;
}
// Store the data (s-g) or update the pointer (!s-g)
if (use_sg)
usb_stor_access_xfer_buf(buffer, len, us->srb,
&index, &offset, TO_XFER_BUF);
else
buffer += len;
// Store the data in the transfer buffer
usb_stor_access_xfer_buf(buffer, len, us->srb,
&index, &offset, TO_XFER_BUF);
page = 0;
lba++;
sectors -= pages;
}
if (use_sg)
kfree(buffer);
kfree(buffer);
return result;
}
......@@ -897,14 +885,13 @@ sddr09_write_lba(struct us_data *us, unsigned int lba,
static int
sddr09_write_data(struct us_data *us,
unsigned long address,
unsigned int sectors,
unsigned char *buffer,
int use_sg) {
unsigned int sectors) {
struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra;
unsigned int lba, page, pages;
unsigned int pagelen, blocklen;
unsigned char *blockbuffer;
unsigned char *buffer;
unsigned int len, index, offset;
int result;
......@@ -923,21 +910,15 @@ sddr09_write_data(struct us_data *us,
}
// Since we don't write the user data directly to the device,
// we have to create a bounce buffer if the transfer uses
// scatter-gather. We will move the data a piece at a time
// between the bounce buffer and the actual transfer buffer.
// If we're not using scatter-gather, we can simply update
// the transfer buffer pointer to get the same effect.
if (use_sg) {
len = min(sectors, (unsigned int) info->blocksize) *
info->pagesize;
buffer = kmalloc(len, GFP_NOIO);
if (buffer == NULL) {
printk("sddr09_write_data: Out of memory\n");
kfree(blockbuffer);
return USB_STOR_TRANSPORT_ERROR;
}
// we have to create a bounce buffer and move the data a piece
// at a time between the bounce buffer and the actual transfer buffer.
len = min(sectors, (unsigned int) info->blocksize) * info->pagesize;
buffer = kmalloc(len, GFP_NOIO);
if (buffer == NULL) {
printk("sddr09_write_data: Out of memory\n");
kfree(blockbuffer);
return USB_STOR_TRANSPORT_ERROR;
}
// Figure out the initial LBA and page
......@@ -954,27 +935,21 @@ sddr09_write_data(struct us_data *us,
pages = min(sectors, info->blocksize - page);
len = (pages << info->pageshift);
// Get the data from the transfer buffer (s-g)
if (use_sg)
usb_stor_access_xfer_buf(buffer, len, us->srb,
&index, &offset, FROM_XFER_BUF);
// Get the data from the transfer buffer
usb_stor_access_xfer_buf(buffer, len, us->srb,
&index, &offset, FROM_XFER_BUF);
result = sddr09_write_lba(us, lba, page, pages,
buffer, blockbuffer);
if (result != USB_STOR_TRANSPORT_GOOD)
break;
// Update the transfer buffer pointer (!s-g)
if (!use_sg)
buffer += len;
page = 0;
lba++;
sectors -= pages;
}
if (use_sg)
kfree(buffer);
kfree(buffer);
kfree(blockbuffer);
return result;
......@@ -1402,19 +1377,19 @@ int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us)
static unsigned char sensekey = 0, sensecode = 0;
static unsigned char havefakesense = 0;
int result, i;
unsigned char *ptr;
unsigned char *ptr = us->iobuf;
unsigned long capacity;
unsigned int page, pages;
char string[64];
struct sddr09_card_info *info;
unsigned char inquiry_response[36] = {
static unsigned char inquiry_response[8] = {
0x00, 0x80, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x00
};
unsigned char mode_page_01[16] = {
0x0F, 0x00, 0, 0x00,
/* note: no block descriptor support */
static unsigned char mode_page_01[19] = {
0x00, 0x0F, 0x00, 0x0, 0x0, 0x0, 0x00,
0x01, 0x0A,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
......@@ -1428,18 +1403,14 @@ int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us)
return USB_STOR_TRANSPORT_ERROR;
}
ptr = (unsigned char *)srb->request_buffer;
if (srb->cmnd[0] == REQUEST_SENSE && havefakesense) {
/* for a faked command, we have to follow with a faked sense */
memset(ptr, 0, srb->request_bufflen);
if (srb->request_bufflen > 7) {
ptr[0] = 0x70;
ptr[2] = sensekey;
ptr[7] = srb->request_bufflen - 7;
}
if (srb->request_bufflen > 12)
ptr[12] = sensecode;
memset(ptr, 0, 18);
ptr[0] = 0x70;
ptr[2] = sensekey;
ptr[7] = 11;
ptr[12] = sensecode;
usb_stor_set_xfer_buf(ptr, 18, srb);
sensekey = sensecode = havefakesense = 0;
return USB_STOR_TRANSPORT_GOOD;
}
......@@ -1450,8 +1421,8 @@ int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us)
respond to INQUIRY commands */
if (srb->cmnd[0] == INQUIRY) {
memset(inquiry_response+8, 0, 28);
fill_inquiry_response(us, inquiry_response, 36);
memcpy(ptr, inquiry_response, 8);
fill_inquiry_response(us, ptr, 36);
return USB_STOR_TRANSPORT_GOOD;
}
......@@ -1486,43 +1457,30 @@ int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us)
capacity = (info->lbact << info->blockshift) - 1;
ptr[0] = MSB_of(capacity>>16);
ptr[1] = LSB_of(capacity>>16);
ptr[2] = MSB_of(capacity&0xFFFF);
ptr[3] = LSB_of(capacity&0xFFFF);
((u32 *) ptr)[0] = cpu_to_be32(capacity);
// Report page size
ptr[4] = MSB_of(info->pagesize>>16);
ptr[5] = LSB_of(info->pagesize>>16);
ptr[6] = MSB_of(info->pagesize&0xFFFF);
ptr[7] = LSB_of(info->pagesize&0xFFFF);
((u32 *) ptr)[1] = cpu_to_be32(info->pagesize);
usb_stor_set_xfer_buf(ptr, 8, srb);
return USB_STOR_TRANSPORT_GOOD;
}
if (srb->cmnd[0] == MODE_SENSE || srb->cmnd[0] == MODE_SENSE_10) {
if (srb->cmnd[0] == MODE_SENSE_10) {
int modepage = (srb->cmnd[2] & 0x3F);
int len;
/* They ask for the Read/Write error recovery page,
or for all pages. Give as much as they have room for. */
or for all pages. */
/* %% We should check DBD %% */
if (modepage == 0x01 || modepage == 0x3F) {
US_DEBUGP("SDDR09: Dummy up request for "
"mode page 0x%x\n", modepage);
if (ptr == NULL)
return USB_STOR_TRANSPORT_ERROR;
len = srb->request_bufflen;
if (len > sizeof(mode_page_01))
len = sizeof(mode_page_01);
mode_page_01[0] = sizeof(mode_page_01) - 1;
mode_page_01[2] = (info->flags & SDDR09_WP) ? 0x80 : 0;
memcpy(ptr, mode_page_01, len);
memcpy(ptr, mode_page_01, sizeof(mode_page_01));
((u16*)ptr)[0] = sizeof(mode_page_01) - 2;
ptr[3] = (info->flags & SDDR09_WP) ? 0x80 : 0;
usb_stor_set_xfer_buf(ptr, sizeof(mode_page_01), srb);
return USB_STOR_TRANSPORT_GOOD;
}
......@@ -1546,7 +1504,7 @@ int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us)
US_DEBUGP("READ_10: read page %d pagect %d\n",
page, pages);
return sddr09_read_data(us, page, pages, ptr, srb->use_sg);
return sddr09_read_data(us, page, pages);
}
if (srb->cmnd[0] == WRITE_10) {
......@@ -1559,11 +1517,12 @@ int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us)
US_DEBUGP("WRITE_10: write page %d pagect %d\n",
page, pages);
return sddr09_write_data(us, page, pages, ptr, srb->use_sg);
return sddr09_write_data(us, page, pages);
}
// Pass TEST_UNIT_READY and REQUEST_SENSE through
/* catch-all for all other commands, except
* pass TEST_UNIT_READY and REQUEST_SENSE through
*/
if (srb->cmnd[0] != TEST_UNIT_READY &&
srb->cmnd[0] != REQUEST_SENSE) {
sensekey = 0x05; /* illegal request */
......@@ -1577,11 +1536,11 @@ int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us)
srb->cmnd[1] = LUNBITS;
string[0] = 0;
ptr[0] = 0;
for (i=0; i<12; i++)
sprintf(string+strlen(string), "%02X ", srb->cmnd[i]);
sprintf(ptr+strlen(ptr), "%02X ", srb->cmnd[i]);
US_DEBUGP("SDDR09: Send control for command %s\n", string);
US_DEBUGP("SDDR09: Send control for command %s\n", ptr);
result = sddr09_send_scsi_command(us, srb->cmnd, 12);
if (result != USB_STOR_TRANSPORT_GOOD) {
......
......@@ -153,14 +153,13 @@ static int sddr55_status(struct us_data *us)
static int sddr55_read_data(struct us_data *us,
unsigned int lba,
unsigned int page,
unsigned short sectors,
unsigned char *buffer,
int use_sg) {
unsigned short sectors) {
int result = USB_STOR_TRANSPORT_GOOD;
unsigned char *command = us->iobuf;
unsigned char *status = us->iobuf;
struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra;
unsigned char *buffer;
unsigned int pba;
unsigned long address;
......@@ -169,20 +168,14 @@ static int sddr55_read_data(struct us_data *us,
unsigned int len, index, offset;
// Since we only read in one block at a time, we have to create
// a bounce buffer if the transfer uses scatter-gather. We will
// move the data a piece at a time between the bounce buffer and
// the actual transfer buffer. If we're not using scatter-gather,
// we can simply update the transfer buffer pointer to get the
// same effect.
if (use_sg) {
len = min((unsigned int) sectors,
(unsigned int) info->blocksize >>
info->smallpageshift) * PAGESIZE;
buffer = kmalloc(len, GFP_NOIO);
if (buffer == NULL)
return USB_STOR_TRANSPORT_ERROR; /* out of memory */
}
// a bounce buffer and move the data a piece at a time between the
// bounce buffer and the actual transfer buffer.
len = min((unsigned int) sectors, (unsigned int) info->blocksize >>
info->smallpageshift) * PAGESIZE;
buffer = kmalloc(len, GFP_NOIO);
if (buffer == NULL)
return USB_STOR_TRANSPORT_ERROR; /* out of memory */
index = offset = 0;
while (sectors>0) {
......@@ -258,12 +251,9 @@ static int sddr55_read_data(struct us_data *us,
}
}
// Store the data (s-g) or update the pointer (!s-g)
if (use_sg)
usb_stor_access_xfer_buf(buffer, len, us->srb,
&index, &offset, TO_XFER_BUF);
else
buffer += len;
// Store the data in the transfer buffer
usb_stor_access_xfer_buf(buffer, len, us->srb,
&index, &offset, TO_XFER_BUF);
page = 0;
lba++;
......@@ -273,8 +263,7 @@ static int sddr55_read_data(struct us_data *us,
result = USB_STOR_TRANSPORT_GOOD;
leave:
if (use_sg)
kfree(buffer);
kfree(buffer);
return result;
}
......@@ -282,14 +271,13 @@ static int sddr55_read_data(struct us_data *us,
static int sddr55_write_data(struct us_data *us,
unsigned int lba,
unsigned int page,
unsigned short sectors,
unsigned char *buffer,
int use_sg) {
unsigned short sectors) {
int result = USB_STOR_TRANSPORT_GOOD;
unsigned char *command = us->iobuf;
unsigned char *status = us->iobuf;
struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra;
unsigned char *buffer;
unsigned int pba;
unsigned int new_pba;
......@@ -306,20 +294,14 @@ static int sddr55_write_data(struct us_data *us,
}
// Since we only write one block at a time, we have to create
// a bounce buffer if the transfer uses scatter-gather. We will
// move the data a piece at a time between the bounce buffer and
// the actual transfer buffer. If we're not using scatter-gather,
// we can simply update the transfer buffer pointer to get the
// same effect.
if (use_sg) {
len = min((unsigned int) sectors,
(unsigned int) info->blocksize >>
info->smallpageshift) * PAGESIZE;
buffer = kmalloc(len, GFP_NOIO);
if (buffer == NULL)
return USB_STOR_TRANSPORT_ERROR;
}
// a bounce buffer and move the data a piece at a time between the
// bounce buffer and the actual transfer buffer.
len = min((unsigned int) sectors, (unsigned int) info->blocksize >>
info->smallpageshift) * PAGESIZE;
buffer = kmalloc(len, GFP_NOIO);
if (buffer == NULL)
return USB_STOR_TRANSPORT_ERROR;
index = offset = 0;
while (sectors > 0) {
......@@ -336,10 +318,9 @@ static int sddr55_write_data(struct us_data *us,
info->blocksize - page);
len = pages << info->pageshift;
// Get the data from the transfer buffer (s-g)
if (use_sg)
usb_stor_access_xfer_buf(buffer, len, us->srb,
&index, &offset, FROM_XFER_BUF);
// Get the data from the transfer buffer
usb_stor_access_xfer_buf(buffer, len, us->srb,
&index, &offset, FROM_XFER_BUF);
US_DEBUGP("Write %02X pages, to PBA %04X"
" (LBA %04X) page %02X\n",
......@@ -480,9 +461,6 @@ static int sddr55_write_data(struct us_data *us,
/* update the pba<->lba maps for new_pba */
info->pba_to_lba[new_pba] = lba % 1000;
// Update the transfer buffer pointer (!s-g)
if (!use_sg)
buffer += len;
page = 0;
lba++;
sectors -= pages >> info->smallpageshift;
......@@ -490,8 +468,7 @@ static int sddr55_write_data(struct us_data *us,
result = USB_STOR_TRANSPORT_GOOD;
leave:
if (use_sg)
kfree(buffer);
kfree(buffer);
return result;
}
......@@ -760,16 +737,16 @@ static void sddr55_card_info_destructor(void *extra) {
int sddr55_transport(Scsi_Cmnd *srb, struct us_data *us)
{
int result;
int i;
unsigned char inquiry_response[36] = {
static unsigned char inquiry_response[8] = {
0x00, 0x80, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x00
};
unsigned char mode_page_01[16] = { // write-protected for now
0x03, 0x00, 0x80, 0x00,
// write-protected for now, no block descriptor support
static unsigned char mode_page_01[20] = {
0x0, 0x12, 0x00, 0x80, 0x0, 0x0, 0x0, 0x0,
0x01, 0x0A,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
unsigned char *ptr;
unsigned char *ptr = us->iobuf;
unsigned long capacity;
unsigned int lba;
unsigned int pba;
......@@ -788,21 +765,13 @@ int sddr55_transport(Scsi_Cmnd *srb, struct us_data *us)
info = (struct sddr55_card_info *)(us->extra);
ptr = (unsigned char *)srb->request_buffer;
if (srb->cmnd[0] == REQUEST_SENSE) {
i = srb->cmnd[4];
if (i > sizeof info->sense_data)
i = sizeof info->sense_data;
US_DEBUGP("SDDR55: request sense %02x/%02x/%02x\n", info->sense_data[2], info->sense_data[12], info->sense_data[13]);
info->sense_data[0] = 0x70;
info->sense_data[7] = 10;
memcpy (ptr, info->sense_data, i);
memcpy (ptr, info->sense_data, sizeof info->sense_data);
ptr[0] = 0x70;
ptr[7] = 11;
usb_stor_set_xfer_buf (ptr, sizeof info->sense_data, srb);
memset (info->sense_data, 0, sizeof info->sense_data);
return USB_STOR_TRANSPORT_GOOD;
......@@ -814,8 +783,8 @@ int sddr55_transport(Scsi_Cmnd *srb, struct us_data *us)
respond to INQUIRY commands */
if (srb->cmnd[0] == INQUIRY) {
memset(inquiry_response+8, 0, 28);
fill_inquiry_response(us, inquiry_response, 36);
memcpy(ptr, inquiry_response, 8);
fill_inquiry_response(us, ptr, 36);
return USB_STOR_TRANSPORT_GOOD;
}
......@@ -858,64 +827,40 @@ int sddr55_transport(Scsi_Cmnd *srb, struct us_data *us)
* the fact that only 250 out of every 256 are used */
info->max_log_blks = ((info->capacity >> (info->pageshift + info->blockshift)) / 256) * 250;
/* Last page in the card, adjust as we only use 250 out of every 256 pages */
/* Last page in the card, adjust as we only use 250 out of
* every 256 pages */
capacity = (capacity / 256) * 250;
capacity /= PAGESIZE;
capacity--;
ptr[0] = MSB_of(capacity>>16);
ptr[1] = LSB_of(capacity>>16);
ptr[2] = MSB_of(capacity&0xFFFF);
ptr[3] = LSB_of(capacity&0xFFFF);
// The page size
ptr[4] = MSB_of(PAGESIZE>>16);
ptr[5] = LSB_of(PAGESIZE>>16);
ptr[6] = MSB_of(PAGESIZE&0xFFFF);
ptr[7] = LSB_of(PAGESIZE&0xFFFF);
((u32 *) ptr)[0] = cpu_to_be32(capacity);
((u32 *) ptr)[1] = cpu_to_be32(PAGESIZE);
usb_stor_set_xfer_buf(ptr, 8, srb);
sddr55_read_map(us);
return USB_STOR_TRANSPORT_GOOD;
}
if (srb->cmnd[0] == MODE_SENSE) {
if (srb->cmnd[0] == MODE_SENSE_10) {
mode_page_01[2] = (info->read_only || info->force_read_only) ? 0x80 : 0;
memcpy(ptr, mode_page_01, sizeof mode_page_01);
ptr[3] = (info->read_only || info->force_read_only) ? 0x80 : 0;
usb_stor_set_xfer_buf(ptr, sizeof(mode_page_01), srb);
if ( (srb->cmnd[2] & 0x3F) == 0x01 ) {
US_DEBUGP(
"SDDR55: Dummy up request for mode page 1\n");
if (ptr==NULL ||
srb->request_bufflen<sizeof(mode_page_01)) {
set_sense_info (5, 0x24, 0); /* invalid field in command */
return USB_STOR_TRANSPORT_FAILED;
}
memcpy(ptr, mode_page_01, sizeof(mode_page_01));
return USB_STOR_TRANSPORT_GOOD;
} else if ( (srb->cmnd[2] & 0x3F) == 0x3F ) {
US_DEBUGP(
"SDDR55: Dummy up request for all mode pages\n");
if (ptr==NULL ||
srb->request_bufflen<sizeof(mode_page_01)) {
set_sense_info (5, 0x24, 0); /* invalid field in command */
return USB_STOR_TRANSPORT_FAILED;
}
memcpy(ptr, mode_page_01, sizeof(mode_page_01));
return USB_STOR_TRANSPORT_GOOD;
}
set_sense_info (5, 0x24, 0); /* invalid field in command */
return USB_STOR_TRANSPORT_FAILED;
}
......@@ -963,13 +908,13 @@ int sddr55_transport(Scsi_Cmnd *srb, struct us_data *us)
" pages %d\n",
pba, lba, page, pages);
return sddr55_write_data(us, lba, page, pages, ptr, srb->use_sg);
return sddr55_write_data(us, lba, page, pages);
} else {
US_DEBUGP("READ_10: read block %04X (LBA %04X) page %01X"
" pages %d\n",
pba, lba, page, pages);
return sddr55_read_data(us, lba, page, pages, ptr, srb->use_sg);
return sddr55_read_data(us, lba, page, pages);
}
}
......
......@@ -569,21 +569,15 @@ int usbat_handle_read10(struct us_data *us,
}
// Since we only read in one block at a time, we have to create
// a bounce buffer if the transfer uses scatter-gather. We will
// move the data a piece at a time between the bounce buffer and
// the actual transfer buffer. If we're not using scatter-gather,
// we can simply update the transfer buffer pointer to get the
// same effect.
// a bounce buffer and move the data a piece at a time between the
// bounce buffer and the actual transfer buffer.
len = (65535/srb->transfersize) * srb->transfersize;
US_DEBUGP("Max read is %d bytes\n", len);
len = min(len, srb->request_bufflen);
if (srb->use_sg) {
buffer = kmalloc(len, GFP_NOIO);
if (buffer == NULL) // bloody hell!
return USB_STOR_TRANSPORT_FAILED;
} else
buffer = srb->request_buffer;
buffer = kmalloc(len, GFP_NOIO);
if (buffer == NULL) // bloody hell!
return USB_STOR_TRANSPORT_FAILED;
sector = short_pack(data[7+3], data[7+2]);
sector <<= 16;
sector |= short_pack(data[7+5], data[7+4]);
......@@ -621,12 +615,9 @@ int usbat_handle_read10(struct us_data *us,
if (result != USB_STOR_TRANSPORT_GOOD)
break;
// Store the data (s-g) or update the pointer (!s-g)
if (srb->use_sg)
usb_stor_access_xfer_buf(buffer, len, srb,
&sg_segment, &sg_offset, TO_XFER_BUF);
else
buffer += len;
// Store the data in the transfer buffer
usb_stor_access_xfer_buf(buffer, len, srb,
&sg_segment, &sg_offset, TO_XFER_BUF);
// Update the amount transferred and the sector number
......@@ -635,8 +626,7 @@ int usbat_handle_read10(struct us_data *us,
} // while transferred != srb->request_bufflen
if (srb->use_sg)
kfree(buffer);
kfree(buffer);
return result;
}
......
......@@ -1089,6 +1089,9 @@ static int usb_stor_reset_common(struct us_data *us,
return FAILED;
}
/* permit the clear-halt transfers to take place */
clear_bit(US_FLIDX_ABORTING, &us->flags);
US_DEBUGP("Soft reset: clearing bulk-in endpoint halt\n");
result = usb_stor_clear_halt(us, us->recv_bulk_pipe);
......
......@@ -45,13 +45,6 @@
*
*/
/* Patch submitted by Martin Berentsen <berentsen at sent5 dot uni-duisburg dot de> */
#define US_FL_START_STOP 0x00000004 /* ignore START_STOP commands */
UNUSUAL_DEV( 0x0686, 0x4014, 0x0001, 0x0001,
"Minolta",
"Dimage S414",
US_SC_SCSI, US_PR_BULK, NULL, US_FL_START_STOP),
UNUSUAL_DEV( 0x03ee, 0x0000, 0x0000, 0x0245,
"Mitsumi",
"CD-R/RW Drive",
......@@ -296,7 +289,7 @@ UNUSUAL_DEV( 0x054c, 0x0069, 0x0000, 0x9999,
UNUSUAL_DEV( 0x054c, 0x006d, 0x0000, 0x9999,
"Sony",
"PEG Mass Storage",
US_SC_8070, US_PR_CBI, NULL,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_FIX_INQUIRY ),
UNUSUAL_DEV( 0x057b, 0x0000, 0x0000, 0x0299,
......@@ -377,7 +370,7 @@ UNUSUAL_DEV( 0x05e3, 0x0700, 0x0000, 0xffff,
UNUSUAL_DEV( 0x05e3, 0x0701, 0x0000, 0xffff,
"",
"USB TO IDE",
US_SC_SCSI, US_PR_BULK, NULL,
US_SC_SCSI, US_PR_DEVICE, NULL,
US_FL_MODE_XLATE ),
/* Reported by Peter Marks <peter.marks@turner.com>
......@@ -643,7 +636,7 @@ UNUSUAL_DEV( 0x08ca, 0x2011, 0x0001, 0x0001,
UNUSUAL_DEV( 0x090c, 0x1132, 0x0000, 0xffff,
"Feiya",
"5-in-1 Card Reader",
US_SC_SCSI, US_PR_BULK, NULL,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_FIX_CAPACITY ),
UNUSUAL_DEV( 0x097a, 0x0001, 0x0000, 0x0001,
......@@ -671,6 +664,13 @@ UNUSUAL_DEV( 0x0a17, 0x0004, 0x1000, 0x1000,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_FIX_INQUIRY ),
/* This entry from <matthias@ma-c.de> in the Debian mailing list */
UNUSUAL_DEV( 0x0a17, 0x0006, 0x0000, 0xffff,
"Pentax",
"Optio 330GS",
US_SC_8070, US_PR_CB, NULL,
US_FL_MODE_XLATE | US_FL_FIX_INQUIRY ),
/* Submitted by Per Winkvist <per.winkvist@uk.com> */
UNUSUAL_DEV( 0x0a17, 0x006, 0x1000, 0x9009,
"Pentax",
......
......@@ -205,14 +205,11 @@ static inline void usb_skel_debug_data (const char *function, int size, const un
*/
static inline void skel_delete (struct usb_skel *dev)
{
if (dev->bulk_in_buffer != NULL)
kfree (dev->bulk_in_buffer);
if (dev->bulk_out_buffer != NULL)
usb_buffer_free (dev->udev, dev->bulk_out_size,
kfree (dev->bulk_in_buffer);
usb_buffer_free (dev->udev, dev->bulk_out_size,
dev->bulk_out_buffer,
dev->write_urb->transfer_dma);
if (dev->write_urb != NULL)
usb_free_urb (dev->write_urb);
usb_free_urb (dev->write_urb);
kfree (dev);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment