From 60f780528f3ae603eb169a221628b93b6c6929f9 Mon Sep 17 00:00:00 2001 From: Luca Risolia Date: Mon, 6 Feb 2006 16:29:35 +0000 Subject: [PATCH] USB: Add ZC0301 Video4Linux2 driver This patch adds a Video4Linux2 driver for ZC0301 Image Processor and Control Chip. Signed-off-by: Luca Risolia Signed-off-by: Greg Kroah-Hartman --- Documentation/usb/zc0301.txt | 250 +++++ MAINTAINERS | 8 + drivers/usb/Makefile | 1 + drivers/usb/media/Kconfig | 15 + drivers/usb/media/Makefile | 2 + drivers/usb/media/zc0301.h | 185 ++++ drivers/usb/media/zc0301_core.c | 2028 ++++++++++++++++++++++++++++++++++ drivers/usb/media/zc0301_pas202bcb.c | 353 ++++++ drivers/usb/media/zc0301_sensor.h | 98 ++ 9 files changed, 2940 insertions(+) create mode 100644 Documentation/usb/zc0301.txt create mode 100644 drivers/usb/media/zc0301.h create mode 100644 drivers/usb/media/zc0301_core.c create mode 100644 drivers/usb/media/zc0301_pas202bcb.c create mode 100644 drivers/usb/media/zc0301_sensor.h diff --git a/Documentation/usb/zc0301.txt b/Documentation/usb/zc0301.txt new file mode 100644 index 000000000000..10590bf625e4 --- /dev/null +++ b/Documentation/usb/zc0301.txt @@ -0,0 +1,250 @@ + + ZC0301 Image Processor and Control Chip + Driver for Linux + ======================================= + + - Documentation - + + +Index +===== +1. Copyright +2. Disclaimer +3. License +4. Overview and features +5. Module dependencies +6. Module loading +7. Module parameters +8. Supported devices +9. Notes for V4L2 application developers +10. Contact information +11. Credits + + +1. Copyright +============ +Copyright (C) 2006 by Luca Risolia + + +2. Disclaimer +============= +This software is not developed or sponsored by Z-Star Microelectronics Corp. +Trademarks are property of their respective owner. + + +3. License +========== +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + +4. Overview and features +======================== +This driver supports the video interface of the devices mounting the ZC0301 +Image Processor and Control Chip. + +The driver relies on the Video4Linux2 and USB core modules. It has been +designed to run properly on SMP systems as well. + +The latest version of the ZC0301 driver can be found at the following URL: +http://www.linux-projects.org/ + +Some of the features of the driver are: + +- full compliance with the Video4Linux2 API (see also "Notes for V4L2 + application developers" paragraph); +- available mmap or read/poll methods for video streaming through isochronous + data transfers; +- automatic detection of image sensor; +- video formats is standard JPEG in various compression qualities + (see also "Notes for V4L2 application developers" paragraph); +- full support for the capabilities of every possible image sensors that can + be connected to the ZC0301 bridges, including, for istance, red, green, + blue and global gain adjustments and exposure control (see "Supported + devices" paragraph for details); +- use of default color settings for sunlight conditions; +- dynamic driver control thanks to various module parameters (see "Module + parameters" paragraph); +- up to 64 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 + the system supports hotplugging; + + +5. Module dependencies +====================== +For it to work properly, the driver needs kernel support for Video4Linux and +USB. + +The following options of the kernel configuration file must be enabled and +corresponding modules must be compiled: + + # Multimedia devices + # + CONFIG_VIDEO_DEV=m + + # USB support + # + CONFIG_USB=m + +In addition, depending on the hardware being used, the modules below are +necessary: + + # USB Host Controller Drivers + # + CONFIG_USB_EHCI_HCD=m + CONFIG_USB_UHCI_HCD=m + CONFIG_USB_OHCI_HCD=m + +The ZC0301 controller also provides a built-in microphone interface. It is +supported by the USB Audio driver thanks to the ALSA API: + + # Sound + # + CONFIG_SOUND=y + + # Advanced Linux Sound Architecture + # + CONFIG_SND=m + + # USB devices + # + CONFIG_SND_USB_AUDIO=m + +And finally: + + # USB Multimedia devices + # + CONFIG_USB_ZC0301=m + + +6. Module loading +================= +To use the driver, it is necessary to load the "zc0301" module into memory +after every other module required: "videodev", "usbcore" and, depending on +the USB host controller you have, "ehci-hcd", "uhci-hcd" or "ohci-hcd". + +Loading can be done as shown below: + + [root@localhost home]# modprobe zc0301 + +At this point the devices should be recognized. You can invoke "dmesg" to +analyze kernel messages and verify that the loading process has gone well: + + [user@localhost home]$ dmesg + + +7. Module parameters +==================== +Module parameters are listed below: +------------------------------------------------------------------------------- +Name: video_nr +Type: short array (min = 0, max = 64) +Syntax: <-1|n[,...]> +Description: Specify V4L2 minor mode number: + -1 = use next available + n = use minor number n + You can specify up to 64 cameras this way. + For example: + video_nr=-1,2,-1 would assign minor number 2 to the second + registered camera and use auto for the first one and for every + other camera. +Default: -1 +------------------------------------------------------------------------------- +Name: force_munmap +Type: bool array (min = 0, max = 64) +Syntax: <0|1[,...]> +Description: Force the application to unmap previously mapped buffer memory + before calling any VIDIOC_S_CROP or VIDIOC_S_FMT ioctl's. Not + all the applications support this feature. This parameter is + specific for each detected camera. + 0 = do not force memory unmapping + 1 = force memory unmapping (save memory) +Default: 0 +------------------------------------------------------------------------------- +Name: debug +Type: ushort +Syntax: +Description: Debugging information level, from 0 to 3: + 0 = none (use carefully) + 1 = critical errors + 2 = significant informations + 3 = more verbose messages + Level 3 is useful for testing only, when only one device + is used at the same time. It also shows some more informations + about the hardware being detected. This module parameter can be + changed at runtime thanks to the /sys filesystem interface. +Default: 2 +------------------------------------------------------------------------------- + + +8. Supported devices +==================== +None of the names of the companies as well as their products will be mentioned +here. They have never collaborated with the author, so no advertising. + +From the point of view of a driver, what unambiguously identify a device are +its vendor and product USB identifiers. Below is a list of known identifiers of +devices mounting the ZC0301 Image Processor and Control Chips: + +Vendor ID Product ID +--------- ---------- +0x046d 0x08ae + +The following image sensors are supported: + +Model Manufacturer +----- ------------ +PAS202BCB PixArt Imaging, Inc. + +All the available control settings of each image sensor are supported through +the V4L2 interface. + + +9. Notes for V4L2 application developers +======================================== +This driver follows the V4L2 API specifications. In particular, it enforces two +rules: + +- exactly one I/O method, either "mmap" or "read", is associated with each +file descriptor. Once it is selected, the application must close and reopen the +device to switch to the other I/O method; + +- although it is not mandatory, previously mapped buffer memory should always +be unmapped before calling any "VIDIOC_S_CROP" or "VIDIOC_S_FMT" ioctl's. +The same number of buffers as before will be allocated again to match the size +of the new video frames, so you have to map the buffers again before any I/O +attempts on them. + +This driver supports the standard JPEG video format. The current compression +quality may vary from 0 to 3 and can be selected or queried thanks to the +VIDIOC_S_JPEGCOMP and VIDIOC_G_JPEGCOMP V4L2 ioctl's. + + +10. Contact information +======================= +The author may be contacted by e-mail at . + +GPG/PGP encrypted e-mail's are accepted. The GPG key ID of the author is +'FCE635A4'; the public 1024-bit key should be available at any keyserver; +the fingerprint is: '88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4'. + + +11. Credits +=========== +- Informations about the chip internals to enable the I2C protocol have been + taken from the documentation of the ZC030x Video4Linux1 driver written by + Andrew Birkett ; +- Initialization values of the ZC0301 connected to the PAS202BCB image sensor + have been taken from the SPCA5XX driver maintained by + Michel Xhaard diff --git a/MAINTAINERS b/MAINTAINERS index 8db5c339845d..ad0d77cbfbc3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2902,6 +2902,14 @@ L: video4linux-list@redhat.com W: http://www.linux-projects.org S: Maintained +USB ZC0301 DRIVER +P: Luca Risolia +M: luca.risolia@studio.unibo.it +L: linux-usb-devel@lists.sourceforge.net +L: video4linux-list@redhat.com +W: http://www.linux-projects.org +S: Maintained + USB ZD1201 DRIVER P: Jeroen Vreeken M: pe1rxq@amsat.org diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 36e476dd9123..bade87b62cf9 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_USB_SN9C102) += media/ obj-$(CONFIG_USB_STV680) += media/ obj-$(CONFIG_USB_VICAM) += media/ obj-$(CONFIG_USB_W9968CF) += media/ +obj-$(CONFIG_USB_ZC0301) += media/ obj-$(CONFIG_USB_CATC) += net/ obj-$(CONFIG_USB_KAWETH) += net/ diff --git a/drivers/usb/media/Kconfig b/drivers/usb/media/Kconfig index 0d3d2cc5d7be..189d40f96be5 100644 --- a/drivers/usb/media/Kconfig +++ b/drivers/usb/media/Kconfig @@ -191,6 +191,21 @@ config USB_W9968CF To compile this driver as a module, choose M here: the module will be called w9968cf. +config USB_ZC0301 + tristate "USB ZC0301 Image Processor and Control Chip support" + depends on USB && VIDEO_DEV + ---help--- + Say Y here if you want support for cameras based on the ZC0301 + Image Processor and Control Chip. + + See for more informations. + + This driver uses the Video For Linux API. You must say Y or M to + "Video For Linux" to use this driver. + + To compile this driver as a module, choose M here: the + module will be called zc0301. + config USB_PWC tristate "USB Philips Cameras" depends on USB && VIDEO_DEV diff --git a/drivers/usb/media/Makefile b/drivers/usb/media/Makefile index 3957aa1be0f2..333bf09ca2d6 100644 --- a/drivers/usb/media/Makefile +++ b/drivers/usb/media/Makefile @@ -4,6 +4,7 @@ sn9c102-objs := sn9c102_core.o sn9c102_hv7131d.o sn9c102_mi0343.o sn9c102_ov7630.o sn9c102_pas106b.o sn9c102_pas202bcb.o sn9c102_tas5110c1b.o sn9c102_tas5130d1b.o et61x251-objs := et61x251_core.o et61x251_tas5130d1b.o +zc0301-objs := zc0301_core.o zc0301_pas202bcb.o obj-$(CONFIG_USB_DABUSB) += dabusb.o obj-$(CONFIG_USB_DSBR) += dsbr100.o @@ -16,4 +17,5 @@ obj-$(CONFIG_USB_SN9C102) += sn9c102.o obj-$(CONFIG_USB_STV680) += stv680.o obj-$(CONFIG_USB_VICAM) += vicam.o usbvideo.o obj-$(CONFIG_USB_W9968CF) += w9968cf.o +obj-$(CONFIG_USB_ZC0301) += zc0301.o obj-$(CONFIG_USB_PWC) += pwc/ diff --git a/drivers/usb/media/zc0301.h b/drivers/usb/media/zc0301.h new file mode 100644 index 000000000000..6fa5075f1e3d --- /dev/null +++ b/drivers/usb/media/zc0301.h @@ -0,0 +1,185 @@ +/*************************************************************************** + * V4L2 driver for ZC0301 Image Processor and Control Chip * + * * + * Copyright (C) 2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#ifndef _ZC0301_H_ +#define _ZC0301_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zc0301_sensor.h" + +/*****************************************************************************/ + +#define ZC0301_DEBUG +#define ZC0301_DEBUG_LEVEL 2 +#define ZC0301_MAX_DEVICES 64 +#define ZC0301_FORCE_MUNMAP 0 +#define ZC0301_MAX_FRAMES 32 +#define ZC0301_COMPRESSION_QUALITY 2 +#define ZC0301_URBS 2 +#define ZC0301_ISO_PACKETS 7 +#define ZC0301_ALTERNATE_SETTING 7 +#define ZC0301_URB_TIMEOUT msecs_to_jiffies(2 * ZC0301_ISO_PACKETS) +#define ZC0301_CTRL_TIMEOUT 100 +#define ZC0301_FRAME_TIMEOUT 2 * 1000 * msecs_to_jiffies(1) + +/*****************************************************************************/ + +ZC0301_ID_TABLE +ZC0301_SENSOR_TABLE + +enum zc0301_frame_state { + F_UNUSED, + F_QUEUED, + F_GRABBING, + F_DONE, + F_ERROR, +}; + +struct zc0301_frame_t { + void* bufmem; + struct v4l2_buffer buf; + enum zc0301_frame_state state; + struct list_head frame; + unsigned long vma_use_count; +}; + +enum zc0301_dev_state { + DEV_INITIALIZED = 0x01, + DEV_DISCONNECTED = 0x02, + DEV_MISCONFIGURED = 0x04, +}; + +enum zc0301_io_method { + IO_NONE, + IO_READ, + IO_MMAP, +}; + +enum zc0301_stream_state { + STREAM_OFF, + STREAM_INTERRUPT, + STREAM_ON, +}; + +struct zc0301_module_param { + u8 force_munmap; +}; + +static DECLARE_RWSEM(zc0301_disconnect); + +struct zc0301_device { + struct video_device* v4ldev; + + struct zc0301_sensor* sensor; + + struct usb_device* usbdev; + struct urb* urb[ZC0301_URBS]; + void* transfer_buffer[ZC0301_URBS]; + u8* control_buffer; + + struct zc0301_frame_t *frame_current, frame[ZC0301_MAX_FRAMES]; + struct list_head inqueue, outqueue; + u32 frame_count, nbuffers, nreadbuffers; + + enum zc0301_io_method io; + enum zc0301_stream_state stream; + + struct v4l2_jpegcompression compression; + + struct zc0301_module_param module_param; + + enum zc0301_dev_state state; + u8 users; + + struct mutex dev_mutex, fileop_mutex; + spinlock_t queue_lock; + wait_queue_head_t open, wait_frame, wait_stream; +}; + +/*****************************************************************************/ + +void +zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor) +{ + cam->sensor = sensor; + cam->sensor->usbdev = cam->usbdev; +} + +/*****************************************************************************/ + +#undef DBG +#undef KDBG +#ifdef ZC0301_DEBUG +# define DBG(level, fmt, args...) \ +do { \ + if (debug >= (level)) { \ + if ((level) == 1) \ + dev_err(&cam->usbdev->dev, fmt "\n", ## args); \ + else if ((level) == 2) \ + dev_info(&cam->usbdev->dev, fmt "\n", ## args); \ + else if ((level) >= 3) \ + dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args); \ + } \ +} while (0) +# define KDBG(level, fmt, args...) \ +do { \ + if (debug >= (level)) { \ + if ((level) == 1 || (level) == 2) \ + pr_info("zc0301: " fmt "\n", ## args); \ + else if ((level) == 3) \ + pr_debug("zc0301: [%s:%d] " fmt "\n", __FUNCTION__, \ + __LINE__ , ## args); \ + } \ +} while (0) +# define V4LDBG(level, name, cmd) \ +do { \ + if (debug >= (level)) \ + v4l_print_ioctl(name, cmd); \ +} while (0) +#else +# define DBG(level, fmt, args...) do {;} while(0) +# define KDBG(level, fmt, args...) do {;} while(0) +# define V4LDBG(level, name, cmd) do {;} while(0) +#endif + +#undef PDBG +#define PDBG(fmt, args...) \ +dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args) + +#undef PDBGG +#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */ + +#endif /* _ZC0301_H_ */ diff --git a/drivers/usb/media/zc0301_core.c b/drivers/usb/media/zc0301_core.c new file mode 100644 index 000000000000..238f60f7f472 --- /dev/null +++ b/drivers/usb/media/zc0301_core.c @@ -0,0 +1,2028 @@ +/*************************************************************************** + * Video4Linux2 driver for ZC0301 Image Processor and Control Chip * + * * + * Copyright (C) 2006 by Luca Risolia * + * * + * Informations about the chip internals to enable the I2C protocol have * + * been taken from the documentation of the ZC030x Video4Linux1 driver * + * written by Andrew Birkett * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zc0301.h" + +/*****************************************************************************/ + +#define ZC0301_MODULE_NAME "V4L2 driver for ZC0301 " \ + "Image Processor and Control Chip" +#define ZC0301_MODULE_AUTHOR "(C) 2006 Luca Risolia" +#define ZC0301_AUTHOR_EMAIL "" +#define ZC0301_MODULE_LICENSE "GPL" +#define ZC0301_MODULE_VERSION "1:1.00" +#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 0) + +/*****************************************************************************/ + +MODULE_DEVICE_TABLE(usb, zc0301_id_table); + +MODULE_AUTHOR(ZC0301_MODULE_AUTHOR " " ZC0301_AUTHOR_EMAIL); +MODULE_DESCRIPTION(ZC0301_MODULE_NAME); +MODULE_VERSION(ZC0301_MODULE_VERSION); +MODULE_LICENSE(ZC0301_MODULE_LICENSE); + +static short video_nr[] = {[0 ... ZC0301_MAX_DEVICES-1] = -1}; +module_param_array(video_nr, short, NULL, 0444); +MODULE_PARM_DESC(video_nr, + "\n<-1|n[,...]> Specify V4L2 minor mode number." + "\n -1 = use next available (default)" + "\n n = use minor number n (integer >= 0)" + "\nYou can specify up to " + __MODULE_STRING(ZC0301_MAX_DEVICES) " cameras this way." + "\nFor example:" + "\nvideo_nr=-1,2,-1 would assign minor number 2 to" + "\nthe second registered camera and use auto for the first" + "\none and for every other camera." + "\n"); + +static short force_munmap[] = {[0 ... ZC0301_MAX_DEVICES-1] = + ZC0301_FORCE_MUNMAP}; +module_param_array(force_munmap, bool, NULL, 0444); +MODULE_PARM_DESC(force_munmap, + "\n<0|1[,...]> Force the application to unmap previously" + "\nmapped buffer memory before calling any VIDIOC_S_CROP or" + "\nVIDIOC_S_FMT ioctl's. Not all the applications support" + "\nthis feature. This parameter is specific for each" + "\ndetected camera." + "\n 0 = do not force memory unmapping" + "\n 1 = force memory unmapping (save memory)" + "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"." + "\n"); + +#ifdef ZC0301_DEBUG +static unsigned short debug = ZC0301_DEBUG_LEVEL; +module_param(debug, ushort, 0644); +MODULE_PARM_DESC(debug, + "\n Debugging information level, from 0 to 3:" + "\n0 = none (use carefully)" + "\n1 = critical errors" + "\n2 = significant informations" + "\n3 = more verbose messages" + "\nLevel 3 is useful for testing only, when only " + "one device is used." + "\nDefault value is "__MODULE_STRING(ZC0301_DEBUG_LEVEL)"." + "\n"); +#endif + +/*****************************************************************************/ + +static u32 +zc0301_request_buffers(struct zc0301_device* cam, u32 count, + enum zc0301_io_method io) +{ + struct v4l2_pix_format* p = &(cam->sensor->pix_format); + struct v4l2_rect* r = &(cam->sensor->cropcap.bounds); + const size_t imagesize = cam->module_param.force_munmap || + io == IO_READ ? + (p->width * p->height * p->priv) / 8 : + (r->width * r->height * p->priv) / 8; + void* buff = NULL; + u32 i; + + if (count > ZC0301_MAX_FRAMES) + count = ZC0301_MAX_FRAMES; + + cam->nbuffers = count; + while (cam->nbuffers > 0) { + if ((buff = vmalloc_32(cam->nbuffers * PAGE_ALIGN(imagesize)))) + break; + cam->nbuffers--; + } + + for (i = 0; i < cam->nbuffers; i++) { + cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize); + cam->frame[i].buf.index = i; + cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize); + cam->frame[i].buf.length = imagesize; + cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cam->frame[i].buf.sequence = 0; + cam->frame[i].buf.field = V4L2_FIELD_NONE; + cam->frame[i].buf.memory = V4L2_MEMORY_MMAP; + cam->frame[i].buf.flags = 0; + } + + return cam->nbuffers; +} + + +static void zc0301_release_buffers(struct zc0301_device* cam) +{ + if (cam->nbuffers) { + vfree(cam->frame[0].bufmem); + cam->nbuffers = 0; + } + cam->frame_current = NULL; +} + + +static void zc0301_empty_framequeues(struct zc0301_device* cam) +{ + u32 i; + + INIT_LIST_HEAD(&cam->inqueue); + INIT_LIST_HEAD(&cam->outqueue); + + for (i = 0; i < ZC0301_MAX_FRAMES; i++) { + cam->frame[i].state = F_UNUSED; + cam->frame[i].buf.bytesused = 0; + } +} + + +static void zc0301_requeue_outqueue(struct zc0301_device* cam) +{ + struct zc0301_frame_t *i; + + list_for_each_entry(i, &cam->outqueue, frame) { + i->state = F_QUEUED; + list_add(&i->frame, &cam->inqueue); + } + + INIT_LIST_HEAD(&cam->outqueue); +} + + +static void zc0301_queue_unusedframes(struct zc0301_device* cam) +{ + unsigned long lock_flags; + u32 i; + + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].state == F_UNUSED) { + cam->frame[i].state = F_QUEUED; + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_add_tail(&cam->frame[i].frame, &cam->inqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + } +} + +/*****************************************************************************/ + +int zc0301_write_reg(struct zc0301_device* cam, u16 index, u16 value) +{ + struct usb_device* udev = cam->usbdev; + int res; + + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0xa0, 0x40, + value, index, NULL, 0, ZC0301_CTRL_TIMEOUT); + if (res < 0) { + DBG(3, "Failed to write a register (index 0x%04X, " + "value 0x%02X, error %d)",index, value, res); + return -1; + } + + return 0; +} + + +int zc0301_read_reg(struct zc0301_device* cam, u16 index) +{ + struct usb_device* udev = cam->usbdev; + u8* buff = cam->control_buffer; + int res; + + res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0xa1, 0xc0, + 0x0001, index, buff, 1, ZC0301_CTRL_TIMEOUT); + if (res < 0) + DBG(3, "Failed to read a register (index 0x%04X, error %d)", + index, res); + + PDBGG("Read: index 0x%04X, value: 0x%04X", index, (int)(*buff)); + + return (res >= 0) ? (int)(*buff) : -1; +} + + +int zc0301_i2c_read(struct zc0301_device* cam, u16 address, u8 length) +{ + int err = 0, res, r0, r1; + + err += zc0301_write_reg(cam, 0x0092, address); + err += zc0301_write_reg(cam, 0x0090, 0x02); + + msleep(1); + + res = zc0301_read_reg(cam, 0x0091); + if (res < 0) + err += res; + r0 = zc0301_read_reg(cam, 0x0095); + if (r0 < 0) + err += r0; + r1 = zc0301_read_reg(cam, 0x0096); + if (r1 < 0) + err += r1; + + res = (length <= 1) ? r0 : r0 | (r1 << 8); + + if (err) + DBG(3, "I2C read failed at address 0x%04X, value: 0x%04X", + address, res); + + + PDBGG("I2C read: address 0x%04X, value: 0x%04X", address, res); + + return err ? -1 : res; +} + + +int zc0301_i2c_write(struct zc0301_device* cam, u16 address, u16 value) +{ + int err = 0, res; + + err += zc0301_write_reg(cam, 0x0092, address); + err += zc0301_write_reg(cam, 0x0093, value & 0xff); + err += zc0301_write_reg(cam, 0x0094, value >> 8); + err += zc0301_write_reg(cam, 0x0090, 0x01); + + msleep(1); + + res = zc0301_read_reg(cam, 0x0091); + if (res < 0) + err += res; + + if (err) + DBG(3, "I2C write failed at address 0x%04X, value: 0x%04X", + address, value); + + PDBGG("I2C write: address 0x%04X, value: 0x%04X", address, value); + + return err ? -1 : 0; +} + +/*****************************************************************************/ + +static void zc0301_urb_complete(struct urb *urb, struct pt_regs* regs) +{ + struct zc0301_device* cam = urb->context; + struct zc0301_frame_t** f; + size_t imagesize; + u8 i; + int err = 0; + + if (urb->status == -ENOENT) + return; + + f = &cam->frame_current; + + if (cam->stream == STREAM_INTERRUPT) { + cam->stream = STREAM_OFF; + if ((*f)) + (*f)->state = F_QUEUED; + DBG(3, "Stream interrupted"); + wake_up_interruptible(&cam->wait_stream); + } + + if (cam->state & DEV_DISCONNECTED) + return; + + if (cam->state & DEV_MISCONFIGURED) { + wake_up_interruptible(&cam->wait_frame); + return; + } + + if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue)) + goto resubmit_urb; + + if (!(*f)) + (*f) = list_entry(cam->inqueue.next, struct zc0301_frame_t, + frame); + + imagesize = (cam->sensor->pix_format.width * + cam->sensor->pix_format.height * + cam->sensor->pix_format.priv) / 8; + + for (i = 0; i < urb->number_of_packets; i++) { + unsigned int len, status; + void *pos; + u16* soi; + u8 sof; + + 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; + + if (status) { + DBG(3, "Error in isochronous frame"); + (*f)->state = F_ERROR; + continue; + } + + sof = (*(soi = pos) == 0xd8ff); + + PDBGG("Isochrnous frame: length %u, #%u i,", len, i); + + if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR) +start_of_frame: + if (sof) { + (*f)->state = F_GRABBING; + (*f)->buf.bytesused = 0; + do_gettimeofday(&(*f)->buf.timestamp); + DBG(3, "SOF detected: new video frame"); + } + + if ((*f)->state == F_GRABBING) { + if (sof && (*f)->buf.bytesused) + goto end_of_frame; + + if ((*f)->buf.bytesused + len > imagesize) { + DBG(3, "Video frame size exceeded"); + (*f)->state = F_ERROR; + continue; + } + + memcpy((*f)->bufmem+(*f)->buf.bytesused, pos, len); + (*f)->buf.bytesused += len; + + if ((*f)->buf.bytesused == imagesize) { + u32 b; +end_of_frame: + b = (*f)->buf.bytesused; + (*f)->state = F_DONE; + (*f)->buf.sequence= ++cam->frame_count; + spin_lock(&cam->queue_lock); + list_move_tail(&(*f)->frame, &cam->outqueue); + if (!list_empty(&cam->inqueue)) + (*f) = list_entry(cam->inqueue.next, + struct zc0301_frame_t, + frame); + else + (*f) = NULL; + spin_unlock(&cam->queue_lock); + DBG(3, "Video frame captured: : %lu bytes", + (unsigned long)(b)); + + if (!(*f)) + goto resubmit_urb; + + if (sof) + goto start_of_frame; + } + } + } + +resubmit_urb: + urb->dev = cam->usbdev; + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0 && err != -EPERM) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "usb_submit_urb() failed"); + } + + wake_up_interruptible(&cam->wait_frame); +} + + +static int zc0301_start_transfer(struct zc0301_device* cam) +{ + struct usb_device *udev = cam->usbdev; + struct urb* urb; + const unsigned int wMaxPacketSize[] = {0, 128, 192, 256, 384, + 512, 768, 1023}; + const unsigned int psz = wMaxPacketSize[ZC0301_ALTERNATE_SETTING]; + s8 i, j; + int err = 0; + + for (i = 0; i < ZC0301_URBS; i++) { + cam->transfer_buffer[i] = kzalloc(ZC0301_ISO_PACKETS * psz, + GFP_KERNEL); + if (!cam->transfer_buffer[i]) { + err = -ENOMEM; + DBG(1, "Not enough memory"); + goto free_buffers; + } + } + + for (i = 0; i < ZC0301_URBS; i++) { + urb = usb_alloc_urb(ZC0301_ISO_PACKETS, GFP_KERNEL); + cam->urb[i] = urb; + if (!urb) { + err = -ENOMEM; + DBG(1, "usb_alloc_urb() failed"); + goto free_urbs; + } + urb->dev = udev; + urb->context = cam; + urb->pipe = usb_rcvisocpipe(udev, 1); + urb->transfer_flags = URB_ISO_ASAP; + urb->number_of_packets = ZC0301_ISO_PACKETS; + urb->complete = zc0301_urb_complete; + urb->transfer_buffer = cam->transfer_buffer[i]; + urb->transfer_buffer_length = psz * ZC0301_ISO_PACKETS; + urb->interval = 1; + for (j = 0; j < ZC0301_ISO_PACKETS; j++) { + urb->iso_frame_desc[j].offset = psz * j; + urb->iso_frame_desc[j].length = psz; + } + } + + err = usb_set_interface(udev, 0, ZC0301_ALTERNATE_SETTING); + if (err) { + DBG(1, "usb_set_interface() failed"); + goto free_urbs; + } + + cam->frame_current = NULL; + + for (i = 0; i < ZC0301_URBS; i++) { + err = usb_submit_urb(cam->urb[i], GFP_KERNEL); + if (err) { + for (j = i-1; j >= 0; j--) + usb_kill_urb(cam->urb[j]); + DBG(1, "usb_submit_urb() failed, error %d", err); + goto free_urbs; + } + } + + return 0; + +free_urbs: + for (i = 0; (i < ZC0301_URBS) && cam->urb[i]; i++) + usb_free_urb(cam->urb[i]); + +free_buffers: + for (i = 0; (i < ZC0301_URBS) && cam->transfer_buffer[i]; i++) + kfree(cam->transfer_buffer[i]); + + return err; +} + + +static int zc0301_stop_transfer(struct zc0301_device* cam) +{ + struct usb_device *udev = cam->usbdev; + s8 i; + int err = 0; + + if (cam->state & DEV_DISCONNECTED) + return 0; + + for (i = ZC0301_URBS-1; i >= 0; i--) { + usb_kill_urb(cam->urb[i]); + usb_free_urb(cam->urb[i]); + kfree(cam->transfer_buffer[i]); + } + + err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */ + if (err) + DBG(3, "usb_set_interface() failed"); + + return err; +} + + +static int zc0301_stream_interrupt(struct zc0301_device* cam) +{ + long timeout; + + cam->stream = STREAM_INTERRUPT; + timeout = wait_event_timeout(cam->wait_stream, + (cam->stream == STREAM_OFF) || + (cam->state & DEV_DISCONNECTED), + ZC0301_URB_TIMEOUT); + if (cam->state & DEV_DISCONNECTED) + return -ENODEV; + else if (!timeout) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "URB timeout reached. The camera is misconfigured. To " + "use it, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -EIO; + } + + return 0; +} + +/*****************************************************************************/ + +static int +zc0301_set_compression(struct zc0301_device* cam, + struct v4l2_jpegcompression* compression) +{ + int r, err = 0; + + if ((r = zc0301_read_reg(cam, 0x0008)) < 0) + err += r; + err += zc0301_write_reg(cam, 0x0008, + r | 0x11 | (compression->quality >> 1)); + + return err ? -EIO : 0; +} + + +static int zc0301_init(struct zc0301_device* cam) +{ + struct zc0301_sensor* s = cam->sensor; + struct v4l2_control ctrl; + struct v4l2_queryctrl *qctrl; + struct v4l2_rect* rect; + u8 i = 0; + int err = 0; + + if (!(cam->state & DEV_INITIALIZED)) { + init_waitqueue_head(&cam->open); + qctrl = s->qctrl; + rect = &(s->cropcap.defrect); + cam->compression.quality = ZC0301_COMPRESSION_QUALITY; + } else { /* use current values */ + qctrl = s->_qctrl; + rect = &(s->_rect); + } + + if (s->init) { + err = s->init(cam); + if (err) { + DBG(3, "Sensor initialization failed"); + return err; + } + } + + if ((err = zc0301_set_compression(cam, &cam->compression))) { + DBG(3, "set_compression() failed"); + return err; + } + + if (s->set_crop) + if ((err = s->set_crop(cam, rect))) { + DBG(3, "set_crop() failed"); + return err; + } + + if (s->set_ctrl) { + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (s->qctrl[i].id != 0 && + !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) { + ctrl.id = s->qctrl[i].id; + ctrl.value = qctrl[i].default_value; + err = s->set_ctrl(cam, &ctrl); + if (err) { + DBG(3, "Set %s control failed", + s->qctrl[i].name); + return err; + } + DBG(3, "Image sensor supports '%s' control", + s->qctrl[i].name); + } + } + + if (!(cam->state & DEV_INITIALIZED)) { + mutex_init(&cam->fileop_mutex); + spin_lock_init(&cam->queue_lock); + init_waitqueue_head(&cam->wait_frame); + init_waitqueue_head(&cam->wait_stream); + cam->nreadbuffers = 2; + memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl)); + memcpy(&(s->_rect), &(s->cropcap.defrect), + sizeof(struct v4l2_rect)); + cam->state |= DEV_INITIALIZED; + } + + DBG(2, "Initialization succeeded"); + return 0; +} + + +static void zc0301_release_resources(struct zc0301_device* cam) +{ + DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor); + video_set_drvdata(cam->v4ldev, NULL); + video_unregister_device(cam->v4ldev); + kfree(cam->control_buffer); +} + +/*****************************************************************************/ + +static int zc0301_open(struct inode* inode, struct file* filp) +{ + struct zc0301_device* cam; + int err = 0; + + /* + This is the only safe way to prevent race conditions with + disconnect + */ + if (!down_read_trylock(&zc0301_disconnect)) + return -ERESTARTSYS; + + cam = video_get_drvdata(video_devdata(filp)); + + if (mutex_lock_interruptible(&cam->dev_mutex)) { + up_read(&zc0301_disconnect); + return -ERESTARTSYS; + } + + if (cam->users) { + DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->minor); + if ((filp->f_flags & O_NONBLOCK) || + (filp->f_flags & O_NDELAY)) { + err = -EWOULDBLOCK; + goto out; + } + mutex_unlock(&cam->dev_mutex); + err = wait_event_interruptible_exclusive(cam->open, + cam->state & DEV_DISCONNECTED + || !cam->users); + if (err) { + up_read(&zc0301_disconnect); + return err; + } + if (cam->state & DEV_DISCONNECTED) { + up_read(&zc0301_disconnect); + return -ENODEV; + } + mutex_lock(&cam->dev_mutex); + } + + + if (cam->state & DEV_MISCONFIGURED) { + err = zc0301_init(cam); + if (err) { + DBG(1, "Initialization failed again. " + "I will retry on next open()."); + goto out; + } + cam->state &= ~DEV_MISCONFIGURED; + } + + if ((err = zc0301_start_transfer(cam))) + goto out; + + filp->private_data = cam; + cam->users++; + cam->io = IO_NONE; + cam->stream = STREAM_OFF; + cam->nbuffers = 0; + cam->frame_count = 0; + zc0301_empty_framequeues(cam); + + DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor); + +out: + mutex_unlock(&cam->dev_mutex); + up_read(&zc0301_disconnect); + return err; +} + + +static int zc0301_release(struct inode* inode, struct file* filp) +{ + struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + + mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */ + + zc0301_stop_transfer(cam); + + zc0301_release_buffers(cam); + + if (cam->state & DEV_DISCONNECTED) { + zc0301_release_resources(cam); + mutex_unlock(&cam->dev_mutex); + kfree(cam); + return 0; + } + + cam->users--; + wake_up_interruptible_nr(&cam->open, 1); + + DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor); + + mutex_unlock(&cam->dev_mutex); + + return 0; +} + + +static ssize_t +zc0301_read(struct file* filp, char __user * buf, + size_t count, loff_t* f_pos) +{ + struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + struct zc0301_frame_t* f, * i; + unsigned long lock_flags; + long timeout; + int err = 0; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return -ERESTARTSYS; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + + if (cam->io == IO_MMAP) { + DBG(3, "Close and open the device again to choose the read " + "method"); + mutex_unlock(&cam->fileop_mutex); + return -EINVAL; + } + + if (cam->io == IO_NONE) { + if (!zc0301_request_buffers(cam, cam->nreadbuffers, IO_READ)) { + DBG(1, "read() failed, not enough memory"); + mutex_unlock(&cam->fileop_mutex); + return -ENOMEM; + } + cam->io = IO_READ; + cam->stream = STREAM_ON; + } + + if (list_empty(&cam->inqueue)) { + if (!list_empty(&cam->outqueue)) + zc0301_empty_framequeues(cam); + zc0301_queue_unusedframes(cam); + } + + if (!count) { + mutex_unlock(&cam->fileop_mutex); + return 0; + } + + if (list_empty(&cam->outqueue)) { + if (filp->f_flags & O_NONBLOCK) { + mutex_unlock(&cam->fileop_mutex); + return -EAGAIN; + } + timeout = wait_event_interruptible_timeout + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + ZC0301_FRAME_TIMEOUT ); + if (timeout < 0) { + mutex_unlock(&cam->fileop_mutex); + return timeout; + } + if (cam->state & DEV_DISCONNECTED) { + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + if (!timeout || (cam->state & DEV_MISCONFIGURED)) { + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + } + + f = list_entry(cam->outqueue.prev, struct zc0301_frame_t, frame); + + if (count > f->buf.bytesused) + count = f->buf.bytesused; + + if (copy_to_user(buf, f->bufmem, count)) { + err = -EFAULT; + goto exit; + } + *f_pos += count; + +exit: + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_for_each_entry(i, &cam->outqueue, frame) + i->state = F_UNUSED; + INIT_LIST_HEAD(&cam->outqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + + zc0301_queue_unusedframes(cam); + + PDBGG("Frame #%lu, bytes read: %zu", + (unsigned long)f->buf.index, count); + + mutex_unlock(&cam->fileop_mutex); + + return err ? err : count; +} + + +static unsigned int zc0301_poll(struct file *filp, poll_table *wait) +{ + struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + struct zc0301_frame_t* f; + unsigned long lock_flags; + unsigned int mask = 0; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return POLLERR; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + goto error; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + goto error; + } + + if (cam->io == IO_NONE) { + if (!zc0301_request_buffers(cam, cam->nreadbuffers, IO_READ)) { + DBG(1, "poll() failed, not enough memory"); + goto error; + } + cam->io = IO_READ; + cam->stream = STREAM_ON; + } + + if (cam->io == IO_READ) { + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_for_each_entry(f, &cam->outqueue, frame) + f->state = F_UNUSED; + INIT_LIST_HEAD(&cam->outqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + zc0301_queue_unusedframes(cam); + } + + poll_wait(filp, &cam->wait_frame, wait); + + if (!list_empty(&cam->outqueue)) + mask |= POLLIN | POLLRDNORM; + + mutex_unlock(&cam->fileop_mutex); + + return mask; + +error: + mutex_unlock(&cam->fileop_mutex); + return POLLERR; +} + + +static void zc0301_vm_open(struct vm_area_struct* vma) +{ + struct zc0301_frame_t* f = vma->vm_private_data; + f->vma_use_count++; +} + + +static void zc0301_vm_close(struct vm_area_struct* vma) +{ + /* NOTE: buffers are not freed here */ + struct zc0301_frame_t* f = vma->vm_private_data; + f->vma_use_count--; +} + + +static struct vm_operations_struct zc0301_vm_ops = { + .open = zc0301_vm_open, + .close = zc0301_vm_close, +}; + + +static int zc0301_mmap(struct file* filp, struct vm_area_struct *vma) +{ + struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + unsigned long size = vma->vm_end - vma->vm_start, + start = vma->vm_start; + void *pos; + u32 i; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return -ERESTARTSYS; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + + if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) || + size != PAGE_ALIGN(cam->frame[0].buf.length)) { + mutex_unlock(&cam->fileop_mutex); + return -EINVAL; + } + + for (i = 0; i < cam->nbuffers; i++) { + if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff) + break; + } + if (i == cam->nbuffers) { + mutex_unlock(&cam->fileop_mutex); + return -EINVAL; + } + + vma->vm_flags |= VM_IO; + vma->vm_flags |= VM_RESERVED; + + pos = cam->frame[i].bufmem; + while (size > 0) { /* size is page-aligned */ + if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { + mutex_unlock(&cam->fileop_mutex); + return -EAGAIN; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + size -= PAGE_SIZE; + } + + vma->vm_ops = &zc0301_vm_ops; + vma->vm_private_data = &cam->frame[i]; + + zc0301_vm_open(vma); + + mutex_unlock(&cam->fileop_mutex); + + return 0; +} + +/*****************************************************************************/ + +static int +zc0301_vidioc_querycap(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_capability cap = { + .driver = "zc0301", + .version = ZC0301_MODULE_VERSION_CODE, + .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING, + }; + + strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); + if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0) + strlcpy(cap.bus_info, cam->usbdev->dev.bus_id, + sizeof(cap.bus_info)); + + if (copy_to_user(arg, &cap, sizeof(cap))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_enuminput(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_input i; + + if (copy_from_user(&i, arg, sizeof(i))) + return -EFAULT; + + if (i.index) + return -EINVAL; + + memset(&i, 0, sizeof(i)); + strcpy(i.name, "Camera"); + + if (copy_to_user(arg, &i, sizeof(i))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_gs_input(struct zc0301_device* cam, void __user * arg) +{ + int index; + + if (copy_from_user(&index, arg, sizeof(index))) + return -EFAULT; + + if (index != 0) + return -EINVAL; + + return 0; +} + + +static int +zc0301_vidioc_query_ctrl(struct zc0301_device* cam, void __user * arg) +{ + struct zc0301_sensor* s = cam->sensor; + struct v4l2_queryctrl qc; + u8 i; + + if (copy_from_user(&qc, arg, sizeof(qc))) + return -EFAULT; + + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (qc.id && qc.id == s->qctrl[i].id) { + memcpy(&qc, &(s->qctrl[i]), sizeof(qc)); + if (copy_to_user(arg, &qc, sizeof(qc))) + return -EFAULT; + return 0; + } + + return -EINVAL; +} + + +static int +zc0301_vidioc_g_ctrl(struct zc0301_device* cam, void __user * arg) +{ + struct zc0301_sensor* s = cam->sensor; + struct v4l2_control ctrl; + int err = 0; + u8 i; + + if (!s->get_ctrl && !s->set_ctrl) + return -EINVAL; + + if (copy_from_user(&ctrl, arg, sizeof(ctrl))) + return -EFAULT; + + if (!s->get_ctrl) { + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (ctrl.id == s->qctrl[i].id) { + ctrl.value = s->_qctrl[i].default_value; + goto exit; + } + return -EINVAL; + } else + err = s->get_ctrl(cam, &ctrl); + +exit: + if (copy_to_user(arg, &ctrl, sizeof(ctrl))) + return -EFAULT; + + return err; +} + + +static int +zc0301_vidioc_s_ctrl(struct zc0301_device* cam, void __user * arg) +{ + struct zc0301_sensor* s = cam->sensor; + struct v4l2_control ctrl; + u8 i; + int err = 0; + + if (!s->set_ctrl) + return -EINVAL; + + if (copy_from_user(&ctrl, arg, sizeof(ctrl))) + return -EFAULT; + + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (ctrl.id == s->qctrl[i].id) { + if (ctrl.value < s->qctrl[i].minimum || + ctrl.value > s->qctrl[i].maximum) + return -ERANGE; + ctrl.value -= ctrl.value % s->qctrl[i].step; + break; + } + + if ((err = s->set_ctrl(cam, &ctrl))) + return err; + + s->_qctrl[i].default_value = ctrl.value; + + return 0; +} + + +static int +zc0301_vidioc_cropcap(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_cropcap* cc = &(cam->sensor->cropcap); + + cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cc->pixelaspect.numerator = 1; + cc->pixelaspect.denominator = 1; + + if (copy_to_user(arg, cc, sizeof(*cc))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_g_crop(struct zc0301_device* cam, void __user * arg) +{ + struct zc0301_sensor* s = cam->sensor; + struct v4l2_crop crop = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + }; + + memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect)); + + if (copy_to_user(arg, &crop, sizeof(crop))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_s_crop(struct zc0301_device* cam, void __user * arg) +{ + struct zc0301_sensor* s = cam->sensor; + struct v4l2_crop crop; + struct v4l2_rect* rect; + struct v4l2_rect* bounds = &(s->cropcap.bounds); + const enum zc0301_stream_state stream = cam->stream; + const u32 nbuffers = cam->nbuffers; + u32 i; + int err = 0; + + if (copy_from_user(&crop, arg, sizeof(crop))) + return -EFAULT; + + rect = &(crop.c); + + if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (cam->module_param.force_munmap) + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].vma_use_count) { + DBG(3, "VIDIOC_S_CROP failed. " + "Unmap the buffers first."); + return -EINVAL; + } + + if (!s->set_crop) { + memcpy(rect, &(s->_rect), sizeof(*rect)); + if (copy_to_user(arg, &crop, sizeof(crop))) + return -EFAULT; + return 0; + } + + rect->left &= ~7L; + rect->top &= ~7L; + if (rect->width < 8) + rect->width = 8; + if (rect->height < 8) + rect->height = 8; + if (rect->width > bounds->width) + rect->width = bounds->width; + if (rect->height > bounds->height) + rect->height = bounds->height; + if (rect->left < bounds->left) + rect->left = bounds->left; + if (rect->top < bounds->top) + rect->top = bounds->top; + if (rect->left + rect->width > bounds->left + bounds->width) + rect->left = bounds->left+bounds->width - rect->width; + if (rect->top + rect->height > bounds->top + bounds->height) + rect->top = bounds->top+bounds->height - rect->height; + rect->width &= ~7L; + rect->height &= ~7L; + + if (cam->stream == STREAM_ON) + if ((err = zc0301_stream_interrupt(cam))) + return err; + + if (copy_to_user(arg, &crop, sizeof(crop))) { + cam->stream = stream; + return -EFAULT; + } + + if (cam->module_param.force_munmap || cam->io == IO_READ) + zc0301_release_buffers(cam); + + if (s->set_crop) + err += s->set_crop(cam, rect); + + if (err) { /* atomic, no rollback in ioctl() */ + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -EIO; + } + + s->pix_format.width = rect->width; + s->pix_format.height = rect->height; + memcpy(&(s->_rect), rect, sizeof(*rect)); + + if ((cam->module_param.force_munmap || cam->io == IO_READ) && + nbuffers != zc0301_request_buffers(cam, nbuffers, cam->io)) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -ENOMEM; + } + + if (cam->io == IO_READ) + zc0301_empty_framequeues(cam); + else if (cam->module_param.force_munmap) + zc0301_requeue_outqueue(cam); + + cam->stream = stream; + + return 0; +} + + +static int +zc0301_vidioc_enum_fmt(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_fmtdesc fmtd; + + if (copy_from_user(&fmtd, arg, sizeof(fmtd))) + return -EFAULT; + + if (fmtd.index == 0) { + strcpy(fmtd.description, "JPEG"); + fmtd.pixelformat = V4L2_PIX_FMT_JPEG; + fmtd.flags = V4L2_FMT_FLAG_COMPRESSED; + } else + return -EINVAL; + + fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + memset(&fmtd.reserved, 0, sizeof(fmtd.reserved)); + + if (copy_to_user(arg, &fmtd, sizeof(fmtd))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_g_fmt(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_format format; + struct v4l2_pix_format* pfmt = &(cam->sensor->pix_format); + + if (copy_from_user(&format, arg, sizeof(format))) + return -EFAULT; + + if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + pfmt->bytesperline = 0; + pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8); + pfmt->field = V4L2_FIELD_NONE; + memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt)); + + if (copy_to_user(arg, &format, sizeof(format))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_try_s_fmt(struct zc0301_device* cam, unsigned int cmd, + void __user * arg) +{ + struct zc0301_sensor* s = cam->sensor; + struct v4l2_format format; + struct v4l2_pix_format* pix; + struct v4l2_pix_format* pfmt = &(s->pix_format); + struct v4l2_rect* bounds = &(s->cropcap.bounds); + struct v4l2_rect rect; + const enum zc0301_stream_state stream = cam->stream; + const u32 nbuffers = cam->nbuffers; + u32 i; + int err = 0; + + if (copy_from_user(&format, arg, sizeof(format))) + return -EFAULT; + + pix = &(format.fmt.pix); + + if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memcpy(&rect, &(s->_rect), sizeof(rect)); + + if (!s->set_crop) { + pix->width = rect.width; + pix->height = rect.height; + } else { + rect.width = pix->width; + rect.height = pix->height; + } + + if (rect.width < 8) + rect.width = 8; + if (rect.height < 8) + rect.height = 8; + if (rect.width > bounds->left + bounds->width - rect.left) + rect.width = bounds->left + bounds->width - rect.left; + if (rect.height > bounds->top + bounds->height - rect.top) + rect.height = bounds->top + bounds->height - rect.top; + rect.width &= ~7L; + rect.height &= ~7L; + + pix->width = rect.width; + pix->height = rect.height; + pix->pixelformat = pfmt->pixelformat; + pix->priv = pfmt->priv; + pix->colorspace = pfmt->colorspace; + pix->bytesperline = 0; + pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8); + pix->field = V4L2_FIELD_NONE; + + if (cmd == VIDIOC_TRY_FMT) { + if (copy_to_user(arg, &format, sizeof(format))) + return -EFAULT; + return 0; + } + + if (cam->module_param.force_munmap) + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].vma_use_count) { + DBG(3, "VIDIOC_S_FMT failed. " + "Unmap the buffers first."); + return -EINVAL; + } + + if (cam->stream == STREAM_ON) + if ((err = zc0301_stream_interrupt(cam))) + return err; + + if (copy_to_user(arg, &format, sizeof(format))) { + cam->stream = stream; + return -EFAULT; + } + + if (cam->module_param.force_munmap || cam->io == IO_READ) + zc0301_release_buffers(cam); + + if (s->set_crop) + err += s->set_crop(cam, &rect); + + if (err) { /* atomic, no rollback in ioctl() */ + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -EIO; + } + + memcpy(pfmt, pix, sizeof(*pix)); + memcpy(&(s->_rect), &rect, sizeof(rect)); + + if ((cam->module_param.force_munmap || cam->io == IO_READ) && + nbuffers != zc0301_request_buffers(cam, nbuffers, cam->io)) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -ENOMEM; + } + + if (cam->io == IO_READ) + zc0301_empty_framequeues(cam); + else if (cam->module_param.force_munmap) + zc0301_requeue_outqueue(cam); + + cam->stream = stream; + + return 0; +} + + +static int +zc0301_vidioc_g_jpegcomp(struct zc0301_device* cam, void __user * arg) +{ + if (copy_to_user(arg, &cam->compression, sizeof(cam->compression))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_s_jpegcomp(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_jpegcompression jc; + const enum zc0301_stream_state stream = cam->stream; + int err = 0; + + if (copy_from_user(&jc, arg, sizeof(jc))) + return -EFAULT; + + if (jc.quality < 0 || jc.quality > 3) + return -EINVAL; + + if (cam->stream == STREAM_ON) + if ((err = zc0301_stream_interrupt(cam))) + return err; + + err += zc0301_set_compression(cam, &jc); + if (err) { /* atomic, no rollback in ioctl() */ + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware " + "problems. To use the camera, close and open " + "/dev/video%d again.", cam->v4ldev->minor); + return -EIO; + } + + cam->compression.quality = jc.quality; + + cam->stream = stream; + + return 0; +} + + +static int +zc0301_vidioc_reqbufs(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_requestbuffers rb; + u32 i; + int err; + + if (copy_from_user(&rb, arg, sizeof(rb))) + return -EFAULT; + + if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + rb.memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + if (cam->io == IO_READ) { + DBG(3, "Close and open the device again to choose the mmap " + "I/O method"); + return -EINVAL; + } + + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].vma_use_count) { + DBG(3, "VIDIOC_REQBUFS failed. " + "Previous buffers are still mapped."); + return -EINVAL; + } + + if (cam->stream == STREAM_ON) + if ((err = zc0301_stream_interrupt(cam))) + return err; + + zc0301_empty_framequeues(cam); + + zc0301_release_buffers(cam); + if (rb.count) + rb.count = zc0301_request_buffers(cam, rb.count, IO_MMAP); + + if (copy_to_user(arg, &rb, sizeof(rb))) { + zc0301_release_buffers(cam); + cam->io = IO_NONE; + return -EFAULT; + } + + cam->io = rb.count ? IO_MMAP : IO_NONE; + + return 0; +} + + +static int +zc0301_vidioc_querybuf(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_buffer b; + + if (copy_from_user(&b, arg, sizeof(b))) + return -EFAULT; + + if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + b.index >= cam->nbuffers || cam->io != IO_MMAP) + return -EINVAL; + + memcpy(&b, &cam->frame[b.index].buf, sizeof(b)); + + if (cam->frame[b.index].vma_use_count) + b.flags |= V4L2_BUF_FLAG_MAPPED; + + if (cam->frame[b.index].state == F_DONE) + b.flags |= V4L2_BUF_FLAG_DONE; + else if (cam->frame[b.index].state != F_UNUSED) + b.flags |= V4L2_BUF_FLAG_QUEUED; + + if (copy_to_user(arg, &b, sizeof(b))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_qbuf(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_buffer b; + unsigned long lock_flags; + + if (copy_from_user(&b, arg, sizeof(b))) + return -EFAULT; + + if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + b.index >= cam->nbuffers || cam->io != IO_MMAP) + return -EINVAL; + + if (cam->frame[b.index].state != F_UNUSED) + return -EINVAL; + + cam->frame[b.index].state = F_QUEUED; + + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_add_tail(&cam->frame[b.index].frame, &cam->inqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + + PDBGG("Frame #%lu queued", (unsigned long)b.index); + + return 0; +} + + +static int +zc0301_vidioc_dqbuf(struct zc0301_device* cam, struct file* filp, + void __user * arg) +{ + struct v4l2_buffer b; + struct zc0301_frame_t *f; + unsigned long lock_flags; + long timeout; + + if (copy_from_user(&b, arg, sizeof(b))) + return -EFAULT; + + if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io!= IO_MMAP) + return -EINVAL; + + if (list_empty(&cam->outqueue)) { + if (cam->stream == STREAM_OFF) + return -EINVAL; + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + timeout = wait_event_interruptible_timeout + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + ZC0301_FRAME_TIMEOUT ); + if (timeout < 0) + return timeout; + if (cam->state & DEV_DISCONNECTED) + return -ENODEV; + if (!timeout || (cam->state & DEV_MISCONFIGURED)) + return -EIO; + } + + spin_lock_irqsave(&cam->queue_lock, lock_flags); + f = list_entry(cam->outqueue.next, struct zc0301_frame_t, frame); + list_del(cam->outqueue.next); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + + f->state = F_UNUSED; + + memcpy(&b, &f->buf, sizeof(b)); + if (f->vma_use_count) + b.flags |= V4L2_BUF_FLAG_MAPPED; + + if (copy_to_user(arg, &b, sizeof(b))) + return -EFAULT; + + PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index); + + return 0; +} + + +static int +zc0301_vidioc_streamon(struct zc0301_device* cam, void __user * arg) +{ + int type; + + if (copy_from_user(&type, arg, sizeof(type))) + return -EFAULT; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) + return -EINVAL; + + if (list_empty(&cam->inqueue)) + return -EINVAL; + + cam->stream = STREAM_ON; + + DBG(3, "Stream on"); + + return 0; +} + + +static int +zc0301_vidioc_streamoff(struct zc0301_device* cam, void __user * arg) +{ + int type, err; + + if (copy_from_user(&type, arg, sizeof(type))) + return -EFAULT; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) + return -EINVAL; + + if (cam->stream == STREAM_ON) + if ((err = zc0301_stream_interrupt(cam))) + return err; + + zc0301_empty_framequeues(cam); + + DBG(3, "Stream off"); + + return 0; +} + + +static int +zc0301_vidioc_g_parm(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_streamparm sp; + + if (copy_from_user(&sp, arg, sizeof(sp))) + return -EFAULT; + + if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + sp.parm.capture.extendedmode = 0; + sp.parm.capture.readbuffers = cam->nreadbuffers; + + if (copy_to_user(arg, &sp, sizeof(sp))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_s_parm(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_streamparm sp; + + if (copy_from_user(&sp, arg, sizeof(sp))) + return -EFAULT; + + if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + sp.parm.capture.extendedmode = 0; + + if (sp.parm.capture.readbuffers == 0) + sp.parm.capture.readbuffers = cam->nreadbuffers; + + if (sp.parm.capture.readbuffers > ZC0301_MAX_FRAMES) + sp.parm.capture.readbuffers = ZC0301_MAX_FRAMES; + + if (copy_to_user(arg, &sp, sizeof(sp))) + return -EFAULT; + + cam->nreadbuffers = sp.parm.capture.readbuffers; + + return 0; +} + + +static int zc0301_ioctl_v4l2(struct inode* inode, struct file* filp, + unsigned int cmd, void __user * arg) +{ + struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + + switch (cmd) { + + case VIDIOC_QUERYCAP: + return zc0301_vidioc_querycap(cam, arg); + + case VIDIOC_ENUMINPUT: + return zc0301_vidioc_enuminput(cam, arg); + + case VIDIOC_G_INPUT: + case VIDIOC_S_INPUT: + return zc0301_vidioc_gs_input(cam, arg); + + case VIDIOC_QUERYCTRL: + return zc0301_vidioc_query_ctrl(cam, arg); + + case VIDIOC_G_CTRL: + return zc0301_vidioc_g_ctrl(cam, arg); + + case VIDIOC_S_CTRL_OLD: + case VIDIOC_S_CTRL: + return zc0301_vidioc_s_ctrl(cam, arg); + + case VIDIOC_CROPCAP_OLD: + case VIDIOC_CROPCAP: + return zc0301_vidioc_cropcap(cam, arg); + + case VIDIOC_G_CROP: + return zc0301_vidioc_g_crop(cam, arg); + + case VIDIOC_S_CROP: + return zc0301_vidioc_s_crop(cam, arg); + + case VIDIOC_ENUM_FMT: + return zc0301_vidioc_enum_fmt(cam, arg); + + case VIDIOC_G_FMT: + return zc0301_vidioc_g_fmt(cam, arg); + + case VIDIOC_TRY_FMT: + case VIDIOC_S_FMT: + return zc0301_vidioc_try_s_fmt(cam, cmd, arg); + + case VIDIOC_G_JPEGCOMP: + return zc0301_vidioc_g_jpegcomp(cam, arg); + + case VIDIOC_S_JPEGCOMP: + return zc0301_vidioc_s_jpegcomp(cam, arg); + + case VIDIOC_REQBUFS: + return zc0301_vidioc_reqbufs(cam, arg); + + case VIDIOC_QUERYBUF: + return zc0301_vidioc_querybuf(cam, arg); + + case VIDIOC_QBUF: + return zc0301_vidioc_qbuf(cam, arg); + + case VIDIOC_DQBUF: + return zc0301_vidioc_dqbuf(cam, filp, arg); + + case VIDIOC_STREAMON: + return zc0301_vidioc_streamon(cam, arg); + + case VIDIOC_STREAMOFF: + return zc0301_vidioc_streamoff(cam, arg); + + case VIDIOC_G_PARM: + return zc0301_vidioc_g_parm(cam, arg); + + case VIDIOC_S_PARM_OLD: + case VIDIOC_S_PARM: + return zc0301_vidioc_s_parm(cam, arg); + + case VIDIOC_G_STD: + case VIDIOC_S_STD: + case VIDIOC_QUERYSTD: + case VIDIOC_ENUMSTD: + case VIDIOC_QUERYMENU: + return -EINVAL; + + default: + return -EINVAL; + + } +} + + +static int zc0301_ioctl(struct inode* inode, struct file* filp, + unsigned int cmd, unsigned long arg) +{ + struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + int err = 0; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return -ERESTARTSYS; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + + V4LDBG(3, "zc0301", cmd); + + err = zc0301_ioctl_v4l2(inode, filp, cmd, (void __user *)arg); + + mutex_unlock(&cam->fileop_mutex); + + return err; +} + + +static struct file_operations zc0301_fops = { + .owner = THIS_MODULE, + .open = zc0301_open, + .release = zc0301_release, + .ioctl = zc0301_ioctl, + .read = zc0301_read, + .poll = zc0301_poll, + .mmap = zc0301_mmap, + .llseek = no_llseek, +}; + +/*****************************************************************************/ + +static int +zc0301_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct zc0301_device* cam; + static unsigned int dev_nr = 0; + unsigned int i; + int err = 0; + + if (!(cam = kzalloc(sizeof(struct zc0301_device), GFP_KERNEL))) + return -ENOMEM; + + cam->usbdev = udev; + + if (!(cam->control_buffer = kzalloc(4, GFP_KERNEL))) { + DBG(1, "kmalloc() failed"); + err = -ENOMEM; + goto fail; + } + + if (!(cam->v4ldev = video_device_alloc())) { + DBG(1, "video_device_alloc() failed"); + err = -ENOMEM; + goto fail; + } + + mutex_init(&cam->dev_mutex); + + DBG(2, "ZC0301 Image Processor and Control Chip detected " + "(vid/pid 0x%04X/0x%04X)",id->idVendor, id->idProduct); + + for (i = 0; zc0301_sensor_table[i]; i++) { + err = zc0301_sensor_table[i](cam); + if (!err) + break; + } + + if (!err && cam->sensor) + DBG(2, "%s image sensor detected", cam->sensor->name); + else { + DBG(1, "No supported image sensor detected"); + err = -ENODEV; + goto fail; + } + + if (zc0301_init(cam)) { + DBG(1, "Initialization failed. I will retry on open()."); + cam->state |= DEV_MISCONFIGURED; + } + + strcpy(cam->v4ldev->name, "ZC0301 PC Camera"); + cam->v4ldev->owner = THIS_MODULE; + cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; + cam->v4ldev->hardware = 0; + cam->v4ldev->fops = &zc0301_fops; + cam->v4ldev->minor = video_nr[dev_nr]; + cam->v4ldev->release = video_device_release; + video_set_drvdata(cam->v4ldev, cam); + + mutex_lock(&cam->dev_mutex); + + err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, + video_nr[dev_nr]); + if (err) { + DBG(1, "V4L2 device registration failed"); + if (err == -ENFILE && video_nr[dev_nr] == -1) + DBG(1, "Free /dev/videoX node not found"); + video_nr[dev_nr] = -1; + dev_nr = (dev_nr < ZC0301_MAX_DEVICES-1) ? dev_nr+1 : 0; + mutex_unlock(&cam->dev_mutex); + goto fail; + } + + DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor); + + cam->module_param.force_munmap = force_munmap[dev_nr]; + + dev_nr = (dev_nr < ZC0301_MAX_DEVICES-1) ? dev_nr+1 : 0; + + usb_set_intfdata(intf, cam); + + mutex_unlock(&cam->dev_mutex); + + return 0; + +fail: + if (cam) { + kfree(cam->control_buffer); + if (cam->v4ldev) + video_device_release(cam->v4ldev); + kfree(cam); + } + return err; +} + + +static void zc0301_usb_disconnect(struct usb_interface* intf) +{ + struct zc0301_device* cam = usb_get_intfdata(intf); + + if (!cam) + return; + + down_write(&zc0301_disconnect); + + mutex_lock(&cam->dev_mutex); + + DBG(2, "Disconnecting %s...", cam->v4ldev->name); + + wake_up_interruptible_all(&cam->open); + + if (cam->users) { + DBG(2, "Device /dev/video%d is open! Deregistration and " + "memory deallocation are deferred on close.", + cam->v4ldev->minor); + cam->state |= DEV_MISCONFIGURED; + zc0301_stop_transfer(cam); + cam->state |= DEV_DISCONNECTED; + wake_up_interruptible(&cam->wait_frame); + wake_up_interruptible(&cam->wait_stream); + } else { + cam->state |= DEV_DISCONNECTED; + zc0301_release_resources(cam); + } + + mutex_unlock(&cam->dev_mutex); + + if (!cam->users) + kfree(cam); + + up_write(&zc0301_disconnect); +} + + +static struct usb_driver zc0301_usb_driver = { + .name = "zc0301", + .id_table = zc0301_id_table, + .probe = zc0301_usb_probe, + .disconnect = zc0301_usb_disconnect, +}; + +/*****************************************************************************/ + +static int __init zc0301_module_init(void) +{ + int err = 0; + + KDBG(2, ZC0301_MODULE_NAME " v" ZC0301_MODULE_VERSION); + KDBG(3, ZC0301_MODULE_AUTHOR); + + if ((err = usb_register(&zc0301_usb_driver))) + KDBG(1, "usb_register() failed"); + + return err; +} + + +static void __exit zc0301_module_exit(void) +{ + usb_deregister(&zc0301_usb_driver); +} + + +module_init(zc0301_module_init); +module_exit(zc0301_module_exit); diff --git a/drivers/usb/media/zc0301_pas202bcb.c b/drivers/usb/media/zc0301_pas202bcb.c new file mode 100644 index 000000000000..83b45f1c0f4e --- /dev/null +++ b/drivers/usb/media/zc0301_pas202bcb.c @@ -0,0 +1,353 @@ +/*************************************************************************** + * Plug-in for PAS202BCB image sensor connected to the ZC030! Image * + * Processor and Control Chip * + * * + * Copyright (C) 2006 by Luca Risolia * + * * + * Initialization values of the ZC0301 have been taken from the SPCA5XX * + * driver maintained by Michel Xhaard * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include +#include "zc0301_sensor.h" + + +static struct zc0301_sensor pas202bcb; + + +static int pas202bcb_init(struct zc0301_device* cam) +{ + int err = 0; + + err += zc0301_write_reg(cam, 0x0002, 0x00); + err += zc0301_write_reg(cam, 0x0003, 0x02); + err += zc0301_write_reg(cam, 0x0004, 0x80); + err += zc0301_write_reg(cam, 0x0005, 0x01); + err += zc0301_write_reg(cam, 0x0006, 0xE0); + err += zc0301_write_reg(cam, 0x0098, 0x00); + err += zc0301_write_reg(cam, 0x009A, 0x03); + err += zc0301_write_reg(cam, 0x011A, 0x00); + err += zc0301_write_reg(cam, 0x011C, 0x03); + err += zc0301_write_reg(cam, 0x009B, 0x01); + err += zc0301_write_reg(cam, 0x009C, 0xE6); + err += zc0301_write_reg(cam, 0x009D, 0x02); + err += zc0301_write_reg(cam, 0x009E, 0x86); + + err += zc0301_i2c_write(cam, 0x02, 0x02); + err += zc0301_i2c_write(cam, 0x0A, 0x01); + err += zc0301_i2c_write(cam, 0x0B, 0x01); + err += zc0301_i2c_write(cam, 0x0D, 0x00); + err += zc0301_i2c_write(cam, 0x12, 0x05); + err += zc0301_i2c_write(cam, 0x13, 0x63); + err += zc0301_i2c_write(cam, 0x15, 0x70); + + err += zc0301_write_reg(cam, 0x0101, 0xB7); + err += zc0301_write_reg(cam, 0x0100, 0x0D); + err += zc0301_write_reg(cam, 0x0189, 0x06); + err += zc0301_write_reg(cam, 0x01AD, 0x00); + err += zc0301_write_reg(cam, 0x01C5, 0x03); + err += zc0301_write_reg(cam, 0x01CB, 0x13); + err += zc0301_write_reg(cam, 0x0250, 0x08); + err += zc0301_write_reg(cam, 0x0301, 0x08); + err += zc0301_write_reg(cam, 0x018D, 0x70); + err += zc0301_write_reg(cam, 0x0008, 0x03); + err += zc0301_write_reg(cam, 0x01C6, 0x04); + err += zc0301_write_reg(cam, 0x01CB, 0x07); + err += zc0301_write_reg(cam, 0x0120, 0x11); + err += zc0301_write_reg(cam, 0x0121, 0x37); + err += zc0301_write_reg(cam, 0x0122, 0x58); + err += zc0301_write_reg(cam, 0x0123, 0x79); + err += zc0301_write_reg(cam, 0x0124, 0x91); + err += zc0301_write_reg(cam, 0x0125, 0xA6); + err += zc0301_write_reg(cam, 0x0126, 0xB8); + err += zc0301_write_reg(cam, 0x0127, 0xC7); + err += zc0301_write_reg(cam, 0x0128, 0xD3); + err += zc0301_write_reg(cam, 0x0129, 0xDE); + err += zc0301_write_reg(cam, 0x012A, 0xE6); + err += zc0301_write_reg(cam, 0x012B, 0xED); + err += zc0301_write_reg(cam, 0x012C, 0xF3); + err += zc0301_write_reg(cam, 0x012D, 0xF8); + err += zc0301_write_reg(cam, 0x012E, 0xFB); + err += zc0301_write_reg(cam, 0x012F, 0xFF); + err += zc0301_write_reg(cam, 0x0130, 0x26); + err += zc0301_write_reg(cam, 0x0131, 0x23); + err += zc0301_write_reg(cam, 0x0132, 0x20); + err += zc0301_write_reg(cam, 0x0133, 0x1C); + err += zc0301_write_reg(cam, 0x0134, 0x16); + err += zc0301_write_reg(cam, 0x0135, 0x13); + err += zc0301_write_reg(cam, 0x0136, 0x10); + err += zc0301_write_reg(cam, 0x0137, 0x0D); + err += zc0301_write_reg(cam, 0x0138, 0x0B); + err += zc0301_write_reg(cam, 0x0139, 0x09); + err += zc0301_write_reg(cam, 0x013A, 0x07); + err += zc0301_write_reg(cam, 0x013B, 0x06); + err += zc0301_write_reg(cam, 0x013C, 0x05); + err += zc0301_write_reg(cam, 0x013D, 0x04); + err += zc0301_write_reg(cam, 0x013E, 0x03); + err += zc0301_write_reg(cam, 0x013F, 0x02); + err += zc0301_write_reg(cam, 0x010A, 0x4C); + err += zc0301_write_reg(cam, 0x010B, 0xF5); + err += zc0301_write_reg(cam, 0x010C, 0xFF); + err += zc0301_write_reg(cam, 0x010D, 0xF9); + err += zc0301_write_reg(cam, 0x010E, 0x51); + err += zc0301_write_reg(cam, 0x010F, 0xF5); + err += zc0301_write_reg(cam, 0x0110, 0xFB); + err += zc0301_write_reg(cam, 0x0111, 0xED); + err += zc0301_write_reg(cam, 0x0112, 0x5F); + err += zc0301_write_reg(cam, 0x0180, 0x00); + err += zc0301_write_reg(cam, 0x0019, 0x00); + err += zc0301_write_reg(cam, 0x0087, 0x20); + err += zc0301_write_reg(cam, 0x0088, 0x21); + + err += zc0301_i2c_write(cam, 0x20, 0x02); + err += zc0301_i2c_write(cam, 0x21, 0x1B); + err += zc0301_i2c_write(cam, 0x03, 0x44); + err += zc0301_i2c_write(cam, 0x0E, 0x01); + err += zc0301_i2c_write(cam, 0x0F, 0x00); + + err += zc0301_write_reg(cam, 0x01A9, 0x14); + err += zc0301_write_reg(cam, 0x01AA, 0x24); + err += zc0301_write_reg(cam, 0x0190, 0x00); + err += zc0301_write_reg(cam, 0x0191, 0x02); + err += zc0301_write_reg(cam, 0x0192, 0x1B); + err += zc0301_write_reg(cam, 0x0195, 0x00); + err += zc0301_write_reg(cam, 0x0196, 0x00); + err += zc0301_write_reg(cam, 0x0197, 0x4D); + err += zc0301_write_reg(cam, 0x018C, 0x10); + err += zc0301_write_reg(cam, 0x018F, 0x20); + err += zc0301_write_reg(cam, 0x001D, 0x44); + err += zc0301_write_reg(cam, 0x001E, 0x6F); + err += zc0301_write_reg(cam, 0x001F, 0xAD); + err += zc0301_write_reg(cam, 0x0020, 0xEB); + err += zc0301_write_reg(cam, 0x0087, 0x0F); + err += zc0301_write_reg(cam, 0x0088, 0x0E); + err += zc0301_write_reg(cam, 0x0180, 0x40); + err += zc0301_write_reg(cam, 0x0192, 0x1B); + err += zc0301_write_reg(cam, 0x0191, 0x02); + err += zc0301_write_reg(cam, 0x0190, 0x00); + err += zc0301_write_reg(cam, 0x0116, 0x1D); + err += zc0301_write_reg(cam, 0x0117, 0x40); + err += zc0301_write_reg(cam, 0x0118, 0x99); + err += zc0301_write_reg(cam, 0x0180, 0x42); + err += zc0301_write_reg(cam, 0x0116, 0x1D); + err += zc0301_write_reg(cam, 0x0117, 0x40); + err += zc0301_write_reg(cam, 0x0118, 0x99); + err += zc0301_write_reg(cam, 0x0007, 0x00); + + err += zc0301_i2c_write(cam, 0x11, 0x01); + + msleep(100); + + return err; +} + + +static int pas202bcb_get_ctrl(struct zc0301_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + { + int r1 = zc0301_i2c_read(cam, 0x04, 1), + r2 = zc0301_i2c_read(cam, 0x05, 1); + if (r1 < 0 || r2 < 0) + return -EIO; + ctrl->value = (r1 << 6) | (r2 & 0x3f); + } + return 0; + case V4L2_CID_RED_BALANCE: + if ((ctrl->value = zc0301_i2c_read(cam, 0x09, 1)) < 0) + return -EIO; + ctrl->value &= 0x0f; + return 0; + case V4L2_CID_BLUE_BALANCE: + if ((ctrl->value = zc0301_i2c_read(cam, 0x07, 1)) < 0) + return -EIO; + ctrl->value &= 0x0f; + return 0; + case V4L2_CID_GAIN: + if ((ctrl->value = zc0301_i2c_read(cam, 0x10, 1)) < 0) + return -EIO; + ctrl->value &= 0x1f; + return 0; + case ZC0301_V4L2_CID_GREEN_BALANCE: + if ((ctrl->value = zc0301_i2c_read(cam, 0x08, 1)) < 0) + return -EIO; + ctrl->value &= 0x0f; + return 0; + case ZC0301_V4L2_CID_DAC_MAGNITUDE: + if ((ctrl->value = zc0301_i2c_read(cam, 0x0c, 1)) < 0) + return -EIO; + return 0; + default: + return -EINVAL; + } +} + + +static int pas202bcb_set_ctrl(struct zc0301_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += zc0301_i2c_write(cam, 0x04, ctrl->value >> 6); + err += zc0301_i2c_write(cam, 0x05, ctrl->value & 0x3f); + break; + case V4L2_CID_RED_BALANCE: + err += zc0301_i2c_write(cam, 0x09, ctrl->value); + break; + case V4L2_CID_BLUE_BALANCE: + err += zc0301_i2c_write(cam, 0x07, ctrl->value); + break; + case V4L2_CID_GAIN: + err += zc0301_i2c_write(cam, 0x10, ctrl->value); + break; + case ZC0301_V4L2_CID_GREEN_BALANCE: + err += zc0301_i2c_write(cam, 0x08, ctrl->value); + break; + case ZC0301_V4L2_CID_DAC_MAGNITUDE: + err += zc0301_i2c_write(cam, 0x0c, ctrl->value); + break; + default: + return -EINVAL; + } + err += zc0301_i2c_write(cam, 0x11, 0x01); + + return err ? -EIO : 0; +} + + +static struct zc0301_sensor pas202bcb = { + .name = "PAS202BCB", + .init = &pas202bcb_init, + .qctrl = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x01e5, + .maximum = 0x3fff, + .step = 0x0001, + .default_value = 0x01e5, + .flags = 0, + }, + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x0c, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x01, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x05, + .flags = 0, + }, + { + .id = ZC0301_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = ZC0301_V4L2_CID_DAC_MAGNITUDE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "DAC magnitude", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x04, + .flags = 0, + }, + }, + .get_ctrl = &pas202bcb_get_ctrl, + .set_ctrl = &pas202bcb_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_JPEG, + .priv = 16, + }, +}; + + +int zc0301_probe_pas202bcb(struct zc0301_device* cam) +{ + int r0 = 0, r1 = 0, err = 0; + unsigned int pid = 0; + + err += zc0301_write_reg(cam, 0x0000, 0x01); + err += zc0301_write_reg(cam, 0x0010, 0x0e); + err += zc0301_write_reg(cam, 0x0001, 0x01); + err += zc0301_write_reg(cam, 0x0012, 0x03); + err += zc0301_write_reg(cam, 0x0012, 0x01); + err += zc0301_write_reg(cam, 0x008d, 0x08); + + msleep(10); + + r0 = zc0301_i2c_read(cam, 0x00, 1); + r1 = zc0301_i2c_read(cam, 0x01, 1); + + if (r0 < 0 || r1 < 0 || err) + return -EIO; + + pid = (r0 << 4) | ((r1 & 0xf0) >> 4); + if (pid != 0x017) + return -ENODEV; + + zc0301_attach_sensor(cam, &pas202bcb); + + return 0; +} diff --git a/drivers/usb/media/zc0301_sensor.h b/drivers/usb/media/zc0301_sensor.h new file mode 100644 index 000000000000..8890e32405d4 --- /dev/null +++ b/drivers/usb/media/zc0301_sensor.h @@ -0,0 +1,98 @@ +/*************************************************************************** + * API for image sensors connected to the ZC030! Image Processor and * + * Control Chip * + * * + * Copyright (C) 2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#ifndef _ZC0301_SENSOR_H_ +#define _ZC0301_SENSOR_H_ + +#include +#include +#include +#include +#include +#include + +struct zc0301_device; +struct zc0301_sensor; + +/*****************************************************************************/ + +extern int zc0301_probe_pas202bcb(struct zc0301_device* cam); + +#define ZC0301_SENSOR_TABLE \ +/* Weak detections must go at the end of the list */ \ +static int (*zc0301_sensor_table[])(struct zc0301_device*) = { \ + &zc0301_probe_pas202bcb, \ + NULL, \ +}; + +extern void +zc0301_attach_sensor(struct zc0301_device* cam, + struct zc0301_sensor* sensor); + +#define ZC0301_USB_DEVICE(vend, prod, intclass) \ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ + USB_DEVICE_ID_MATCH_INT_CLASS, \ + .idVendor = (vend), \ + .idProduct = (prod), \ + .bInterfaceClass = (intclass) + +#define ZC0301_ID_TABLE \ +static const struct usb_device_id zc0301_id_table[] = { \ + { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202BCB */ \ + { } \ +}; + +/*****************************************************************************/ + +extern int zc0301_write_reg(struct zc0301_device*, u16 index, u16 value); +extern int zc0301_read_reg(struct zc0301_device*, u16 index); +extern int zc0301_i2c_write(struct zc0301_device*, u16 address, u16 value); +extern int zc0301_i2c_read(struct zc0301_device*, u16 address, u8 length); + +/*****************************************************************************/ + +#define ZC0301_MAX_CTRLS V4L2_CID_LASTP1-V4L2_CID_BASE+10 +#define ZC0301_V4L2_CID_DAC_MAGNITUDE V4L2_CID_PRIVATE_BASE +#define ZC0301_V4L2_CID_GREEN_BALANCE V4L2_CID_PRIVATE_BASE + 1 + +struct zc0301_sensor { + char name[32]; + + struct v4l2_queryctrl qctrl[ZC0301_MAX_CTRLS]; + struct v4l2_cropcap cropcap; + struct v4l2_pix_format pix_format; + + int (*init)(struct zc0301_device* cam); + int (*get_ctrl)(struct zc0301_device* cam, + struct v4l2_control* ctrl); + int (*set_ctrl)(struct zc0301_device* cam, + const struct v4l2_control* ctrl); + int (*set_crop)(struct zc0301_device* cam, + const struct v4l2_rect* rect); + + const struct usb_device* usbdev; + + /* Private */ + struct v4l2_queryctrl _qctrl[ZC0301_MAX_CTRLS]; + struct v4l2_rect _rect; +}; + +#endif /* _ZC0301_SENSOR_H_ */ -- cgit v1.2.3-59-g8ed1b From 8781ba0aa9d9dd2870b75dba8d9a47e0f5a3f96a Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 6 Jan 2006 21:24:25 +0100 Subject: [PATCH] USB: optimise devio.c::usbdev_read this is a small optimisation. It is ridiculous to do a kmalloc for 18 bytes. This puts it onto the stack. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 2b68998fe4b3..d01bd77e7b14 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -134,26 +134,21 @@ static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, l } if (pos < sizeof(struct usb_device_descriptor)) { - struct usb_device_descriptor *desc = kmalloc(sizeof(*desc), GFP_KERNEL); - if (!desc) { - ret = -ENOMEM; - goto err; - } - memcpy(desc, &dev->descriptor, sizeof(dev->descriptor)); - le16_to_cpus(&desc->bcdUSB); - le16_to_cpus(&desc->idVendor); - le16_to_cpus(&desc->idProduct); - le16_to_cpus(&desc->bcdDevice); + struct usb_device_descriptor temp_desc ; /* 18 bytes - fits on the stack */ + + memcpy(&temp_desc, &dev->descriptor, sizeof(dev->descriptor)); + le16_to_cpus(&temp_desc->bcdUSB); + le16_to_cpus(&temp_desc->idVendor); + le16_to_cpus(&temp_desc->idProduct); + le16_to_cpus(&temp_desc->bcdDevice); len = sizeof(struct usb_device_descriptor) - pos; if (len > nbytes) len = nbytes; - if (copy_to_user(buf, ((char *)desc) + pos, len)) { - kfree(desc); + if (copy_to_user(buf, ((char *)&temp_desc) + pos, len)) { ret = -EFAULT; goto err; } - kfree(desc); *ppos += len; buf += len; -- cgit v1.2.3-59-g8ed1b From 9fcd5c322ca2ee636e06e0c099cf8f1a692f832e Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 18 Jan 2006 23:55:07 -0800 Subject: [PATCH] USB: optimise devio.c usbdev_read fix drivers/usb/core/devio.c: In function `usbdev_read': drivers/usb/core/devio.c:140: error: invalid type argument of `->' drivers/usb/core/devio.c:141: error: invalid type argument of `->' drivers/usb/core/devio.c:142: error: invalid type argument of `->' drivers/usb/core/devio.c:143: error: invalid type argument of `->' Cc: Oliver Neukum Cc: Pete Zaitcev Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index d01bd77e7b14..de6a7c07cf97 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -137,10 +137,10 @@ static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, l struct usb_device_descriptor temp_desc ; /* 18 bytes - fits on the stack */ memcpy(&temp_desc, &dev->descriptor, sizeof(dev->descriptor)); - le16_to_cpus(&temp_desc->bcdUSB); - le16_to_cpus(&temp_desc->idVendor); - le16_to_cpus(&temp_desc->idProduct); - le16_to_cpus(&temp_desc->bcdDevice); + le16_to_cpus(&temp_desc.bcdUSB); + le16_to_cpus(&temp_desc.idVendor); + le16_to_cpus(&temp_desc.idProduct); + le16_to_cpus(&temp_desc.bcdDevice); len = sizeof(struct usb_device_descriptor) - pos; if (len > nbytes) -- cgit v1.2.3-59-g8ed1b From 9ff87d7326d9e4666721070040474f60a68ab467 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 6 Jan 2006 20:45:11 +0100 Subject: [PATCH] USB: mdc800.c to kzalloc one more conversion to kzalloc. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/image/mdc800.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c index 049871145d63..4e3e4d048c14 100644 --- a/drivers/usb/image/mdc800.c +++ b/drivers/usb/image/mdc800.c @@ -978,13 +978,11 @@ static int __init usb_mdc800_init (void) { int retval = -ENODEV; /* Allocate Memory */ - mdc800=kmalloc (sizeof (struct mdc800_data), GFP_KERNEL); + mdc800=kzalloc (sizeof (struct mdc800_data), GFP_KERNEL); if (!mdc800) goto cleanup_on_fail; - memset(mdc800, 0, sizeof(struct mdc800_data)); mdc800->dev = NULL; - mdc800->open=0; mdc800->state=NOT_CONNECTED; init_MUTEX (&mdc800->io_lock); -- cgit v1.2.3-59-g8ed1b From 887c2560b6ceb5fe7ac24704e85af507c6d960e5 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Sun, 8 Jan 2006 12:33:45 +0100 Subject: [PATCH] USB: kzalloc for storage another one for kzalloc. This covers the storage subdirectory. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/datafab.c | 3 +-- drivers/usb/storage/isd200.c | 8 +++----- drivers/usb/storage/jumpshot.c | 3 +-- drivers/usb/storage/sddr55.c | 3 +-- drivers/usb/storage/shuttle_usbat.c | 3 +-- 5 files changed, 7 insertions(+), 13 deletions(-) diff --git a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c index 54e3e6c7ecd8..01d8971ad7db 100644 --- a/drivers/usb/storage/datafab.c +++ b/drivers/usb/storage/datafab.c @@ -512,13 +512,12 @@ int datafab_transport(struct scsi_cmnd * srb, struct us_data *us) }; if (!us->extra) { - us->extra = kmalloc(sizeof(struct datafab_info), GFP_NOIO); + us->extra = kzalloc(sizeof(struct datafab_info), GFP_NOIO); if (!us->extra) { US_DEBUGP("datafab_transport: Gah! " "Can't allocate storage for Datafab info struct!\n"); return USB_STOR_TRANSPORT_ERROR; } - memset(us->extra, 0, sizeof(struct datafab_info)); us->extra_destructor = datafab_info_destructor; ((struct datafab_info *)us->extra)->lun = -1; } diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c index ecb328aa9ea1..7308e8cbe8f9 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -1361,21 +1361,19 @@ static int isd200_init_info(struct us_data *us) struct isd200_info *info; info = (struct isd200_info *) - kmalloc(sizeof(struct isd200_info), GFP_KERNEL); + kzalloc(sizeof(struct isd200_info), GFP_KERNEL); if (!info) retStatus = ISD200_ERROR; else { - memset(info, 0, sizeof(struct isd200_info)); info->id = (struct hd_driveid *) - kmalloc(sizeof(struct hd_driveid), GFP_KERNEL); + kzalloc(sizeof(struct hd_driveid), GFP_KERNEL); info->RegsBuf = (unsigned char *) kmalloc(sizeof(info->ATARegs), GFP_KERNEL); if (!info->id || !info->RegsBuf) { isd200_free_info_ptrs(info); kfree(info); retStatus = ISD200_ERROR; - } else - memset(info->id, 0, sizeof(struct hd_driveid)); + } } if (retStatus == ISD200_GOOD) { diff --git a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c index aff9d51c327c..5031aa98f6a9 100644 --- a/drivers/usb/storage/jumpshot.c +++ b/drivers/usb/storage/jumpshot.c @@ -441,12 +441,11 @@ int jumpshot_transport(struct scsi_cmnd * srb, struct us_data *us) }; if (!us->extra) { - us->extra = kmalloc(sizeof(struct jumpshot_info), GFP_NOIO); + us->extra = kzalloc(sizeof(struct jumpshot_info), GFP_NOIO); if (!us->extra) { US_DEBUGP("jumpshot_transport: Gah! Can't allocate storage for jumpshot info struct!\n"); return USB_STOR_TRANSPORT_ERROR; } - memset(us->extra, 0, sizeof(struct jumpshot_info)); us->extra_destructor = jumpshot_info_destructor; } diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c index 8451779f4269..0b1b5b59ca7b 100644 --- a/drivers/usb/storage/sddr55.c +++ b/drivers/usb/storage/sddr55.c @@ -751,11 +751,10 @@ int sddr55_transport(struct scsi_cmnd *srb, struct us_data *us) struct sddr55_card_info *info; if (!us->extra) { - us->extra = kmalloc( + us->extra = kzalloc( sizeof(struct sddr55_card_info), GFP_NOIO); if (!us->extra) return USB_STOR_TRANSPORT_ERROR; - memset(us->extra, 0, sizeof(struct sddr55_card_info)); us->extra_destructor = sddr55_card_info_destructor; } diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c index fea176d7e79a..f2bc5c9e23d5 100644 --- a/drivers/usb/storage/shuttle_usbat.c +++ b/drivers/usb/storage/shuttle_usbat.c @@ -1318,12 +1318,11 @@ int init_usbat(struct us_data *us) unsigned char subcountL = USBAT_ATA_LBA_ME; unsigned char *status = us->iobuf; - us->extra = kmalloc(sizeof(struct usbat_info), GFP_NOIO); + us->extra = kzalloc(sizeof(struct usbat_info), GFP_NOIO); if (!us->extra) { US_DEBUGP("init_usbat: Gah! Can't allocate storage for usbat info struct!\n"); return 1; } - memset(us->extra, 0, sizeof(struct usbat_info)); info = (struct usbat_info *) (us->extra); /* Enable peripheral control signals */ -- cgit v1.2.3-59-g8ed1b From bbdb7dafb5b5a3c0197cbabd5055d8e9c093e3ea Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 6 Jan 2006 20:54:29 +0100 Subject: [PATCH] USB: kzalloc for hid this uses kzalloc in hid. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/input/hid-core.c | 18 +++++------------- drivers/usb/input/hid-lgff.c | 6 ++---- drivers/usb/input/hid-tmff.c | 3 +-- drivers/usb/input/hiddev.c | 6 ++---- 4 files changed, 10 insertions(+), 23 deletions(-) diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c index 07a012f88772..f187a719f17e 100644 --- a/drivers/usb/input/hid-core.c +++ b/drivers/usb/input/hid-core.c @@ -66,9 +66,8 @@ static struct hid_report *hid_register_report(struct hid_device *device, unsigne if (report_enum->report_id_hash[id]) return report_enum->report_id_hash[id]; - if (!(report = kmalloc(sizeof(struct hid_report), GFP_KERNEL))) + if (!(report = kzalloc(sizeof(struct hid_report), GFP_KERNEL))) return NULL; - memset(report, 0, sizeof(struct hid_report)); if (id != 0) report_enum->numbered = 1; @@ -97,12 +96,9 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned return NULL; } - if (!(field = kmalloc(sizeof(struct hid_field) + usages * sizeof(struct hid_usage) + if (!(field = kzalloc(sizeof(struct hid_field) + usages * sizeof(struct hid_usage) + values * sizeof(unsigned), GFP_KERNEL))) return NULL; - memset(field, 0, sizeof(struct hid_field) + usages * sizeof(struct hid_usage) - + values * sizeof(unsigned)); - field->index = report->maxfield++; report->field[field->index] = field; field->usage = (struct hid_usage *)(field + 1); @@ -651,17 +647,14 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size) hid_parser_reserved }; - if (!(device = kmalloc(sizeof(struct hid_device), GFP_KERNEL))) + if (!(device = kzalloc(sizeof(struct hid_device), GFP_KERNEL))) return NULL; - memset(device, 0, sizeof(struct hid_device)); - if (!(device->collection = kmalloc(sizeof(struct hid_collection) * + if (!(device->collection = kzalloc(sizeof(struct hid_collection) * HID_DEFAULT_NUM_COLLECTIONS, GFP_KERNEL))) { kfree(device); return NULL; } - memset(device->collection, 0, sizeof(struct hid_collection) * - HID_DEFAULT_NUM_COLLECTIONS); device->collection_size = HID_DEFAULT_NUM_COLLECTIONS; for (i = 0; i < HID_REPORT_TYPES; i++) @@ -675,13 +668,12 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size) memcpy(device->rdesc, start, size); device->rsize = size; - if (!(parser = kmalloc(sizeof(struct hid_parser), GFP_KERNEL))) { + if (!(parser = kzalloc(sizeof(struct hid_parser), GFP_KERNEL))) { kfree(device->rdesc); kfree(device->collection); kfree(device); return NULL; } - memset(parser, 0, sizeof(struct hid_parser)); parser->device = device; end = start + size; diff --git a/drivers/usb/input/hid-lgff.c b/drivers/usb/input/hid-lgff.c index f82c9c9e5d51..f07d44357ff1 100644 --- a/drivers/usb/input/hid-lgff.c +++ b/drivers/usb/input/hid-lgff.c @@ -154,10 +154,9 @@ int hid_lgff_init(struct hid_device* hid) return -1; } - private = kmalloc(sizeof(struct lgff_device), GFP_KERNEL); + private = kzalloc(sizeof(struct lgff_device), GFP_KERNEL); if (!private) return -1; - memset(private, 0, sizeof(struct lgff_device)); hid->ff_private = private; /* Input init */ @@ -228,13 +227,12 @@ static struct hid_report* hid_lgff_duplicate_report(struct hid_report* report) } *ret->field[0] = *report->field[0]; - ret->field[0]->value = kmalloc(sizeof(s32[8]), GFP_KERNEL); + ret->field[0]->value = kzalloc(sizeof(s32[8]), GFP_KERNEL); if (!ret->field[0]->value) { kfree(ret->field[0]); kfree(ret); return NULL; } - memset(ret->field[0]->value, 0, sizeof(s32[8])); return ret; } diff --git a/drivers/usb/input/hid-tmff.c b/drivers/usb/input/hid-tmff.c index 023fd5ac31c8..534425c69c0a 100644 --- a/drivers/usb/input/hid-tmff.c +++ b/drivers/usb/input/hid-tmff.c @@ -113,11 +113,10 @@ int hid_tmff_init(struct hid_device *hid) struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); struct input_dev *input_dev = hidinput->input; - private = kmalloc(sizeof(struct tmff_device), GFP_KERNEL); + private = kzalloc(sizeof(struct tmff_device), GFP_KERNEL); if (!private) return -ENOMEM; - memset(private, 0, sizeof(struct tmff_device)); hid->ff_private = private; /* Find the report to use */ diff --git a/drivers/usb/input/hiddev.c b/drivers/usb/input/hiddev.c index 925f5aba06f5..6dd666696178 100644 --- a/drivers/usb/input/hiddev.c +++ b/drivers/usb/input/hiddev.c @@ -257,9 +257,8 @@ static int hiddev_open(struct inode * inode, struct file * file) { if (i >= HIDDEV_MINORS || !hiddev_table[i]) return -ENODEV; - if (!(list = kmalloc(sizeof(struct hiddev_list), GFP_KERNEL))) + if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL))) return -ENOMEM; - memset(list, 0, sizeof(struct hiddev_list)); list->hiddev = hiddev_table[i]; list->next = hiddev_table[i]->list; @@ -754,9 +753,8 @@ int hiddev_connect(struct hid_device *hid) if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDDEV) == 0) return -1; - if (!(hiddev = kmalloc(sizeof(struct hiddev), GFP_KERNEL))) + if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL))) return -1; - memset(hiddev, 0, sizeof(struct hiddev)); retval = usb_register_dev(hid->intf, &hiddev_class); if (retval) { -- cgit v1.2.3-59-g8ed1b From d8e298dc9feed17f397be4bb2cd8c1dd47248c74 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 6 Jan 2006 21:01:47 +0100 Subject: [PATCH] USB: kzalloc in dabusb kzalloc in dabusb. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/media/dabusb.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/media/dabusb.c b/drivers/usb/media/dabusb.c index 18d8eaf408d5..6f560fe9a519 100644 --- a/drivers/usb/media/dabusb.c +++ b/drivers/usb/media/dabusb.c @@ -217,12 +217,11 @@ static int dabusb_alloc_buffers (pdabusb_t s) pipesize, packets, transfer_buffer_length); while (buffers < (s->total_buffer_size << 10)) { - b = (pbuff_t) kmalloc (sizeof (buff_t), GFP_KERNEL); + b = (pbuff_t) kzalloc (sizeof (buff_t), GFP_KERNEL); if (!b) { - err("kmalloc(sizeof(buff_t))==NULL"); + err("kzalloc(sizeof(buff_t))==NULL"); goto err; } - memset (b, 0, sizeof (buff_t)); b->s = s; b->purb = usb_alloc_urb(packets, GFP_KERNEL); if (!b->purb) { -- cgit v1.2.3-59-g8ed1b From b10b4177881c50f22a79832558054f2e42be5cfa Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 6 Jan 2006 21:28:40 +0100 Subject: [PATCH] USB: kzalloc in w9968cf another one for kzalloc. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/media/w9968cf.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/usb/media/w9968cf.c b/drivers/usb/media/w9968cf.c index 9937fc64c8bf..78cd1a577d26 100644 --- a/drivers/usb/media/w9968cf.c +++ b/drivers/usb/media/w9968cf.c @@ -695,13 +695,12 @@ static int w9968cf_allocate_memory(struct w9968cf_device* cam) /* Allocate memory for the isochronous transfer buffers */ for (i = 0; i < W9968CF_URBS; i++) { if (!(cam->transfer_buffer[i] = - kmalloc(W9968CF_ISO_PACKETS*p_size, GFP_KERNEL))) { + kzalloc(W9968CF_ISO_PACKETS*p_size, GFP_KERNEL))) { DBG(1, "Couldn't allocate memory for the isochronous " "transfer buffers (%u bytes)", p_size * W9968CF_ISO_PACKETS) return -ENOMEM; } - memset(cam->transfer_buffer[i], 0, W9968CF_ISO_PACKETS*p_size); } /* Allocate memory for the temporary frame buffer */ @@ -3499,12 +3498,10 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) return -ENODEV; cam = (struct w9968cf_device*) - kmalloc(sizeof(struct w9968cf_device), GFP_KERNEL); + kzalloc(sizeof(struct w9968cf_device), GFP_KERNEL); if (!cam) return -ENOMEM; - memset(cam, 0, sizeof(*cam)); - init_MUTEX(&cam->dev_sem); down(&cam->dev_sem); @@ -3532,21 +3529,19 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) /* Allocate 2 bytes of memory for camera control USB transfers */ - if (!(cam->control_buffer = kmalloc(2, GFP_KERNEL))) { + if (!(cam->control_buffer = kzalloc(2, GFP_KERNEL))) { DBG(1,"Couldn't allocate memory for camera control transfers") err = -ENOMEM; goto fail; } - memset(cam->control_buffer, 0, 2); /* Allocate 8 bytes of memory for USB data transfers to the FSB */ - if (!(cam->data_buffer = kmalloc(8, GFP_KERNEL))) { + if (!(cam->data_buffer = kzalloc(8, GFP_KERNEL))) { DBG(1, "Couldn't allocate memory for data " "transfers to the FSB") err = -ENOMEM; goto fail; } - memset(cam->data_buffer, 0, 8); /* Register the V4L device */ cam->v4ldev = video_device_alloc(); -- cgit v1.2.3-59-g8ed1b From 51b208ddf6e492c58609e07fa50bce98bb02cb27 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 6 Jan 2006 21:35:08 +0100 Subject: [PATCH] USB: kzalloc in usbvideo another for kzalloc. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/media/usbvideo.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/media/usbvideo.c b/drivers/usb/media/usbvideo.c index 63a72e550a1b..63d279ec722b 100644 --- a/drivers/usb/media/usbvideo.c +++ b/drivers/usb/media/usbvideo.c @@ -690,14 +690,13 @@ int usbvideo_register( } base_size = num_cams * sizeof(struct uvd) + sizeof(struct usbvideo); - cams = (struct usbvideo *) kmalloc(base_size, GFP_KERNEL); + cams = (struct usbvideo *) kzalloc(base_size, GFP_KERNEL); if (cams == NULL) { err("Failed to allocate %d. bytes for usbvideo struct", base_size); return -ENOMEM; } dbg("%s: Allocated $%p (%d. bytes) for %d. cameras", __FUNCTION__, cams, base_size, num_cams); - memset(cams, 0, base_size); /* Copy callbacks, apply defaults for those that are not set */ memmove(&cams->cb, cbTbl, sizeof(cams->cb)); -- cgit v1.2.3-59-g8ed1b From 5f7481337cc803926b5c43aac44441f683ff91ed Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 6 Jan 2006 22:24:56 +0100 Subject: [PATCH] USB: kzalloc in cytherm another one for kzalloc. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/cytherm.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/misc/cytherm.c b/drivers/usb/misc/cytherm.c index 6671317b495f..a04204292aa3 100644 --- a/drivers/usb/misc/cytherm.c +++ b/drivers/usb/misc/cytherm.c @@ -351,12 +351,11 @@ static int cytherm_probe(struct usb_interface *interface, struct usb_cytherm *dev = NULL; int retval = -ENOMEM; - dev = kmalloc (sizeof(struct usb_cytherm), GFP_KERNEL); + dev = kzalloc (sizeof(struct usb_cytherm), 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); -- cgit v1.2.3-59-g8ed1b From 092e462a537ba60e5f78ff208c8a95e6fd071fa5 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 6 Jan 2006 22:36:27 +0100 Subject: [PATCH] USB: kzalloc in idmouse another for kzalloc. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/idmouse.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index d8cde1017985..b3aca5124339 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -340,10 +340,9 @@ static int idmouse_probe(struct usb_interface *interface, return -ENODEV; /* allocate memory for our device state and initialize it */ - dev = kmalloc(sizeof(*dev), GFP_KERNEL); + dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (dev == NULL) return -ENOMEM; - memset(dev, 0x00, sizeof(*dev)); init_MUTEX(&dev->sem); dev->udev = udev; -- cgit v1.2.3-59-g8ed1b From 1144cf7af2ff8e6816e360d03f867439f7efde40 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 6 Jan 2006 22:40:02 +0100 Subject: [PATCH] USB: kzalloc in ldusb another one for kzalloc Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/ldusb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index e2d1198623eb..f6bdaf08a80f 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -626,12 +626,11 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id * /* allocate memory for our device state and intialize it */ - dev = kmalloc(sizeof(*dev), GFP_KERNEL); + dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (dev == NULL) { dev_err(&intf->dev, "Out of memory\n"); goto exit; } - memset(dev, 0x00, sizeof(*dev)); init_MUTEX(&dev->sem); dev->intf = intf; init_waitqueue_head(&dev->read_wait); -- cgit v1.2.3-59-g8ed1b From 17590840dc3c14cb059817466884ac9bd97c4a35 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 6 Jan 2006 22:41:51 +0100 Subject: [PATCH] USB: kzalloc in PhidgetInterfaceKit another for kzalloc. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/phidgetkit.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/usb/misc/phidgetkit.c b/drivers/usb/misc/phidgetkit.c index 605a3c87e05c..997db5d8e35b 100644 --- a/drivers/usb/misc/phidgetkit.c +++ b/drivers/usb/misc/phidgetkit.c @@ -88,7 +88,7 @@ static int change_outputs(struct phidget_interfacekit *kit, int output_num, int int retval; int n; - buffer = kmalloc(4, GFP_KERNEL); + buffer = kzalloc(4, GFP_KERNEL); if (!buffer) { dev_err(&kit->udev->dev, "%s - out of memory\n", __FUNCTION__); @@ -96,7 +96,6 @@ static int change_outputs(struct phidget_interfacekit *kit, int output_num, int } kit->outputs[output_num] = enable; - memset(buffer, 0, 4); for (n=0; n<8; n++) { if (kit->outputs[n]) { buffer[0] |= 1 << n; @@ -192,7 +191,7 @@ static ssize_t set_backlight(struct device *dev, struct device_attribute *attr, unsigned char *buffer; int retval = -ENOMEM; - buffer = kmalloc(8, GFP_KERNEL); + buffer = kzalloc(8, GFP_KERNEL); if (!buffer) { dev_err(&kit->udev->dev, "%s - out of memory\n", __FUNCTION__); goto exit; @@ -202,7 +201,6 @@ static ssize_t set_backlight(struct device *dev, struct device_attribute *attr, retval = -EINVAL; goto exit; } - memset(buffer, 0x00, 8); if (enabled) buffer[0] = 0x01; buffer[7] = 0x11; @@ -406,12 +404,11 @@ static int interfacekit_probe(struct usb_interface *intf, const struct usb_devic pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); - kit = kmalloc(sizeof(*kit), GFP_KERNEL); + kit = kzalloc(sizeof(*kit), GFP_KERNEL); if (kit == NULL) { dev_err(&intf->dev, "%s - out of memory\n", __FUNCTION__); return -ENOMEM; } - memset(kit, 0, sizeof(*kit)); kit->ifkit = ifkit; kit->data = usb_buffer_alloc(dev, 8, SLAB_ATOMIC, &kit->data_dma); -- cgit v1.2.3-59-g8ed1b From d874a2bade6eec21abb30d4822d7b3e43cfbae95 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 6 Jan 2006 22:43:32 +0100 Subject: [PATCH] USB: kzalloc in PhidgetServo another for kzalloc. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/phidgetservo.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/misc/phidgetservo.c b/drivers/usb/misc/phidgetservo.c index b3418d2bcc69..5a040c205eed 100644 --- a/drivers/usb/misc/phidgetservo.c +++ b/drivers/usb/misc/phidgetservo.c @@ -252,12 +252,11 @@ servo_probe(struct usb_interface *interface, const struct usb_device_id *id) struct usb_device *udev = interface_to_usbdev(interface); struct phidget_servo *dev; - dev = kmalloc(sizeof (struct phidget_servo), GFP_KERNEL); + dev = kzalloc(sizeof (struct phidget_servo), GFP_KERNEL); if (dev == NULL) { dev_err(&interface->dev, "%s - out of memory\n", __FUNCTION__); return -ENOMEM; } - memset(dev, 0x00, sizeof (*dev)); dev->udev = usb_get_dev(udev); dev->type = id->driver_info; -- cgit v1.2.3-59-g8ed1b From 06d694748b5173d972d7e4169173adad62ee67c0 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 6 Jan 2006 22:44:52 +0100 Subject: [PATCH] USB: kzalloc in usbled another one for kzalloc. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usbled.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/misc/usbled.c b/drivers/usb/misc/usbled.c index 877b081a3a6e..f441964132c0 100644 --- a/drivers/usb/misc/usbled.c +++ b/drivers/usb/misc/usbled.c @@ -106,12 +106,11 @@ static int led_probe(struct usb_interface *interface, const struct usb_device_id struct usb_led *dev = NULL; int retval = -ENOMEM; - dev = kmalloc(sizeof(struct usb_led), GFP_KERNEL); + dev = kzalloc(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); -- cgit v1.2.3-59-g8ed1b From 9ee884cc89dc339276c719ab4ff53913ac03d083 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 6 Jan 2006 23:27:17 +0100 Subject: [PATCH] USB: kzalloc in sisusbvga this does two things: - use kzalloc where appropriate - correct error return codes in ioctl Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/sisusbvga/sisusb.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index 3260d595441f..196c8794a73c 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -3188,7 +3188,7 @@ sisusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, break; default: - retval = -EINVAL; + retval = -ENOTTY; break; } @@ -3251,12 +3251,11 @@ static int sisusb_probe(struct usb_interface *intf, dev->devnum); /* Allocate memory for our private */ - if (!(sisusb = kmalloc(sizeof(*sisusb), GFP_KERNEL))) { + if (!(sisusb = kzalloc(sizeof(*sisusb), GFP_KERNEL))) { printk(KERN_ERR "sisusb: Failed to allocate memory for private data\n"); return -ENOMEM; } - memset(sisusb, 0, sizeof(*sisusb)); kref_init(&sisusb->kref); init_MUTEX(&(sisusb->lock)); -- cgit v1.2.3-59-g8ed1b From 29da7937a11a47bad64558c3be4d998c4ea5e823 Mon Sep 17 00:00:00 2001 From: Pete Zaitcev Date: Mon, 13 Feb 2006 20:35:57 -0800 Subject: [PATCH] ub: use kzalloc Switch from kmalloc+memset to kzalloc. Signed-off-by: Pete Zaitcev Signed-off-by: Greg Kroah-Hartman --- drivers/block/ub.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/block/ub.c b/drivers/block/ub.c index f04d864770ad..3e7689494c66 100644 --- a/drivers/block/ub.c +++ b/drivers/block/ub.c @@ -2007,9 +2007,8 @@ static int ub_sync_tur(struct ub_dev *sc, struct ub_lun *lun) init_completion(&compl); rc = -ENOMEM; - if ((cmd = kmalloc(ALLOC_SIZE, GFP_KERNEL)) == NULL) + if ((cmd = kzalloc(ALLOC_SIZE, GFP_KERNEL)) == NULL) goto err_alloc; - memset(cmd, 0, ALLOC_SIZE); cmd->cdb[0] = TEST_UNIT_READY; cmd->cdb_len = 6; @@ -2062,9 +2061,8 @@ static int ub_sync_read_cap(struct ub_dev *sc, struct ub_lun *lun, init_completion(&compl); rc = -ENOMEM; - if ((cmd = kmalloc(ALLOC_SIZE, GFP_KERNEL)) == NULL) + if ((cmd = kzalloc(ALLOC_SIZE, GFP_KERNEL)) == NULL) goto err_alloc; - memset(cmd, 0, ALLOC_SIZE); p = (char *)cmd + sizeof(struct ub_scsi_cmd); cmd->cdb[0] = 0x25; @@ -2405,9 +2403,8 @@ static int ub_probe(struct usb_interface *intf, return -ENXIO; rc = -ENOMEM; - if ((sc = kmalloc(sizeof(struct ub_dev), GFP_KERNEL)) == NULL) + if ((sc = kzalloc(sizeof(struct ub_dev), GFP_KERNEL)) == NULL) goto err_core; - memset(sc, 0, sizeof(struct ub_dev)); sc->lock = ub_next_lock(); INIT_LIST_HEAD(&sc->luns); usb_init_urb(&sc->work_urb); @@ -2524,9 +2521,8 @@ static int ub_probe_lun(struct ub_dev *sc, int lnum) int rc; rc = -ENOMEM; - if ((lun = kmalloc(sizeof(struct ub_lun), GFP_KERNEL)) == NULL) + if ((lun = kzalloc(sizeof(struct ub_lun), GFP_KERNEL)) == NULL) goto err_alloc; - memset(lun, 0, sizeof(struct ub_lun)); lun->num = lnum; rc = -ENOSR; -- cgit v1.2.3-59-g8ed1b From 9628416a542488230b53df48d90d4a967dea41c8 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Sun, 5 Feb 2006 00:03:28 +0100 Subject: [PATCH] USB: remove OBSOLETE_OSS_USB_DRIVER drivers This patch removes the obsolete USB_MIDI and USB_AUDIO drivers. Signed-off-by: Adrian Bunk Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Makefile | 2 - drivers/usb/class/Kconfig | 47 - drivers/usb/class/Makefile | 2 - drivers/usb/class/audio.c | 3869 ------------------------------------------ drivers/usb/class/audio.h | 110 -- drivers/usb/class/usb-midi.c | 2153 ----------------------- drivers/usb/class/usb-midi.h | 164 -- 7 files changed, 6347 deletions(-) delete mode 100644 drivers/usb/class/audio.c delete mode 100644 drivers/usb/class/audio.h delete mode 100644 drivers/usb/class/usb-midi.c delete mode 100644 drivers/usb/class/usb-midi.h diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index bade87b62cf9..7796ecfc0765 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -17,8 +17,6 @@ obj-$(CONFIG_USB_SL811_HCD) += host/ obj-$(CONFIG_ETRAX_USB_HOST) += host/ obj-$(CONFIG_USB_ACM) += class/ -obj-$(CONFIG_USB_AUDIO) += class/ -obj-$(CONFIG_USB_MIDI) += class/ obj-$(CONFIG_USB_PRINTER) += class/ obj-$(CONFIG_USB_STORAGE) += storage/ diff --git a/drivers/usb/class/Kconfig b/drivers/usb/class/Kconfig index ef105a92a7bd..3a9102d2591b 100644 --- a/drivers/usb/class/Kconfig +++ b/drivers/usb/class/Kconfig @@ -4,53 +4,6 @@ comment "USB Device Class drivers" depends on USB -config OBSOLETE_OSS_USB_DRIVER - bool "Obsolete OSS USB drivers" - depends on USB && SOUND - help - This option enables support for the obsolete USB Audio and Midi - drivers that are scheduled for removal in the near future since - there are ALSA drivers for the same hardware. - - Please contact Adrian Bunk if you had to - say Y here because of missing support in the ALSA drivers. - - If unsure, say N. - -config USB_AUDIO - tristate "USB Audio support" - depends on USB && SOUND && OBSOLETE_OSS_USB_DRIVER - help - Say Y here if you want to connect USB audio equipment such as - speakers to your computer's USB port. You only need this if you use - the OSS sound driver; ALSA has its own option for usb audio support. - - To compile this driver as a module, choose M here: the - module will be called audio. - -config USB_MIDI - tristate "USB MIDI support" - depends on USB && SOUND && OBSOLETE_OSS_USB_DRIVER - ---help--- - Say Y here if you want to connect a USB MIDI device to your - computer's USB port. You only need this if you use the OSS - sound system; USB MIDI devices are supported by ALSA's USB - audio driver. This driver is for devices that comply with - 'Universal Serial Bus Device Class Definition for MIDI Device'. - - The following devices are known to work: - * Steinberg USB2MIDI - * Roland MPU64 - * Roland PC-300 - * Roland SC8850 - * Roland UM-1 - * Roland UM-2 - * Roland UA-100 - * Yamaha MU1000 - - To compile this driver as a module, choose M here: the - module will be called usb-midi. - config USB_ACM tristate "USB Modem (CDC ACM) support" depends on USB diff --git a/drivers/usb/class/Makefile b/drivers/usb/class/Makefile index 229471247751..cc391e6c2af8 100644 --- a/drivers/usb/class/Makefile +++ b/drivers/usb/class/Makefile @@ -4,6 +4,4 @@ # obj-$(CONFIG_USB_ACM) += cdc-acm.o -obj-$(CONFIG_USB_AUDIO) += audio.o -obj-$(CONFIG_USB_MIDI) += usb-midi.o obj-$(CONFIG_USB_PRINTER) += usblp.o diff --git a/drivers/usb/class/audio.c b/drivers/usb/class/audio.c deleted file mode 100644 index 3ad9ee8b84a9..000000000000 --- a/drivers/usb/class/audio.c +++ /dev/null @@ -1,3869 +0,0 @@ -/*****************************************************************************/ - -/* - * audio.c -- USB Audio Class driver - * - * Copyright (C) 1999, 2000, 2001, 2003, 2004 - * Alan Cox (alan@lxorguk.ukuu.org.uk) - * Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Debugging: - * Use the 'lsusb' utility to dump the descriptors. - * - * 1999-09-07: Alan Cox - * Parsing Audio descriptor patch - * 1999-09-08: Thomas Sailer - * Added OSS compatible data io functions; both parts of the - * driver remain to be glued together - * 1999-09-10: Thomas Sailer - * Beautified the driver. Added sample format conversions. - * Still not properly glued with the parsing code. - * The parsing code seems to have its problems btw, - * Since it parses all available configs but doesn't - * store which iface/altsetting belongs to which config. - * 1999-09-20: Thomas Sailer - * Threw out Alan's parsing code and implemented my own one. - * You cannot reasonnably linearly parse audio descriptors, - * especially the AudioClass descriptors have to be considered - * pointer lists. Mixer parsing untested, due to lack of device. - * First stab at synch pipe implementation, the Dallas USB DAC - * wants to use an Asynch out pipe. usb_audio_state now basically - * only contains lists of mixer and wave devices. We can therefore - * now have multiple mixer/wave devices per USB device. - * 1999-10-28: Thomas Sailer - * Converted to URB API. Fixed a taskstate/wakeup semantics mistake - * that made the driver consume all available CPU cycles. - * Now runs stable on UHCI-Acher/Fliegl/Sailer. - * 1999-10-31: Thomas Sailer - * Audio can now be unloaded if it is not in use by any mixer - * or dsp client (formerly you had to disconnect the audio devices - * from the USB port) - * Finally, about three months after ordering, my "Maxxtro SPK222" - * speakers arrived, isn't disdata a great mail order company 8-) - * Parse class specific endpoint descriptor of the audiostreaming - * interfaces and take the endpoint attributes from there. - * Unbelievably, the Philips USB DAC has a sampling rate range - * of over a decade, yet does not support the sampling rate control! - * No wonder it sounds so bad, has very audible sampling rate - * conversion distortion. Don't try to listen to it using - * decent headphones! - * "Let's make things better" -> but please Philips start with your - * own stuff!!!! - * 1999-11-02: Thomas Sailer - * It takes the Philips boxes several seconds to acquire synchronisation - * that means they won't play short sounds. Should probably maintain - * the ISO datastream even if there's nothing to play. - * Fix counting the total_bytes counter, RealPlayer G2 depends on it. - * 1999-12-20: Thomas Sailer - * Fix bad bug in conversion to per interface probing. - * disconnect was called multiple times for the audio device, - * leading to a premature freeing of the audio structures - * 2000-05-13: Thomas Sailer - * I don't remember who changed the find_format routine, - * but the change was completely broken for the Dallas - * chip. Anyway taking sampling rate into account in find_format - * is bad and should not be done unless there are devices with - * completely broken audio descriptors. Unless someone shows - * me such a descriptor, I will not allow find_format to - * take the sampling rate into account. - * Also, the former find_format made: - * - mpg123 play mono instead of stereo - * - sox completely fail for wav's with sample rates < 44.1kHz - * for the Dallas chip. - * Also fix a rather long standing problem with applications that - * use "small" writes producing no sound at all. - * 2000-05-15: Thomas Sailer - * My fears came true, the Philips camera indeed has pretty stupid - * audio descriptors. - * 2000-05-17: Thomas Sailer - * Nemsoft spotted my stupid last minute change, thanks - * 2000-05-19: Thomas Sailer - * Fixed FEATURE_UNIT thinkos found thanks to the KC Technology - * Xtend device. Basically the driver treated FEATURE_UNIT's sourced - * by mono terminals as stereo. - * 2000-05-20: Thomas Sailer - * SELECTOR support (and thus selecting record channels from the mixer). - * Somewhat peculiar due to OSS interface limitations. Only works - * for channels where a "slider" is already in front of it (i.e. - * a MIXER unit or a FEATURE unit with volume capability). - * 2000-11-26: Thomas Sailer - * Workaround for Dallas DS4201. The DS4201 uses PCM8 as format tag for - * its 8 bit modes, but expects signed data (and should therefore have used PCM). - * 2001-03-10: Thomas Sailer - * provide abs function, prevent picking up a bogus kernel macro - * for abs. Bug report by Andrew Morton - * 2001-06-16: Bryce Nesbitt - * Fix SNDCTL_DSP_STEREO API violation - * 2003-04-08: Oliver Neukum (oliver@neukum.name): - * Setting a configuration is done by usbcore and must not be overridden - * 2004-02-27: Workaround for broken synch descriptors - * 2004-03-07: Alan Stern - * Add usb_ifnum_to_if() and usb_altnum_to_altsetting() support. - * Use the in-memory descriptors instead of reading them from the device. - * - */ - -/* - * Strategy: - * - * Alan Cox and Thomas Sailer are starting to dig at opposite ends and - * are hoping to meet in the middle, just like tunnel diggers :) - * Alan tackles the descriptor parsing, Thomas the actual data IO and the - * OSS compatible interface. - * - * Data IO implementation issues - * - * A mmap'able ring buffer per direction is implemented, because - * almost every OSS app expects it. It is however impractical to - * transmit/receive USB data directly into and out of the ring buffer, - * due to alignment and synchronisation issues. Instead, the ring buffer - * feeds a constant time delay line that handles the USB issues. - * - * Now we first try to find an alternate setting that exactly matches - * the sample format requested by the user. If we find one, we do not - * need to perform any sample rate conversions. If there is no matching - * altsetting, we choose the closest one and perform sample format - * conversions. We never do sample rate conversion; these are too - * expensive to be performed in the kernel. - * - * Current status: no known HCD-specific issues. - * - * Generally: Due to the brokenness of the Audio Class spec - * it seems generally impossible to write a generic Audio Class driver, - * so a reasonable driver should implement the features that are actually - * used. - * - * Parsing implementation issues - * - * One cannot reasonably parse the AudioClass descriptors linearly. - * Therefore the current implementation features routines to look - * for a specific descriptor in the descriptor list. - * - * How does the parsing work? First, all interfaces are searched - * for an AudioControl class interface. If found, the config descriptor - * that belongs to the current configuration is searched and - * the HEADER descriptor is found. It contains a list of - * all AudioStreaming and MIDIStreaming devices. This list is then walked, - * and all AudioStreaming interfaces are classified into input and output - * interfaces (according to the endpoint0 direction in altsetting1) (MIDIStreaming - * is currently not supported). The input & output list is then used - * to group inputs and outputs together and issued pairwise to the - * AudioStreaming class parser. Finally, all OUTPUT_TERMINAL descriptors - * are walked and issued to the mixer construction routine. - * - * The AudioStreaming parser simply enumerates all altsettings belonging - * to the specified interface. It looks for AS_GENERAL and FORMAT_TYPE - * class specific descriptors to extract the sample format/sample rate - * data. Only sample format types PCM and PCM8 are supported right now, and - * only FORMAT_TYPE_I is handled. The isochronous data endpoint needs to - * be the first endpoint of the interface, and the optional synchronisation - * isochronous endpoint the second one. - * - * Mixer construction works as follows: The various TERMINAL and UNIT - * descriptors span a tree from the root (OUTPUT_TERMINAL) through the - * intermediate nodes (UNITs) to the leaves (INPUT_TERMINAL). We walk - * that tree in a depth first manner. FEATURE_UNITs may contribute volume, - * bass and treble sliders to the mixer, MIXER_UNITs volume sliders. - * The terminal type encoded in the INPUT_TERMINALs feeds a heuristic - * to determine "meaningful" OSS slider numbers, however we will see - * how well this works in practice. Other features are not used at the - * moment, they seem less often used. Also, it seems difficult at least - * to construct recording source switches from SELECTOR_UNITs, but - * since there are not many USB ADC's available, we leave that for later. - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "audio.h" - -/* - * Version Information - */ -#define DRIVER_VERSION "v1.0.0" -#define DRIVER_AUTHOR "Alan Cox , Thomas Sailer (sailer@ife.ee.ethz.ch)" -#define DRIVER_DESC "USB Audio Class driver" - -#define AUDIO_DEBUG 1 - -#define SND_DEV_DSP16 5 - -#define dprintk(x) - -/* --------------------------------------------------------------------- */ - -/* - * Linked list of all audio devices... - */ -static struct list_head audiodevs = LIST_HEAD_INIT(audiodevs); -static DECLARE_MUTEX(open_sem); - -/* - * wait queue for processes wanting to open an USB audio device - */ -static DECLARE_WAIT_QUEUE_HEAD(open_wait); - - -#define MAXFORMATS MAX_ALT -#define DMABUFSHIFT 17 /* 128k worth of DMA buffer */ -#define NRSGBUF (1U<<(DMABUFSHIFT-PAGE_SHIFT)) - -/* - * This influences: - * - Latency - * - Interrupt rate - * - Synchronisation behaviour - * Don't touch this if you don't understand all of the above. - */ -#define DESCFRAMES 5 -#define SYNCFRAMES DESCFRAMES - -#define MIXFLG_STEREOIN 1 -#define MIXFLG_STEREOOUT 2 - -struct mixerchannel { - __u16 value; - __u16 osschannel; /* number of the OSS channel */ - __s16 minval, maxval; - __u16 slctunitid; - __u8 unitid; - __u8 selector; - __u8 chnum; - __u8 flags; -}; - -struct audioformat { - unsigned int format; - unsigned int sratelo; - unsigned int sratehi; - unsigned char altsetting; - unsigned char attributes; -}; - -struct dmabuf { - /* buffer data format */ - unsigned int format; - unsigned int srate; - /* physical buffer */ - unsigned char *sgbuf[NRSGBUF]; - unsigned bufsize; - unsigned numfrag; - unsigned fragshift; - unsigned wrptr, rdptr; - unsigned total_bytes; - int count; - unsigned error; /* over/underrun */ - wait_queue_head_t wait; - /* redundant, but makes calculations easier */ - unsigned fragsize; - unsigned dmasize; - /* OSS stuff */ - unsigned mapped:1; - unsigned ready:1; - unsigned ossfragshift; - int ossmaxfrags; - unsigned subdivision; -}; - -struct usb_audio_state; - -#define FLG_URB0RUNNING 1 -#define FLG_URB1RUNNING 2 -#define FLG_SYNC0RUNNING 4 -#define FLG_SYNC1RUNNING 8 -#define FLG_RUNNING 16 -#define FLG_CONNECTED 32 - -struct my_data_urb { - struct urb *urb; -}; - -struct my_sync_urb { - struct urb *urb; -}; - - -struct usb_audiodev { - struct list_head list; - struct usb_audio_state *state; - - /* soundcore stuff */ - int dev_audio; - - /* wave stuff */ - mode_t open_mode; - spinlock_t lock; /* DMA buffer access spinlock */ - - struct usbin { - int interface; /* Interface number, -1 means not used */ - unsigned int format; /* USB data format */ - unsigned int datapipe; /* the data input pipe */ - unsigned int syncpipe; /* the synchronisation pipe - 0 for anything but adaptive IN mode */ - unsigned int syncinterval; /* P for adaptive IN mode, 0 otherwise */ - unsigned int freqn; /* nominal sampling rate in USB format, i.e. fs/1000 in Q10.14 */ - unsigned int freqmax; /* maximum sampling rate, used for buffer management */ - unsigned int phase; /* phase accumulator */ - unsigned int flags; /* see FLG_ defines */ - - struct my_data_urb durb[2]; /* ISO descriptors for the data endpoint */ - struct my_sync_urb surb[2]; /* ISO sync pipe descriptor if needed */ - - struct dmabuf dma; - } usbin; - - struct usbout { - int interface; /* Interface number, -1 means not used */ - unsigned int format; /* USB data format */ - unsigned int datapipe; /* the data input pipe */ - unsigned int syncpipe; /* the synchronisation pipe - 0 for anything but asynchronous OUT mode */ - unsigned int syncinterval; /* P for asynchronous OUT mode, 0 otherwise */ - unsigned int freqn; /* nominal sampling rate in USB format, i.e. fs/1000 in Q10.14 */ - unsigned int freqm; /* momentary sampling rate in USB format, i.e. fs/1000 in Q10.14 */ - unsigned int freqmax; /* maximum sampling rate, used for buffer management */ - unsigned int phase; /* phase accumulator */ - unsigned int flags; /* see FLG_ defines */ - - struct my_data_urb durb[2]; /* ISO descriptors for the data endpoint */ - struct my_sync_urb surb[2]; /* ISO sync pipe descriptor if needed */ - - struct dmabuf dma; - } usbout; - - - unsigned int numfmtin, numfmtout; - struct audioformat fmtin[MAXFORMATS]; - struct audioformat fmtout[MAXFORMATS]; -}; - -struct usb_mixerdev { - struct list_head list; - struct usb_audio_state *state; - - /* soundcore stuff */ - int dev_mixer; - - unsigned char iface; /* interface number of the AudioControl interface */ - - /* USB format descriptions */ - unsigned int numch, modcnt; - - /* mixch is last and gets allocated dynamically */ - struct mixerchannel ch[0]; -}; - -struct usb_audio_state { - struct list_head audiodev; - - /* USB device */ - struct usb_device *usbdev; - - struct list_head audiolist; - struct list_head mixerlist; - - unsigned count; /* usage counter; NOTE: the usb stack is also considered a user */ -}; - -/* private audio format extensions */ -#define AFMT_STEREO 0x80000000 -#define AFMT_ISSTEREO(x) ((x) & AFMT_STEREO) -#define AFMT_IS16BIT(x) ((x) & (AFMT_S16_LE|AFMT_S16_BE|AFMT_U16_LE|AFMT_U16_BE)) -#define AFMT_ISUNSIGNED(x) ((x) & (AFMT_U8|AFMT_U16_LE|AFMT_U16_BE)) -#define AFMT_BYTESSHIFT(x) ((AFMT_ISSTEREO(x) ? 1 : 0) + (AFMT_IS16BIT(x) ? 1 : 0)) -#define AFMT_BYTES(x) (1<= 0x10000) { - x >>= 16; - r += 16; - } - if (x >= 0x100) { - x >>= 8; - r += 8; - } - if (x >= 0x10) { - x >>= 4; - r += 4; - } - if (x >= 4) { - x >>= 2; - r += 2; - } - if (x >= 2) - r++; - return r; -} - -/* --------------------------------------------------------------------- */ - -/* - * OSS compatible ring buffer management. The ring buffer may be mmap'ed into - * an application address space. - * - * I first used the rvmalloc stuff copied from bttv. Alan Cox did not like it, so - * we now use an array of pointers to a single page each. This saves us the - * kernel page table manipulations, but we have to do a page table alike mechanism - * (though only one indirection) in software. - */ - -static void dmabuf_release(struct dmabuf *db) -{ - unsigned int nr; - void *p; - - for(nr = 0; nr < NRSGBUF; nr++) { - if (!(p = db->sgbuf[nr])) - continue; - ClearPageReserved(virt_to_page(p)); - free_page((unsigned long)p); - db->sgbuf[nr] = NULL; - } - db->mapped = db->ready = 0; -} - -static int dmabuf_init(struct dmabuf *db) -{ - unsigned int nr, bytepersec, bufs; - void *p; - - /* initialize some fields */ - db->rdptr = db->wrptr = db->total_bytes = db->count = db->error = 0; - /* calculate required buffer size */ - bytepersec = db->srate << AFMT_BYTESSHIFT(db->format); - bufs = 1U << DMABUFSHIFT; - if (db->ossfragshift) { - if ((1000 << db->ossfragshift) < bytepersec) - db->fragshift = ld2(bytepersec/1000); - else - db->fragshift = db->ossfragshift; - } else { - db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); - if (db->fragshift < 3) - db->fragshift = 3; - } - db->numfrag = bufs >> db->fragshift; - while (db->numfrag < 4 && db->fragshift > 3) { - db->fragshift--; - db->numfrag = bufs >> db->fragshift; - } - db->fragsize = 1 << db->fragshift; - if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) - db->numfrag = db->ossmaxfrags; - db->dmasize = db->numfrag << db->fragshift; - for(nr = 0; nr < NRSGBUF; nr++) { - if (!db->sgbuf[nr]) { - p = (void *)get_zeroed_page(GFP_KERNEL); - if (!p) - return -ENOMEM; - db->sgbuf[nr] = p; - SetPageReserved(virt_to_page(p)); - } - memset(db->sgbuf[nr], AFMT_ISUNSIGNED(db->format) ? 0x80 : 0, PAGE_SIZE); - if ((nr << PAGE_SHIFT) >= db->dmasize) - break; - } - db->bufsize = nr << PAGE_SHIFT; - db->ready = 1; - dprintk((KERN_DEBUG "usbaudio: dmabuf_init bytepersec %d bufs %d ossfragshift %d ossmaxfrags %d " - "fragshift %d fragsize %d numfrag %d dmasize %d bufsize %d fmt 0x%x srate %d\n", - bytepersec, bufs, db->ossfragshift, db->ossmaxfrags, db->fragshift, db->fragsize, - db->numfrag, db->dmasize, db->bufsize, db->format, db->srate)); - return 0; -} - -static int dmabuf_mmap(struct vm_area_struct *vma, struct dmabuf *db, unsigned long start, unsigned long size, pgprot_t prot) -{ - unsigned int nr; - - if (!db->ready || db->mapped || (start | size) & (PAGE_SIZE-1) || size > db->bufsize) - return -EINVAL; - size >>= PAGE_SHIFT; - for(nr = 0; nr < size; nr++) - if (!db->sgbuf[nr]) - return -EINVAL; - db->mapped = 1; - for(nr = 0; nr < size; nr++) { - unsigned long pfn; - - pfn = virt_to_phys(db->sgbuf[nr]) >> PAGE_SHIFT; - if (remap_pfn_range(vma, start, pfn, PAGE_SIZE, prot)) - return -EAGAIN; - start += PAGE_SIZE; - } - return 0; -} - -static void dmabuf_copyin(struct dmabuf *db, const void *buffer, unsigned int size) -{ - unsigned int pgrem, rem; - - db->total_bytes += size; - for (;;) { - if (size <= 0) - return; - pgrem = ((~db->wrptr) & (PAGE_SIZE-1)) + 1; - if (pgrem > size) - pgrem = size; - rem = db->dmasize - db->wrptr; - if (pgrem > rem) - pgrem = rem; - memcpy((db->sgbuf[db->wrptr >> PAGE_SHIFT]) + (db->wrptr & (PAGE_SIZE-1)), buffer, pgrem); - size -= pgrem; - buffer += pgrem; - db->wrptr += pgrem; - if (db->wrptr >= db->dmasize) - db->wrptr = 0; - } -} - -static void dmabuf_copyout(struct dmabuf *db, void *buffer, unsigned int size) -{ - unsigned int pgrem, rem; - - db->total_bytes += size; - for (;;) { - if (size <= 0) - return; - pgrem = ((~db->rdptr) & (PAGE_SIZE-1)) + 1; - if (pgrem > size) - pgrem = size; - rem = db->dmasize - db->rdptr; - if (pgrem > rem) - pgrem = rem; - memcpy(buffer, (db->sgbuf[db->rdptr >> PAGE_SHIFT]) + (db->rdptr & (PAGE_SIZE-1)), pgrem); - size -= pgrem; - buffer += pgrem; - db->rdptr += pgrem; - if (db->rdptr >= db->dmasize) - db->rdptr = 0; - } -} - -static int dmabuf_copyin_user(struct dmabuf *db, unsigned int ptr, const void __user *buffer, unsigned int size) -{ - unsigned int pgrem, rem; - - if (!db->ready || db->mapped) - return -EINVAL; - for (;;) { - if (size <= 0) - return 0; - pgrem = ((~ptr) & (PAGE_SIZE-1)) + 1; - if (pgrem > size) - pgrem = size; - rem = db->dmasize - ptr; - if (pgrem > rem) - pgrem = rem; - if (copy_from_user((db->sgbuf[ptr >> PAGE_SHIFT]) + (ptr & (PAGE_SIZE-1)), buffer, pgrem)) - return -EFAULT; - size -= pgrem; - buffer += pgrem; - ptr += pgrem; - if (ptr >= db->dmasize) - ptr = 0; - } -} - -static int dmabuf_copyout_user(struct dmabuf *db, unsigned int ptr, void __user *buffer, unsigned int size) -{ - unsigned int pgrem, rem; - - if (!db->ready || db->mapped) - return -EINVAL; - for (;;) { - if (size <= 0) - return 0; - pgrem = ((~ptr) & (PAGE_SIZE-1)) + 1; - if (pgrem > size) - pgrem = size; - rem = db->dmasize - ptr; - if (pgrem > rem) - pgrem = rem; - if (copy_to_user(buffer, (db->sgbuf[ptr >> PAGE_SHIFT]) + (ptr & (PAGE_SIZE-1)), pgrem)) - return -EFAULT; - size -= pgrem; - buffer += pgrem; - ptr += pgrem; - if (ptr >= db->dmasize) - ptr = 0; - } -} - -/* --------------------------------------------------------------------- */ -/* - * USB I/O code. We do sample format conversion if necessary - */ - -static void usbin_stop(struct usb_audiodev *as) -{ - struct usbin *u = &as->usbin; - unsigned long flags; - unsigned int i, notkilled = 1; - - spin_lock_irqsave(&as->lock, flags); - u->flags &= ~FLG_RUNNING; - i = u->flags; - spin_unlock_irqrestore(&as->lock, flags); - while (i & (FLG_URB0RUNNING|FLG_URB1RUNNING|FLG_SYNC0RUNNING|FLG_SYNC1RUNNING)) { - if (notkilled) - schedule_timeout_interruptible(1); - else - schedule_timeout_uninterruptible(1); - spin_lock_irqsave(&as->lock, flags); - i = u->flags; - spin_unlock_irqrestore(&as->lock, flags); - if (notkilled && signal_pending(current)) { - if (i & FLG_URB0RUNNING) - usb_kill_urb(u->durb[0].urb); - if (i & FLG_URB1RUNNING) - usb_kill_urb(u->durb[1].urb); - if (i & FLG_SYNC0RUNNING) - usb_kill_urb(u->surb[0].urb); - if (i & FLG_SYNC1RUNNING) - usb_kill_urb(u->surb[1].urb); - notkilled = 0; - } - } - set_current_state(TASK_RUNNING); - kfree(u->durb[0].urb->transfer_buffer); - kfree(u->durb[1].urb->transfer_buffer); - kfree(u->surb[0].urb->transfer_buffer); - kfree(u->surb[1].urb->transfer_buffer); - u->durb[0].urb->transfer_buffer = u->durb[1].urb->transfer_buffer = - u->surb[0].urb->transfer_buffer = u->surb[1].urb->transfer_buffer = NULL; -} - -static inline void usbin_release(struct usb_audiodev *as) -{ - usbin_stop(as); -} - -static void usbin_disc(struct usb_audiodev *as) -{ - struct usbin *u = &as->usbin; - - unsigned long flags; - - spin_lock_irqsave(&as->lock, flags); - u->flags &= ~(FLG_RUNNING | FLG_CONNECTED); - spin_unlock_irqrestore(&as->lock, flags); - usbin_stop(as); -} - -static void conversion(const void *ibuf, unsigned int ifmt, void *obuf, unsigned int ofmt, void *tmp, unsigned int scnt) -{ - unsigned int cnt, i; - __s16 *sp, *sp2, s; - unsigned char *bp; - - cnt = scnt; - if (AFMT_ISSTEREO(ifmt)) - cnt <<= 1; - sp = ((__s16 *)tmp) + cnt; - switch (ifmt & ~AFMT_STEREO) { - case AFMT_U8: - for (bp = ((unsigned char *)ibuf)+cnt, i = 0; i < cnt; i++) { - bp--; - sp--; - *sp = (*bp ^ 0x80) << 8; - } - break; - - case AFMT_S8: - for (bp = ((unsigned char *)ibuf)+cnt, i = 0; i < cnt; i++) { - bp--; - sp--; - *sp = *bp << 8; - } - break; - - case AFMT_U16_LE: - for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) { - bp -= 2; - sp--; - *sp = (bp[0] | (bp[1] << 8)) ^ 0x8000; - } - break; - - case AFMT_U16_BE: - for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) { - bp -= 2; - sp--; - *sp = (bp[1] | (bp[0] << 8)) ^ 0x8000; - } - break; - - case AFMT_S16_LE: - for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) { - bp -= 2; - sp--; - *sp = bp[0] | (bp[1] << 8); - } - break; - - case AFMT_S16_BE: - for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) { - bp -= 2; - sp--; - *sp = bp[1] | (bp[0] << 8); - } - break; - } - if (!AFMT_ISSTEREO(ifmt) && AFMT_ISSTEREO(ofmt)) { - /* expand from mono to stereo */ - for (sp = ((__s16 *)tmp)+scnt, sp2 = ((__s16 *)tmp)+2*scnt, i = 0; i < scnt; i++) { - sp--; - sp2 -= 2; - sp2[0] = sp2[1] = sp[0]; - } - } - if (AFMT_ISSTEREO(ifmt) && !AFMT_ISSTEREO(ofmt)) { - /* contract from stereo to mono */ - for (sp = sp2 = ((__s16 *)tmp), i = 0; i < scnt; i++, sp++, sp2 += 2) - sp[0] = (sp2[0] + sp2[1]) >> 1; - } - cnt = scnt; - if (AFMT_ISSTEREO(ofmt)) - cnt <<= 1; - sp = ((__s16 *)tmp); - bp = ((unsigned char *)obuf); - switch (ofmt & ~AFMT_STEREO) { - case AFMT_U8: - for (i = 0; i < cnt; i++, sp++, bp++) - *bp = (*sp >> 8) ^ 0x80; - break; - - case AFMT_S8: - for (i = 0; i < cnt; i++, sp++, bp++) - *bp = *sp >> 8; - break; - - case AFMT_U16_LE: - for (i = 0; i < cnt; i++, sp++, bp += 2) { - s = *sp; - bp[0] = s; - bp[1] = (s >> 8) ^ 0x80; - } - break; - - case AFMT_U16_BE: - for (i = 0; i < cnt; i++, sp++, bp += 2) { - s = *sp; - bp[1] = s; - bp[0] = (s >> 8) ^ 0x80; - } - break; - - case AFMT_S16_LE: - for (i = 0; i < cnt; i++, sp++, bp += 2) { - s = *sp; - bp[0] = s; - bp[1] = s >> 8; - } - break; - - case AFMT_S16_BE: - for (i = 0; i < cnt; i++, sp++, bp += 2) { - s = *sp; - bp[1] = s; - bp[0] = s >> 8; - } - break; - } - -} - -static void usbin_convert(struct usbin *u, unsigned char *buffer, unsigned int samples) -{ - union { - __s16 s[64]; - unsigned char b[0]; - } tmp; - unsigned int scnt, maxs, ufmtsh, dfmtsh; - - ufmtsh = AFMT_BYTESSHIFT(u->format); - dfmtsh = AFMT_BYTESSHIFT(u->dma.format); - maxs = (AFMT_ISSTEREO(u->dma.format | u->format)) ? 32 : 64; - while (samples > 0) { - scnt = samples; - if (scnt > maxs) - scnt = maxs; - conversion(buffer, u->format, tmp.b, u->dma.format, tmp.b, scnt); - dmabuf_copyin(&u->dma, tmp.b, scnt << dfmtsh); - buffer += scnt << ufmtsh; - samples -= scnt; - } -} - -static int usbin_prepare_desc(struct usbin *u, struct urb *urb) -{ - unsigned int i, maxsize, offs; - - maxsize = (u->freqmax + 0x3fff) >> (14 - AFMT_BYTESSHIFT(u->format)); - //printk(KERN_DEBUG "usbin_prepare_desc: maxsize %d freq 0x%x format 0x%x\n", maxsize, u->freqn, u->format); - for (i = offs = 0; i < DESCFRAMES; i++, offs += maxsize) { - urb->iso_frame_desc[i].length = maxsize; - urb->iso_frame_desc[i].offset = offs; - } - urb->interval = 1; - return 0; -} - -/* - * return value: 0 if descriptor should be restarted, -1 otherwise - * convert sample format on the fly if necessary - */ -static int usbin_retire_desc(struct usbin *u, struct urb *urb) -{ - unsigned int i, ufmtsh, dfmtsh, err = 0, cnt, scnt, dmafree; - unsigned char *cp; - - ufmtsh = AFMT_BYTESSHIFT(u->format); - dfmtsh = AFMT_BYTESSHIFT(u->dma.format); - for (i = 0; i < DESCFRAMES; i++) { - cp = ((unsigned char *)urb->transfer_buffer) + urb->iso_frame_desc[i].offset; - if (urb->iso_frame_desc[i].status) { - dprintk((KERN_DEBUG "usbin_retire_desc: frame %u status %d\n", i, urb->iso_frame_desc[i].status)); - continue; - } - scnt = urb->iso_frame_desc[i].actual_length >> ufmtsh; - if (!scnt) - continue; - cnt = scnt << dfmtsh; - if (!u->dma.mapped) { - dmafree = u->dma.dmasize - u->dma.count; - if (cnt > dmafree) { - scnt = dmafree >> dfmtsh; - cnt = scnt << dfmtsh; - err++; - } - } - u->dma.count += cnt; - if (u->format == u->dma.format) { - /* we do not need format conversion */ - dprintk((KERN_DEBUG "usbaudio: no sample format conversion\n")); - dmabuf_copyin(&u->dma, cp, cnt); - } else { - /* we need sampling format conversion */ - dprintk((KERN_DEBUG "usbaudio: sample format conversion %x != %x\n", u->format, u->dma.format)); - usbin_convert(u, cp, scnt); - } - } - if (err) - u->dma.error++; - if (u->dma.count >= (signed)u->dma.fragsize) - wake_up(&u->dma.wait); - return err ? -1 : 0; -} - -static void usbin_completed(struct urb *urb, struct pt_regs *regs) -{ - struct usb_audiodev *as = (struct usb_audiodev *)urb->context; - struct usbin *u = &as->usbin; - unsigned long flags; - unsigned int mask; - int suret = 0; - -#if 0 - printk(KERN_DEBUG "usbin_completed: status %d errcnt %d flags 0x%x\n", urb->status, urb->error_count, u->flags); -#endif - if (urb == u->durb[0].urb) - mask = FLG_URB0RUNNING; - else if (urb == u->durb[1].urb) - mask = FLG_URB1RUNNING; - else { - mask = 0; - printk(KERN_ERR "usbin_completed: panic: unknown URB\n"); - } - urb->dev = as->state->usbdev; - spin_lock_irqsave(&as->lock, flags); - if (!usbin_retire_desc(u, urb) && - u->flags & FLG_RUNNING && - !usbin_prepare_desc(u, urb) && - (suret = usb_submit_urb(urb, GFP_ATOMIC)) == 0) { - u->flags |= mask; - } else { - u->flags &= ~(mask | FLG_RUNNING); - wake_up(&u->dma.wait); - printk(KERN_DEBUG "usbin_completed: descriptor not restarted (usb_submit_urb: %d)\n", suret); - } - spin_unlock_irqrestore(&as->lock, flags); -} - -/* - * we output sync data - */ -static int usbin_sync_prepare_desc(struct usbin *u, struct urb *urb) -{ - unsigned char *cp = urb->transfer_buffer; - unsigned int i, offs; - - for (i = offs = 0; i < SYNCFRAMES; i++, offs += 3, cp += 3) { - urb->iso_frame_desc[i].length = 3; - urb->iso_frame_desc[i].offset = offs; - cp[0] = u->freqn; - cp[1] = u->freqn >> 8; - cp[2] = u->freqn >> 16; - } - urb->interval = 1; - return 0; -} - -/* - * return value: 0 if descriptor should be restarted, -1 otherwise - */ -static int usbin_sync_retire_desc(struct usbin *u, struct urb *urb) -{ - unsigned int i; - - for (i = 0; i < SYNCFRAMES; i++) - if (urb->iso_frame_desc[0].status) - dprintk((KERN_DEBUG "usbin_sync_retire_desc: frame %u status %d\n", i, urb->iso_frame_desc[i].status)); - return 0; -} - -static void usbin_sync_completed(struct urb *urb, struct pt_regs *regs) -{ - struct usb_audiodev *as = (struct usb_audiodev *)urb->context; - struct usbin *u = &as->usbin; - unsigned long flags; - unsigned int mask; - int suret = 0; - -#if 0 - printk(KERN_DEBUG "usbin_sync_completed: status %d errcnt %d flags 0x%x\n", urb->status, urb->error_count, u->flags); -#endif - if (urb == u->surb[0].urb) - mask = FLG_SYNC0RUNNING; - else if (urb == u->surb[1].urb) - mask = FLG_SYNC1RUNNING; - else { - mask = 0; - printk(KERN_ERR "usbin_sync_completed: panic: unknown URB\n"); - } - urb->dev = as->state->usbdev; - spin_lock_irqsave(&as->lock, flags); - if (!usbin_sync_retire_desc(u, urb) && - u->flags & FLG_RUNNING && - !usbin_sync_prepare_desc(u, urb) && - (suret = usb_submit_urb(urb, GFP_ATOMIC)) == 0) { - u->flags |= mask; - } else { - u->flags &= ~(mask | FLG_RUNNING); - wake_up(&u->dma.wait); - dprintk((KERN_DEBUG "usbin_sync_completed: descriptor not restarted (usb_submit_urb: %d)\n", suret)); - } - spin_unlock_irqrestore(&as->lock, flags); -} - -static int usbin_start(struct usb_audiodev *as) -{ - struct usb_device *dev = as->state->usbdev; - struct usbin *u = &as->usbin; - struct urb *urb; - unsigned long flags; - unsigned int maxsze, bufsz; - -#if 0 - printk(KERN_DEBUG "usbin_start: device %d ufmt 0x%08x dfmt 0x%08x srate %d\n", - dev->devnum, u->format, u->dma.format, u->dma.srate); -#endif - /* allocate USB storage if not already done */ - spin_lock_irqsave(&as->lock, flags); - if (!(u->flags & FLG_CONNECTED)) { - spin_unlock_irqrestore(&as->lock, flags); - return -EIO; - } - if (!(u->flags & FLG_RUNNING)) { - spin_unlock_irqrestore(&as->lock, flags); - u->freqn = ((u->dma.srate << 11) + 62) / 125; /* this will overflow at approx 2MSPS */ - u->freqmax = u->freqn + (u->freqn >> 2); - u->phase = 0; - maxsze = (u->freqmax + 0x3fff) >> (14 - AFMT_BYTESSHIFT(u->format)); - bufsz = DESCFRAMES * maxsze; - kfree(u->durb[0].urb->transfer_buffer); - u->durb[0].urb->transfer_buffer = kmalloc(bufsz, GFP_KERNEL); - u->durb[0].urb->transfer_buffer_length = bufsz; - kfree(u->durb[1].urb->transfer_buffer); - u->durb[1].urb->transfer_buffer = kmalloc(bufsz, GFP_KERNEL); - u->durb[1].urb->transfer_buffer_length = bufsz; - if (u->syncpipe) { - kfree(u->surb[0].urb->transfer_buffer); - u->surb[0].urb->transfer_buffer = kmalloc(3*SYNCFRAMES, GFP_KERNEL); - u->surb[0].urb->transfer_buffer_length = 3*SYNCFRAMES; - kfree(u->surb[1].urb->transfer_buffer); - u->surb[1].urb->transfer_buffer = kmalloc(3*SYNCFRAMES, GFP_KERNEL); - u->surb[1].urb->transfer_buffer_length = 3*SYNCFRAMES; - } - if (!u->durb[0].urb->transfer_buffer || !u->durb[1].urb->transfer_buffer || - (u->syncpipe && (!u->surb[0].urb->transfer_buffer || !u->surb[1].urb->transfer_buffer))) { - printk(KERN_ERR "usbaudio: cannot start playback device %d\n", dev->devnum); - return 0; - } - spin_lock_irqsave(&as->lock, flags); - } - if (u->dma.count >= u->dma.dmasize && !u->dma.mapped) { - spin_unlock_irqrestore(&as->lock, flags); - return 0; - } - u->flags |= FLG_RUNNING; - if (!(u->flags & FLG_URB0RUNNING)) { - urb = u->durb[0].urb; - urb->dev = dev; - urb->pipe = u->datapipe; - urb->transfer_flags = URB_ISO_ASAP; - urb->number_of_packets = DESCFRAMES; - urb->context = as; - urb->complete = usbin_completed; - if (!usbin_prepare_desc(u, urb) && !usb_submit_urb(urb, GFP_KERNEL)) - u->flags |= FLG_URB0RUNNING; - else - u->flags &= ~FLG_RUNNING; - } - if (u->flags & FLG_RUNNING && !(u->flags & FLG_URB1RUNNING)) { - urb = u->durb[1].urb; - urb->dev = dev; - urb->pipe = u->datapipe; - urb->transfer_flags = URB_ISO_ASAP; - urb->number_of_packets = DESCFRAMES; - urb->context = as; - urb->complete = usbin_completed; - if (!usbin_prepare_desc(u, urb) && !usb_submit_urb(urb, GFP_KERNEL)) - u->flags |= FLG_URB1RUNNING; - else - u->flags &= ~FLG_RUNNING; - } - if (u->syncpipe) { - if (u->flags & FLG_RUNNING && !(u->flags & FLG_SYNC0RUNNING)) { - urb = u->surb[0].urb; - urb->dev = dev; - urb->pipe = u->syncpipe; - urb->transfer_flags = URB_ISO_ASAP; - urb->number_of_packets = SYNCFRAMES; - urb->context = as; - urb->complete = usbin_sync_completed; - /* stride: u->syncinterval */ - if (!usbin_sync_prepare_desc(u, urb) && !usb_submit_urb(urb, GFP_KERNEL)) - u->flags |= FLG_SYNC0RUNNING; - else - u->flags &= ~FLG_RUNNING; - } - if (u->flags & FLG_RUNNING && !(u->flags & FLG_SYNC1RUNNING)) { - urb = u->surb[1].urb; - urb->dev = dev; - urb->pipe = u->syncpipe; - urb->transfer_flags = URB_ISO_ASAP; - urb->number_of_packets = SYNCFRAMES; - urb->context = as; - urb->complete = usbin_sync_completed; - /* stride: u->syncinterval */ - if (!usbin_sync_prepare_desc(u, urb) && !usb_submit_urb(urb, GFP_KERNEL)) - u->flags |= FLG_SYNC1RUNNING; - else - u->flags &= ~FLG_RUNNING; - } - } - spin_unlock_irqrestore(&as->lock, flags); - return 0; -} - -static void usbout_stop(struct usb_audiodev *as) -{ - struct usbout *u = &as->usbout; - unsigned long flags; - unsigned int i, notkilled = 1; - - spin_lock_irqsave(&as->lock, flags); - u->flags &= ~FLG_RUNNING; - i = u->flags; - spin_unlock_irqrestore(&as->lock, flags); - while (i & (FLG_URB0RUNNING|FLG_URB1RUNNING|FLG_SYNC0RUNNING|FLG_SYNC1RUNNING)) { - if (notkilled) - schedule_timeout_interruptible(1); - else - schedule_timeout_uninterruptible(1); - spin_lock_irqsave(&as->lock, flags); - i = u->flags; - spin_unlock_irqrestore(&as->lock, flags); - if (notkilled && signal_pending(current)) { - if (i & FLG_URB0RUNNING) - usb_kill_urb(u->durb[0].urb); - if (i & FLG_URB1RUNNING) - usb_kill_urb(u->durb[1].urb); - if (i & FLG_SYNC0RUNNING) - usb_kill_urb(u->surb[0].urb); - if (i & FLG_SYNC1RUNNING) - usb_kill_urb(u->surb[1].urb); - notkilled = 0; - } - } - set_current_state(TASK_RUNNING); - kfree(u->durb[0].urb->transfer_buffer); - kfree(u->durb[1].urb->transfer_buffer); - kfree(u->surb[0].urb->transfer_buffer); - kfree(u->surb[1].urb->transfer_buffer); - u->durb[0].urb->transfer_buffer = u->durb[1].urb->transfer_buffer = - u->surb[0].urb->transfer_buffer = u->surb[1].urb->transfer_buffer = NULL; -} - -static inline void usbout_release(struct usb_audiodev *as) -{ - usbout_stop(as); -} - -static void usbout_disc(struct usb_audiodev *as) -{ - struct usbout *u = &as->usbout; - unsigned long flags; - - spin_lock_irqsave(&as->lock, flags); - u->flags &= ~(FLG_RUNNING | FLG_CONNECTED); - spin_unlock_irqrestore(&as->lock, flags); - usbout_stop(as); -} - -static void usbout_convert(struct usbout *u, unsigned char *buffer, unsigned int samples) -{ - union { - __s16 s[64]; - unsigned char b[0]; - } tmp; - unsigned int scnt, maxs, ufmtsh, dfmtsh; - - ufmtsh = AFMT_BYTESSHIFT(u->format); - dfmtsh = AFMT_BYTESSHIFT(u->dma.format); - maxs = (AFMT_ISSTEREO(u->dma.format | u->format)) ? 32 : 64; - while (samples > 0) { - scnt = samples; - if (scnt > maxs) - scnt = maxs; - dmabuf_copyout(&u->dma, tmp.b, scnt << dfmtsh); - conversion(tmp.b, u->dma.format, buffer, u->format, tmp.b, scnt); - buffer += scnt << ufmtsh; - samples -= scnt; - } -} - -static int usbout_prepare_desc(struct usbout *u, struct urb *urb) -{ - unsigned int i, ufmtsh, dfmtsh, err = 0, cnt, scnt, offs; - unsigned char *cp = urb->transfer_buffer; - - ufmtsh = AFMT_BYTESSHIFT(u->format); - dfmtsh = AFMT_BYTESSHIFT(u->dma.format); - for (i = offs = 0; i < DESCFRAMES; i++) { - urb->iso_frame_desc[i].offset = offs; - u->phase = (u->phase & 0x3fff) + u->freqm; - scnt = u->phase >> 14; - if (!scnt) { - urb->iso_frame_desc[i].length = 0; - continue; - } - cnt = scnt << dfmtsh; - if (!u->dma.mapped) { - if (cnt > u->dma.count) { - scnt = u->dma.count >> dfmtsh; - cnt = scnt << dfmtsh; - err++; - } - u->dma.count -= cnt; - } else - u->dma.count += cnt; - if (u->format == u->dma.format) { - /* we do not need format conversion */ - dmabuf_copyout(&u->dma, cp, cnt); - } else { - /* we need sampling format conversion */ - usbout_convert(u, cp, scnt); - } - cnt = scnt << ufmtsh; - urb->iso_frame_desc[i].length = cnt; - offs += cnt; - cp += cnt; - } - urb->interval = 1; - if (err) - u->dma.error++; - if (u->dma.mapped) { - if (u->dma.count >= (signed)u->dma.fragsize) - wake_up(&u->dma.wait); - } else { - if ((signed)u->dma.dmasize >= u->dma.count + (signed)u->dma.fragsize) - wake_up(&u->dma.wait); - } - return err ? -1 : 0; -} - -/* - * return value: 0 if descriptor should be restarted, -1 otherwise - */ -static int usbout_retire_desc(struct usbout *u, struct urb *urb) -{ - unsigned int i; - - for (i = 0; i < DESCFRAMES; i++) { - if (urb->iso_frame_desc[i].status) { - dprintk((KERN_DEBUG "usbout_retire_desc: frame %u status %d\n", i, urb->iso_frame_desc[i].status)); - continue; - } - } - return 0; -} - -static void usbout_completed(struct urb *urb, struct pt_regs *regs) -{ - struct usb_audiodev *as = (struct usb_audiodev *)urb->context; - struct usbout *u = &as->usbout; - unsigned long flags; - unsigned int mask; - int suret = 0; - -#if 0 - printk(KERN_DEBUG "usbout_completed: status %d errcnt %d flags 0x%x\n", urb->status, urb->error_count, u->flags); -#endif - if (urb == u->durb[0].urb) - mask = FLG_URB0RUNNING; - else if (urb == u->durb[1].urb) - mask = FLG_URB1RUNNING; - else { - mask = 0; - printk(KERN_ERR "usbout_completed: panic: unknown URB\n"); - } - urb->dev = as->state->usbdev; - spin_lock_irqsave(&as->lock, flags); - if (!usbout_retire_desc(u, urb) && - u->flags & FLG_RUNNING && - !usbout_prepare_desc(u, urb) && - (suret = usb_submit_urb(urb, GFP_ATOMIC)) == 0) { - u->flags |= mask; - } else { - u->flags &= ~(mask | FLG_RUNNING); - wake_up(&u->dma.wait); - dprintk((KERN_DEBUG "usbout_completed: descriptor not restarted (usb_submit_urb: %d)\n", suret)); - } - spin_unlock_irqrestore(&as->lock, flags); -} - -static int usbout_sync_prepare_desc(struct usbout *u, struct urb *urb) -{ - unsigned int i, offs; - - for (i = offs = 0; i < SYNCFRAMES; i++, offs += 3) { - urb->iso_frame_desc[i].length = 3; - urb->iso_frame_desc[i].offset = offs; - } - urb->interval = 1; - return 0; -} - -/* - * return value: 0 if descriptor should be restarted, -1 otherwise - */ -static int usbout_sync_retire_desc(struct usbout *u, struct urb *urb) -{ - unsigned char *cp = urb->transfer_buffer; - unsigned int f, i; - - for (i = 0; i < SYNCFRAMES; i++, cp += 3) { - if (urb->iso_frame_desc[i].status) { - dprintk((KERN_DEBUG "usbout_sync_retire_desc: frame %u status %d\n", i, urb->iso_frame_desc[i].status)); - continue; - } - if (urb->iso_frame_desc[i].actual_length < 3) { - dprintk((KERN_DEBUG "usbout_sync_retire_desc: frame %u length %d\n", i, urb->iso_frame_desc[i].actual_length)); - continue; - } - f = cp[0] | (cp[1] << 8) | (cp[2] << 16); - if (abs(f - u->freqn) > (u->freqn >> 3) || f > u->freqmax) { - printk(KERN_WARNING "usbout_sync_retire_desc: requested frequency %u (nominal %u) out of range!\n", f, u->freqn); - continue; - } - u->freqm = f; - } - return 0; -} - -static void usbout_sync_completed(struct urb *urb, struct pt_regs *regs) -{ - struct usb_audiodev *as = (struct usb_audiodev *)urb->context; - struct usbout *u = &as->usbout; - unsigned long flags; - unsigned int mask; - int suret = 0; - -#if 0 - printk(KERN_DEBUG "usbout_sync_completed: status %d errcnt %d flags 0x%x\n", urb->status, urb->error_count, u->flags); -#endif - if (urb == u->surb[0].urb) - mask = FLG_SYNC0RUNNING; - else if (urb == u->surb[1].urb) - mask = FLG_SYNC1RUNNING; - else { - mask = 0; - printk(KERN_ERR "usbout_sync_completed: panic: unknown URB\n"); - } - urb->dev = as->state->usbdev; - spin_lock_irqsave(&as->lock, flags); - if (!usbout_sync_retire_desc(u, urb) && - u->flags & FLG_RUNNING && - !usbout_sync_prepare_desc(u, urb) && - (suret = usb_submit_urb(urb, GFP_ATOMIC)) == 0) { - u->flags |= mask; - } else { - u->flags &= ~(mask | FLG_RUNNING); - wake_up(&u->dma.wait); - dprintk((KERN_DEBUG "usbout_sync_completed: descriptor not restarted (usb_submit_urb: %d)\n", suret)); - } - spin_unlock_irqrestore(&as->lock, flags); -} - -static int usbout_start(struct usb_audiodev *as) -{ - struct usb_device *dev = as->state->usbdev; - struct usbout *u = &as->usbout; - struct urb *urb; - unsigned long flags; - unsigned int maxsze, bufsz; - -#if 0 - printk(KERN_DEBUG "usbout_start: device %d ufmt 0x%08x dfmt 0x%08x srate %d\n", - dev->devnum, u->format, u->dma.format, u->dma.srate); -#endif - /* allocate USB storage if not already done */ - spin_lock_irqsave(&as->lock, flags); - if (!(u->flags & FLG_CONNECTED)) { - spin_unlock_irqrestore(&as->lock, flags); - return -EIO; - } - if (!(u->flags & FLG_RUNNING)) { - spin_unlock_irqrestore(&as->lock, flags); - u->freqn = u->freqm = ((u->dma.srate << 11) + 62) / 125; /* this will overflow at approx 2MSPS */ - u->freqmax = u->freqn + (u->freqn >> 2); - u->phase = 0; - maxsze = (u->freqmax + 0x3fff) >> (14 - AFMT_BYTESSHIFT(u->format)); - bufsz = DESCFRAMES * maxsze; - kfree(u->durb[0].urb->transfer_buffer); - u->durb[0].urb->transfer_buffer = kmalloc(bufsz, GFP_KERNEL); - u->durb[0].urb->transfer_buffer_length = bufsz; - kfree(u->durb[1].urb->transfer_buffer); - u->durb[1].urb->transfer_buffer = kmalloc(bufsz, GFP_KERNEL); - u->durb[1].urb->transfer_buffer_length = bufsz; - if (u->syncpipe) { - kfree(u->surb[0].urb->transfer_buffer); - u->surb[0].urb->transfer_buffer = kmalloc(3*SYNCFRAMES, GFP_KERNEL); - u->surb[0].urb->transfer_buffer_length = 3*SYNCFRAMES; - kfree(u->surb[1].urb->transfer_buffer); - u->surb[1].urb->transfer_buffer = kmalloc(3*SYNCFRAMES, GFP_KERNEL); - u->surb[1].urb->transfer_buffer_length = 3*SYNCFRAMES; - } - if (!u->durb[0].urb->transfer_buffer || !u->durb[1].urb->transfer_buffer || - (u->syncpipe && (!u->surb[0].urb->transfer_buffer || !u->surb[1].urb->transfer_buffer))) { - printk(KERN_ERR "usbaudio: cannot start playback device %d\n", dev->devnum); - return 0; - } - spin_lock_irqsave(&as->lock, flags); - } - if (u->dma.count <= 0 && !u->dma.mapped) { - spin_unlock_irqrestore(&as->lock, flags); - return 0; - } - u->flags |= FLG_RUNNING; - if (!(u->flags & FLG_URB0RUNNING)) { - urb = u->durb[0].urb; - urb->dev = dev; - urb->pipe = u->datapipe; - urb->transfer_flags = URB_ISO_ASAP; - urb->number_of_packets = DESCFRAMES; - urb->context = as; - urb->complete = usbout_completed; - if (!usbout_prepare_desc(u, urb) && !usb_submit_urb(urb, GFP_ATOMIC)) - u->flags |= FLG_URB0RUNNING; - else - u->flags &= ~FLG_RUNNING; - } - if (u->flags & FLG_RUNNING && !(u->flags & FLG_URB1RUNNING)) { - urb = u->durb[1].urb; - urb->dev = dev; - urb->pipe = u->datapipe; - urb->transfer_flags = URB_ISO_ASAP; - urb->number_of_packets = DESCFRAMES; - urb->context = as; - urb->complete = usbout_completed; - if (!usbout_prepare_desc(u, urb) && !usb_submit_urb(urb, GFP_ATOMIC)) - u->flags |= FLG_URB1RUNNING; - else - u->flags &= ~FLG_RUNNING; - } - if (u->syncpipe) { - if (u->flags & FLG_RUNNING && !(u->flags & FLG_SYNC0RUNNING)) { - urb = u->surb[0].urb; - urb->dev = dev; - urb->pipe = u->syncpipe; - urb->transfer_flags = URB_ISO_ASAP; - urb->number_of_packets = SYNCFRAMES; - urb->context = as; - urb->complete = usbout_sync_completed; - /* stride: u->syncinterval */ - if (!usbout_sync_prepare_desc(u, urb) && !usb_submit_urb(urb, GFP_ATOMIC)) - u->flags |= FLG_SYNC0RUNNING; - else - u->flags &= ~FLG_RUNNING; - } - if (u->flags & FLG_RUNNING && !(u->flags & FLG_SYNC1RUNNING)) { - urb = u->surb[1].urb; - urb->dev = dev; - urb->pipe = u->syncpipe; - urb->transfer_flags = URB_ISO_ASAP; - urb->number_of_packets = SYNCFRAMES; - urb->context = as; - urb->complete = usbout_sync_completed; - /* stride: u->syncinterval */ - if (!usbout_sync_prepare_desc(u, urb) && !usb_submit_urb(urb, GFP_ATOMIC)) - u->flags |= FLG_SYNC1RUNNING; - else - u->flags &= ~FLG_RUNNING; - } - } - spin_unlock_irqrestore(&as->lock, flags); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static unsigned int format_goodness(struct audioformat *afp, unsigned int fmt, unsigned int srate) -{ - unsigned int g = 0; - - if (srate < afp->sratelo) - g += afp->sratelo - srate; - if (srate > afp->sratehi) - g += srate - afp->sratehi; - if (AFMT_ISSTEREO(afp->format) && !AFMT_ISSTEREO(fmt)) - g += 0x100000; - if (!AFMT_ISSTEREO(afp->format) && AFMT_ISSTEREO(fmt)) - g += 0x400000; - if (AFMT_IS16BIT(afp->format) && !AFMT_IS16BIT(fmt)) - g += 0x100000; - if (!AFMT_IS16BIT(afp->format) && AFMT_IS16BIT(fmt)) - g += 0x400000; - return g; -} - -static int find_format(struct audioformat *afp, unsigned int nr, unsigned int fmt, unsigned int srate) -{ - unsigned int i, g, gb = ~0; - int j = -1; /* default to failure */ - - /* find "best" format (according to format_goodness) */ - for (i = 0; i < nr; i++) { - g = format_goodness(&afp[i], fmt, srate); - if (g >= gb) - continue; - j = i; - gb = g; - } - return j; -} - -static int set_format_in(struct usb_audiodev *as) -{ - struct usb_device *dev = as->state->usbdev; - struct usb_host_interface *alts; - struct usb_interface *iface; - struct usbin *u = &as->usbin; - struct dmabuf *d = &u->dma; - struct audioformat *fmt; - unsigned int ep; - unsigned char data[3]; - int fmtnr, ret; - - iface = usb_ifnum_to_if(dev, u->interface); - if (!iface) - return 0; - - fmtnr = find_format(as->fmtin, as->numfmtin, d->format, d->srate); - if (fmtnr < 0) { - printk(KERN_ERR "usbaudio: set_format_in(): failed to find desired format/speed combination.\n"); - return -1; - } - - fmt = as->fmtin + fmtnr; - alts = usb_altnum_to_altsetting(iface, fmt->altsetting); - u->format = fmt->format; - u->datapipe = usb_rcvisocpipe(dev, alts->endpoint[0].desc.bEndpointAddress & 0xf); - u->syncpipe = u->syncinterval = 0; - if ((alts->endpoint[0].desc.bmAttributes & 0x0c) == 0x08) { - if (alts->desc.bNumEndpoints < 2 || - alts->endpoint[1].desc.bmAttributes != 0x01 || - alts->endpoint[1].desc.bSynchAddress != 0 || - alts->endpoint[1].desc.bEndpointAddress != (alts->endpoint[0].desc.bSynchAddress & 0x7f)) { - printk(KERN_WARNING "usbaudio: device %d interface %d altsetting %d claims adaptive in " - "but has invalid synch pipe; treating as asynchronous in\n", - dev->devnum, u->interface, fmt->altsetting); - } else { - u->syncpipe = usb_sndisocpipe(dev, alts->endpoint[1].desc.bEndpointAddress & 0xf); - u->syncinterval = alts->endpoint[1].desc.bRefresh; - } - } - if (d->srate < fmt->sratelo) - d->srate = fmt->sratelo; - if (d->srate > fmt->sratehi) - d->srate = fmt->sratehi; - dprintk((KERN_DEBUG "usbaudio: set_format_in: usb_set_interface %u %u\n", - u->interface, fmt->altsetting)); - if (usb_set_interface(dev, alts->desc.bInterfaceNumber, fmt->altsetting) < 0) { - printk(KERN_WARNING "usbaudio: usb_set_interface failed, device %d interface %d altsetting %d\n", - dev->devnum, u->interface, fmt->altsetting); - return -1; - } - if (fmt->sratelo == fmt->sratehi) - return 0; - ep = usb_pipeendpoint(u->datapipe) | (u->datapipe & USB_DIR_IN); - /* if endpoint has pitch control, enable it */ - if (fmt->attributes & 0x02) { - data[0] = 1; - if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, - PITCH_CONTROL << 8, ep, data, 1, 1000)) < 0) { - printk(KERN_ERR "usbaudio: failure (error %d) to set output pitch control device %d interface %u endpoint 0x%x to %u\n", - ret, dev->devnum, u->interface, ep, d->srate); - return -1; - } - } - /* if endpoint has sampling rate control, set it */ - if (fmt->attributes & 0x01) { - data[0] = d->srate; - data[1] = d->srate >> 8; - data[2] = d->srate >> 16; - if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, - SAMPLING_FREQ_CONTROL << 8, ep, data, 3, 1000)) < 0) { - printk(KERN_ERR "usbaudio: failure (error %d) to set input sampling frequency device %d interface %u endpoint 0x%x to %u\n", - ret, dev->devnum, u->interface, ep, d->srate); - return -1; - } - if ((ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN, - SAMPLING_FREQ_CONTROL << 8, ep, data, 3, 1000)) < 0) { - printk(KERN_ERR "usbaudio: failure (error %d) to get input sampling frequency device %d interface %u endpoint 0x%x\n", - ret, dev->devnum, u->interface, ep); - return -1; - } - dprintk((KERN_DEBUG "usbaudio: set_format_in: device %d interface %d altsetting %d srate req: %u real %u\n", - dev->devnum, u->interface, fmt->altsetting, d->srate, data[0] | (data[1] << 8) | (data[2] << 16))); - d->srate = data[0] | (data[1] << 8) | (data[2] << 16); - } - dprintk((KERN_DEBUG "usbaudio: set_format_in: USB format 0x%x, DMA format 0x%x srate %u\n", u->format, d->format, d->srate)); - return 0; -} - -static int set_format_out(struct usb_audiodev *as) -{ - struct usb_device *dev = as->state->usbdev; - struct usb_host_interface *alts; - struct usb_interface *iface; - struct usbout *u = &as->usbout; - struct dmabuf *d = &u->dma; - struct audioformat *fmt; - unsigned int ep; - unsigned char data[3]; - int fmtnr, ret; - - iface = usb_ifnum_to_if(dev, u->interface); - if (!iface) - return 0; - - fmtnr = find_format(as->fmtout, as->numfmtout, d->format, d->srate); - if (fmtnr < 0) { - printk(KERN_ERR "usbaudio: set_format_out(): failed to find desired format/speed combination.\n"); - return -1; - } - - fmt = as->fmtout + fmtnr; - u->format = fmt->format; - alts = usb_altnum_to_altsetting(iface, fmt->altsetting); - u->datapipe = usb_sndisocpipe(dev, alts->endpoint[0].desc.bEndpointAddress & 0xf); - u->syncpipe = u->syncinterval = 0; - if ((alts->endpoint[0].desc.bmAttributes & 0x0c) == 0x04) { -#if 0 - printk(KERN_DEBUG "bNumEndpoints 0x%02x endpoint[1].bmAttributes 0x%02x\n" - KERN_DEBUG "endpoint[1].bSynchAddress 0x%02x endpoint[1].bEndpointAddress 0x%02x\n" - KERN_DEBUG "endpoint[0].bSynchAddress 0x%02x\n", alts->bNumEndpoints, - alts->endpoint[1].bmAttributes, alts->endpoint[1].bSynchAddress, - alts->endpoint[1].bEndpointAddress, alts->endpoint[0].bSynchAddress); -#endif - if (alts->desc.bNumEndpoints < 2 || - alts->endpoint[1].desc.bmAttributes != 0x01 || - alts->endpoint[1].desc.bSynchAddress != 0 || - alts->endpoint[1].desc.bEndpointAddress != (alts->endpoint[0].desc.bSynchAddress | 0x80)) { - printk(KERN_WARNING "usbaudio: device %d interface %d altsetting %d claims asynch out " - "but has invalid synch pipe; treating as adaptive out\n", - dev->devnum, u->interface, fmt->altsetting); - } else { - u->syncpipe = usb_rcvisocpipe(dev, alts->endpoint[1].desc.bEndpointAddress & 0xf); - u->syncinterval = alts->endpoint[1].desc.bRefresh; - } - } - if (d->srate < fmt->sratelo) - d->srate = fmt->sratelo; - if (d->srate > fmt->sratehi) - d->srate = fmt->sratehi; - dprintk((KERN_DEBUG "usbaudio: set_format_out: usb_set_interface %u %u\n", - u->interface, fmt->altsetting)); - if (usb_set_interface(dev, u->interface, fmt->altsetting) < 0) { - printk(KERN_WARNING "usbaudio: usb_set_interface failed, device %d interface %d altsetting %d\n", - dev->devnum, u->interface, fmt->altsetting); - return -1; - } - if (fmt->sratelo == fmt->sratehi) - return 0; - ep = usb_pipeendpoint(u->datapipe) | (u->datapipe & USB_DIR_IN); - /* if endpoint has pitch control, enable it */ - if (fmt->attributes & 0x02) { - data[0] = 1; - if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, - PITCH_CONTROL << 8, ep, data, 1, 1000)) < 0) { - printk(KERN_ERR "usbaudio: failure (error %d) to set output pitch control device %d interface %u endpoint 0x%x to %u\n", - ret, dev->devnum, u->interface, ep, d->srate); - return -1; - } - } - /* if endpoint has sampling rate control, set it */ - if (fmt->attributes & 0x01) { - data[0] = d->srate; - data[1] = d->srate >> 8; - data[2] = d->srate >> 16; - if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, - SAMPLING_FREQ_CONTROL << 8, ep, data, 3, 1000)) < 0) { - printk(KERN_ERR "usbaudio: failure (error %d) to set output sampling frequency device %d interface %u endpoint 0x%x to %u\n", - ret, dev->devnum, u->interface, ep, d->srate); - return -1; - } - if ((ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN, - SAMPLING_FREQ_CONTROL << 8, ep, data, 3, 1000)) < 0) { - printk(KERN_ERR "usbaudio: failure (error %d) to get output sampling frequency device %d interface %u endpoint 0x%x\n", - ret, dev->devnum, u->interface, ep); - return -1; - } - dprintk((KERN_DEBUG "usbaudio: set_format_out: device %d interface %d altsetting %d srate req: %u real %u\n", - dev->devnum, u->interface, fmt->altsetting, d->srate, data[0] | (data[1] << 8) | (data[2] << 16))); - d->srate = data[0] | (data[1] << 8) | (data[2] << 16); - } - dprintk((KERN_DEBUG "usbaudio: set_format_out: USB format 0x%x, DMA format 0x%x srate %u\n", u->format, d->format, d->srate)); - return 0; -} - -static int set_format(struct usb_audiodev *s, unsigned int fmode, unsigned int fmt, unsigned int srate) -{ - int ret1 = 0, ret2 = 0; - - if (!(fmode & (FMODE_READ|FMODE_WRITE))) - return -EINVAL; - if (fmode & FMODE_READ) { - usbin_stop(s); - s->usbin.dma.ready = 0; - if (fmt == AFMT_QUERY) - fmt = s->usbin.dma.format; - else - s->usbin.dma.format = fmt; - if (!srate) - srate = s->usbin.dma.srate; - else - s->usbin.dma.srate = srate; - } - if (fmode & FMODE_WRITE) { - usbout_stop(s); - s->usbout.dma.ready = 0; - if (fmt == AFMT_QUERY) - fmt = s->usbout.dma.format; - else - s->usbout.dma.format = fmt; - if (!srate) - srate = s->usbout.dma.srate; - else - s->usbout.dma.srate = srate; - } - if (fmode & FMODE_READ) - ret1 = set_format_in(s); - if (fmode & FMODE_WRITE) - ret2 = set_format_out(s); - return ret1 ? ret1 : ret2; -} - -/* --------------------------------------------------------------------- */ - -static int wrmixer(struct usb_mixerdev *ms, unsigned mixch, unsigned value) -{ - struct usb_device *dev = ms->state->usbdev; - unsigned char data[2]; - struct mixerchannel *ch; - int v1, v2, v3; - - if (mixch >= ms->numch) - return -1; - ch = &ms->ch[mixch]; - v3 = ch->maxval - ch->minval; - v1 = value & 0xff; - v2 = (value >> 8) & 0xff; - if (v1 > 100) - v1 = 100; - if (v2 > 100) - v2 = 100; - if (!(ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT))) - v2 = v1; - ch->value = v1 | (v2 << 8); - v1 = (v1 * v3) / 100 + ch->minval; - v2 = (v2 * v3) / 100 + ch->minval; - switch (ch->selector) { - case 0: /* mixer unit request */ - data[0] = v1; - data[1] = v1 >> 8; - if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, - (ch->chnum << 8) | 1, ms->iface | (ch->unitid << 8), data, 2, 1000) < 0) - goto err; - if (!(ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT))) - return 0; - data[0] = v2; - data[1] = v2 >> 8; - if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, - ((ch->chnum + !!(ch->flags & MIXFLG_STEREOIN)) << 8) | (1 + !!(ch->flags & MIXFLG_STEREOOUT)), - ms->iface | (ch->unitid << 8), data, 2, 1000) < 0) - goto err; - return 0; - - /* various feature unit controls */ - case VOLUME_CONTROL: - data[0] = v1; - data[1] = v1 >> 8; - if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, - (ch->selector << 8) | ch->chnum, ms->iface | (ch->unitid << 8), data, 2, 1000) < 0) - goto err; - if (!(ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT))) - return 0; - data[0] = v2; - data[1] = v2 >> 8; - if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, - (ch->selector << 8) | (ch->chnum + 1), ms->iface | (ch->unitid << 8), data, 2, 1000) < 0) - goto err; - return 0; - - case BASS_CONTROL: - case MID_CONTROL: - case TREBLE_CONTROL: - data[0] = v1 >> 8; - if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, - (ch->selector << 8) | ch->chnum, ms->iface | (ch->unitid << 8), data, 1, 1000) < 0) - goto err; - if (!(ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT))) - return 0; - data[0] = v2 >> 8; - if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, - (ch->selector << 8) | (ch->chnum + 1), ms->iface | (ch->unitid << 8), data, 1, 1000) < 0) - goto err; - return 0; - - default: - return -1; - } - return 0; - - err: - printk(KERN_ERR "usbaudio: mixer request device %u if %u unit %u ch %u selector %u failed\n", - dev->devnum, ms->iface, ch->unitid, ch->chnum, ch->selector); - return -1; -} - -static int get_rec_src(struct usb_mixerdev *ms) -{ - struct usb_device *dev = ms->state->usbdev; - unsigned int mask = 0, retmask = 0; - unsigned int i, j; - unsigned char buf; - int err = 0; - - for (i = 0; i < ms->numch; i++) { - if (!ms->ch[i].slctunitid || (mask & (1 << i))) - continue; - if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, - 0, ms->iface | (ms->ch[i].slctunitid << 8), &buf, 1, 1000) < 0) { - err = -EIO; - printk(KERN_ERR "usbaudio: selector read request device %u if %u unit %u failed\n", - dev->devnum, ms->iface, ms->ch[i].slctunitid & 0xff); - continue; - } - for (j = i; j < ms->numch; j++) { - if ((ms->ch[i].slctunitid ^ ms->ch[j].slctunitid) & 0xff) - continue; - mask |= 1 << j; - if (buf == (ms->ch[j].slctunitid >> 8)) - retmask |= 1 << ms->ch[j].osschannel; - } - } - if (err) - return -EIO; - return retmask; -} - -static int set_rec_src(struct usb_mixerdev *ms, int srcmask) -{ - struct usb_device *dev = ms->state->usbdev; - unsigned int mask = 0, smask, bmask; - unsigned int i, j; - unsigned char buf; - int err = 0; - - for (i = 0; i < ms->numch; i++) { - if (!ms->ch[i].slctunitid || (mask & (1 << i))) - continue; - if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, - 0, ms->iface | (ms->ch[i].slctunitid << 8), &buf, 1, 1000) < 0) { - err = -EIO; - printk(KERN_ERR "usbaudio: selector read request device %u if %u unit %u failed\n", - dev->devnum, ms->iface, ms->ch[i].slctunitid & 0xff); - continue; - } - /* first generate smask */ - smask = bmask = 0; - for (j = i; j < ms->numch; j++) { - if ((ms->ch[i].slctunitid ^ ms->ch[j].slctunitid) & 0xff) - continue; - smask |= 1 << ms->ch[j].osschannel; - if (buf == (ms->ch[j].slctunitid >> 8)) - bmask |= 1 << ms->ch[j].osschannel; - mask |= 1 << j; - } - /* check for multiple set sources */ - j = hweight32(srcmask & smask); - if (j == 0) - continue; - if (j > 1) - srcmask &= ~bmask; - for (j = i; j < ms->numch; j++) { - if ((ms->ch[i].slctunitid ^ ms->ch[j].slctunitid) & 0xff) - continue; - if (!(srcmask & (1 << ms->ch[j].osschannel))) - continue; - buf = ms->ch[j].slctunitid >> 8; - if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, - 0, ms->iface | (ms->ch[j].slctunitid << 8), &buf, 1, 1000) < 0) { - err = -EIO; - printk(KERN_ERR "usbaudio: selector write request device %u if %u unit %u failed\n", - dev->devnum, ms->iface, ms->ch[j].slctunitid & 0xff); - continue; - } - } - } - return err ? -EIO : 0; -} - -/* --------------------------------------------------------------------- */ - -/* - * should be called with open_sem hold, so that no new processes - * look at the audio device to be destroyed - */ - -static void release(struct usb_audio_state *s) -{ - struct usb_audiodev *as; - struct usb_mixerdev *ms; - - s->count--; - if (s->count) { - up(&open_sem); - return; - } - up(&open_sem); - wake_up(&open_wait); - while (!list_empty(&s->audiolist)) { - as = list_entry(s->audiolist.next, struct usb_audiodev, list); - list_del(&as->list); - usbin_release(as); - usbout_release(as); - dmabuf_release(&as->usbin.dma); - dmabuf_release(&as->usbout.dma); - usb_free_urb(as->usbin.durb[0].urb); - usb_free_urb(as->usbin.durb[1].urb); - usb_free_urb(as->usbin.surb[0].urb); - usb_free_urb(as->usbin.surb[1].urb); - usb_free_urb(as->usbout.durb[0].urb); - usb_free_urb(as->usbout.durb[1].urb); - usb_free_urb(as->usbout.surb[0].urb); - usb_free_urb(as->usbout.surb[1].urb); - kfree(as); - } - while (!list_empty(&s->mixerlist)) { - ms = list_entry(s->mixerlist.next, struct usb_mixerdev, list); - list_del(&ms->list); - kfree(ms); - } - kfree(s); -} - -static inline int prog_dmabuf_in(struct usb_audiodev *as) -{ - usbin_stop(as); - return dmabuf_init(&as->usbin.dma); -} - -static inline int prog_dmabuf_out(struct usb_audiodev *as) -{ - usbout_stop(as); - return dmabuf_init(&as->usbout.dma); -} - -/* --------------------------------------------------------------------- */ - -static int usb_audio_open_mixdev(struct inode *inode, struct file *file) -{ - unsigned int minor = iminor(inode); - struct usb_mixerdev *ms; - struct usb_audio_state *s; - - down(&open_sem); - list_for_each_entry(s, &audiodevs, audiodev) { - list_for_each_entry(ms, &s->mixerlist, list) { - if (ms->dev_mixer == minor) - goto mixer_found; - } - } - up(&open_sem); - return -ENODEV; - - mixer_found: - if (!s->usbdev) { - up(&open_sem); - return -EIO; - } - file->private_data = ms; - s->count++; - - up(&open_sem); - return nonseekable_open(inode, file); -} - -static int usb_audio_release_mixdev(struct inode *inode, struct file *file) -{ - struct usb_mixerdev *ms = (struct usb_mixerdev *)file->private_data; - struct usb_audio_state *s; - - lock_kernel(); - s = ms->state; - down(&open_sem); - release(s); - unlock_kernel(); - return 0; -} - -static int usb_audio_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct usb_mixerdev *ms = (struct usb_mixerdev *)file->private_data; - int i, j, val; - int __user *user_arg = (int __user *)arg; - - if (!ms->state->usbdev) - return -ENODEV; - - if (cmd == SOUND_MIXER_INFO) { - mixer_info info; - - memset(&info, 0, sizeof(info)); - strncpy(info.id, "USB_AUDIO", sizeof(info.id)); - strncpy(info.name, "USB Audio Class Driver", sizeof(info.name)); - info.modify_counter = ms->modcnt; - if (copy_to_user((void __user *)arg, &info, sizeof(info))) - return -EFAULT; - return 0; - } - if (cmd == SOUND_OLD_MIXER_INFO) { - _old_mixer_info info; - - memset(&info, 0, sizeof(info)); - strncpy(info.id, "USB_AUDIO", sizeof(info.id)); - strncpy(info.name, "USB Audio Class Driver", sizeof(info.name)); - if (copy_to_user((void __user *)arg, &info, sizeof(info))) - return -EFAULT; - return 0; - } - if (cmd == OSS_GETVERSION) - return put_user(SOUND_VERSION, user_arg); - if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) - return -EINVAL; - if (_IOC_DIR(cmd) == _IOC_READ) { - switch (_IOC_NR(cmd)) { - case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ - val = get_rec_src(ms); - if (val < 0) - return val; - return put_user(val, user_arg); - - case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ - for (val = i = 0; i < ms->numch; i++) - val |= 1 << ms->ch[i].osschannel; - return put_user(val, user_arg); - - case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ - for (val = i = 0; i < ms->numch; i++) - if (ms->ch[i].slctunitid) - val |= 1 << ms->ch[i].osschannel; - return put_user(val, user_arg); - - case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ - for (val = i = 0; i < ms->numch; i++) - if (ms->ch[i].flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)) - val |= 1 << ms->ch[i].osschannel; - return put_user(val, user_arg); - - case SOUND_MIXER_CAPS: - return put_user(SOUND_CAP_EXCL_INPUT, user_arg); - - default: - i = _IOC_NR(cmd); - if (i >= SOUND_MIXER_NRDEVICES) - return -EINVAL; - for (j = 0; j < ms->numch; j++) { - if (ms->ch[j].osschannel == i) { - return put_user(ms->ch[j].value, user_arg); - } - } - return -EINVAL; - } - } - if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE)) - return -EINVAL; - ms->modcnt++; - switch (_IOC_NR(cmd)) { - case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ - if (get_user(val, user_arg)) - return -EFAULT; - return set_rec_src(ms, val); - - default: - i = _IOC_NR(cmd); - if (i >= SOUND_MIXER_NRDEVICES) - return -EINVAL; - for (j = 0; j < ms->numch && ms->ch[j].osschannel != i; j++); - if (j >= ms->numch) - return -EINVAL; - if (get_user(val, user_arg)) - return -EFAULT; - if (wrmixer(ms, j, val)) - return -EIO; - return put_user(ms->ch[j].value, user_arg); - } -} - -static /*const*/ struct file_operations usb_mixer_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .ioctl = usb_audio_ioctl_mixdev, - .open = usb_audio_open_mixdev, - .release = usb_audio_release_mixdev, -}; - -/* --------------------------------------------------------------------- */ - -static int drain_out(struct usb_audiodev *as, int nonblock) -{ - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - int count, tmo; - - if (as->usbout.dma.mapped || !as->usbout.dma.ready) - return 0; - usbout_start(as); - add_wait_queue(&as->usbout.dma.wait, &wait); - for (;;) { - __set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&as->lock, flags); - count = as->usbout.dma.count; - spin_unlock_irqrestore(&as->lock, flags); - if (count <= 0) - break; - if (signal_pending(current)) - break; - if (nonblock) { - remove_wait_queue(&as->usbout.dma.wait, &wait); - set_current_state(TASK_RUNNING); - return -EBUSY; - } - tmo = 3 * HZ * count / as->usbout.dma.srate; - tmo >>= AFMT_BYTESSHIFT(as->usbout.dma.format); - if (!schedule_timeout(tmo + 1)) { - printk(KERN_DEBUG "usbaudio: dma timed out??\n"); - break; - } - } - remove_wait_queue(&as->usbout.dma.wait, &wait); - set_current_state(TASK_RUNNING); - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -/* --------------------------------------------------------------------- */ - -static ssize_t usb_audio_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) -{ - struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret = 0; - unsigned long flags; - unsigned int ptr; - int cnt, err; - - if (as->usbin.dma.mapped) - return -ENXIO; - if (!as->usbin.dma.ready && (ret = prog_dmabuf_in(as))) - return ret; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - add_wait_queue(&as->usbin.dma.wait, &wait); - while (count > 0) { - spin_lock_irqsave(&as->lock, flags); - ptr = as->usbin.dma.rdptr; - cnt = as->usbin.dma.count; - /* set task state early to avoid wakeup races */ - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&as->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (usbin_start(as)) { - if (!ret) - ret = -ENODEV; - break; - } - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if ((err = dmabuf_copyout_user(&as->usbin.dma, ptr, buffer, cnt))) { - if (!ret) - ret = err; - break; - } - ptr += cnt; - if (ptr >= as->usbin.dma.dmasize) - ptr -= as->usbin.dma.dmasize; - spin_lock_irqsave(&as->lock, flags); - as->usbin.dma.rdptr = ptr; - as->usbin.dma.count -= cnt; - spin_unlock_irqrestore(&as->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&as->usbin.dma.wait, &wait); - return ret; -} - -static ssize_t usb_audio_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) -{ - struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; - DECLARE_WAITQUEUE(wait, current); - ssize_t ret = 0; - unsigned long flags; - unsigned int ptr; - unsigned int start_thr; - int cnt, err; - - if (as->usbout.dma.mapped) - return -ENXIO; - if (!as->usbout.dma.ready && (ret = prog_dmabuf_out(as))) - return ret; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - start_thr = (as->usbout.dma.srate << AFMT_BYTESSHIFT(as->usbout.dma.format)) / (1000 / (3 * DESCFRAMES)); - add_wait_queue(&as->usbout.dma.wait, &wait); - while (count > 0) { -#if 0 - printk(KERN_DEBUG "usb_audio_write: count %u dma: count %u rdptr %u wrptr %u dmasize %u fragsize %u flags 0x%02x taskst 0x%lx\n", - count, as->usbout.dma.count, as->usbout.dma.rdptr, as->usbout.dma.wrptr, as->usbout.dma.dmasize, as->usbout.dma.fragsize, - as->usbout.flags, current->state); -#endif - spin_lock_irqsave(&as->lock, flags); - if (as->usbout.dma.count < 0) { - as->usbout.dma.count = 0; - as->usbout.dma.rdptr = as->usbout.dma.wrptr; - } - ptr = as->usbout.dma.wrptr; - cnt = as->usbout.dma.dmasize - as->usbout.dma.count; - /* set task state early to avoid wakeup races */ - if (cnt <= 0) - __set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irqrestore(&as->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - if (usbout_start(as)) { - if (!ret) - ret = -ENODEV; - break; - } - if (file->f_flags & O_NONBLOCK) { - if (!ret) - ret = -EAGAIN; - break; - } - schedule(); - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - continue; - } - if ((err = dmabuf_copyin_user(&as->usbout.dma, ptr, buffer, cnt))) { - if (!ret) - ret = err; - break; - } - ptr += cnt; - if (ptr >= as->usbout.dma.dmasize) - ptr -= as->usbout.dma.dmasize; - spin_lock_irqsave(&as->lock, flags); - as->usbout.dma.wrptr = ptr; - as->usbout.dma.count += cnt; - spin_unlock_irqrestore(&as->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - if (as->usbout.dma.count >= start_thr && usbout_start(as)) { - if (!ret) - ret = -ENODEV; - break; - } - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&as->usbout.dma.wait, &wait); - return ret; -} - -/* Called without the kernel lock - fine */ -static unsigned int usb_audio_poll(struct file *file, struct poll_table_struct *wait) -{ - struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; - unsigned long flags; - unsigned int mask = 0; - - if (file->f_mode & FMODE_WRITE) { - if (!as->usbout.dma.ready) - prog_dmabuf_out(as); - poll_wait(file, &as->usbout.dma.wait, wait); - } - if (file->f_mode & FMODE_READ) { - if (!as->usbin.dma.ready) - prog_dmabuf_in(as); - poll_wait(file, &as->usbin.dma.wait, wait); - } - spin_lock_irqsave(&as->lock, flags); - if (file->f_mode & FMODE_READ) { - if (as->usbin.dma.count >= (signed)as->usbin.dma.fragsize) - mask |= POLLIN | POLLRDNORM; - } - if (file->f_mode & FMODE_WRITE) { - if (as->usbout.dma.mapped) { - if (as->usbout.dma.count >= (signed)as->usbout.dma.fragsize) - mask |= POLLOUT | POLLWRNORM; - } else { - if ((signed)as->usbout.dma.dmasize >= as->usbout.dma.count + (signed)as->usbout.dma.fragsize) - mask |= POLLOUT | POLLWRNORM; - } - } - spin_unlock_irqrestore(&as->lock, flags); - return mask; -} - -static int usb_audio_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; - struct dmabuf *db; - int ret = -EINVAL; - - lock_kernel(); - if (vma->vm_flags & VM_WRITE) { - if ((ret = prog_dmabuf_out(as)) != 0) - goto out; - db = &as->usbout.dma; - } else if (vma->vm_flags & VM_READ) { - if ((ret = prog_dmabuf_in(as)) != 0) - goto out; - db = &as->usbin.dma; - } else - goto out; - - ret = -EINVAL; - if (vma->vm_pgoff != 0) - goto out; - - ret = dmabuf_mmap(vma, db, vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_page_prot); -out: - unlock_kernel(); - return ret; -} - -static int usb_audio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; - struct usb_audio_state *s = as->state; - int __user *user_arg = (int __user *)arg; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int val = 0; - int val2, mapped, ret; - - if (!s->usbdev) - return -EIO; - mapped = ((file->f_mode & FMODE_WRITE) && as->usbout.dma.mapped) || - ((file->f_mode & FMODE_READ) && as->usbin.dma.mapped); -#if 0 - if (arg) - get_user(val, (int *)arg); - printk(KERN_DEBUG "usbaudio: usb_audio_ioctl cmd=%x arg=%lx *arg=%d\n", cmd, arg, val) -#endif - switch (cmd) { - case OSS_GETVERSION: - return put_user(SOUND_VERSION, user_arg); - - case SNDCTL_DSP_SYNC: - if (file->f_mode & FMODE_WRITE) - return drain_out(as, 0/*file->f_flags & O_NONBLOCK*/); - return 0; - - case SNDCTL_DSP_SETDUPLEX: - return 0; - - case SNDCTL_DSP_GETCAPS: - return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | - DSP_CAP_MMAP | DSP_CAP_BATCH, user_arg); - - case SNDCTL_DSP_RESET: - if (file->f_mode & FMODE_WRITE) { - usbout_stop(as); - as->usbout.dma.rdptr = as->usbout.dma.wrptr = as->usbout.dma.count = as->usbout.dma.total_bytes = 0; - } - if (file->f_mode & FMODE_READ) { - usbin_stop(as); - as->usbin.dma.rdptr = as->usbin.dma.wrptr = as->usbin.dma.count = as->usbin.dma.total_bytes = 0; - } - return 0; - - case SNDCTL_DSP_SPEED: - if (get_user(val, user_arg)) - return -EFAULT; - if (val >= 0) { - if (val < 4000) - val = 4000; - if (val > 100000) - val = 100000; - if (set_format(as, file->f_mode, AFMT_QUERY, val)) - return -EIO; - } - return put_user((file->f_mode & FMODE_READ) ? - as->usbin.dma.srate : as->usbout.dma.srate, - user_arg); - - case SNDCTL_DSP_STEREO: - if (get_user(val, user_arg)) - return -EFAULT; - val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; - if (val) - val2 |= AFMT_STEREO; - else - val2 &= ~AFMT_STEREO; - if (set_format(as, file->f_mode, val2, 0)) - return -EIO; - return 0; - - case SNDCTL_DSP_CHANNELS: - if (get_user(val, user_arg)) - return -EFAULT; - if (val != 0) { - val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; - if (val == 1) - val2 &= ~AFMT_STEREO; - else - val2 |= AFMT_STEREO; - if (set_format(as, file->f_mode, val2, 0)) - return -EIO; - } - val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; - return put_user(AFMT_ISSTEREO(val2) ? 2 : 1, user_arg); - - case SNDCTL_DSP_GETFMTS: /* Returns a mask */ - return put_user(AFMT_U8 | AFMT_U16_LE | AFMT_U16_BE | - AFMT_S8 | AFMT_S16_LE | AFMT_S16_BE, user_arg); - - case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ - if (get_user(val, user_arg)) - return -EFAULT; - if (val != AFMT_QUERY) { - if (hweight32(val) != 1) - return -EINVAL; - if (!(val & (AFMT_U8 | AFMT_U16_LE | AFMT_U16_BE | - AFMT_S8 | AFMT_S16_LE | AFMT_S16_BE))) - return -EINVAL; - val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; - val |= val2 & AFMT_STEREO; - if (set_format(as, file->f_mode, val, 0)) - return -EIO; - } - val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; - return put_user(val2 & ~AFMT_STEREO, user_arg); - - case SNDCTL_DSP_POST: - return 0; - - case SNDCTL_DSP_GETTRIGGER: - val = 0; - if (file->f_mode & FMODE_READ && as->usbin.flags & FLG_RUNNING) - val |= PCM_ENABLE_INPUT; - if (file->f_mode & FMODE_WRITE && as->usbout.flags & FLG_RUNNING) - val |= PCM_ENABLE_OUTPUT; - return put_user(val, user_arg); - - case SNDCTL_DSP_SETTRIGGER: - if (get_user(val, user_arg)) - return -EFAULT; - if (file->f_mode & FMODE_READ) { - if (val & PCM_ENABLE_INPUT) { - if (!as->usbin.dma.ready && (ret = prog_dmabuf_in(as))) - return ret; - if (usbin_start(as)) - return -ENODEV; - } else - usbin_stop(as); - } - if (file->f_mode & FMODE_WRITE) { - if (val & PCM_ENABLE_OUTPUT) { - if (!as->usbout.dma.ready && (ret = prog_dmabuf_out(as))) - return ret; - if (usbout_start(as)) - return -ENODEV; - } else - usbout_stop(as); - } - return 0; - - case SNDCTL_DSP_GETOSPACE: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if (!(as->usbout.flags & FLG_RUNNING) && (val = prog_dmabuf_out(as)) != 0) - return val; - spin_lock_irqsave(&as->lock, flags); - abinfo.fragsize = as->usbout.dma.fragsize; - abinfo.bytes = as->usbout.dma.dmasize - as->usbout.dma.count; - abinfo.fragstotal = as->usbout.dma.numfrag; - abinfo.fragments = abinfo.bytes >> as->usbout.dma.fragshift; - spin_unlock_irqrestore(&as->lock, flags); - return copy_to_user((void __user *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETISPACE: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - if (!(as->usbin.flags & FLG_RUNNING) && (val = prog_dmabuf_in(as)) != 0) - return val; - spin_lock_irqsave(&as->lock, flags); - abinfo.fragsize = as->usbin.dma.fragsize; - abinfo.bytes = as->usbin.dma.count; - abinfo.fragstotal = as->usbin.dma.numfrag; - abinfo.fragments = abinfo.bytes >> as->usbin.dma.fragshift; - spin_unlock_irqrestore(&as->lock, flags); - return copy_to_user((void __user *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_NONBLOCK: - file->f_flags |= O_NONBLOCK; - return 0; - - case SNDCTL_DSP_GETODELAY: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - spin_lock_irqsave(&as->lock, flags); - val = as->usbout.dma.count; - spin_unlock_irqrestore(&as->lock, flags); - return put_user(val, user_arg); - - case SNDCTL_DSP_GETIPTR: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - spin_lock_irqsave(&as->lock, flags); - cinfo.bytes = as->usbin.dma.total_bytes; - cinfo.blocks = as->usbin.dma.count >> as->usbin.dma.fragshift; - cinfo.ptr = as->usbin.dma.wrptr; - if (as->usbin.dma.mapped) - as->usbin.dma.count &= as->usbin.dma.fragsize-1; - spin_unlock_irqrestore(&as->lock, flags); - if (copy_to_user((void __user *)arg, &cinfo, sizeof(cinfo))) - return -EFAULT; - return 0; - - case SNDCTL_DSP_GETOPTR: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - spin_lock_irqsave(&as->lock, flags); - cinfo.bytes = as->usbout.dma.total_bytes; - cinfo.blocks = as->usbout.dma.count >> as->usbout.dma.fragshift; - cinfo.ptr = as->usbout.dma.rdptr; - if (as->usbout.dma.mapped) - as->usbout.dma.count &= as->usbout.dma.fragsize-1; - spin_unlock_irqrestore(&as->lock, flags); - if (copy_to_user((void __user *)arg, &cinfo, sizeof(cinfo))) - return -EFAULT; - return 0; - - case SNDCTL_DSP_GETBLKSIZE: - if (file->f_mode & FMODE_WRITE) { - if ((val = prog_dmabuf_out(as))) - return val; - return put_user(as->usbout.dma.fragsize, user_arg); - } - if ((val = prog_dmabuf_in(as))) - return val; - return put_user(as->usbin.dma.fragsize, user_arg); - - case SNDCTL_DSP_SETFRAGMENT: - if (get_user(val, user_arg)) - return -EFAULT; - if (file->f_mode & FMODE_READ) { - as->usbin.dma.ossfragshift = val & 0xffff; - as->usbin.dma.ossmaxfrags = (val >> 16) & 0xffff; - if (as->usbin.dma.ossfragshift < 4) - as->usbin.dma.ossfragshift = 4; - if (as->usbin.dma.ossfragshift > 15) - as->usbin.dma.ossfragshift = 15; - if (as->usbin.dma.ossmaxfrags < 4) - as->usbin.dma.ossmaxfrags = 4; - } - if (file->f_mode & FMODE_WRITE) { - as->usbout.dma.ossfragshift = val & 0xffff; - as->usbout.dma.ossmaxfrags = (val >> 16) & 0xffff; - if (as->usbout.dma.ossfragshift < 4) - as->usbout.dma.ossfragshift = 4; - if (as->usbout.dma.ossfragshift > 15) - as->usbout.dma.ossfragshift = 15; - if (as->usbout.dma.ossmaxfrags < 4) - as->usbout.dma.ossmaxfrags = 4; - } - return 0; - - case SNDCTL_DSP_SUBDIVIDE: - if ((file->f_mode & FMODE_READ && as->usbin.dma.subdivision) || - (file->f_mode & FMODE_WRITE && as->usbout.dma.subdivision)) - return -EINVAL; - if (get_user(val, user_arg)) - return -EFAULT; - if (val != 1 && val != 2 && val != 4) - return -EINVAL; - if (file->f_mode & FMODE_READ) - as->usbin.dma.subdivision = val; - if (file->f_mode & FMODE_WRITE) - as->usbout.dma.subdivision = val; - return 0; - - case SOUND_PCM_READ_RATE: - return put_user((file->f_mode & FMODE_READ) ? - as->usbin.dma.srate : as->usbout.dma.srate, - user_arg); - - case SOUND_PCM_READ_CHANNELS: - val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; - return put_user(AFMT_ISSTEREO(val2) ? 2 : 1, user_arg); - - case SOUND_PCM_READ_BITS: - val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; - return put_user(AFMT_IS16BIT(val2) ? 16 : 8, user_arg); - - case SOUND_PCM_WRITE_FILTER: - case SNDCTL_DSP_SETSYNCRO: - case SOUND_PCM_READ_FILTER: - return -EINVAL; - } - dprintk((KERN_DEBUG "usbaudio: usb_audio_ioctl - no command found\n")); - return -ENOIOCTLCMD; -} - -static int usb_audio_open(struct inode *inode, struct file *file) -{ - unsigned int minor = iminor(inode); - DECLARE_WAITQUEUE(wait, current); - struct usb_audiodev *as; - struct usb_audio_state *s; - - for (;;) { - down(&open_sem); - list_for_each_entry(s, &audiodevs, audiodev) { - list_for_each_entry(as, &s->audiolist, list) { - if (!((as->dev_audio ^ minor) & ~0xf)) - goto device_found; - } - } - up(&open_sem); - return -ENODEV; - - device_found: - if (!s->usbdev) { - up(&open_sem); - return -EIO; - } - /* wait for device to become free */ - if (!(as->open_mode & file->f_mode)) - break; - if (file->f_flags & O_NONBLOCK) { - up(&open_sem); - return -EBUSY; - } - __set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&open_wait, &wait); - up(&open_sem); - schedule(); - __set_current_state(TASK_RUNNING); - remove_wait_queue(&open_wait, &wait); - if (signal_pending(current)) - return -ERESTARTSYS; - } - if (file->f_mode & FMODE_READ) - as->usbin.dma.ossfragshift = as->usbin.dma.ossmaxfrags = as->usbin.dma.subdivision = 0; - if (file->f_mode & FMODE_WRITE) - as->usbout.dma.ossfragshift = as->usbout.dma.ossmaxfrags = as->usbout.dma.subdivision = 0; - if (set_format(as, file->f_mode, ((minor & 0xf) == SND_DEV_DSP16) ? AFMT_S16_LE : AFMT_U8 /* AFMT_ULAW */, 8000)) { - up(&open_sem); - return -EIO; - } - file->private_data = as; - as->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); - s->count++; - up(&open_sem); - return nonseekable_open(inode, file); -} - -static int usb_audio_release(struct inode *inode, struct file *file) -{ - struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; - struct usb_audio_state *s; - struct usb_device *dev; - - lock_kernel(); - s = as->state; - dev = s->usbdev; - if (file->f_mode & FMODE_WRITE) - drain_out(as, file->f_flags & O_NONBLOCK); - down(&open_sem); - if (file->f_mode & FMODE_WRITE) { - usbout_stop(as); - if (dev && as->usbout.interface >= 0) - usb_set_interface(dev, as->usbout.interface, 0); - dmabuf_release(&as->usbout.dma); - usbout_release(as); - } - if (file->f_mode & FMODE_READ) { - usbin_stop(as); - if (dev && as->usbin.interface >= 0) - usb_set_interface(dev, as->usbin.interface, 0); - dmabuf_release(&as->usbin.dma); - usbin_release(as); - } - as->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); - release(s); - wake_up(&open_wait); - unlock_kernel(); - return 0; -} - -static /*const*/ struct file_operations usb_audio_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = usb_audio_read, - .write = usb_audio_write, - .poll = usb_audio_poll, - .ioctl = usb_audio_ioctl, - .mmap = usb_audio_mmap, - .open = usb_audio_open, - .release = usb_audio_release, -}; - -/* --------------------------------------------------------------------- */ - -static int usb_audio_probe(struct usb_interface *iface, - const struct usb_device_id *id); -static void usb_audio_disconnect(struct usb_interface *iface); - -static struct usb_device_id usb_audio_ids [] = { - { .match_flags = (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS), - .bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceSubClass = 1}, - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE (usb, usb_audio_ids); - -static struct usb_driver usb_audio_driver = { - .name = "audio", - .probe = usb_audio_probe, - .disconnect = usb_audio_disconnect, - .id_table = usb_audio_ids, -}; - -static void *find_descriptor(void *descstart, unsigned int desclen, void *after, - u8 dtype, int iface, int altsetting) -{ - u8 *p, *end, *next; - int ifc = -1, as = -1; - - p = descstart; - end = p + desclen; - for (; p < end;) { - if (p[0] < 2) - return NULL; - next = p + p[0]; - if (next > end) - return NULL; - if (p[1] == USB_DT_INTERFACE) { - /* minimum length of interface descriptor */ - if (p[0] < 9) - return NULL; - ifc = p[2]; - as = p[3]; - } - if (p[1] == dtype && (!after || (void *)p > after) && - (iface == -1 || iface == ifc) && (altsetting == -1 || altsetting == as)) { - return p; - } - p = next; - } - return NULL; -} - -static void *find_csinterface_descriptor(void *descstart, unsigned int desclen, void *after, u8 dsubtype, int iface, int altsetting) -{ - unsigned char *p; - - p = find_descriptor(descstart, desclen, after, USB_DT_CS_INTERFACE, iface, altsetting); - while (p) { - if (p[0] >= 3 && p[2] == dsubtype) - return p; - p = find_descriptor(descstart, desclen, p, USB_DT_CS_INTERFACE, iface, altsetting); - } - return NULL; -} - -static void *find_audiocontrol_unit(void *descstart, unsigned int desclen, void *after, u8 unit, int iface) -{ - unsigned char *p; - - p = find_descriptor(descstart, desclen, after, USB_DT_CS_INTERFACE, iface, -1); - while (p) { - if (p[0] >= 4 && p[2] >= INPUT_TERMINAL && p[2] <= EXTENSION_UNIT && p[3] == unit) - return p; - p = find_descriptor(descstart, desclen, p, USB_DT_CS_INTERFACE, iface, -1); - } - return NULL; -} - -static void usb_audio_parsestreaming(struct usb_audio_state *s, unsigned char *buffer, unsigned int buflen, int asifin, int asifout) -{ - struct usb_device *dev = s->usbdev; - struct usb_audiodev *as; - struct usb_host_interface *alts; - struct usb_interface *iface; - struct audioformat *fp; - unsigned char *fmt, *csep; - unsigned int i, j, k, format, idx; - - if (!(as = kmalloc(sizeof(struct usb_audiodev), GFP_KERNEL))) - return; - memset(as, 0, sizeof(struct usb_audiodev)); - init_waitqueue_head(&as->usbin.dma.wait); - init_waitqueue_head(&as->usbout.dma.wait); - spin_lock_init(&as->lock); - as->usbin.durb[0].urb = usb_alloc_urb (DESCFRAMES, GFP_KERNEL); - as->usbin.durb[1].urb = usb_alloc_urb (DESCFRAMES, GFP_KERNEL); - as->usbin.surb[0].urb = usb_alloc_urb (SYNCFRAMES, GFP_KERNEL); - as->usbin.surb[1].urb = usb_alloc_urb (SYNCFRAMES, GFP_KERNEL); - as->usbout.durb[0].urb = usb_alloc_urb (DESCFRAMES, GFP_KERNEL); - as->usbout.durb[1].urb = usb_alloc_urb (DESCFRAMES, GFP_KERNEL); - as->usbout.surb[0].urb = usb_alloc_urb (SYNCFRAMES, GFP_KERNEL); - as->usbout.surb[1].urb = usb_alloc_urb (SYNCFRAMES, GFP_KERNEL); - if ((!as->usbin.durb[0].urb) || - (!as->usbin.durb[1].urb) || - (!as->usbin.surb[0].urb) || - (!as->usbin.surb[1].urb) || - (!as->usbout.durb[0].urb) || - (!as->usbout.durb[1].urb) || - (!as->usbout.surb[0].urb) || - (!as->usbout.surb[1].urb)) { - usb_free_urb(as->usbin.durb[0].urb); - usb_free_urb(as->usbin.durb[1].urb); - usb_free_urb(as->usbin.surb[0].urb); - usb_free_urb(as->usbin.surb[1].urb); - usb_free_urb(as->usbout.durb[0].urb); - usb_free_urb(as->usbout.durb[1].urb); - usb_free_urb(as->usbout.surb[0].urb); - usb_free_urb(as->usbout.surb[1].urb); - kfree(as); - return; - } - as->state = s; - as->usbin.interface = asifin; - as->usbout.interface = asifout; - /* search for input formats */ - if (asifin >= 0) { - as->usbin.flags = FLG_CONNECTED; - iface = usb_ifnum_to_if(dev, asifin); - for (idx = 0; idx < iface->num_altsetting; idx++) { - alts = &iface->altsetting[idx]; - i = alts->desc.bAlternateSetting; - if (alts->desc.bInterfaceClass != USB_CLASS_AUDIO || alts->desc.bInterfaceSubClass != 2) - continue; - if (alts->desc.bNumEndpoints < 1) { - if (i != 0) { /* altsetting 0 has no endpoints (Section B.3.4.1) */ - printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u does not have an endpoint\n", - dev->devnum, asifin, i); - } - continue; - } - if ((alts->endpoint[0].desc.bmAttributes & 0x03) != 0x01 || - !(alts->endpoint[0].desc.bEndpointAddress & 0x80)) { - printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u first endpoint not isochronous in\n", - dev->devnum, asifin, i); - continue; - } - fmt = find_csinterface_descriptor(buffer, buflen, NULL, AS_GENERAL, asifin, i); - if (!fmt) { - printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not found\n", - dev->devnum, asifin, i); - continue; - } - if (fmt[0] < 7 || fmt[6] != 0 || (fmt[5] != 1 && fmt[5] != 2)) { - printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u format not supported\n", - dev->devnum, asifin, i); - continue; - } - format = (fmt[5] == 2) ? (AFMT_U16_LE | AFMT_U8) : (AFMT_S16_LE | AFMT_S8); - fmt = find_csinterface_descriptor(buffer, buflen, NULL, FORMAT_TYPE, asifin, i); - if (!fmt) { - printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not found\n", - dev->devnum, asifin, i); - continue; - } - if (fmt[0] < 8+3*(fmt[7] ? fmt[7] : 2) || fmt[3] != 1) { - printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not supported\n", - dev->devnum, asifin, i); - continue; - } - if (fmt[4] < 1 || fmt[4] > 2 || fmt[5] < 1 || fmt[5] > 2) { - printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u unsupported channels %u framesize %u\n", - dev->devnum, asifin, i, fmt[4], fmt[5]); - continue; - } - csep = find_descriptor(buffer, buflen, NULL, USB_DT_CS_ENDPOINT, asifin, i); - if (!csep || csep[0] < 7 || csep[2] != EP_GENERAL) { - printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u no or invalid class specific endpoint descriptor\n", - dev->devnum, asifin, i); - continue; - } - if (as->numfmtin >= MAXFORMATS) - continue; - fp = &as->fmtin[as->numfmtin++]; - if (fmt[5] == 2) - format &= (AFMT_U16_LE | AFMT_S16_LE); - else - format &= (AFMT_U8 | AFMT_S8); - if (fmt[4] == 2) - format |= AFMT_STEREO; - fp->format = format; - fp->altsetting = i; - fp->sratelo = fp->sratehi = fmt[8] | (fmt[9] << 8) | (fmt[10] << 16); - printk(KERN_INFO "usbaudio: valid input sample rate %u\n", fp->sratelo); - for (j = fmt[7] ? (fmt[7]-1) : 1; j > 0; j--) { - k = fmt[8+3*j] | (fmt[9+3*j] << 8) | (fmt[10+3*j] << 16); - printk(KERN_INFO "usbaudio: valid input sample rate %u\n", k); - if (k > fp->sratehi) - fp->sratehi = k; - if (k < fp->sratelo) - fp->sratelo = k; - } - fp->attributes = csep[3]; - printk(KERN_INFO "usbaudio: device %u interface %u altsetting %u: format 0x%08x sratelo %u sratehi %u attributes 0x%02x\n", - dev->devnum, asifin, i, fp->format, fp->sratelo, fp->sratehi, fp->attributes); - } - } - /* search for output formats */ - if (asifout >= 0) { - as->usbout.flags = FLG_CONNECTED; - iface = usb_ifnum_to_if(dev, asifout); - for (idx = 0; idx < iface->num_altsetting; idx++) { - alts = &iface->altsetting[idx]; - i = alts->desc.bAlternateSetting; - if (alts->desc.bInterfaceClass != USB_CLASS_AUDIO || alts->desc.bInterfaceSubClass != 2) - continue; - if (alts->desc.bNumEndpoints < 1) { - /* altsetting 0 should never have iso EPs */ - if (i != 0) - printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u does not have an endpoint\n", - dev->devnum, asifout, i); - continue; - } - if ((alts->endpoint[0].desc.bmAttributes & 0x03) != 0x01 || - (alts->endpoint[0].desc.bEndpointAddress & 0x80)) { - printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u first endpoint not isochronous out\n", - dev->devnum, asifout, i); - continue; - } - /* See USB audio formats manual, section 2 */ - fmt = find_csinterface_descriptor(buffer, buflen, NULL, AS_GENERAL, asifout, i); - if (!fmt) { - printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not found\n", - dev->devnum, asifout, i); - continue; - } - if (fmt[0] < 7 || fmt[6] != 0 || (fmt[5] != 1 && fmt[5] != 2)) { - printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u format not supported\n", - dev->devnum, asifout, i); - continue; - } - format = (fmt[5] == 2) ? (AFMT_U16_LE | AFMT_U8) : (AFMT_S16_LE | AFMT_S8); - /* Dallas DS4201 workaround */ - if (le16_to_cpu(dev->descriptor.idVendor) == 0x04fa && - le16_to_cpu(dev->descriptor.idProduct) == 0x4201) - format = (AFMT_S16_LE | AFMT_S8); - fmt = find_csinterface_descriptor(buffer, buflen, NULL, FORMAT_TYPE, asifout, i); - if (!fmt) { - printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not found\n", - dev->devnum, asifout, i); - continue; - } - if (fmt[0] < 8+3*(fmt[7] ? fmt[7] : 2) || fmt[3] != 1) { - printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not supported\n", - dev->devnum, asifout, i); - continue; - } - if (fmt[4] < 1 || fmt[4] > 2 || fmt[5] < 1 || fmt[5] > 2) { - printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u unsupported channels %u framesize %u\n", - dev->devnum, asifout, i, fmt[4], fmt[5]); - continue; - } - csep = find_descriptor(buffer, buflen, NULL, USB_DT_CS_ENDPOINT, asifout, i); - if (!csep || csep[0] < 7 || csep[2] != EP_GENERAL) { - printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u no or invalid class specific endpoint descriptor\n", - dev->devnum, asifout, i); - continue; - } - if (as->numfmtout >= MAXFORMATS) - continue; - fp = &as->fmtout[as->numfmtout++]; - if (fmt[5] == 2) - format &= (AFMT_U16_LE | AFMT_S16_LE); - else - format &= (AFMT_U8 | AFMT_S8); - if (fmt[4] == 2) - format |= AFMT_STEREO; - fp->format = format; - fp->altsetting = i; - fp->sratelo = fp->sratehi = fmt[8] | (fmt[9] << 8) | (fmt[10] << 16); - printk(KERN_INFO "usbaudio: valid output sample rate %u\n", fp->sratelo); - for (j = fmt[7] ? (fmt[7]-1) : 1; j > 0; j--) { - k = fmt[8+3*j] | (fmt[9+3*j] << 8) | (fmt[10+3*j] << 16); - printk(KERN_INFO "usbaudio: valid output sample rate %u\n", k); - if (k > fp->sratehi) - fp->sratehi = k; - if (k < fp->sratelo) - fp->sratelo = k; - } - fp->attributes = csep[3]; - printk(KERN_INFO "usbaudio: device %u interface %u altsetting %u: format 0x%08x sratelo %u sratehi %u attributes 0x%02x\n", - dev->devnum, asifout, i, fp->format, fp->sratelo, fp->sratehi, fp->attributes); - } - } - if (as->numfmtin == 0 && as->numfmtout == 0) { - usb_free_urb(as->usbin.durb[0].urb); - usb_free_urb(as->usbin.durb[1].urb); - usb_free_urb(as->usbin.surb[0].urb); - usb_free_urb(as->usbin.surb[1].urb); - usb_free_urb(as->usbout.durb[0].urb); - usb_free_urb(as->usbout.durb[1].urb); - usb_free_urb(as->usbout.surb[0].urb); - usb_free_urb(as->usbout.surb[1].urb); - kfree(as); - return; - } - if ((as->dev_audio = register_sound_dsp(&usb_audio_fops, -1)) < 0) { - printk(KERN_ERR "usbaudio: cannot register dsp\n"); - usb_free_urb(as->usbin.durb[0].urb); - usb_free_urb(as->usbin.durb[1].urb); - usb_free_urb(as->usbin.surb[0].urb); - usb_free_urb(as->usbin.surb[1].urb); - usb_free_urb(as->usbout.durb[0].urb); - usb_free_urb(as->usbout.durb[1].urb); - usb_free_urb(as->usbout.surb[0].urb); - usb_free_urb(as->usbout.surb[1].urb); - kfree(as); - return; - } - printk(KERN_INFO "usbaudio: registered dsp 14,%d\n", as->dev_audio); - /* everything successful */ - list_add_tail(&as->list, &s->audiolist); -} - -struct consmixstate { - struct usb_audio_state *s; - unsigned char *buffer; - unsigned int buflen; - unsigned int ctrlif; - struct mixerchannel mixch[SOUND_MIXER_NRDEVICES]; - unsigned int nrmixch; - unsigned int mixchmask; - unsigned long unitbitmap[32/sizeof(unsigned long)]; - /* return values */ - unsigned int nrchannels; - unsigned int termtype; - unsigned int chconfig; -}; - -static struct mixerchannel *getmixchannel(struct consmixstate *state, unsigned int nr) -{ - struct mixerchannel *c; - - if (nr >= SOUND_MIXER_NRDEVICES) { - printk(KERN_ERR "usbaudio: invalid OSS mixer channel %u\n", nr); - return NULL; - } - if (!(state->mixchmask & (1 << nr))) { - printk(KERN_WARNING "usbaudio: OSS mixer channel %u already in use\n", nr); - return NULL; - } - c = &state->mixch[state->nrmixch++]; - c->osschannel = nr; - state->mixchmask &= ~(1 << nr); - return c; -} - -static unsigned int getvolchannel(struct consmixstate *state) -{ - unsigned int u; - - if ((state->termtype & 0xff00) == 0x0000 && (state->mixchmask & SOUND_MASK_VOLUME)) - return SOUND_MIXER_VOLUME; - if ((state->termtype & 0xff00) == 0x0100) { - if (state->mixchmask & SOUND_MASK_PCM) - return SOUND_MIXER_PCM; - if (state->mixchmask & SOUND_MASK_ALTPCM) - return SOUND_MIXER_ALTPCM; - } - if ((state->termtype & 0xff00) == 0x0200 && (state->mixchmask & SOUND_MASK_MIC)) - return SOUND_MIXER_MIC; - if ((state->termtype & 0xff00) == 0x0300 && (state->mixchmask & SOUND_MASK_SPEAKER)) - return SOUND_MIXER_SPEAKER; - if ((state->termtype & 0xff00) == 0x0500) { - if (state->mixchmask & SOUND_MASK_PHONEIN) - return SOUND_MIXER_PHONEIN; - if (state->mixchmask & SOUND_MASK_PHONEOUT) - return SOUND_MIXER_PHONEOUT; - } - if (state->termtype >= 0x710 && state->termtype <= 0x711 && (state->mixchmask & SOUND_MASK_RADIO)) - return SOUND_MIXER_RADIO; - if (state->termtype >= 0x709 && state->termtype <= 0x70f && (state->mixchmask & SOUND_MASK_VIDEO)) - return SOUND_MIXER_VIDEO; - u = ffs(state->mixchmask & (SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | SOUND_MASK_LINE3 | - SOUND_MASK_DIGITAL1 | SOUND_MASK_DIGITAL2 | SOUND_MASK_DIGITAL3)); - return u-1; -} - -static void prepmixch(struct consmixstate *state) -{ - struct usb_device *dev = state->s->usbdev; - struct mixerchannel *ch; - unsigned char *buf; - __s16 v1; - unsigned int v2, v3; - - if (!state->nrmixch || state->nrmixch > SOUND_MIXER_NRDEVICES) - return; - buf = kmalloc(sizeof(*buf) * 2, GFP_KERNEL); - if (!buf) { - printk(KERN_ERR "prepmixch: out of memory\n") ; - return; - } - - ch = &state->mixch[state->nrmixch-1]; - switch (ch->selector) { - case 0: /* mixer unit request */ - if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MIN, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, - (ch->chnum << 8) | 1, state->ctrlif | (ch->unitid << 8), buf, 2, 1000) < 0) - goto err; - ch->minval = buf[0] | (buf[1] << 8); - if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MAX, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, - (ch->chnum << 8) | 1, state->ctrlif | (ch->unitid << 8), buf, 2, 1000) < 0) - goto err; - ch->maxval = buf[0] | (buf[1] << 8); - v2 = ch->maxval - ch->minval; - if (!v2) - v2 = 1; - if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, - (ch->chnum << 8) | 1, state->ctrlif | (ch->unitid << 8), buf, 2, 1000) < 0) - goto err; - v1 = buf[0] | (buf[1] << 8); - v3 = v1 - ch->minval; - v3 = 100 * v3 / v2; - if (v3 > 100) - v3 = 100; - ch->value = v3; - if (ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)) { - if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, - ((ch->chnum + !!(ch->flags & MIXFLG_STEREOIN)) << 8) | (1 + !!(ch->flags & MIXFLG_STEREOOUT)), - state->ctrlif | (ch->unitid << 8), buf, 2, 1000) < 0) - goto err; - v1 = buf[0] | (buf[1] << 8); - v3 = v1 - ch->minval; - v3 = 100 * v3 / v2; - if (v3 > 100) - v3 = 100; - } - ch->value |= v3 << 8; - break; - - /* various feature unit controls */ - case VOLUME_CONTROL: - if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MIN, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, - (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 2, 1000) < 0) - goto err; - ch->minval = buf[0] | (buf[1] << 8); - if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MAX, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, - (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 2, 1000) < 0) - goto err; - ch->maxval = buf[0] | (buf[1] << 8); - if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, - (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 2, 1000) < 0) - goto err; - v1 = buf[0] | (buf[1] << 8); - v2 = ch->maxval - ch->minval; - v3 = v1 - ch->minval; - if (!v2) - v2 = 1; - v3 = 100 * v3 / v2; - if (v3 > 100) - v3 = 100; - ch->value = v3; - if (ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)) { - if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, - (ch->selector << 8) | (ch->chnum + 1), state->ctrlif | (ch->unitid << 8), buf, 2, 1000) < 0) - goto err; - v1 = buf[0] | (buf[1] << 8); - v3 = v1 - ch->minval; - v3 = 100 * v3 / v2; - if (v3 > 100) - v3 = 100; - } - ch->value |= v3 << 8; - break; - - case BASS_CONTROL: - case MID_CONTROL: - case TREBLE_CONTROL: - if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MIN, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, - (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 1, 1000) < 0) - goto err; - ch->minval = buf[0] << 8; - if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MAX, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, - (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 1, 1000) < 0) - goto err; - ch->maxval = buf[0] << 8; - if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, - (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 1, 1000) < 0) - goto err; - v1 = buf[0] << 8; - v2 = ch->maxval - ch->minval; - v3 = v1 - ch->minval; - if (!v2) - v2 = 1; - v3 = 100 * v3 / v2; - if (v3 > 100) - v3 = 100; - ch->value = v3; - if (ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)) { - if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, - (ch->selector << 8) | (ch->chnum + 1), state->ctrlif | (ch->unitid << 8), buf, 1, 1000) < 0) - goto err; - v1 = buf[0] << 8; - v3 = v1 - ch->minval; - v3 = 100 * v3 / v2; - if (v3 > 100) - v3 = 100; - } - ch->value |= v3 << 8; - break; - - default: - goto err; - } - - freebuf: - kfree(buf); - return; - err: - printk(KERN_ERR "usbaudio: mixer request device %u if %u unit %u ch %u selector %u failed\n", - dev->devnum, state->ctrlif, ch->unitid, ch->chnum, ch->selector); - if (state->nrmixch) - state->nrmixch--; - goto freebuf; -} - - -static void usb_audio_recurseunit(struct consmixstate *state, unsigned char unitid); - -static inline int checkmixbmap(unsigned char *bmap, unsigned char flg, unsigned int inidx, unsigned int numoch) -{ - unsigned int idx; - - idx = inidx*numoch; - if (!(bmap[-(idx >> 3)] & (0x80 >> (idx & 7)))) - return 0; - if (!(flg & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT))) - return 1; - idx = (inidx+!!(flg & MIXFLG_STEREOIN))*numoch+!!(flg & MIXFLG_STEREOOUT); - if (!(bmap[-(idx >> 3)] & (0x80 >> (idx & 7)))) - return 0; - return 1; -} - -static void usb_audio_mixerunit(struct consmixstate *state, unsigned char *mixer) -{ - unsigned int nroutch = mixer[5+mixer[4]]; - unsigned int chidx[SOUND_MIXER_NRDEVICES+1]; - unsigned int termt[SOUND_MIXER_NRDEVICES]; - unsigned char flg = (nroutch >= 2) ? MIXFLG_STEREOOUT : 0; - unsigned char *bmap = &mixer[9+mixer[4]]; - unsigned int bmapsize; - struct mixerchannel *ch; - unsigned int i; - - if (!mixer[4]) { - printk(KERN_ERR "usbaudio: unit %u invalid MIXER_UNIT descriptor\n", mixer[3]); - return; - } - if (mixer[4] > SOUND_MIXER_NRDEVICES) { - printk(KERN_ERR "usbaudio: mixer unit %u: too many input pins\n", mixer[3]); - return; - } - chidx[0] = 0; - for (i = 0; i < mixer[4]; i++) { - usb_audio_recurseunit(state, mixer[5+i]); - chidx[i+1] = chidx[i] + state->nrchannels; - termt[i] = state->termtype; - } - state->termtype = 0; - state->chconfig = mixer[6+mixer[4]] | (mixer[7+mixer[4]] << 8); - bmapsize = (nroutch * chidx[mixer[4]] + 7) >> 3; - bmap += bmapsize - 1; - if (mixer[0] < 10+mixer[4]+bmapsize) { - printk(KERN_ERR "usbaudio: unit %u invalid MIXER_UNIT descriptor (bitmap too small)\n", mixer[3]); - return; - } - for (i = 0; i < mixer[4]; i++) { - state->termtype = termt[i]; - if (chidx[i+1]-chidx[i] >= 2) { - flg |= MIXFLG_STEREOIN; - if (checkmixbmap(bmap, flg, chidx[i], nroutch)) { - ch = getmixchannel(state, getvolchannel(state)); - if (ch) { - ch->unitid = mixer[3]; - ch->selector = 0; - ch->chnum = chidx[i]+1; - ch->flags = flg; - prepmixch(state); - } - continue; - } - } - flg &= ~MIXFLG_STEREOIN; - if (checkmixbmap(bmap, flg, chidx[i], nroutch)) { - ch = getmixchannel(state, getvolchannel(state)); - if (ch) { - ch->unitid = mixer[3]; - ch->selector = 0; - ch->chnum = chidx[i]+1; - ch->flags = flg; - prepmixch(state); - } - } - } - state->termtype = 0; -} - -static struct mixerchannel *slctsrc_findunit(struct consmixstate *state, __u8 unitid) -{ - unsigned int i; - - for (i = 0; i < state->nrmixch; i++) - if (state->mixch[i].unitid == unitid) - return &state->mixch[i]; - return NULL; -} - -static void usb_audio_selectorunit(struct consmixstate *state, unsigned char *selector) -{ - unsigned int chnum, i, mixch; - struct mixerchannel *mch; - - if (!selector[4]) { - printk(KERN_ERR "usbaudio: unit %u invalid SELECTOR_UNIT descriptor\n", selector[3]); - return; - } - mixch = state->nrmixch; - usb_audio_recurseunit(state, selector[5]); - if (state->nrmixch != mixch) { - mch = &state->mixch[state->nrmixch-1]; - mch->slctunitid = selector[3] | (1 << 8); - } else if ((mch = slctsrc_findunit(state, selector[5]))) { - mch->slctunitid = selector[3] | (1 << 8); - } else { - printk(KERN_INFO "usbaudio: selector unit %u: ignoring channel 1\n", selector[3]); - } - chnum = state->nrchannels; - for (i = 1; i < selector[4]; i++) { - mixch = state->nrmixch; - usb_audio_recurseunit(state, selector[5+i]); - if (chnum != state->nrchannels) { - printk(KERN_ERR "usbaudio: selector unit %u: input pins with varying channel numbers\n", selector[3]); - state->termtype = 0; - state->chconfig = 0; - state->nrchannels = 0; - return; - } - if (state->nrmixch != mixch) { - mch = &state->mixch[state->nrmixch-1]; - mch->slctunitid = selector[3] | ((i + 1) << 8); - } else if ((mch = slctsrc_findunit(state, selector[5+i]))) { - mch->slctunitid = selector[3] | ((i + 1) << 8); - } else { - printk(KERN_INFO "usbaudio: selector unit %u: ignoring channel %u\n", selector[3], i+1); - } - } - state->termtype = 0; - state->chconfig = 0; -} - -/* in the future we might try to handle 3D etc. effect units */ - -static void usb_audio_processingunit(struct consmixstate *state, unsigned char *proc) -{ - unsigned int i; - - for (i = 0; i < proc[6]; i++) - usb_audio_recurseunit(state, proc[7+i]); - state->nrchannels = proc[7+proc[6]]; - state->termtype = 0; - state->chconfig = proc[8+proc[6]] | (proc[9+proc[6]] << 8); -} - - -/* See Audio Class Spec, section 4.3.2.5 */ -static void usb_audio_featureunit(struct consmixstate *state, unsigned char *ftr) -{ - struct mixerchannel *ch; - unsigned short chftr, mchftr; -#if 0 - struct usb_device *dev = state->s->usbdev; - unsigned char data[1]; -#endif - unsigned char nr_logical_channels, i; - - usb_audio_recurseunit(state, ftr[4]); - - if (ftr[5] == 0 ) { - printk(KERN_ERR "usbaudio: wrong controls size in feature unit %u\n",ftr[3]); - return; - } - - if (state->nrchannels == 0) { - printk(KERN_ERR "usbaudio: feature unit %u source has no channels\n", ftr[3]); - return; - } - if (state->nrchannels > 2) - printk(KERN_WARNING "usbaudio: feature unit %u: OSS mixer interface does not support more than 2 channels\n", ftr[3]); - - nr_logical_channels=(ftr[0]-7)/ftr[5]-1; - - if (nr_logical_channels != state->nrchannels) { - printk(KERN_WARNING "usbaudio: warning: found %d of %d logical channels.\n", state->nrchannels,nr_logical_channels); - - if (state->nrchannels == 1 && nr_logical_channels==0) { - printk(KERN_INFO "usbaudio: assuming the channel found is the master channel (got a Philips camera?). Should be fine.\n"); - } else if (state->nrchannels == 1 && nr_logical_channels==2) { - printk(KERN_INFO "usbaudio: assuming that a stereo channel connected directly to a mixer is missing in search (got Labtec headset?). Should be fine.\n"); - state->nrchannels=nr_logical_channels; - } else { - printk(KERN_WARNING "usbaudio: no idea what's going on..., contact linux-usb-devel@lists.sourceforge.net\n"); - } - } - - /* There is always a master channel */ - mchftr = ftr[6]; - /* Binary AND over logical channels if they exist */ - if (nr_logical_channels) { - chftr = ftr[6+ftr[5]]; - for (i = 2; i <= nr_logical_channels; i++) - chftr &= ftr[6+i*ftr[5]]; - } else { - chftr = 0; - } - - /* volume control */ - if (chftr & 2) { - ch = getmixchannel(state, getvolchannel(state)); - if (ch) { - ch->unitid = ftr[3]; - ch->selector = VOLUME_CONTROL; - ch->chnum = 1; - ch->flags = (state->nrchannels > 1) ? (MIXFLG_STEREOIN | MIXFLG_STEREOOUT) : 0; - prepmixch(state); - } - } else if (mchftr & 2) { - ch = getmixchannel(state, getvolchannel(state)); - if (ch) { - ch->unitid = ftr[3]; - ch->selector = VOLUME_CONTROL; - ch->chnum = 0; - ch->flags = 0; - prepmixch(state); - } - } - /* bass control */ - if (chftr & 4) { - ch = getmixchannel(state, SOUND_MIXER_BASS); - if (ch) { - ch->unitid = ftr[3]; - ch->selector = BASS_CONTROL; - ch->chnum = 1; - ch->flags = (state->nrchannels > 1) ? (MIXFLG_STEREOIN | MIXFLG_STEREOOUT) : 0; - prepmixch(state); - } - } else if (mchftr & 4) { - ch = getmixchannel(state, SOUND_MIXER_BASS); - if (ch) { - ch->unitid = ftr[3]; - ch->selector = BASS_CONTROL; - ch->chnum = 0; - ch->flags = 0; - prepmixch(state); - } - } - /* treble control */ - if (chftr & 16) { - ch = getmixchannel(state, SOUND_MIXER_TREBLE); - if (ch) { - ch->unitid = ftr[3]; - ch->selector = TREBLE_CONTROL; - ch->chnum = 1; - ch->flags = (state->nrchannels > 1) ? (MIXFLG_STEREOIN | MIXFLG_STEREOOUT) : 0; - prepmixch(state); - } - } else if (mchftr & 16) { - ch = getmixchannel(state, SOUND_MIXER_TREBLE); - if (ch) { - ch->unitid = ftr[3]; - ch->selector = TREBLE_CONTROL; - ch->chnum = 0; - ch->flags = 0; - prepmixch(state); - } - } -#if 0 - /* if there are mute controls, unmute them */ - /* does not seem to be necessary, and the Dallas chip does not seem to support the "all" channel (255) */ - if ((chftr & 1) || (mchftr & 1)) { - printk(KERN_DEBUG "usbaudio: unmuting feature unit %u interface %u\n", ftr[3], state->ctrlif); - data[0] = 0; - if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, - (MUTE_CONTROL << 8) | 0xff, state->ctrlif | (ftr[3] << 8), data, 1, 1000) < 0) - printk(KERN_WARNING "usbaudio: failure to unmute feature unit %u interface %u\n", ftr[3], state->ctrlif); - } -#endif -} - -static void usb_audio_recurseunit(struct consmixstate *state, unsigned char unitid) -{ - unsigned char *p1; - unsigned int i, j; - - if (test_and_set_bit(unitid, state->unitbitmap)) { - printk(KERN_INFO "usbaudio: mixer path revisits unit %d\n", unitid); - return; - } - p1 = find_audiocontrol_unit(state->buffer, state->buflen, NULL, unitid, state->ctrlif); - if (!p1) { - printk(KERN_ERR "usbaudio: unit %d not found!\n", unitid); - return; - } - state->nrchannels = 0; - state->termtype = 0; - state->chconfig = 0; - switch (p1[2]) { - case INPUT_TERMINAL: - if (p1[0] < 12) { - printk(KERN_ERR "usbaudio: unit %u: invalid INPUT_TERMINAL descriptor\n", unitid); - return; - } - state->nrchannels = p1[7]; - state->termtype = p1[4] | (p1[5] << 8); - state->chconfig = p1[8] | (p1[9] << 8); - return; - - case MIXER_UNIT: - if (p1[0] < 10 || p1[0] < 10+p1[4]) { - printk(KERN_ERR "usbaudio: unit %u: invalid MIXER_UNIT descriptor\n", unitid); - return; - } - usb_audio_mixerunit(state, p1); - return; - - case SELECTOR_UNIT: - if (p1[0] < 6 || p1[0] < 6+p1[4]) { - printk(KERN_ERR "usbaudio: unit %u: invalid SELECTOR_UNIT descriptor\n", unitid); - return; - } - usb_audio_selectorunit(state, p1); - return; - - case FEATURE_UNIT: /* See USB Audio Class Spec 4.3.2.5 */ - if (p1[0] < 7 || p1[0] < 7+p1[5]) { - printk(KERN_ERR "usbaudio: unit %u: invalid FEATURE_UNIT descriptor\n", unitid); - return; - } - usb_audio_featureunit(state, p1); - return; - - case PROCESSING_UNIT: - if (p1[0] < 13 || p1[0] < 13+p1[6] || p1[0] < 13+p1[6]+p1[11+p1[6]]) { - printk(KERN_ERR "usbaudio: unit %u: invalid PROCESSING_UNIT descriptor\n", unitid); - return; - } - usb_audio_processingunit(state, p1); - return; - - case EXTENSION_UNIT: - if (p1[0] < 13 || p1[0] < 13+p1[6] || p1[0] < 13+p1[6]+p1[11+p1[6]]) { - printk(KERN_ERR "usbaudio: unit %u: invalid EXTENSION_UNIT descriptor\n", unitid); - return; - } - for (j = i = 0; i < p1[6]; i++) { - usb_audio_recurseunit(state, p1[7+i]); - if (!i) - j = state->termtype; - else if (j != state->termtype) - j = 0; - } - state->nrchannels = p1[7+p1[6]]; - state->chconfig = p1[8+p1[6]] | (p1[9+p1[6]] << 8); - state->termtype = j; - return; - - default: - printk(KERN_ERR "usbaudio: unit %u: unexpected type 0x%02x\n", unitid, p1[2]); - return; - } -} - -static void usb_audio_constructmixer(struct usb_audio_state *s, unsigned char *buffer, unsigned int buflen, unsigned int ctrlif, unsigned char *oterm) -{ - struct usb_mixerdev *ms; - struct consmixstate state; - - memset(&state, 0, sizeof(state)); - state.s = s; - state.nrmixch = 0; - state.mixchmask = ~0; - state.buffer = buffer; - state.buflen = buflen; - state.ctrlif = ctrlif; - set_bit(oterm[3], state.unitbitmap); /* mark terminal ID as visited */ - printk(KERN_DEBUG "usbaudio: constructing mixer for Terminal %u type 0x%04x\n", - oterm[3], oterm[4] | (oterm[5] << 8)); - usb_audio_recurseunit(&state, oterm[7]); - if (!state.nrmixch) { - printk(KERN_INFO "usbaudio: no mixer controls found for Terminal %u\n", oterm[3]); - return; - } - if (!(ms = kmalloc(sizeof(struct usb_mixerdev)+state.nrmixch*sizeof(struct mixerchannel), GFP_KERNEL))) - return; - memset(ms, 0, sizeof(struct usb_mixerdev)); - memcpy(&ms->ch, &state.mixch, state.nrmixch*sizeof(struct mixerchannel)); - ms->state = s; - ms->iface = ctrlif; - ms->numch = state.nrmixch; - if ((ms->dev_mixer = register_sound_mixer(&usb_mixer_fops, -1)) < 0) { - printk(KERN_ERR "usbaudio: cannot register mixer\n"); - kfree(ms); - return; - } - printk(KERN_INFO "usbaudio: registered mixer 14,%d\n", ms->dev_mixer); - list_add_tail(&ms->list, &s->mixerlist); -} - -/* arbitrary limit, we won't check more interfaces than this */ -#define USB_MAXINTERFACES 32 - -static struct usb_audio_state *usb_audio_parsecontrol(struct usb_device *dev, unsigned char *buffer, unsigned int buflen, unsigned int ctrlif) -{ - struct usb_audio_state *s; - struct usb_interface *iface; - struct usb_host_interface *alt; - unsigned char ifin[USB_MAXINTERFACES], ifout[USB_MAXINTERFACES]; - unsigned char *p1; - unsigned int i, j, k, numifin = 0, numifout = 0; - - if (!(s = kmalloc(sizeof(struct usb_audio_state), GFP_KERNEL))) - return NULL; - memset(s, 0, sizeof(struct usb_audio_state)); - INIT_LIST_HEAD(&s->audiolist); - INIT_LIST_HEAD(&s->mixerlist); - s->usbdev = dev; - s->count = 1; - - /* find audiocontrol interface */ - if (!(p1 = find_csinterface_descriptor(buffer, buflen, NULL, HEADER, ctrlif, -1))) { - printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u no HEADER found\n", - dev->devnum, ctrlif); - goto ret; - } - if (p1[0] < 8 + p1[7]) { - printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u HEADER error\n", - dev->devnum, ctrlif); - goto ret; - } - if (!p1[7]) - printk(KERN_INFO "usbaudio: device %d audiocontrol interface %u has no AudioStreaming and MidiStreaming interfaces\n", - dev->devnum, ctrlif); - for (i = 0; i < p1[7]; i++) { - j = p1[8+i]; - iface = usb_ifnum_to_if(dev, j); - if (!iface) { - printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u interface %u does not exist\n", - dev->devnum, ctrlif, j); - continue; - } - if (iface->num_altsetting == 1) { - printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u has only 1 altsetting.\n", dev->devnum, ctrlif); - continue; - } - alt = usb_altnum_to_altsetting(iface, 0); - if (!alt) { - printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u interface %u has no altsetting 0\n", - dev->devnum, ctrlif, j); - continue; - } - if (alt->desc.bInterfaceClass != USB_CLASS_AUDIO) { - printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u interface %u is not an AudioClass interface\n", - dev->devnum, ctrlif, j); - continue; - } - if (alt->desc.bInterfaceSubClass == 3) { - printk(KERN_INFO "usbaudio: device %d audiocontrol interface %u interface %u MIDIStreaming not supported\n", - dev->devnum, ctrlif, j); - continue; - } - if (alt->desc.bInterfaceSubClass != 2) { - printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u interface %u invalid AudioClass subtype\n", - dev->devnum, ctrlif, j); - continue; - } - if (alt->desc.bNumEndpoints > 0) { - /* Check all endpoints; should they all have a bandwidth of 0 ? */ - for (k = 0; k < alt->desc.bNumEndpoints; k++) { - if (le16_to_cpu(alt->endpoint[k].desc.wMaxPacketSize) > 0) { - printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u endpoint %d does not have 0 bandwidth at alt[0]\n", dev->devnum, ctrlif, k); - break; - } - } - if (k < alt->desc.bNumEndpoints) - continue; - } - - alt = usb_altnum_to_altsetting(iface, 1); - if (!alt) { - printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u interface %u has no altsetting 1\n", - dev->devnum, ctrlif, j); - continue; - } - if (alt->desc.bNumEndpoints < 1) { - printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u interface %u has no endpoint\n", - dev->devnum, ctrlif, j); - continue; - } - /* note: this requires the data endpoint to be ep0 and the optional sync - ep to be ep1, which seems to be the case */ - if (alt->endpoint[0].desc.bEndpointAddress & USB_DIR_IN) { - if (numifin < USB_MAXINTERFACES) { - ifin[numifin++] = j; - usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1); - } - } else { - if (numifout < USB_MAXINTERFACES) { - ifout[numifout++] = j; - usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1); - } - } - } - printk(KERN_INFO "usbaudio: device %d audiocontrol interface %u has %u input and %u output AudioStreaming interfaces\n", - dev->devnum, ctrlif, numifin, numifout); - for (i = 0; i < numifin && i < numifout; i++) - usb_audio_parsestreaming(s, buffer, buflen, ifin[i], ifout[i]); - for (j = i; j < numifin; j++) - usb_audio_parsestreaming(s, buffer, buflen, ifin[i], -1); - for (j = i; j < numifout; j++) - usb_audio_parsestreaming(s, buffer, buflen, -1, ifout[i]); - /* now walk through all OUTPUT_TERMINAL descriptors to search for mixers */ - p1 = find_csinterface_descriptor(buffer, buflen, NULL, OUTPUT_TERMINAL, ctrlif, -1); - while (p1) { - if (p1[0] >= 9) - usb_audio_constructmixer(s, buffer, buflen, ctrlif, p1); - p1 = find_csinterface_descriptor(buffer, buflen, p1, OUTPUT_TERMINAL, ctrlif, -1); - } - -ret: - if (list_empty(&s->audiolist) && list_empty(&s->mixerlist)) { - kfree(s); - return NULL; - } - /* everything successful */ - down(&open_sem); - list_add_tail(&s->audiodev, &audiodevs); - up(&open_sem); - printk(KERN_DEBUG "usb_audio_parsecontrol: usb_audio_state at %p\n", s); - return s; -} - -/* we only care for the currently active configuration */ - -static int usb_audio_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *dev = interface_to_usbdev (intf); - struct usb_audio_state *s; - unsigned char *buffer; - unsigned int buflen; - -#if 0 - printk(KERN_DEBUG "usbaudio: Probing if %i: IC %x, ISC %x\n", ifnum, - config->interface[ifnum].altsetting[0].desc.bInterfaceClass, - config->interface[ifnum].altsetting[0].desc.bInterfaceSubClass); -#endif - - /* - * audiocontrol interface found - * find which configuration number is active - */ - buffer = dev->rawdescriptors[dev->actconfig - dev->config]; - buflen = le16_to_cpu(dev->actconfig->desc.wTotalLength); - s = usb_audio_parsecontrol(dev, buffer, buflen, intf->altsetting->desc.bInterfaceNumber); - if (s) { - usb_set_intfdata (intf, s); - return 0; - } - return -ENODEV; -} - - -/* a revoke facility would make things simpler */ - -static void usb_audio_disconnect(struct usb_interface *intf) -{ - struct usb_audio_state *s = usb_get_intfdata (intf); - struct usb_audiodev *as; - struct usb_mixerdev *ms; - - if (!s) - return; - - /* we get called with -1 for every audiostreaming interface registered */ - if (s == (struct usb_audio_state *)-1) { - dprintk((KERN_DEBUG "usbaudio: note, usb_audio_disconnect called with -1\n")); - return; - } - if (!s->usbdev) { - dprintk((KERN_DEBUG "usbaudio: error, usb_audio_disconnect already called for %p!\n", s)); - return; - } - down(&open_sem); - list_del_init(&s->audiodev); - s->usbdev = NULL; - usb_set_intfdata (intf, NULL); - - /* deregister all audio and mixer devices, so no new processes can open this device */ - list_for_each_entry(as, &s->audiolist, list) { - usbin_disc(as); - usbout_disc(as); - wake_up(&as->usbin.dma.wait); - wake_up(&as->usbout.dma.wait); - if (as->dev_audio >= 0) { - unregister_sound_dsp(as->dev_audio); - printk(KERN_INFO "usbaudio: unregister dsp 14,%d\n", as->dev_audio); - } - as->dev_audio = -1; - } - list_for_each_entry(ms, &s->mixerlist, list) { - if (ms->dev_mixer >= 0) { - unregister_sound_mixer(ms->dev_mixer); - printk(KERN_INFO "usbaudio: unregister mixer 14,%d\n", ms->dev_mixer); - } - ms->dev_mixer = -1; - } - release(s); - wake_up(&open_wait); -} - -static int __init usb_audio_init(void) -{ - int result = usb_register(&usb_audio_driver); - if (result == 0) - info(DRIVER_VERSION ":" DRIVER_DESC); - return result; -} - - -static void __exit usb_audio_cleanup(void) -{ - usb_deregister(&usb_audio_driver); -} - -module_init(usb_audio_init); -module_exit(usb_audio_cleanup); - -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); -MODULE_LICENSE("GPL"); - diff --git a/drivers/usb/class/audio.h b/drivers/usb/class/audio.h deleted file mode 100644 index 45916eb12103..000000000000 --- a/drivers/usb/class/audio.h +++ /dev/null @@ -1,110 +0,0 @@ -#define CS_AUDIO_UNDEFINED 0x20 -#define CS_AUDIO_DEVICE 0x21 -#define CS_AUDIO_CONFIGURATION 0x22 -#define CS_AUDIO_STRING 0x23 -#define CS_AUDIO_INTERFACE 0x24 -#define CS_AUDIO_ENDPOINT 0x25 - -#define HEADER 0x01 -#define INPUT_TERMINAL 0x02 -#define OUTPUT_TERMINAL 0x03 -#define MIXER_UNIT 0x04 -#define SELECTOR_UNIT 0x05 -#define FEATURE_UNIT 0x06 -#define PROCESSING_UNIT 0x07 -#define EXTENSION_UNIT 0x08 - -#define AS_GENERAL 0x01 -#define FORMAT_TYPE 0x02 -#define FORMAT_SPECIFIC 0x03 - -#define EP_GENERAL 0x01 - -#define MAX_CHAN 9 -#define MAX_FREQ 16 -#define MAX_IFACE 8 -#define MAX_FORMAT 8 -#define MAX_ALT 32 /* Sorry, we need quite a few for the Philips webcams */ - -struct usb_audio_terminal -{ - u8 flags; - u8 assoc; - u16 type; /* Mic etc */ - u8 channels; - u8 source; - u16 chancfg; -}; - -struct usb_audio_format -{ - u8 type; - u8 channels; - u8 num_freq; - u8 sfz; - u8 bits; - u16 freq[MAX_FREQ]; -}; - -struct usb_audio_interface -{ - u8 terminal; - u8 delay; - u16 num_formats; - u16 format_type; - u8 flags; - u8 idleconf; /* Idle config */ -#define AU_IFACE_FOUND 1 - struct usb_audio_format format[MAX_FORMAT]; -}; - -struct usb_audio_device -{ - struct list_head list; - u8 mixer; - u8 selector; - void *irq_handle; - u8 num_channels; - u8 num_dsp_iface; - u8 channel_map[MAX_CHAN]; - struct usb_audio_terminal terminal[MAX_CHAN]; - struct usb_audio_interface interface[MAX_IFACE][MAX_ALT]; -}; - - - -/* Audio Class specific Request Codes */ - -#define SET_CUR 0x01 -#define GET_CUR 0x81 -#define SET_MIN 0x02 -#define GET_MIN 0x82 -#define SET_MAX 0x03 -#define GET_MAX 0x83 -#define SET_RES 0x04 -#define GET_RES 0x84 -#define SET_MEM 0x05 -#define GET_MEM 0x85 -#define GET_STAT 0xff - -/* Terminal Control Selectors */ - -#define COPY_PROTECT_CONTROL 0x01 - -/* Feature Unit Control Selectors */ - -#define MUTE_CONTROL 0x01 -#define VOLUME_CONTROL 0x02 -#define BASS_CONTROL 0x03 -#define MID_CONTROL 0x04 -#define TREBLE_CONTROL 0x05 -#define GRAPHIC_EQUALIZER_CONTROL 0x06 -#define AUTOMATIC_GAIN_CONTROL 0x07 -#define DELAY_CONTROL 0x08 -#define BASS_BOOST_CONTROL 0x09 -#define LOUDNESS_CONTROL 0x0a - -/* Endpoint Control Selectors */ - -#define SAMPLING_FREQ_CONTROL 0x01 -#define PITCH_CONTROL 0x02 diff --git a/drivers/usb/class/usb-midi.c b/drivers/usb/class/usb-midi.c deleted file mode 100644 index f13f004d311f..000000000000 --- a/drivers/usb/class/usb-midi.c +++ /dev/null @@ -1,2153 +0,0 @@ -/* - usb-midi.c -- USB-MIDI driver - - Copyright (C) 2001 - NAGANO Daisuke - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - This driver is based on: - - 'Universal Serial Bus Device Class Definition for MIDI Device' - - linux/drivers/sound/es1371.c, linux/drivers/usb/audio.c - - alsa/lowlevel/pci/cs64xx.c - - umidi.c for NetBSD - */ - -/* ------------------------------------------------------------------------- */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "usb-midi.h" - -/* ------------------------------------------------------------------------- */ - -/* More verbose on syslog */ -#undef MIDI_DEBUG - -#define MIDI_IN_BUFSIZ 1024 - -#define HAVE_SUPPORT_USB_MIDI_CLASS - -#undef HAVE_SUPPORT_ALSA - -/* ------------------------------------------------------------------------- */ - -static int singlebyte = 0; -module_param(singlebyte, int, 0); -MODULE_PARM_DESC(singlebyte,"Enable sending MIDI messages with single message packet"); - -static int maxdevices = 4; -module_param(maxdevices, int, 0); -MODULE_PARM_DESC(maxdevices,"Max number of allocatable MIDI device"); - -static int uvendor = -1; -module_param(uvendor, int, 0); -MODULE_PARM_DESC(uvendor, "The USB Vendor ID of a semi-compliant interface"); - -static int uproduct = -1; -module_param(uproduct, int, 0); -MODULE_PARM_DESC(uproduct, "The USB Product ID of a semi-compliant interface"); - -static int uinterface = -1; -module_param(uinterface, int, 0); -MODULE_PARM_DESC(uinterface, "The Interface number of a semi-compliant interface"); - -static int ualt = -1; -module_param(ualt, int, 0); -MODULE_PARM_DESC(ualt, "The optional alternative setting of a semi-compliant interface"); - -static int umin = -1; -module_param(umin, int, 0); -MODULE_PARM_DESC(umin, "The input endpoint of a semi-compliant interface"); - -static int umout = -1; -module_param(umout, int, 0); -MODULE_PARM_DESC(umout, "The output endpoint of a semi-compliant interface"); - -static int ucable = -1; -module_param(ucable, int, 0); -MODULE_PARM_DESC(ucable, "The cable number used for a semi-compliant interface"); - -/** Note -- the usb_string() returns only Latin-1 characters. - * (unicode chars <= 255). To support Japanese, a unicode16LE-to-EUC or - * unicode16LE-to-JIS routine is needed to wrap around usb_get_string(). - **/ -static unsigned short ulangid = 0x0409; /** 0x0411 for Japanese **/ -module_param(ulangid, ushort, 0); -MODULE_PARM_DESC(ulangid, "The optional preferred USB Language ID for all devices"); - -MODULE_AUTHOR("NAGANO Daisuke "); -MODULE_DESCRIPTION("USB-MIDI driver"); -MODULE_LICENSE("GPL"); - -/* ------------------------------------------------------------------------- */ - -/** MIDIStreaming Class-Specific Interface Descriptor Subtypes **/ - -#define MS_DESCRIPTOR_UNDEFINED 0 -#define MS_HEADER 1 -#define MIDI_IN_JACK 2 -#define MIDI_OUT_JACK 3 -/* Spec reads: ELEMENT */ -#define ELEMENT_DESCRIPTOR 4 - -#define MS_HEADER_LENGTH 7 - -/** MIDIStreaming Class-Specific Endpoint Descriptor Subtypes **/ - -#define DESCRIPTOR_UNDEFINED 0 -/* Spec reads: MS_GENERAL */ -#define MS_GENERAL_ENDPOINT 1 - -/** MIDIStreaming MIDI IN and OUT Jack Types **/ - -#define JACK_TYPE_UNDEFINED 0 -/* Spec reads: EMBEDDED */ -#define EMBEDDED_JACK 1 -/* Spec reads: EXTERNAL */ -#define EXTERNAL_JACK 2 - - -/* structure summary - - usb_midi_state usb_device - | | - *| *| per ep - in_ep out_ep - | | - *| *| per cable - min mout - | | (cable to device pairing magic) - | | - usb_midi_dev dev_id (major,minor) == file->private_data - -*/ - -/* usb_midi_state: corresponds to a USB-MIDI module */ -struct usb_midi_state { - struct list_head mididev; - - struct usb_device *usbdev; - - struct list_head midiDevList; - struct list_head inEndpointList; - struct list_head outEndpointList; - - spinlock_t lock; - - unsigned int count; /* usage counter */ -}; - -/* midi_out_endpoint: corresponds to an output endpoint */ -struct midi_out_endpoint { - struct list_head list; - - struct usb_device *usbdev; - int endpoint; - spinlock_t lock; - wait_queue_head_t wait; - - unsigned char *buf; - int bufWrPtr; - int bufSize; - - struct urb *urb; -}; - -/* midi_in_endpoint: corresponds to an input endpoint */ -struct midi_in_endpoint { - struct list_head list; - - struct usb_device *usbdev; - int endpoint; - spinlock_t lock; - wait_queue_head_t wait; - - struct usb_mididev *cables[16]; // cables open for read - int readers; // number of cables open for read - - struct urb *urb; - unsigned char *recvBuf; - int recvBufSize; - int urbSubmitted; //FIXME: == readers > 0 -}; - -/* usb_mididev: corresponds to a logical device */ -struct usb_mididev { - struct list_head list; - - struct usb_midi_state *midi; - int dev_midi; - mode_t open_mode; - - struct { - struct midi_in_endpoint *ep; - int cableId; - -// as we are pushing data from usb_bulk_read to usb_midi_read, -// we need a larger, cyclic buffer here. - unsigned char buf[MIDI_IN_BUFSIZ]; - int bufRdPtr; - int bufWrPtr; - int bufRemains; - } min; - - struct { - struct midi_out_endpoint *ep; - int cableId; - - unsigned char buf[3]; - int bufPtr; - int bufRemains; - - int isInExclusive; - unsigned char lastEvent; - } mout; - - int singlebyte; -}; - -/** Map the high nybble of MIDI voice messages to number of Message bytes. - * High nyble ranges from 0x8 to 0xe - */ - -static int remains_80e0[] = { - 3, /** 0x8X Note Off **/ - 3, /** 0x9X Note On **/ - 3, /** 0xAX Poly-key pressure **/ - 3, /** 0xBX Control Change **/ - 2, /** 0xCX Program Change **/ - 2, /** 0xDX Channel pressure **/ - 3 /** 0xEX PitchBend Change **/ -}; - -/** Map the messages to a number of Message bytes. - * - **/ -static int remains_f0f6[] = { - 0, /** 0xF0 **/ - 2, /** 0XF1 **/ - 3, /** 0XF2 **/ - 2, /** 0XF3 **/ - 2, /** 0XF4 (Undefined by MIDI Spec, and subject to change) **/ - 2, /** 0XF5 (Undefined by MIDI Spec, and subject to change) **/ - 1 /** 0XF6 **/ -}; - -/** Map the messages to a CIN (Code Index Number). - * - **/ -static int cin_f0ff[] = { - 4, /** 0xF0 System Exclusive Message Start (special cases may be 6 or 7) */ - 2, /** 0xF1 **/ - 3, /** 0xF2 **/ - 2, /** 0xF3 **/ - 2, /** 0xF4 **/ - 2, /** 0xF5 **/ - 5, /** 0xF6 **/ - 5, /** 0xF7 End of System Exclusive Message (May be 6 or 7) **/ - 5, /** 0xF8 **/ - 5, /** 0xF9 **/ - 5, /** 0xFA **/ - 5, /** 0xFB **/ - 5, /** 0xFC **/ - 5, /** 0xFD **/ - 5, /** 0xFE **/ - 5 /** 0xFF **/ -}; - -/** Map MIDIStreaming Event packet Code Index Number (low nybble of byte 0) - * to the number of bytes of valid MIDI data. - * - * CIN of 0 and 1 are NOT USED in MIDIStreaming 1.0. - * - **/ -static int cin_to_len[] = { - 0, 0, 2, 3, - 3, 1, 2, 3, - 3, 3, 3, 3, - 2, 2, 3, 1 -}; - - -/* ------------------------------------------------------------------------- */ - -static struct list_head mididevs = LIST_HEAD_INIT(mididevs); - -static DECLARE_MUTEX(open_sem); -static DECLARE_WAIT_QUEUE_HEAD(open_wait); - - -/* ------------------------------------------------------------------------- */ - -static void usb_write_callback(struct urb *urb, struct pt_regs *regs) -{ - struct midi_out_endpoint *ep = (struct midi_out_endpoint *)urb->context; - - if ( waitqueue_active( &ep->wait ) ) - wake_up_interruptible( &ep->wait ); -} - - -static int usb_write( struct midi_out_endpoint *ep, unsigned char *buf, int len ) -{ - struct usb_device *d; - int pipe; - int ret = 0; - int status; - int maxretry = 50; - - DECLARE_WAITQUEUE(wait,current); - init_waitqueue_head(&ep->wait); - - d = ep->usbdev; - pipe = usb_sndbulkpipe(d, ep->endpoint); - usb_fill_bulk_urb( ep->urb, d, pipe, (unsigned char*)buf, len, - usb_write_callback, ep ); - - status = usb_submit_urb(ep->urb, GFP_KERNEL); - - if (status) { - printk(KERN_ERR "usbmidi: Cannot submit urb (%d)\n",status); - ret = -EIO; - goto error; - } - - add_wait_queue( &ep->wait, &wait ); - set_current_state( TASK_INTERRUPTIBLE ); - - while( ep->urb->status == -EINPROGRESS ) { - if ( maxretry-- < 0 ) { - printk(KERN_ERR "usbmidi: usb_bulk_msg timed out\n"); - ret = -ETIME; - break; - } - interruptible_sleep_on_timeout( &ep->wait, 10 ); - } - set_current_state( TASK_RUNNING ); - remove_wait_queue( &ep->wait, &wait ); - -error: - return ret; -} - - -/** Copy data from URB to In endpoint buf. - * Discard if CIN == 0 or CIN = 1. - * - * - **/ - -static void usb_bulk_read(struct urb *urb, struct pt_regs *regs) -{ - struct midi_in_endpoint *ep = (struct midi_in_endpoint *)(urb->context); - unsigned char *data = urb->transfer_buffer; - int i, j, wake; - - if ( !ep->urbSubmitted ) { - return; - } - - if ( (urb->status == 0) && (urb->actual_length > 0) ) { - wake = 0; - spin_lock( &ep->lock ); - - for(j = 0; j < urb->actual_length; j += 4) { - int cin = (data[j]>>0)&0xf; - int cab = (data[j]>>4)&0xf; - struct usb_mididev *cable = ep->cables[cab]; - if ( cable ) { - int len = cin_to_len[cin]; /** length of MIDI data **/ - for (i = 0; i < len; i++) { - cable->min.buf[cable->min.bufWrPtr] = data[1+i+j]; - cable->min.bufWrPtr = (cable->min.bufWrPtr+1)%MIDI_IN_BUFSIZ; - if (cable->min.bufRemains < MIDI_IN_BUFSIZ) - cable->min.bufRemains += 1; - else /** need to drop data **/ - cable->min.bufRdPtr += (cable->min.bufRdPtr+1)%MIDI_IN_BUFSIZ; - wake = 1; - } - } - } - - spin_unlock ( &ep->lock ); - if ( wake ) { - wake_up( &ep->wait ); - } - } - - /* urb->dev must be reinitialized on 2.4.x kernels */ - urb->dev = ep->usbdev; - - urb->actual_length = 0; - usb_submit_urb(urb, GFP_ATOMIC); -} - - - -/* ------------------------------------------------------------------------- */ - -/* This routine must be called with spin_lock */ - -/** Wrapper around usb_write(). - * This routine must be called with spin_lock held on ep. - * Called by midiWrite(), putOneMidiEvent(), and usb_midi_write(); - **/ -static int flush_midi_buffer( struct midi_out_endpoint *ep ) -{ - int ret=0; - - if ( ep->bufWrPtr > 0 ) { - ret = usb_write( ep, ep->buf, ep->bufWrPtr ); - ep->bufWrPtr = 0; - } - - return ret; -} - - -/* ------------------------------------------------------------------------- */ - - -/** Given a MIDI Event, determine size of data to be attached to - * USB-MIDI packet. - * Returns 1, 2 or 3. - * Called by midiWrite(); - * Uses remains_80e0 and remains_f0f6; - **/ -static int get_remains(int event) -{ - int ret; - - if ( event < 0x80 ) { - ret = 1; - } else if ( event < 0xf0 ) { - ret = remains_80e0[((event-0x80)>>4)&0x0f]; - } else if ( event < 0xf7 ) { - ret = remains_f0f6[event-0xf0]; - } else { - ret = 1; - } - - return ret; -} - -/** Given the output MIDI data in the output buffer, computes a reasonable - * CIN. - * Called by putOneMidiEvent(). - **/ -static int get_CIN( struct usb_mididev *m ) -{ - int cin; - - if ( m->mout.buf[0] == 0xf7 ) { - cin = 5; - } - else if ( m->mout.buf[1] == 0xf7 ) { - cin = 6; - } - else if ( m->mout.buf[2] == 0xf7 ) { - cin = 7; - } - else { - if ( m->mout.isInExclusive == 1 ) { - cin = 4; - } else if ( m->mout.buf[0] < 0x80 ) { - /** One byte that we know nothing about. **/ - cin = 0xF; - } else if ( m->mout.buf[0] < 0xf0 ) { - /** MIDI Voice messages 0x8X to 0xEX map to cin 0x8 to 0xE. **/ - cin = (m->mout.buf[0]>>4)&0x0f; - } - else { - /** Special lookup table exists for real-time events. **/ - cin = cin_f0ff[m->mout.buf[0]-0xf0]; - } - } - - return cin; -} - - -/* ------------------------------------------------------------------------- */ - - - -/** Move data to USB endpoint buffer. - * - **/ -static int put_one_midi_event(struct usb_mididev *m) -{ - int cin; - unsigned long flags; - struct midi_out_endpoint *ep = m->mout.ep; - int ret=0; - - cin = get_CIN( m ); - if ( cin > 0x0f || cin < 0 ) { - return -EINVAL; - } - - spin_lock_irqsave( &ep->lock, flags ); - ep->buf[ep->bufWrPtr++] = (m->mout.cableId<<4) | cin; - ep->buf[ep->bufWrPtr++] = m->mout.buf[0]; - ep->buf[ep->bufWrPtr++] = m->mout.buf[1]; - ep->buf[ep->bufWrPtr++] = m->mout.buf[2]; - if ( ep->bufWrPtr >= ep->bufSize ) { - ret = flush_midi_buffer( ep ); - } - spin_unlock_irqrestore( &ep->lock, flags); - - m->mout.buf[0] = m->mout.buf[1] = m->mout.buf[2] = 0; - m->mout.bufPtr = 0; - - return ret; -} - -/** Write the MIDI message v on the midi device. - * Called by usb_midi_write(); - * Responsible for packaging a MIDI data stream into USB-MIDI packets. - **/ - -static int midi_write( struct usb_mididev *m, int v ) -{ - unsigned long flags; - struct midi_out_endpoint *ep = m->mout.ep; - int ret=0; - unsigned char c = (unsigned char)v; - unsigned char sysrt_buf[4]; - - if ( m->singlebyte != 0 ) { - /** Simple code to handle the single-byte USB-MIDI protocol. */ - spin_lock_irqsave( &ep->lock, flags ); - if ( ep->bufWrPtr+4 > ep->bufSize ) { - ret = flush_midi_buffer( ep ); - if ( !ret ) { - spin_unlock_irqrestore( &ep->lock, flags ); - return ret; - } - } - ep->buf[ep->bufWrPtr++] = (m->mout.cableId<<4) | 0x0f; /* single byte */ - ep->buf[ep->bufWrPtr++] = c; - ep->buf[ep->bufWrPtr++] = 0; - ep->buf[ep->bufWrPtr++] = 0; - if ( ep->bufWrPtr >= ep->bufSize ) { - ret = flush_midi_buffer( ep ); - } - spin_unlock_irqrestore( &ep->lock, flags ); - - return ret; - } - /** Normal USB-MIDI protocol begins here. */ - - if ( c > 0xf7 ) { /* system: Realtime messages */ - /** Realtime messages are written IMMEDIATELY. */ - sysrt_buf[0] = (m->mout.cableId<<4) | 0x0f; - sysrt_buf[1] = c; - sysrt_buf[2] = 0; - sysrt_buf[3] = 0; - spin_lock_irqsave( &ep->lock, flags ); - ret = usb_write( ep, sysrt_buf, 4 ); - spin_unlock_irqrestore( &ep->lock, flags ); - /* m->mout.lastEvent = 0; */ - - return ret; - } - - if ( c >= 0x80 ) { - if ( c < 0xf0 ) { - m->mout.lastEvent = c; - m->mout.isInExclusive = 0; - m->mout.bufRemains = get_remains(c); - } else if ( c == 0xf0 ) { - /* m->mout.lastEvent = 0; */ - m->mout.isInExclusive = 1; - m->mout.bufRemains = get_remains(c); - } else if ( c == 0xf7 && m->mout.isInExclusive == 1 ) { - /* m->mout.lastEvent = 0; */ - m->mout.isInExclusive = 0; - m->mout.bufRemains = 1; - } else if ( c > 0xf0 ) { - /* m->mout.lastEvent = 0; */ - m->mout.isInExclusive = 0; - m->mout.bufRemains = get_remains(c); - } - - } else if ( m->mout.bufRemains == 0 && m->mout.isInExclusive == 0 ) { - if ( m->mout.lastEvent == 0 ) { - return 0; /* discard, waiting for the first event */ - } - /** track status **/ - m->mout.buf[0] = m->mout.lastEvent; - m->mout.bufPtr = 1; - m->mout.bufRemains = get_remains(m->mout.lastEvent)-1; - } - - m->mout.buf[m->mout.bufPtr++] = c; - m->mout.bufRemains--; - if ( m->mout.bufRemains == 0 || m->mout.bufPtr >= 3) { - ret = put_one_midi_event(m); - } - - return ret; -} - - -/* ------------------------------------------------------------------------- */ - -/** Basic operation on /dev/midiXX as registered through struct file_operations. - * - * Basic contract: Used to change the current read/write position in a file. - * On success, the non-negative position is reported. - * On failure, the negative of an error code is reported. - * - * Because a MIDIStream is not a file, all seek operations are doomed to fail. - * - **/ -static loff_t usb_midi_llseek(struct file *file, loff_t offset, int origin) -{ - /** Tell user you cannot seek on a PIPE-like device. **/ - return -ESPIPE; -} - - -/** Basic operation on /dev/midiXX as registered through struct file_operations. - * - * Basic contract: Block until count bytes have been read or an error occurs. - * - **/ - -static ssize_t usb_midi_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) -{ - struct usb_mididev *m = (struct usb_mididev *)file->private_data; - struct midi_in_endpoint *ep = m->min.ep; - ssize_t ret; - DECLARE_WAITQUEUE(wait, current); - - if ( !access_ok(VERIFY_READ, buffer, count) ) { - return -EFAULT; - } - if ( count == 0 ) { - return 0; - } - - add_wait_queue( &ep->wait, &wait ); - ret = 0; - while( count > 0 ) { - int cnt; - int d = (int)count; - - cnt = m->min.bufRemains; - if ( cnt > d ) { - cnt = d; - } - - if ( cnt <= 0 ) { - if ( file->f_flags & O_NONBLOCK ) { - if (!ret) - ret = -EAGAIN; - break; - } - __set_current_state(TASK_INTERRUPTIBLE); - schedule(); - if (signal_pending(current)) { - if(!ret) - ret=-ERESTARTSYS; - break; - } - continue; - } - - { - int i; - unsigned long flags; /* used to synchronize access to the endpoint */ - spin_lock_irqsave( &ep->lock, flags ); - for (i = 0; i < cnt; i++) { - if ( copy_to_user( buffer+i, m->min.buf+m->min.bufRdPtr, 1 ) ) { - if ( !ret ) - ret = -EFAULT; - break; - } - m->min.bufRdPtr = (m->min.bufRdPtr+1)%MIDI_IN_BUFSIZ; - m->min.bufRemains -= 1; - } - spin_unlock_irqrestore( &ep->lock, flags ); - } - - count-=cnt; - buffer+=cnt; - ret+=cnt; - - break; - } - - remove_wait_queue( &ep->wait, &wait ); - set_current_state(TASK_RUNNING); - - return ret; -} - - -/** Basic operation on /dev/midiXX as registered through struct file_operations. - * - * Basic Contract: Take MIDI data byte-by-byte and pass it to - * writeMidi() which packages MIDI data into USB-MIDI stream. - * Then flushMidiData() is called to ensure all bytes have been written - * in a timely fashion. - * - **/ - -static ssize_t usb_midi_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) -{ - struct usb_mididev *m = (struct usb_mididev *)file->private_data; - ssize_t ret; - unsigned long int flags; - - if ( !access_ok(VERIFY_READ, buffer, count) ) { - return -EFAULT; - } - if ( count == 0 ) { - return 0; - } - - ret = 0; - while( count > 0 ) { - unsigned char c; - - if (copy_from_user((unsigned char *)&c, buffer, 1)) { - if ( ret == 0 ) - ret = -EFAULT; - break; - } - if( midi_write(m, (int)c) ) { - if ( ret == 0 ) - ret = -EFAULT; - break; - } - count--; - buffer++; - ret++; - } - - spin_lock_irqsave( &m->mout.ep->lock, flags ); - if ( flush_midi_buffer(m->mout.ep) < 0 ) { - ret = -EFAULT; - } - spin_unlock_irqrestore( &m->mout.ep->lock, flags ); - - return ret; -} - -/** Basic operation on /dev/midiXX as registered through struct file_operations. - * - * Basic contract: Wait (spin) until ready to read or write on the file. - * - **/ -static unsigned int usb_midi_poll(struct file *file, struct poll_table_struct *wait) -{ - struct usb_mididev *m = (struct usb_mididev *)file->private_data; - struct midi_in_endpoint *iep = m->min.ep; - struct midi_out_endpoint *oep = m->mout.ep; - unsigned long flags; - unsigned int mask = 0; - - if ( file->f_mode & FMODE_READ ) { - poll_wait( file, &iep->wait, wait ); - spin_lock_irqsave( &iep->lock, flags ); - if ( m->min.bufRemains > 0 ) - mask |= POLLIN | POLLRDNORM; - spin_unlock_irqrestore( &iep->lock, flags ); - } - - if ( file->f_mode & FMODE_WRITE ) { - poll_wait( file, &oep->wait, wait ); - spin_lock_irqsave( &oep->lock, flags ); - if ( oep->bufWrPtr < oep->bufSize ) - mask |= POLLOUT | POLLWRNORM; - spin_unlock_irqrestore( &oep->lock, flags ); - } - - return mask; -} - - -/** Basic operation on /dev/midiXX as registered through struct file_operations. - * - * Basic contract: This is always the first operation performed on the - * device node. If no method is defined, the open succeeds without any - * notification given to the module. - * - **/ - -static int usb_midi_open(struct inode *inode, struct file *file) -{ - int minor = iminor(inode); - DECLARE_WAITQUEUE(wait, current); - struct usb_midi_state *s; - struct usb_mididev *m; - unsigned long flags; - int succeed = 0; - -#if 0 - printk(KERN_INFO "usb-midi: Open minor= %d.\n", minor); -#endif - - for(;;) { - down(&open_sem); - list_for_each_entry(s, &mididevs, mididev) { - list_for_each_entry(m, &s->midiDevList, list) { - if ( !((m->dev_midi ^ minor) & ~0xf) ) - goto device_found; - } - } - up(&open_sem); - return -ENODEV; - - device_found: - if ( !s->usbdev ) { - up(&open_sem); - return -EIO; - } - if ( !(m->open_mode & file->f_mode) ) { - break; - } - if ( file->f_flags & O_NONBLOCK ) { - up(&open_sem); - return -EBUSY; - } - __set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue( &open_wait, &wait ); - up(&open_sem); - schedule(); - remove_wait_queue( &open_wait, &wait ); - if ( signal_pending(current) ) { - return -ERESTARTSYS; - } - } - - file->private_data = m; - spin_lock_irqsave( &s->lock, flags ); - - if ( !(m->open_mode & (FMODE_READ | FMODE_WRITE)) ) { - //FIXME: intented semantics unclear here - m->min.bufRdPtr = 0; - m->min.bufWrPtr = 0; - m->min.bufRemains = 0; - spin_lock_init(&m->min.ep->lock); - - m->mout.bufPtr = 0; - m->mout.bufRemains = 0; - m->mout.isInExclusive = 0; - m->mout.lastEvent = 0; - spin_lock_init(&m->mout.ep->lock); - } - - if ( (file->f_mode & FMODE_READ) && m->min.ep != NULL ) { - unsigned long int flagsep; - spin_lock_irqsave( &m->min.ep->lock, flagsep ); - m->min.ep->cables[m->min.cableId] = m; - m->min.ep->readers += 1; - m->min.bufRdPtr = 0; - m->min.bufWrPtr = 0; - m->min.bufRemains = 0; - spin_unlock_irqrestore( &m->min.ep->lock, flagsep ); - - if ( !(m->min.ep->urbSubmitted)) { - - /* urb->dev must be reinitialized on 2.4.x kernels */ - m->min.ep->urb->dev = m->min.ep->usbdev; - - if ( usb_submit_urb(m->min.ep->urb, GFP_ATOMIC) ) { - printk(KERN_ERR "usbmidi: Cannot submit urb for MIDI-IN\n"); - } - m->min.ep->urbSubmitted = 1; - } - m->open_mode |= FMODE_READ; - succeed = 1; - } - - if ( (file->f_mode & FMODE_WRITE) && m->mout.ep != NULL ) { - m->mout.bufPtr = 0; - m->mout.bufRemains = 0; - m->mout.isInExclusive = 0; - m->mout.lastEvent = 0; - m->open_mode |= FMODE_WRITE; - succeed = 1; - } - - spin_unlock_irqrestore( &s->lock, flags ); - - s->count++; - up(&open_sem); - - /** Changed to prevent extra increments to USE_COUNT. **/ - if (!succeed) { - return -EBUSY; - } - -#if 0 - printk(KERN_INFO "usb-midi: Open Succeeded. minor= %d.\n", minor); -#endif - - return nonseekable_open(inode, file); /** Success. **/ -} - - -/** Basic operation on /dev/midiXX as registered through struct file_operations. - * - * Basic contract: Close an opened file and deallocate anything we allocated. - * Like open(), this can be missing. If open set file->private_data, - * release() must clear it. - * - **/ - -static int usb_midi_release(struct inode *inode, struct file *file) -{ - struct usb_mididev *m = (struct usb_mididev *)file->private_data; - struct usb_midi_state *s = (struct usb_midi_state *)m->midi; - -#if 0 - printk(KERN_INFO "usb-midi: Close.\n"); -#endif - - down(&open_sem); - - if ( m->open_mode & FMODE_WRITE ) { - m->open_mode &= ~FMODE_WRITE; - usb_kill_urb( m->mout.ep->urb ); - } - - if ( m->open_mode & FMODE_READ ) { - unsigned long int flagsep; - spin_lock_irqsave( &m->min.ep->lock, flagsep ); - m->min.ep->cables[m->min.cableId] = NULL; // discard cable - m->min.ep->readers -= 1; - m->open_mode &= ~FMODE_READ; - if ( m->min.ep->readers == 0 && - m->min.ep->urbSubmitted ) { - m->min.ep->urbSubmitted = 0; - usb_kill_urb(m->min.ep->urb); - } - spin_unlock_irqrestore( &m->min.ep->lock, flagsep ); - } - - s->count--; - - up(&open_sem); - wake_up(&open_wait); - - file->private_data = NULL; - return 0; -} - -static struct file_operations usb_midi_fops = { - .owner = THIS_MODULE, - .llseek = usb_midi_llseek, - .read = usb_midi_read, - .write = usb_midi_write, - .poll = usb_midi_poll, - .open = usb_midi_open, - .release = usb_midi_release, -}; - -/* ------------------------------------------------------------------------- */ - -/** Returns filled midi_in_endpoint structure or null on failure. - * - * Parameters: - * d - a usb_device - * endPoint - An usb endpoint in the range 0 to 15. - * Called by allocUsbMidiDev(); - * - **/ - -static struct midi_in_endpoint *alloc_midi_in_endpoint( struct usb_device *d, int endPoint ) -{ - struct midi_in_endpoint *ep; - int bufSize; - int pipe; - - endPoint &= 0x0f; /* Silently force endPoint to lie in range 0 to 15. */ - - pipe = usb_rcvbulkpipe( d, endPoint ); - bufSize = usb_maxpacket( d, pipe, 0 ); - /* usb_pipein() = ! usb_pipeout() = true for an in Endpoint */ - - ep = (struct midi_in_endpoint *)kmalloc(sizeof(struct midi_in_endpoint), GFP_KERNEL); - if ( !ep ) { - printk(KERN_ERR "usbmidi: no memory for midi in-endpoint\n"); - return NULL; - } - memset( ep, 0, sizeof(struct midi_in_endpoint) ); -// this sets cables[] and readers to 0, too. -// for (i=0; i<16; i++) ep->cables[i] = 0; // discard cable -// ep->readers = 0; - - ep->endpoint = endPoint; - - ep->recvBuf = (unsigned char *)kmalloc(sizeof(unsigned char)*(bufSize), GFP_KERNEL); - if ( !ep->recvBuf ) { - printk(KERN_ERR "usbmidi: no memory for midi in-endpoint buffer\n"); - kfree(ep); - return NULL; - } - - ep->urb = usb_alloc_urb(0, GFP_KERNEL); /* no ISO */ - if ( !ep->urb ) { - printk(KERN_ERR "usbmidi: no memory for midi in-endpoint urb\n"); - kfree(ep->recvBuf); - kfree(ep); - return NULL; - } - usb_fill_bulk_urb( ep->urb, d, - usb_rcvbulkpipe(d, endPoint), - (unsigned char *)ep->recvBuf, bufSize, - usb_bulk_read, ep ); - - /* ep->bufRdPtr = 0; */ - /* ep->bufWrPtr = 0; */ - /* ep->bufRemains = 0; */ - /* ep->urbSubmitted = 0; */ - ep->recvBufSize = bufSize; - - init_waitqueue_head(&ep->wait); - - return ep; -} - -static int remove_midi_in_endpoint( struct midi_in_endpoint *min ) -{ - usb_kill_urb( min->urb ); - usb_free_urb( min->urb ); - kfree( min->recvBuf ); - kfree( min ); - - return 0; -} - -/** Returns filled midi_out_endpoint structure or null on failure. - * - * Parameters: - * d - a usb_device - * endPoint - An usb endpoint in the range 0 to 15. - * Called by allocUsbMidiDev(); - * - **/ -static struct midi_out_endpoint *alloc_midi_out_endpoint( struct usb_device *d, int endPoint ) -{ - struct midi_out_endpoint *ep = NULL; - int pipe; - int bufSize; - - endPoint &= 0x0f; - pipe = usb_sndbulkpipe( d, endPoint ); - bufSize = usb_maxpacket( d, pipe, 1 ); - - ep = (struct midi_out_endpoint *)kmalloc(sizeof(struct midi_out_endpoint), GFP_KERNEL); - if ( !ep ) { - printk(KERN_ERR "usbmidi: no memory for midi out-endpoint\n"); - return NULL; - } - memset( ep, 0, sizeof(struct midi_out_endpoint) ); - - ep->endpoint = endPoint; - ep->buf = (unsigned char *)kmalloc(sizeof(unsigned char)*bufSize, GFP_KERNEL); - if ( !ep->buf ) { - printk(KERN_ERR "usbmidi: no memory for midi out-endpoint buffer\n"); - kfree(ep); - return NULL; - } - - ep->urb = usb_alloc_urb(0, GFP_KERNEL); /* no ISO */ - if ( !ep->urb ) { - printk(KERN_ERR "usbmidi: no memory for midi out-endpoint urb\n"); - kfree(ep->buf); - kfree(ep); - return NULL; - } - - ep->bufSize = bufSize; - /* ep->bufWrPtr = 0; */ - - init_waitqueue_head(&ep->wait); - - return ep; -} - - -static int remove_midi_out_endpoint( struct midi_out_endpoint *mout ) -{ - usb_kill_urb( mout->urb ); - usb_free_urb( mout->urb ); - kfree( mout->buf ); - kfree( mout ); - - return 0; -} - - -/** Returns a filled usb_mididev structure, registered as a Linux MIDI device. - * - * Returns null if memory is not available or the device cannot be registered. - * Called by allocUsbMidiDev(); - * - **/ -static struct usb_mididev *allocMidiDev( - struct usb_midi_state *s, - struct midi_in_endpoint *min, - struct midi_out_endpoint *mout, - int inCableId, - int outCableId ) -{ - struct usb_mididev *m; - - m = (struct usb_mididev *)kmalloc(sizeof(struct usb_mididev), GFP_KERNEL); - if (!m) { - printk(KERN_ERR "usbmidi: no memory for midi device\n"); - return NULL; - } - - memset(m, 0, sizeof(struct usb_mididev)); - - if ((m->dev_midi = register_sound_midi(&usb_midi_fops, -1)) < 0) { - printk(KERN_ERR "usbmidi: cannot register midi device\n"); - kfree(m); - return NULL; - } - - m->midi = s; - /* m->open_mode = 0; */ - - if ( min ) { - m->min.ep = min; - m->min.ep->usbdev = s->usbdev; - m->min.cableId = inCableId; - } - /* m->min.bufPtr = 0; */ - /* m->min.bufRemains = 0; */ - - if ( mout ) { - m->mout.ep = mout; - m->mout.ep->usbdev = s->usbdev; - m->mout.cableId = outCableId; - } - /* m->mout.bufPtr = 0; */ - /* m->mout.bufRemains = 0; */ - /* m->mout.isInExclusive = 0; */ - /* m->mout.lastEvent = 0; */ - - m->singlebyte = singlebyte; - - return m; -} - - -static void release_midi_device( struct usb_midi_state *s ) -{ - struct usb_mididev *m; - struct midi_in_endpoint *min; - struct midi_out_endpoint *mout; - - if ( s->count > 0 ) { - up(&open_sem); - return; - } - up( &open_sem ); - wake_up( &open_wait ); - - while(!list_empty(&s->inEndpointList)) { - min = list_entry(s->inEndpointList.next, struct midi_in_endpoint, list); - list_del(&min->list); - remove_midi_in_endpoint(min); - } - - while(!list_empty(&s->outEndpointList)) { - mout = list_entry(s->outEndpointList.next, struct midi_out_endpoint, list); - list_del(&mout->list); - remove_midi_out_endpoint(mout); - } - - while(!list_empty(&s->midiDevList)) { - m = list_entry(s->midiDevList.next, struct usb_mididev, list); - list_del(&m->list); - kfree(m); - } - - kfree(s); - - return; -} - - -/* ------------------------------------------------------------------------- */ - -/** Utility routine to find a descriptor in a dump of many descriptors. - * Returns start of descriptor or NULL if not found. - * descStart pointer to list of interfaces. - * descLength length (in bytes) of dump - * after (ignored if NULL) this routine returns only descriptors after "after" - * dtype (mandatory) The descriptor type. - * iface (ignored if -1) returns descriptor at/following given interface - * altSetting (ignored if -1) returns descriptor at/following given altSetting - * - * - * Called by parseDescriptor(), find_csinterface_descriptor(); - * - */ -static void *find_descriptor( void *descStart, unsigned int descLength, void *after, unsigned char dtype, int iface, int altSetting ) -{ - unsigned char *p, *end, *next; - int interfaceNumber = -1, altSet = -1; - - p = descStart; - end = p + descLength; - for( ; p < end; ) { - if ( p[0] < 2 ) - return NULL; - next = p + p[0]; - if ( next > end ) - return NULL; - if ( p[1] == USB_DT_INTERFACE ) { - if ( p[0] < USB_DT_INTERFACE_SIZE ) - return NULL; - interfaceNumber = p[2]; - altSet = p[3]; - } - if ( p[1] == dtype && - ( !after || ( p > (unsigned char *)after) ) && - ( ( iface == -1) || (iface == interfaceNumber) ) && - ( (altSetting == -1) || (altSetting == altSet) )) { - return p; - } - p = next; - } - return NULL; -} - -/** Utility to find a class-specific interface descriptor. - * dsubtype is a descriptor subtype - * Called by parseDescriptor(); - **/ -static void *find_csinterface_descriptor(void *descStart, unsigned int descLength, void *after, u8 dsubtype, int iface, int altSetting) -{ - unsigned char *p; - - p = find_descriptor( descStart, descLength, after, USB_DT_CS_INTERFACE, iface, altSetting ); - while ( p ) { - if ( p[0] >= 3 && p[2] == dsubtype ) - return p; - p = find_descriptor( descStart, descLength, p, USB_DT_CS_INTERFACE, - iface, altSetting ); - } - return NULL; -} - - -/** The magic of making a new usb_midi_device from config happens here. - * - * The caller is responsible for free-ing this return value (if not NULL). - * - **/ -static struct usb_midi_device *parse_descriptor( struct usb_device *d, unsigned char *buffer, int bufSize, unsigned int ifnum , unsigned int altSetting, int quirks) -{ - struct usb_midi_device *u; - unsigned char *p1; - unsigned char *p2; - unsigned char *next; - int iep, oep; - int length; - unsigned long longBits; - int pins, nbytes, offset, shift, jack; -#ifdef HAVE_JACK_STRINGS - /** Jacks can have associated names. **/ - unsigned char jack2string[256]; -#endif - - u = NULL; - /* find audiocontrol interface */ - p1 = find_csinterface_descriptor( buffer, bufSize, NULL, - MS_HEADER, ifnum, altSetting); - - if ( !p1 ) { - goto error_end; - } - - if ( p1[0] < MS_HEADER_LENGTH ) { - goto error_end; - } - - /* Assume success. Since the device corresponds to USB-MIDI spec, we assume - that the rest of the USB 2.0 spec is obeyed. */ - - u = (struct usb_midi_device *)kmalloc( sizeof(struct usb_midi_device), GFP_KERNEL ); - if ( !u ) { - return NULL; - } - u->deviceName = NULL; - u->idVendor = le16_to_cpu(d->descriptor.idVendor); - u->idProduct = le16_to_cpu(d->descriptor.idProduct); - u->interface = ifnum; - u->altSetting = altSetting; - u->in[0].endpoint = -1; - u->in[0].cableId = -1; - u->out[0].endpoint = -1; - u->out[0].cableId = -1; - - - printk(KERN_INFO "usb-midi: Found MIDIStreaming device corresponding to Release %d.%02d of spec.\n", - (p1[4] >> 4) * 10 + (p1[4] & 0x0f ), - (p1[3] >> 4) * 10 + (p1[3] & 0x0f ) - ); - - length = p1[5] | (p1[6] << 8); - -#ifdef HAVE_JACK_STRINGS - memset(jack2string, 0, sizeof(unsigned char) * 256); -#endif - - length -= p1[0]; - for (p2 = p1 + p1[0]; length > 0; p2 = next) { - next = p2 + p2[0]; - length -= p2[0]; - - if (p2[0] < 2 ) - break; - if (p2[1] != USB_DT_CS_INTERFACE) - break; - if (p2[2] == MIDI_IN_JACK && p2[0] >= 6 ) { - jack = p2[4]; -#ifdef HAVE_JACK_STRINGS - jack2string[jack] = p2[5]; -#endif - printk(KERN_INFO "usb-midi: Found IN Jack 0x%02x %s\n", - jack, (p2[3] == EMBEDDED_JACK)?"EMBEDDED":"EXTERNAL" ); - } else if ( p2[2] == MIDI_OUT_JACK && p2[0] >= 6) { - pins = p2[5]; - if ( p2[0] < (6 + 2 * pins) ) - continue; - jack = p2[4]; -#ifdef HAVE_JACK_STRINGS - jack2string[jack] = p2[5 + 2 * pins]; -#endif - printk(KERN_INFO "usb-midi: Found OUT Jack 0x%02x %s, %d pins\n", - jack, (p2[3] == EMBEDDED_JACK)?"EMBEDDED":"EXTERNAL", pins ); - } else if ( p2[2] == ELEMENT_DESCRIPTOR && p2[0] >= 10) { - pins = p2[4]; - if ( p2[0] < (9 + 2 * pins ) ) - continue; - nbytes = p2[8 + 2 * pins ]; - if ( p2[0] < (10 + 2 * pins + nbytes) ) - continue; - longBits = 0L; - for ( offset = 0, shift = 0; offset < nbytes && offset < 8; offset ++, shift += 8) { - longBits |= ((long)(p2[9 + 2 * pins + offset])) << shift; - } - jack = p2[3]; -#ifdef HAVE_JACK_STRINGS - jack2string[jack] = p2[9 + 2 * pins + nbytes]; -#endif - printk(KERN_INFO "usb-midi: Found ELEMENT 0x%02x, %d/%d pins in/out, bits: 0x%016lx\n", - jack, pins, (int)(p2[5 + 2 * pins]), (long)longBits ); - } else { - } - } - - iep=0; - oep=0; - - if (quirks==0) { - /* MIDISTREAM */ - p2 = NULL; - for (p1 = find_descriptor(buffer, bufSize, NULL, USB_DT_ENDPOINT, - ifnum, altSetting ); p1; p1 = next ) { - next = find_descriptor(buffer, bufSize, p1, USB_DT_ENDPOINT, - ifnum, altSetting ); - p2 = find_descriptor(buffer, bufSize, p1, USB_DT_CS_ENDPOINT, - ifnum, altSetting ); - - if ( p2 && next && ( p2 > next ) ) - p2 = NULL; - - if ( p1[0] < 9 || !p2 || p2[0] < 4 ) - continue; - - if ( (p1[2] & 0x80) == 0x80 ) { - if ( iep < 15 ) { - pins = p2[3]; /* not pins -- actually "cables" */ - if ( pins > 16 ) - pins = 16; - u->in[iep].endpoint = p1[2]; - u->in[iep].cableId = ( 1 << pins ) - 1; - if ( u->in[iep].cableId ) - iep ++; - if ( iep < 15 ) { - u->in[iep].endpoint = -1; - u->in[iep].cableId = -1; - } - } - } else { - if ( oep < 15 ) { - pins = p2[3]; /* not pins -- actually "cables" */ - if ( pins > 16 ) - pins = 16; - u->out[oep].endpoint = p1[2]; - u->out[oep].cableId = ( 1 << pins ) - 1; - if ( u->out[oep].cableId ) - oep ++; - if ( oep < 15 ) { - u->out[oep].endpoint = -1; - u->out[oep].cableId = -1; - } - } - } - - } - } else if (quirks==1) { - /* YAMAHA quirks */ - for (p1 = find_descriptor(buffer, bufSize, NULL, USB_DT_ENDPOINT, - ifnum, altSetting ); p1; p1 = next ) { - next = find_descriptor(buffer, bufSize, p1, USB_DT_ENDPOINT, - ifnum, altSetting ); - - if ( p1[0] < 7 ) - continue; - - if ( (p1[2] & 0x80) == 0x80 ) { - if ( iep < 15 ) { - pins = iep+1; - if ( pins > 16 ) - pins = 16; - u->in[iep].endpoint = p1[2]; - u->in[iep].cableId = ( 1 << pins ) - 1; - if ( u->in[iep].cableId ) - iep ++; - if ( iep < 15 ) { - u->in[iep].endpoint = -1; - u->in[iep].cableId = -1; - } - } - } else { - if ( oep < 15 ) { - pins = oep+1; - u->out[oep].endpoint = p1[2]; - u->out[oep].cableId = ( 1 << pins ) - 1; - if ( u->out[oep].cableId ) - oep ++; - if ( oep < 15 ) { - u->out[oep].endpoint = -1; - u->out[oep].cableId = -1; - } - } - } - - } - } - - if ( !iep && ! oep ) { - goto error_end; - } - - return u; - -error_end: - kfree(u); - return NULL; -} - -/* ------------------------------------------------------------------------- */ - -/** Returns number between 0 and 16. - * - **/ -static int on_bits( unsigned short v ) -{ - int i; - int ret=0; - - for ( i=0 ; i<16 ; i++ ) { - if ( v & (1<num_altsetting; - - for ( alt=0 ; altaltsetting[alt]; - epin = -1; - epout = -1; - - for ( i=0 ; idesc.bNumEndpoints ; i++ ) { - ep = &interface->endpoint[i].desc; - if ( (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK ) { - continue; - } - if ( (ep->bEndpointAddress & USB_DIR_IN) && epin < 0 ) { - epin = i; - } else if ( epout < 0 ) { - epout = i; - } - if ( epin >= 0 && epout >= 0 ) { - return interface->desc.bAlternateSetting; - } - } - } - - return -ENODEV; -} - - -/* ------------------------------------------------------------------------- */ - - -/** Returns 0 if successful in allocating and registering internal structures. - * Returns negative on failure. - * Calls allocMidiDev which additionally registers /dev/midiXX devices. - * Writes messages on success to indicate which /dev/midiXX is which physical - * endpoint. - * - **/ -static int alloc_usb_midi_device( struct usb_device *d, struct usb_midi_state *s, struct usb_midi_device *u ) -{ - struct usb_mididev **mdevs=NULL; - struct midi_in_endpoint *mins[15], *min; - struct midi_out_endpoint *mouts[15], *mout; - int inDevs=0, outDevs=0; - int inEndpoints=0, outEndpoints=0; - int inEndpoint, outEndpoint; - int inCableId, outCableId; - int i; - int devices = 0; - int alt = 0; - - /* Obtain altSetting or die.. */ - alt = u->altSetting; - if ( alt < 0 ) { - alt = get_alt_setting( d, u->interface ); - } - if ( alt < 0 ) - return -ENXIO; - - /* Configure interface */ - if ( usb_set_interface( d, u->interface, alt ) < 0 ) { - return -ENXIO; - } - - for ( i = 0 ; i < 15 ; i++ ) { - mins[i] = NULL; - mouts[i] = NULL; - } - - /* Begin Allocation */ - while( inEndpoints < 15 - && inDevs < maxdevices - && u->in[inEndpoints].cableId >= 0 ) { - inDevs += on_bits((unsigned short)u->in[inEndpoints].cableId); - mins[inEndpoints] = alloc_midi_in_endpoint( d, u->in[inEndpoints].endpoint ); - if ( mins[inEndpoints] == NULL ) - goto error_end; - inEndpoints++; - } - - while( outEndpoints < 15 - && outDevs < maxdevices - && u->out[outEndpoints].cableId >= 0 ) { - outDevs += on_bits((unsigned short)u->out[outEndpoints].cableId); - mouts[outEndpoints] = alloc_midi_out_endpoint( d, u->out[outEndpoints].endpoint ); - if ( mouts[outEndpoints] == NULL ) - goto error_end; - outEndpoints++; - } - - devices = inDevs > outDevs ? inDevs : outDevs; - devices = maxdevices > devices ? devices : maxdevices; - - /* obtain space for device name (iProduct) if not known. */ - if ( ! u->deviceName ) { - mdevs = (struct usb_mididev **) - kmalloc(sizeof(struct usb_mididevs *)*devices - + sizeof(char) * 256, GFP_KERNEL); - } else { - mdevs = (struct usb_mididev **) - kmalloc(sizeof(struct usb_mididevs *)*devices, GFP_KERNEL); - } - - if ( !mdevs ) { - /* devices = 0; */ - /* mdevs = NULL; */ - goto error_end; - } - for ( i=0 ; ideviceName ) { - u->deviceName = (char *) (mdevs + devices); - if ( ! d->have_langid && d->descriptor.iProduct) { - alt = usb_get_string(d, 0, 0, u->deviceName, 250); - if (alt < 0) { - printk(KERN_INFO "error getting string descriptor 0 (error=%d)\n", alt); - } else if (u->deviceName[0] < 4) { - printk(KERN_INFO "string descriptor 0 too short (length = %d)\n", alt); - } else { - printk(KERN_INFO "string descriptor 0 found (length = %d)\n", alt); - for(; alt >= 4; alt -= 2) { - i = u->deviceName[alt-2] | (u->deviceName[alt-1]<< 8); - printk(KERN_INFO "usb-midi: langid(%d) 0x%04x\n", - (alt-4) >> 1, i); - if ( ( ( i ^ ulangid ) & 0xff ) == 0 ) { - d->have_langid = 1; - d->string_langid = i; - printk(KERN_INFO "usb-midi: langid(match) 0x%04x\n", i); - if ( i == ulangid ) - break; - } - } - } - } - u->deviceName[0] = (char) 0; - if (d->descriptor.iProduct) { - printk(KERN_INFO "usb-midi: fetchString(%d)\n", d->descriptor.iProduct); - alt = usb_string(d, d->descriptor.iProduct, u->deviceName, 255); - if( alt < 0 ) { - u->deviceName[0] = (char) 0; - } - printk(KERN_INFO "usb-midi: fetchString = %d\n", alt); - } - /* Failsafe */ - if ( !u->deviceName[0] ) { - if (le16_to_cpu(d->descriptor.idVendor) == USB_VENDOR_ID_ROLAND ) { - strcpy(u->deviceName, "Unknown Roland"); - } else if (le16_to_cpu(d->descriptor.idVendor) == USB_VENDOR_ID_STEINBERG ) { - strcpy(u->deviceName, "Unknown Steinberg"); - } else if (le16_to_cpu(d->descriptor.idVendor) == USB_VENDOR_ID_YAMAHA ) { - strcpy(u->deviceName, "Unknown Yamaha"); - } else { - strcpy(u->deviceName, "Unknown"); - } - } - } - - inEndpoint = 0; inCableId = -1; - outEndpoint = 0; outCableId = -1; - - for ( i=0 ; iin[inEndpoint].cableId & (1<= 16 ) { - inEndpoint ++; - inCableId = 0; - } - } - min = mins[inEndpoint]; - for ( outCableId ++ ; - outEndpoint <15 - && mouts[outEndpoint] - && !(u->out[outEndpoint].cableId & (1<= 16 ) { - outEndpoint ++; - outCableId = 0; - } - } - mout = mouts[outEndpoint]; - - mdevs[i] = allocMidiDev( s, min, mout, inCableId, outCableId ); - if ( mdevs[i] == NULL ) - goto error_end; - - } - - /* Success! */ - for ( i=0 ; ilist, &s->midiDevList ); - } - for ( i=0 ; ilist, &s->inEndpointList ); - } - for ( i=0 ; ilist, &s->outEndpointList ); - } - - printk(KERN_INFO "usbmidi: found [ %s ] (0x%04x:0x%04x), attached:\n", u->deviceName, u->idVendor, u->idProduct ); - for ( i=0 ; idev_midi-2)>>4; - if ( mdevs[i]->mout.ep != NULL && mdevs[i]->min.ep != NULL ) { - printk(KERN_INFO "usbmidi: /dev/midi%02d: in (ep:%02x cid:%2d bufsiz:%2d) out (ep:%02x cid:%2d bufsiz:%2d)\n", - dm, - mdevs[i]->min.ep->endpoint|USB_DIR_IN, mdevs[i]->min.cableId, mdevs[i]->min.ep->recvBufSize, - mdevs[i]->mout.ep->endpoint, mdevs[i]->mout.cableId, mdevs[i]->mout.ep->bufSize); - } else if ( mdevs[i]->min.ep != NULL ) { - printk(KERN_INFO "usbmidi: /dev/midi%02d: in (ep:%02x cid:%2d bufsiz:%02d)\n", - dm, - mdevs[i]->min.ep->endpoint|USB_DIR_IN, mdevs[i]->min.cableId, mdevs[i]->min.ep->recvBufSize); - } else if ( mdevs[i]->mout.ep != NULL ) { - printk(KERN_INFO "usbmidi: /dev/midi%02d: out (ep:%02x cid:%2d bufsiz:%02d)\n", - dm, - mdevs[i]->mout.ep->endpoint, mdevs[i]->mout.cableId, mdevs[i]->mout.ep->bufSize); - } - } - - kfree(mdevs); - return 0; - - error_end: - if ( mdevs != NULL ) { - for ( i=0 ; idev_midi ); - kfree(mdevs[i]); - } - } - kfree(mdevs); - } - - for ( i=0 ; i<15 ; i++ ) { - if ( mins[i] != NULL ) { - remove_midi_in_endpoint( mins[i] ); - } - if ( mouts[i] != NULL ) { - remove_midi_out_endpoint( mouts[i] ); - } - } - - return -ENOMEM; -} - -/* ------------------------------------------------------------------------- */ - -/** Attempt to scan YAMAHA's device descriptor and detect correct values of - * them. - * Return 0 on succes, negative on failure. - * Called by usb_midi_probe(); - **/ - -static int detect_yamaha_device( struct usb_device *d, - struct usb_interface *iface, unsigned int ifnum, - struct usb_midi_state *s) -{ - struct usb_host_interface *interface; - struct usb_midi_device *u; - unsigned char *buffer; - int bufSize; - int i; - int alts=-1; - int ret; - - if (le16_to_cpu(d->descriptor.idVendor) != USB_VENDOR_ID_YAMAHA) { - return -EINVAL; - } - - for ( i=0 ; i < iface->num_altsetting; i++ ) { - interface = iface->altsetting + i; - - if ( interface->desc.bInterfaceClass != 255 || - interface->desc.bInterfaceSubClass != 0 ) - continue; - alts = interface->desc.bAlternateSetting; - } - if ( alts == -1 ) { - return -EINVAL; - } - - printk(KERN_INFO "usb-midi: Found YAMAHA USB-MIDI device on dev %04x:%04x, iface %d\n", - le16_to_cpu(d->descriptor.idVendor), - le16_to_cpu(d->descriptor.idProduct), ifnum); - - i = d->actconfig - d->config; - buffer = d->rawdescriptors[i]; - bufSize = le16_to_cpu(d->actconfig->desc.wTotalLength); - - u = parse_descriptor( d, buffer, bufSize, ifnum, alts, 1); - if ( u == NULL ) { - return -EINVAL; - } - - ret = alloc_usb_midi_device( d, s, u ); - - kfree(u); - - return ret; -} - - -/** Scan table of known devices which are only partially compliant with - * the MIDIStreaming specification. - * Called by usb_midi_probe(); - * - **/ - -static int detect_vendor_specific_device( struct usb_device *d, unsigned int ifnum, struct usb_midi_state *s ) -{ - struct usb_midi_device *u; - int i; - int ret = -ENXIO; - - for ( i=0; idescriptor.idVendor) != u->idVendor || - le16_to_cpu(d->descriptor.idProduct) != u->idProduct || - ifnum != u->interface ) - continue; - - ret = alloc_usb_midi_device( d, s, u ); - break; - } - - return ret; -} - - -/** Attempt to match any config of an interface to a MIDISTREAMING interface. - * Returns 0 on success, negative on failure. - * Called by usb_midi_probe(); - **/ -static int detect_midi_subclass(struct usb_device *d, - struct usb_interface *iface, unsigned int ifnum, - struct usb_midi_state *s) -{ - struct usb_host_interface *interface; - struct usb_midi_device *u; - unsigned char *buffer; - int bufSize; - int i; - int alts=-1; - int ret; - - for ( i=0 ; i < iface->num_altsetting; i++ ) { - interface = iface->altsetting + i; - - if ( interface->desc.bInterfaceClass != USB_CLASS_AUDIO || - interface->desc.bInterfaceSubClass != USB_SUBCLASS_MIDISTREAMING ) - continue; - alts = interface->desc.bAlternateSetting; - } - if ( alts == -1 ) { - return -EINVAL; - } - - printk(KERN_INFO "usb-midi: Found MIDISTREAMING on dev %04x:%04x, iface %d\n", - le16_to_cpu(d->descriptor.idVendor), - le16_to_cpu(d->descriptor.idProduct), ifnum); - - - /* From USB Spec v2.0, Section 9.5. - If the class or vendor specific descriptors use the same format - as standard descriptors (e.g., start with a length byte and - followed by a type byte), they must be returned interleaved with - standard descriptors in the configuration information returned by - a GetDescriptor(Configuration) request. In this case, the class - or vendor-specific descriptors must follow a related standard - descriptor they modify or extend. - */ - - i = d->actconfig - d->config; - buffer = d->rawdescriptors[i]; - bufSize = le16_to_cpu(d->actconfig->desc.wTotalLength); - - u = parse_descriptor( d, buffer, bufSize, ifnum, alts, 0); - if ( u == NULL ) { - return -EINVAL; - } - - ret = alloc_usb_midi_device( d, s, u ); - - kfree(u); - - return ret; -} - - -/** When user has requested a specific device, match it exactly. - * - * Uses uvendor, uproduct, uinterface, ualt, umin, umout and ucable. - * Called by usb_midi_probe(); - * - **/ -static int detect_by_hand(struct usb_device *d, unsigned int ifnum, struct usb_midi_state *s) -{ - struct usb_midi_device u; - - if ( le16_to_cpu(d->descriptor.idVendor) != uvendor || - le16_to_cpu(d->descriptor.idProduct) != uproduct || - ifnum != uinterface ) { - return -EINVAL; - } - - if ( ualt < 0 ) - ualt = -1; - - if ( umin < 0 || umin > 15 ) - umin = 0x01 | USB_DIR_IN; - if ( umout < 0 || umout > 15 ) - umout = 0x01; - if ( ucable < 0 || ucable > 15 ) - ucable = 0; - - u.deviceName = NULL; /* A flag for alloc_usb_midi_device to get device - name from device. */ - u.idVendor = uvendor; - u.idProduct = uproduct; - u.interface = uinterface; - u.altSetting = ualt; - - u.in[0].endpoint = umin; - u.in[0].cableId = (1<cur_altsetting->desc.bInterfaceNumber; - - s = (struct usb_midi_state *)kmalloc(sizeof(struct usb_midi_state), GFP_KERNEL); - if ( !s ) - return -ENOMEM; - - memset( s, 0, sizeof(struct usb_midi_state) ); - INIT_LIST_HEAD(&s->midiDevList); - INIT_LIST_HEAD(&s->inEndpointList); - INIT_LIST_HEAD(&s->outEndpointList); - s->usbdev = dev; - s->count = 0; - spin_lock_init(&s->lock); - - if ( - detect_by_hand( dev, ifnum, s ) && - detect_midi_subclass( dev, intf, ifnum, s ) && - detect_vendor_specific_device( dev, ifnum, s ) && - detect_yamaha_device( dev, intf, ifnum, s) ) { - kfree(s); - return -EIO; - } - - down(&open_sem); - list_add_tail(&s->mididev, &mididevs); - up(&open_sem); - - usb_set_intfdata (intf, s); - return 0; -} - - -static void usb_midi_disconnect(struct usb_interface *intf) -{ - struct usb_midi_state *s = usb_get_intfdata (intf); - struct usb_mididev *m; - - if ( !s ) - return; - - if ( s == (struct usb_midi_state *)-1 ) { - return; - } - if ( !s->usbdev ) { - return; - } - down(&open_sem); - list_del(&s->mididev); - INIT_LIST_HEAD(&s->mididev); - s->usbdev = NULL; - usb_set_intfdata (intf, NULL); - - list_for_each_entry(m, &s->midiDevList, list) { - wake_up(&(m->min.ep->wait)); - wake_up(&(m->mout.ep->wait)); - if ( m->dev_midi >= 0 ) { - unregister_sound_midi(m->dev_midi); - } - m->dev_midi = -1; - } - release_midi_device(s); - wake_up(&open_wait); -} - -/* we want to look at all devices by hand */ -static struct usb_device_id id_table[] = { - {.driver_info = 42}, - {} -}; - -static struct usb_driver usb_midi_driver = { - .name = "midi", - .probe = usb_midi_probe, - .disconnect = usb_midi_disconnect, - .id_table = id_table, -}; - -/* ------------------------------------------------------------------------- */ - -static int __init usb_midi_init(void) -{ - return usb_register(&usb_midi_driver); -} - -static void __exit usb_midi_exit(void) -{ - usb_deregister(&usb_midi_driver); -} - -module_init(usb_midi_init) ; -module_exit(usb_midi_exit) ; - -#ifdef HAVE_ALSA_SUPPORT -#define SNDRV_MAIN_OBJECT_FILE -#include "../../include/driver.h" -#include "../../include/control.h" -#include "../../include/info.h" -#include "../../include/cs46xx.h" - -/* ------------------------------------------------------------------------- */ - -static int snd_usbmidi_input_close(snd_rawmidi_substream_t * substream) -{ - return 0; -} - -static int snd_usbmidi_input_open(snd_rawmidi_substream_t * substream ) -{ - return 0; -} - -static void snd_usbmidi_input_trigger(snd_rawmidi_substream_t * substream, int up) -{ - return 0; -} - - -/* ------------------------------------------------------------------------- */ - -static int snd_usbmidi_output_close(snd_rawmidi_substream_t * substream) -{ - return 0; -} - -static int snd_usbmidi_output_open(snd_rawmidi_substream_t * substream) -{ - return 0; -} - -static void snd_usb_midi_output_trigger(snd_rawmidi_substream_t * substream, - int up) -{ - return 0; -} - -/* ------------------------------------------------------------------------- */ - -static snd_rawmidi_ops_t snd_usbmidi_output = -{ - .open = snd_usbmidi_output_open, - .close = snd_usbmidi_output_close, - .trigger = snd_usbmidi_output_trigger, -}; -static snd_rawmidi_ops_t snd_usbmidi_input = -{ - .open = snd_usbmidi_input_open, - .close = snd_usbmidi_input_close, - .trigger = snd_usbmidi_input_trigger, -}; - -int snd_usbmidi_midi(cs46xx_t *chip, int device, snd_rawmidi_t **rrawmidi) -{ - snd_rawmidi_t *rmidi; - int err; - - if (rrawmidi) - *rrawmidi = NULL; - if ((err = snd_rawmidi_new(chip->card, "USB-MIDI", device, 1, 1, &rmidi)) < 0) - return err; - strcpy(rmidi->name, "USB-MIDI"); - - snd_rawmidi_set_ops( rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_usbmidi_output ); - snd_rawmidi_set_ops( rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_usbmidi_input ); - - rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; - - rmidi->private_data = chip; - chip->rmidi = rmidi; - if (rrawmidi) - *rrawmidi = NULL; - - return 0; -} - -int snd_usbmidi_create( snd_card_t * card, - struct pci_dev * pci, - usbmidi_t ** rchip ) -{ - usbmidi_t *chip; - int err, idx; - snd_region_t *region; - static snd_device_opt_t ops = { - .dev_free = snd_usbmidi_dev_free, - }; - - *rchip = NULL; - chip = snd_magic_kcalloc( usbmidi_t, 0, GFP_KERNEL ); - if ( chip == NULL ) - return -ENOMEM; -} - -EXPORT_SYMBOL(snd_usbmidi_create); -EXPORT_SYMBOL(snd_usbmidi_midi); -#endif /* HAVE_ALSA_SUPPORT */ - diff --git a/drivers/usb/class/usb-midi.h b/drivers/usb/class/usb-midi.h deleted file mode 100644 index 358cdef8492e..000000000000 --- a/drivers/usb/class/usb-midi.h +++ /dev/null @@ -1,164 +0,0 @@ -/* - usb-midi.h -- USB-MIDI driver - - Copyright (C) 2001 - NAGANO Daisuke - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* ------------------------------------------------------------------------- */ - -#ifndef _USB_MIDI_H_ -#define _USB_MIDI_H_ - -#ifndef USB_SUBCLASS_MIDISTREAMING -#define USB_SUBCLASS_MIDISTREAMING 3 -#endif - -/* ------------------------------------------------------------------------- */ -/* Roland MIDI Devices */ - -#define USB_VENDOR_ID_ROLAND 0x0582 -#define USBMIDI_ROLAND_UA100G 0x0000 -#define USBMIDI_ROLAND_MPU64 0x0002 -#define USBMIDI_ROLAND_SC8850 0x0003 -#define USBMIDI_ROLAND_SC8820 0x0007 -#define USBMIDI_ROLAND_UM2 0x0005 -#define USBMIDI_ROLAND_UM1 0x0009 -#define USBMIDI_ROLAND_PC300 0x0008 - -/* YAMAHA MIDI Devices */ -#define USB_VENDOR_ID_YAMAHA 0x0499 -#define USBMIDI_YAMAHA_MU1000 0x1001 - -/* Steinberg MIDI Devices */ -#define USB_VENDOR_ID_STEINBERG 0x0763 -#define USBMIDI_STEINBERG_USB2MIDI 0x1001 - -/* Mark of the Unicorn MIDI Devices */ -#define USB_VENDOR_ID_MOTU 0x07fd -#define USBMIDI_MOTU_FASTLANE 0x0001 - -/* ------------------------------------------------------------------------- */ -/* Supported devices */ - -struct usb_midi_endpoint { - int endpoint; - int cableId; /* if bit-n == 1 then cableId-n is enabled (n: 0 - 15) */ -}; - -struct usb_midi_device { - char *deviceName; - - u16 idVendor; - u16 idProduct; - int interface; - int altSetting; /* -1: auto detect */ - - struct usb_midi_endpoint in[15]; - struct usb_midi_endpoint out[15]; -}; - -static struct usb_midi_device usb_midi_devices[] = { - { /* Roland UM-1 */ - "Roland UM-1", - USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_UM1, 2, -1, - { { 0x81, 1 }, {-1, -1} }, - { { 0x01, 1,}, {-1, -1} }, - }, - - { /* Roland UM-2 */ - "Roland UM-2" , - USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_UM2, 2, -1, - { { 0x81, 3 }, {-1, -1} }, - { { 0x01, 3,}, {-1, -1} }, - }, - -/** Next entry courtesy research by Michael Minn **/ - { /* Roland UA-100 */ - "Roland UA-100", - USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_UA100G, 2, -1, - { { 0x82, 7 }, {-1, -1} }, /** cables 0,1 and 2 for SYSEX **/ - { { 0x02, 7 }, {-1, -1} }, - }, - -/** Next entry courtesy research by Michael Minn **/ - { /* Roland SC8850 */ - "Roland SC8850", - USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_SC8850, 2, -1, - { { 0x81, 0x3f }, {-1, -1} }, - { { 0x01, 0x3f }, {-1, -1} }, - }, - - { /* Roland SC8820 */ - "Roland SC8820", - USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_SC8820, 2, -1, - { { 0x81, 0x13 }, {-1, -1} }, - { { 0x01, 0x13 }, {-1, -1} }, - }, - - { /* Roland SC8820 */ - "Roland SC8820", - USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_SC8820, 2, -1, - { { 0x81, 17 }, {-1, -1} }, - { { 0x01, 17 }, {-1, -1} }, - }, - - { /* YAMAHA MU1000 */ - "YAMAHA MU1000", - USB_VENDOR_ID_YAMAHA, USBMIDI_YAMAHA_MU1000, 0, -1, - { { 0x81, 1 }, {-1, -1} }, - { { 0x01, 15 }, {-1, -1} }, - }, - { /* Roland PC-300 */ - "Roland PC-300", - USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_PC300, 2, -1, - { { 0x81, 1 }, {-1, -1} }, - { { 0x01, 1 }, {-1, -1} }, - }, - { /* MOTU Fastlane USB */ - "MOTU Fastlane USB", - USB_VENDOR_ID_MOTU, USBMIDI_MOTU_FASTLANE, 1, 0, - { { 0x82, 3 }, {-1, -1} }, - { { 0x02, 3 }, {-1, -1} }, - } -}; - -#define VENDOR_SPECIFIC_USB_MIDI_DEVICES (sizeof(usb_midi_devices)/sizeof(struct usb_midi_device)) - -/* for Hot-Plugging */ - -static struct usb_device_id usb_midi_ids [] = { - { .match_flags = (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS), - .bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING}, - { USB_DEVICE( USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_UM1 ) }, - { USB_DEVICE( USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_UM2 ) }, - { USB_DEVICE( USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_UA100G ) }, - { USB_DEVICE( USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_PC300 ) }, - { USB_DEVICE( USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_SC8850 ) }, - { USB_DEVICE( USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_SC8820 ) }, - { USB_DEVICE( USB_VENDOR_ID_YAMAHA, USBMIDI_YAMAHA_MU1000 ) }, - { USB_DEVICE( USB_VENDOR_ID_MOTU, USBMIDI_MOTU_FASTLANE ) }, -/* { USB_DEVICE( USB_VENDOR_ID_STEINBERG, USBMIDI_STEINBERG_USB2MIDI ) },*/ - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE (usb, usb_midi_ids); - -/* ------------------------------------------------------------------------- */ -#endif /* _USB_MIDI_H_ */ - - -- cgit v1.2.3-59-g8ed1b From e266a12492f7ca9142882710bff92e902b7c95c8 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 8 Nov 2005 21:05:43 +0100 Subject: [PATCH] USB: drivers/usb/core/message.c: make usb_get_string() static After the removal of usb-midi.c, there's no longer any external user of usb_get_string(). Signed-off-by: Adrian Bunk Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/message.c | 5 ++--- include/linux/usb.h | 2 -- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 7135e542679d..2f6009b0cffc 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -631,8 +631,8 @@ int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char * Returns the number of bytes received on success, or else the status code * returned by the underlying usb_control_msg() call. */ -int usb_get_string(struct usb_device *dev, unsigned short langid, - unsigned char index, void *buf, int size) +static int usb_get_string(struct usb_device *dev, unsigned short langid, + unsigned char index, void *buf, int size) { int i; int result; @@ -1488,7 +1488,6 @@ EXPORT_SYMBOL(usb_sg_wait); // synchronous control message convenience routines EXPORT_SYMBOL(usb_get_descriptor); EXPORT_SYMBOL(usb_get_status); -EXPORT_SYMBOL(usb_get_string); EXPORT_SYMBOL(usb_string); // synchronous calls that also maintain usbcore state diff --git a/include/linux/usb.h b/include/linux/usb.h index 827cc6de5f5c..130d125fda12 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1018,8 +1018,6 @@ extern int usb_get_descriptor(struct usb_device *dev, unsigned char desctype, unsigned char descindex, void *buf, int size); extern int usb_get_status(struct usb_device *dev, int type, int target, void *data); -extern int usb_get_string(struct usb_device *dev, - unsigned short langid, unsigned char index, void *buf, int size); extern int usb_string(struct usb_device *dev, int index, char *buf, size_t size); -- cgit v1.2.3-59-g8ed1b From 35cce732d9d4d9af6b4ad4d26d8f8c0eddb573a2 Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Thu, 12 Jan 2006 20:35:25 +0200 Subject: [PATCH] USB: remove LINUX_VERSION_CODE macro usage This patch removes unnecessary LINUX_VERSION_CODE macro usage from drivers/usb/. Signed-off-by: Pekka Enberg Signed-off-by: Greg Kroah-Hartman --- drivers/usb/media/pwc/pwc-ctrl.c | 1 - drivers/usb/media/pwc/pwc-if.c | 6 +----- drivers/usb/misc/sisusbvga/sisusb.h | 8 -------- 3 files changed, 1 insertion(+), 14 deletions(-) diff --git a/drivers/usb/media/pwc/pwc-ctrl.c b/drivers/usb/media/pwc/pwc-ctrl.c index 3ebb6e9cdf92..0398b812e0ce 100644 --- a/drivers/usb/media/pwc/pwc-ctrl.c +++ b/drivers/usb/media/pwc/pwc-ctrl.c @@ -41,7 +41,6 @@ #include #endif #include -#include #include "pwc.h" #include "pwc-ioctl.h" diff --git a/drivers/usb/media/pwc/pwc-if.c b/drivers/usb/media/pwc/pwc-if.c index 4f9b0dc6fd7b..db6753dc5ba5 100644 --- a/drivers/usb/media/pwc/pwc-if.c +++ b/drivers/usb/media/pwc/pwc-if.c @@ -62,7 +62,6 @@ #include #include #include -#include #include #include "pwc.h" @@ -827,13 +826,10 @@ static int pwc_isoc_init(struct pwc_device *pdev) /* Get the current alternate interface, adjust packet size */ if (!udev->actconfig) return -EFAULT; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,5) - idesc = &udev->actconfig->interface[0]->altsetting[pdev->valternate]; -#else + intf = usb_ifnum_to_if(udev, 0); if (intf) idesc = usb_altnum_to_altsetting(intf, pdev->valternate); -#endif if (!idesc) return -EFAULT; diff --git a/drivers/usb/misc/sisusbvga/sisusb.h b/drivers/usb/misc/sisusbvga/sisusb.h index 1d7a77cc7c4a..a716825d1f9b 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.h +++ b/drivers/usb/misc/sisusbvga/sisusb.h @@ -37,24 +37,16 @@ #ifndef _SISUSB_H_ #define _SISUSB_H_ -#include #ifdef CONFIG_COMPAT -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,10) -#include -#define SISUSB_OLD_CONFIG_COMPAT -#else #define SISUSB_NEW_CONFIG_COMPAT #endif -#endif /* For older kernels, support for text consoles is by default * off. To ensable text console support, change the following: */ #if 0 -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,13) #define CONFIG_USB_SISUSBVGA_CON #endif -#endif /* Version Information */ -- cgit v1.2.3-59-g8ed1b From 4186ecf8ad16dd05759a09594de6a87e48759ba6 Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Wed, 11 Jan 2006 15:55:29 +0100 Subject: [PATCH] USB: convert a bunch of USB semaphores to mutexes the patch below converts a bunch of semaphores-used-as-mutex in the USB code to mutexes Signed-off-by: Arjan van de Ven Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 23 +++--- drivers/usb/class/usblp.c | 15 ++-- drivers/usb/core/devices.c | 7 +- drivers/usb/core/hcd.c | 25 ++++--- drivers/usb/core/hcd.h | 2 +- drivers/usb/core/hub.c | 7 +- drivers/usb/core/notify.c | 15 ++-- drivers/usb/core/usb.c | 5 +- drivers/usb/image/mdc800.c | 63 ++++++++-------- drivers/usb/input/ati_remote.c | 2 - drivers/usb/media/dabusb.c | 31 ++++---- drivers/usb/media/dabusb.h | 2 +- drivers/usb/media/ov511.c | 91 +++++++++++------------ drivers/usb/media/ov511.h | 11 ++- drivers/usb/media/se401.c | 12 +-- drivers/usb/media/se401.h | 3 +- drivers/usb/media/sn9c102.h | 5 +- drivers/usb/media/sn9c102_core.c | 156 +++++++++++++++++++-------------------- drivers/usb/media/stv680.c | 13 ++-- drivers/usb/media/stv680.h | 2 +- drivers/usb/media/usbvideo.c | 28 +++---- drivers/usb/media/usbvideo.h | 5 +- drivers/usb/media/vicam.c | 21 +++--- drivers/usb/media/w9968cf.c | 75 ++++++++++--------- drivers/usb/media/w9968cf.h | 14 +--- drivers/usb/misc/idmouse.c | 25 ++++--- drivers/usb/misc/ldusb.c | 11 +-- drivers/usb/misc/legousbtower.c | 11 +-- drivers/usb/mon/mon_main.c | 19 ++--- drivers/usb/mon/mon_text.c | 21 +++--- drivers/usb/mon/usb_mon.h | 2 +- drivers/usb/storage/scsiglue.c | 9 ++- drivers/usb/storage/usb.c | 25 ++++--- drivers/usb/storage/usb.h | 5 +- 34 files changed, 389 insertions(+), 372 deletions(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 97bdeb1c2181..6dd339f4c0fc 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -60,6 +60,7 @@ #include #include #include +#include #include #include #include @@ -80,7 +81,7 @@ static struct usb_driver acm_driver; static struct tty_driver *acm_tty_driver; static struct acm *acm_table[ACM_TTY_MINORS]; -static DECLARE_MUTEX(open_sem); +static DEFINE_MUTEX(open_mutex); #define ACM_READY(acm) (acm && acm->dev && acm->used) @@ -431,8 +432,8 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) int rv = -EINVAL; int i; dbg("Entering acm_tty_open.\n"); - - down(&open_sem); + + mutex_lock(&open_mutex); acm = acm_table[tty->index]; if (!acm || !acm->dev) @@ -474,14 +475,14 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) done: err_out: - up(&open_sem); + mutex_unlock(&open_mutex); return rv; full_bailout: usb_kill_urb(acm->ctrlurb); bail_out: acm->used--; - up(&open_sem); + mutex_unlock(&open_mutex); return -EIO; } @@ -507,7 +508,7 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp) if (!acm || !acm->used) return; - down(&open_sem); + mutex_lock(&open_mutex); if (!--acm->used) { if (acm->dev) { acm_set_control(acm, acm->ctrlout = 0); @@ -518,7 +519,7 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp) } else acm_tty_unregister(acm); } - up(&open_sem); + mutex_unlock(&open_mutex); } static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) @@ -1013,9 +1014,9 @@ static void acm_disconnect(struct usb_interface *intf) return; } - down(&open_sem); + mutex_lock(&open_mutex); if (!usb_get_intfdata(intf)) { - up(&open_sem); + mutex_unlock(&open_mutex); return; } acm->dev = NULL; @@ -1045,11 +1046,11 @@ static void acm_disconnect(struct usb_interface *intf) if (!acm->used) { acm_tty_unregister(acm); - up(&open_sem); + mutex_unlock(&open_mutex); return; } - up(&open_sem); + mutex_unlock(&open_mutex); if (acm->tty) tty_hangup(acm->tty); diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index d34848ac30b0..48dee4b8d8e5 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -55,6 +55,7 @@ #include #include #include +#include #undef DEBUG #include @@ -223,7 +224,7 @@ static int usblp_cache_device_id_string(struct usblp *usblp); /* forward reference to make our lives easier */ static struct usb_driver usblp_driver; -static DECLARE_MUTEX(usblp_sem); /* locks the existence of usblp's */ +static DEFINE_MUTEX(usblp_mutex); /* locks the existence of usblp's */ /* * Functions for usblp control messages. @@ -351,7 +352,7 @@ static int usblp_open(struct inode *inode, struct file *file) if (minor < 0) return -ENODEV; - down (&usblp_sem); + mutex_lock (&usblp_mutex); retval = -ENODEV; intf = usb_find_interface(&usblp_driver, minor); @@ -399,7 +400,7 @@ static int usblp_open(struct inode *inode, struct file *file) } } out: - up (&usblp_sem); + mutex_unlock (&usblp_mutex); return retval; } @@ -425,13 +426,13 @@ static int usblp_release(struct inode *inode, struct file *file) { struct usblp *usblp = file->private_data; - down (&usblp_sem); + mutex_lock (&usblp_mutex); usblp->used = 0; if (usblp->present) { usblp_unlink_urbs(usblp); } else /* finish cleanup from disconnect */ usblp_cleanup (usblp); - up (&usblp_sem); + mutex_unlock (&usblp_mutex); return 0; } @@ -1152,7 +1153,7 @@ static void usblp_disconnect(struct usb_interface *intf) device_remove_file(&intf->dev, &dev_attr_ieee1284_id); - down (&usblp_sem); + mutex_lock (&usblp_mutex); down (&usblp->sem); usblp->present = 0; usb_set_intfdata (intf, NULL); @@ -1166,7 +1167,7 @@ static void usblp_disconnect(struct usb_interface *intf) if (!usblp->used) usblp_cleanup (usblp); - up (&usblp_sem); + mutex_unlock (&usblp_mutex); } static struct usb_device_id usblp_ids [] = { diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 2684e15b813b..c0f37343a276 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -57,6 +57,7 @@ #include #include #include +#include #include #include "usb.h" @@ -570,7 +571,7 @@ static ssize_t usb_device_read(struct file *file, char __user *buf, size_t nbyte if (!access_ok(VERIFY_WRITE, buf, nbytes)) return -EFAULT; - down (&usb_bus_list_lock); + mutex_lock(&usb_bus_list_lock); /* print devices for all busses */ list_for_each_entry(bus, &usb_bus_list, bus_list) { /* recurse through all children of the root hub */ @@ -580,12 +581,12 @@ static ssize_t usb_device_read(struct file *file, char __user *buf, size_t nbyte ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos, bus->root_hub, bus, 0, 0, 0); usb_unlock_device(bus->root_hub); if (ret < 0) { - up(&usb_bus_list_lock); + mutex_unlock(&usb_bus_list_lock); return ret; } total_written += ret; } - up (&usb_bus_list_lock); + mutex_unlock(&usb_bus_list_lock); return total_written; } diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 0018bbc4de34..9223f2869674 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -93,7 +94,7 @@ struct usb_busmap { static struct usb_busmap busmap; /* used when updating list of hcds */ -DECLARE_MUTEX (usb_bus_list_lock); /* exported only for usbfs */ +DEFINE_MUTEX(usb_bus_list_lock); /* exported only for usbfs */ EXPORT_SYMBOL_GPL (usb_bus_list_lock); /* used for controlling access to virtual root hubs */ @@ -761,14 +762,14 @@ static int usb_register_bus(struct usb_bus *bus) { int busnum; - down (&usb_bus_list_lock); + mutex_lock(&usb_bus_list_lock); busnum = find_next_zero_bit (busmap.busmap, USB_MAXBUS, 1); if (busnum < USB_MAXBUS) { set_bit (busnum, busmap.busmap); bus->busnum = busnum; } else { printk (KERN_ERR "%s: too many buses\n", usbcore_name); - up(&usb_bus_list_lock); + mutex_unlock(&usb_bus_list_lock); return -E2BIG; } @@ -776,7 +777,7 @@ static int usb_register_bus(struct usb_bus *bus) bus->controller, "usb_host%d", busnum); if (IS_ERR(bus->class_dev)) { clear_bit(busnum, busmap.busmap); - up(&usb_bus_list_lock); + mutex_unlock(&usb_bus_list_lock); return PTR_ERR(bus->class_dev); } @@ -784,7 +785,7 @@ static int usb_register_bus(struct usb_bus *bus) /* Add it to the local list of buses */ list_add (&bus->bus_list, &usb_bus_list); - up (&usb_bus_list_lock); + mutex_unlock(&usb_bus_list_lock); usb_notify_add_bus(bus); @@ -809,9 +810,9 @@ static void usb_deregister_bus (struct usb_bus *bus) * controller code, as well as having it call this when cleaning * itself up */ - down (&usb_bus_list_lock); + mutex_lock(&usb_bus_list_lock); list_del (&bus->bus_list); - up (&usb_bus_list_lock); + mutex_unlock(&usb_bus_list_lock); usb_notify_remove_bus(bus); @@ -844,14 +845,14 @@ static int register_root_hub (struct usb_device *usb_dev, set_bit (devnum, usb_dev->bus->devmap.devicemap); usb_set_device_state(usb_dev, USB_STATE_ADDRESS); - down (&usb_bus_list_lock); + mutex_lock(&usb_bus_list_lock); usb_dev->bus->root_hub = usb_dev; usb_dev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64); retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE); if (retval != sizeof usb_dev->descriptor) { usb_dev->bus->root_hub = NULL; - up (&usb_bus_list_lock); + mutex_unlock(&usb_bus_list_lock); dev_dbg (parent_dev, "can't read %s device descriptor %d\n", usb_dev->dev.bus_id, retval); return (retval < 0) ? retval : -EMSGSIZE; @@ -863,7 +864,7 @@ static int register_root_hub (struct usb_device *usb_dev, dev_err (parent_dev, "can't register root hub for %s, %d\n", usb_dev->dev.bus_id, retval); } - up (&usb_bus_list_lock); + mutex_unlock(&usb_bus_list_lock); if (retval == 0) { spin_lock_irq (&hcd_root_hub_lock); @@ -1891,9 +1892,9 @@ void usb_remove_hcd(struct usb_hcd *hcd) hcd->rh_registered = 0; spin_unlock_irq (&hcd_root_hub_lock); - down(&usb_bus_list_lock); + mutex_lock(&usb_bus_list_lock); usb_disconnect(&hcd->self.root_hub); - up(&usb_bus_list_lock); + mutex_unlock(&usb_bus_list_lock); hcd->poll_rh = 0; del_timer_sync(&hcd->rh_timer); diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 591b5aad1a18..f44a2fe62a9d 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -364,7 +364,7 @@ extern void usb_set_device_state(struct usb_device *udev, /* exported only within usbcore */ extern struct list_head usb_bus_list; -extern struct semaphore usb_bus_list_lock; +extern struct mutex usb_bus_list_lock; extern wait_queue_head_t usb_kill_urb_queue; extern struct usb_bus *usb_bus_get (struct usb_bus *bus); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 650d5ee5871b..867fa8130238 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -2162,7 +2163,7 @@ static int hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, int retry_counter) { - static DECLARE_MUTEX(usb_address0_sem); + static DEFINE_MUTEX(usb_address0_mutex); struct usb_device *hdev = hub->hdev; int i, j, retval; @@ -2183,7 +2184,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, if (oldspeed == USB_SPEED_LOW) delay = HUB_LONG_RESET_TIME; - down(&usb_address0_sem); + mutex_lock(&usb_address0_mutex); /* Reset the device; full speed may morph to high speed */ retval = hub_port_reset(hub, port1, udev, delay); @@ -2381,7 +2382,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, fail: if (retval) hub_port_disable(hub, port1, 0); - up(&usb_address0_sem); + mutex_unlock(&usb_address0_mutex); return retval; } diff --git a/drivers/usb/core/notify.c b/drivers/usb/core/notify.c index fbbebab52fbd..4b55285de9a0 100644 --- a/drivers/usb/core/notify.c +++ b/drivers/usb/core/notify.c @@ -13,16 +13,17 @@ #include #include #include +#include #include "usb.h" static struct notifier_block *usb_notifier_list; -static DECLARE_MUTEX(usb_notifier_lock); +static DEFINE_MUTEX(usb_notifier_lock); static void usb_notifier_chain_register(struct notifier_block **list, struct notifier_block *n) { - down(&usb_notifier_lock); + mutex_lock(&usb_notifier_lock); while (*list) { if (n->priority > (*list)->priority) break; @@ -30,13 +31,13 @@ static void usb_notifier_chain_register(struct notifier_block **list, } n->next = *list; *list = n; - up(&usb_notifier_lock); + mutex_unlock(&usb_notifier_lock); } static void usb_notifier_chain_unregister(struct notifier_block **nl, struct notifier_block *n) { - down(&usb_notifier_lock); + mutex_lock(&usb_notifier_lock); while ((*nl)!=NULL) { if ((*nl)==n) { *nl = n->next; @@ -45,7 +46,7 @@ static void usb_notifier_chain_unregister(struct notifier_block **nl, nl=&((*nl)->next); } exit: - up(&usb_notifier_lock); + mutex_unlock(&usb_notifier_lock); } static int usb_notifier_call_chain(struct notifier_block **n, @@ -54,7 +55,7 @@ static int usb_notifier_call_chain(struct notifier_block **n, int ret=NOTIFY_DONE; struct notifier_block *nb = *n; - down(&usb_notifier_lock); + mutex_lock(&usb_notifier_lock); while (nb) { ret = nb->notifier_call(nb,val,v); if (ret&NOTIFY_STOP_MASK) { @@ -63,7 +64,7 @@ static int usb_notifier_call_chain(struct notifier_block **n, nb = nb->next; } exit: - up(&usb_notifier_lock); + mutex_unlock(&usb_notifier_lock); return ret; } diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 13d1d367f7f1..d7352aa73b5e 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -639,7 +640,7 @@ struct usb_device *usb_find_device(u16 vendor_id, u16 product_id) struct usb_bus *bus; struct usb_device *dev = NULL; - down(&usb_bus_list_lock); + mutex_lock(&usb_bus_list_lock); for (buslist = usb_bus_list.next; buslist != &usb_bus_list; buslist = buslist->next) { @@ -653,7 +654,7 @@ struct usb_device *usb_find_device(u16 vendor_id, u16 product_id) goto exit; } exit: - up(&usb_bus_list_lock); + mutex_unlock(&usb_bus_list_lock); return dev; } diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c index 4e3e4d048c14..08daf400f985 100644 --- a/drivers/usb/image/mdc800.c +++ b/drivers/usb/image/mdc800.c @@ -96,6 +96,7 @@ #include #include #include +#include #include #include @@ -169,7 +170,7 @@ struct mdc800_data int out_count; // Bytes in the buffer int open; // Camera device open ? - struct semaphore io_lock; // IO -lock + struct mutex io_lock; // IO -lock char in [8]; // Command Input Buffer int in_count; @@ -497,7 +498,7 @@ static int mdc800_usb_probe (struct usb_interface *intf, info ("Found Mustek MDC800 on USB."); - down (&mdc800->io_lock); + mutex_lock(&mdc800->io_lock); retval = usb_register_dev(intf, &mdc800_class); if (retval) { @@ -542,7 +543,7 @@ static int mdc800_usb_probe (struct usb_interface *intf, mdc800->state=READY; - up (&mdc800->io_lock); + mutex_unlock(&mdc800->io_lock); usb_set_intfdata(intf, mdc800); return 0; @@ -620,7 +621,7 @@ static int mdc800_device_open (struct inode* inode, struct file *file) int retval=0; int errn=0; - down (&mdc800->io_lock); + mutex_lock(&mdc800->io_lock); if (mdc800->state == NOT_CONNECTED) { @@ -656,7 +657,7 @@ static int mdc800_device_open (struct inode* inode, struct file *file) dbg ("Mustek MDC800 device opened."); error_out: - up (&mdc800->io_lock); + mutex_unlock(&mdc800->io_lock); return errn; } @@ -669,7 +670,7 @@ static int mdc800_device_release (struct inode* inode, struct file *file) int retval=0; dbg ("Mustek MDC800 device closed."); - down (&mdc800->io_lock); + mutex_lock(&mdc800->io_lock); if (mdc800->open && (mdc800->state != NOT_CONNECTED)) { usb_kill_urb(mdc800->irq_urb); @@ -682,7 +683,7 @@ static int mdc800_device_release (struct inode* inode, struct file *file) retval=-EIO; } - up(&mdc800->io_lock); + mutex_unlock(&mdc800->io_lock); return retval; } @@ -695,21 +696,21 @@ static ssize_t mdc800_device_read (struct file *file, char __user *buf, size_t l size_t left=len, sts=len; /* single transfer size */ char __user *ptr = buf; - down (&mdc800->io_lock); + mutex_lock(&mdc800->io_lock); if (mdc800->state == NOT_CONNECTED) { - up (&mdc800->io_lock); + mutex_unlock(&mdc800->io_lock); return -EBUSY; } if (mdc800->state == WORKING) { warn ("Illegal State \"working\" reached during read ?!"); - up (&mdc800->io_lock); + mutex_unlock(&mdc800->io_lock); return -EBUSY; } if (!mdc800->open) { - up (&mdc800->io_lock); + mutex_unlock(&mdc800->io_lock); return -EBUSY; } @@ -717,7 +718,7 @@ static ssize_t mdc800_device_read (struct file *file, char __user *buf, size_t l { if (signal_pending (current)) { - up (&mdc800->io_lock); + mutex_unlock(&mdc800->io_lock); return -EINTR; } @@ -736,7 +737,7 @@ static ssize_t mdc800_device_read (struct file *file, char __user *buf, size_t l if (usb_submit_urb (mdc800->download_urb, GFP_KERNEL)) { err ("Can't submit download urb (status=%i)",mdc800->download_urb->status); - up (&mdc800->io_lock); + mutex_unlock(&mdc800->io_lock); return len-left; } wait_event_timeout(mdc800->download_wait, mdc800->downloaded, @@ -745,14 +746,14 @@ static ssize_t mdc800_device_read (struct file *file, char __user *buf, size_t l if (mdc800->download_urb->status != 0) { err ("request download-bytes fails (status=%i)",mdc800->download_urb->status); - up (&mdc800->io_lock); + mutex_unlock(&mdc800->io_lock); return len-left; } } else { /* No more bytes -> that's an error*/ - up (&mdc800->io_lock); + mutex_unlock(&mdc800->io_lock); return -EIO; } } @@ -761,7 +762,7 @@ static ssize_t mdc800_device_read (struct file *file, char __user *buf, size_t l /* Copy Bytes */ if (copy_to_user(ptr, &mdc800->out [mdc800->out_ptr], sts)) { - up(&mdc800->io_lock); + mutex_unlock(&mdc800->io_lock); return -EFAULT; } ptr+=sts; @@ -770,7 +771,7 @@ static ssize_t mdc800_device_read (struct file *file, char __user *buf, size_t l } } - up (&mdc800->io_lock); + mutex_unlock(&mdc800->io_lock); return len-left; } @@ -785,15 +786,15 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s { size_t i=0; - down (&mdc800->io_lock); + mutex_lock(&mdc800->io_lock); if (mdc800->state != READY) { - up (&mdc800->io_lock); + mutex_unlock(&mdc800->io_lock); return -EBUSY; } if (!mdc800->open ) { - up (&mdc800->io_lock); + mutex_unlock(&mdc800->io_lock); return -EBUSY; } @@ -802,13 +803,13 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s unsigned char c; if (signal_pending (current)) { - up (&mdc800->io_lock); + mutex_unlock(&mdc800->io_lock); return -EINTR; } if(get_user(c, buf+i)) { - up(&mdc800->io_lock); + mutex_unlock(&mdc800->io_lock); return -EFAULT; } @@ -829,7 +830,7 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s } else { - up (&mdc800->io_lock); + mutex_unlock(&mdc800->io_lock); return -EIO; } @@ -841,7 +842,7 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s if (mdc800_usb_waitForIRQ (0,TO_GET_READY)) { err ("Camera didn't get ready.\n"); - up (&mdc800->io_lock); + mutex_unlock(&mdc800->io_lock); return -EIO; } @@ -853,7 +854,7 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s if (usb_submit_urb (mdc800->write_urb, GFP_KERNEL)) { err ("submitting write urb fails (status=%i)", mdc800->write_urb->status); - up (&mdc800->io_lock); + mutex_unlock(&mdc800->io_lock); return -EIO; } wait_event_timeout(mdc800->write_wait, mdc800->written, TO_WRITE_GET_READY*HZ/1000); @@ -861,7 +862,7 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s if (mdc800->state == WORKING) { usb_kill_urb(mdc800->write_urb); - up (&mdc800->io_lock); + mutex_unlock(&mdc800->io_lock); return -EIO; } @@ -873,7 +874,7 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s { err ("call 0x07 before 0x05,0x3e"); mdc800->state=READY; - up (&mdc800->io_lock); + mutex_unlock(&mdc800->io_lock); return -EIO; } mdc800->pic_len=-1; @@ -892,7 +893,7 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s if (mdc800_usb_waitForIRQ (1,TO_READ_FROM_IRQ)) { err ("requesting answer from irq fails"); - up (&mdc800->io_lock); + mutex_unlock(&mdc800->io_lock); return -EIO; } @@ -920,7 +921,7 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s if (mdc800_usb_waitForIRQ (0,TO_DEFAULT_COMMAND)) { err ("Command Timeout."); - up (&mdc800->io_lock); + mutex_unlock(&mdc800->io_lock); return -EIO; } } @@ -930,7 +931,7 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s } i++; } - up (&mdc800->io_lock); + mutex_unlock(&mdc800->io_lock); return i; } @@ -984,7 +985,7 @@ static int __init usb_mdc800_init (void) mdc800->dev = NULL; mdc800->state=NOT_CONNECTED; - init_MUTEX (&mdc800->io_lock); + mutex_init (&mdc800->io_lock); init_waitqueue_head (&mdc800->irq_wait); init_waitqueue_head (&mdc800->write_wait); diff --git a/drivers/usb/input/ati_remote.c b/drivers/usb/input/ati_remote.c index f7bdc506e613..99f986cb6e95 100644 --- a/drivers/usb/input/ati_remote.c +++ b/drivers/usb/input/ati_remote.c @@ -159,8 +159,6 @@ static const char accel[] = { 1, 2, 4, 6, 9, 13, 20 }; */ #define FILTER_TIME (HZ / 20) -static DECLARE_MUTEX(disconnect_sem); - struct ati_remote { struct input_dev *idev; struct usb_device *udev; diff --git a/drivers/usb/media/dabusb.c b/drivers/usb/media/dabusb.c index 6f560fe9a519..1774ab7a40d2 100644 --- a/drivers/usb/media/dabusb.c +++ b/drivers/usb/media/dabusb.c @@ -38,6 +38,7 @@ #include #include #include +#include #include "dabusb.h" #include "dabfirmware.h" @@ -570,7 +571,7 @@ static ssize_t dabusb_read (struct file *file, char __user *buf, size_t count, l s->readptr = 0; } } - err: //up(&s->mutex); + err: //mutex_unlock(&s->mutex); return ret; } @@ -585,10 +586,10 @@ static int dabusb_open (struct inode *inode, struct file *file) s = &dabusb[devnum - DABUSB_MINOR]; dbg("dabusb_open"); - down (&s->mutex); + mutex_lock(&s->mutex); while (!s->usbdev || s->opened) { - up (&s->mutex); + mutex_unlock(&s->mutex); if (file->f_flags & O_NONBLOCK) { return -EBUSY; @@ -598,15 +599,15 @@ static int dabusb_open (struct inode *inode, struct file *file) if (signal_pending (current)) { return -EAGAIN; } - down (&s->mutex); + mutex_lock(&s->mutex); } if (usb_set_interface (s->usbdev, _DABUSB_IF, 1) < 0) { - up(&s->mutex); + mutex_unlock(&s->mutex); err("set_interface failed"); return -EINVAL; } s->opened = 1; - up (&s->mutex); + mutex_unlock(&s->mutex); file->f_pos = 0; file->private_data = s; @@ -620,10 +621,10 @@ static int dabusb_release (struct inode *inode, struct file *file) dbg("dabusb_release"); - down (&s->mutex); + mutex_lock(&s->mutex); dabusb_stop (s); dabusb_free_buffers (s); - up (&s->mutex); + mutex_unlock(&s->mutex); if (!s->remove_pending) { if (usb_set_interface (s->usbdev, _DABUSB_IF, 0) < 0) @@ -648,10 +649,10 @@ static int dabusb_ioctl (struct inode *inode, struct file *file, unsigned int cm if (s->remove_pending) return -EIO; - down (&s->mutex); + mutex_lock(&s->mutex); if (!s->usbdev) { - up (&s->mutex); + mutex_unlock(&s->mutex); return -EIO; } @@ -691,7 +692,7 @@ static int dabusb_ioctl (struct inode *inode, struct file *file, unsigned int cm ret = -ENOIOCTLCMD; break; } - up (&s->mutex); + mutex_unlock(&s->mutex); return ret; } @@ -737,7 +738,7 @@ static int dabusb_probe (struct usb_interface *intf, s = &dabusb[intf->minor]; - down (&s->mutex); + mutex_lock(&s->mutex); s->remove_pending = 0; s->usbdev = usbdev; s->devnum = intf->minor; @@ -760,7 +761,7 @@ static int dabusb_probe (struct usb_interface *intf, } dbg("bound to interface: %d", intf->altsetting->desc.bInterfaceNumber); usb_set_intfdata (intf, s); - up (&s->mutex); + mutex_unlock(&s->mutex); retval = usb_register_dev(intf, &dabusb_class); if (retval) { @@ -771,7 +772,7 @@ static int dabusb_probe (struct usb_interface *intf, return 0; reject: - up (&s->mutex); + mutex_unlock(&s->mutex); s->usbdev = NULL; return -ENODEV; } @@ -828,7 +829,7 @@ static int __init dabusb_init (void) for (u = 0; u < NRDABUSB; u++) { pdabusb_t s = &dabusb[u]; memset (s, 0, sizeof (dabusb_t)); - init_MUTEX (&s->mutex); + mutex_init (&s->mutex); s->usbdev = NULL; s->total_buffer_size = buffers; init_waitqueue_head (&s->wait); diff --git a/drivers/usb/media/dabusb.h b/drivers/usb/media/dabusb.h index 10b666e43abc..96b03e4af8b9 100644 --- a/drivers/usb/media/dabusb.h +++ b/drivers/usb/media/dabusb.h @@ -18,7 +18,7 @@ typedef enum { _stopped=0, _started } driver_state_t; typedef struct { - struct semaphore mutex; + struct mutex mutex; struct usb_device *usbdev; wait_queue_head_t wait; wait_queue_head_t remove_ok; diff --git a/drivers/usb/media/ov511.c b/drivers/usb/media/ov511.c index 51e9cc06f7e3..cfec8c61574e 100644 --- a/drivers/usb/media/ov511.c +++ b/drivers/usb/media/ov511.c @@ -365,14 +365,14 @@ reg_w(struct usb_ov511 *ov, unsigned char reg, unsigned char value) PDEBUG(5, "0x%02X:0x%02X", reg, value); - down(&ov->cbuf_lock); + mutex_lock(&ov->cbuf_lock); ov->cbuf[0] = value; rc = usb_control_msg(ov->dev, usb_sndctrlpipe(ov->dev, 0), (ov->bclass == BCL_OV518)?1:2 /* REG_IO */, USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, (__u16)reg, &ov->cbuf[0], 1, 1000); - up(&ov->cbuf_lock); + mutex_unlock(&ov->cbuf_lock); if (rc < 0) err("reg write: error %d: %s", rc, symbolic(urb_errlist, rc)); @@ -387,7 +387,7 @@ reg_r(struct usb_ov511 *ov, unsigned char reg) { int rc; - down(&ov->cbuf_lock); + mutex_lock(&ov->cbuf_lock); rc = usb_control_msg(ov->dev, usb_rcvctrlpipe(ov->dev, 0), (ov->bclass == BCL_OV518)?1:3 /* REG_IO */, @@ -401,7 +401,7 @@ reg_r(struct usb_ov511 *ov, unsigned char reg) PDEBUG(5, "0x%02X:0x%02X", reg, ov->cbuf[0]); } - up(&ov->cbuf_lock); + mutex_unlock(&ov->cbuf_lock); return rc; } @@ -444,7 +444,7 @@ ov518_reg_w32(struct usb_ov511 *ov, unsigned char reg, u32 val, int n) PDEBUG(5, "0x%02X:%7d, n=%d", reg, val, n); - down(&ov->cbuf_lock); + mutex_lock(&ov->cbuf_lock); *((__le32 *)ov->cbuf) = __cpu_to_le32(val); @@ -453,7 +453,7 @@ ov518_reg_w32(struct usb_ov511 *ov, unsigned char reg, u32 val, int n) 1 /* REG_IO */, USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, (__u16)reg, ov->cbuf, n, 1000); - up(&ov->cbuf_lock); + mutex_unlock(&ov->cbuf_lock); if (rc < 0) err("reg write multiple: error %d: %s", rc, @@ -768,14 +768,14 @@ i2c_r(struct usb_ov511 *ov, unsigned char reg) { int rc; - down(&ov->i2c_lock); + mutex_lock(&ov->i2c_lock); if (ov->bclass == BCL_OV518) rc = ov518_i2c_read_internal(ov, reg); else rc = ov511_i2c_read_internal(ov, reg); - up(&ov->i2c_lock); + mutex_unlock(&ov->i2c_lock); return rc; } @@ -785,14 +785,14 @@ i2c_w(struct usb_ov511 *ov, unsigned char reg, unsigned char value) { int rc; - down(&ov->i2c_lock); + mutex_lock(&ov->i2c_lock); if (ov->bclass == BCL_OV518) rc = ov518_i2c_write_internal(ov, reg, value); else rc = ov511_i2c_write_internal(ov, reg, value); - up(&ov->i2c_lock); + mutex_unlock(&ov->i2c_lock); return rc; } @@ -842,9 +842,9 @@ i2c_w_mask(struct usb_ov511 *ov, { int rc; - down(&ov->i2c_lock); + mutex_lock(&ov->i2c_lock); rc = ov51x_i2c_write_mask_internal(ov, reg, value, mask); - up(&ov->i2c_lock); + mutex_unlock(&ov->i2c_lock); return rc; } @@ -880,7 +880,7 @@ i2c_w_slave(struct usb_ov511 *ov, { int rc = 0; - down(&ov->i2c_lock); + mutex_lock(&ov->i2c_lock); /* Set new slave IDs */ rc = i2c_set_slave_internal(ov, slave); @@ -894,7 +894,7 @@ out: if (i2c_set_slave_internal(ov, ov->primary_i2c_slave) < 0) err("Couldn't restore primary I2C slave"); - up(&ov->i2c_lock); + mutex_unlock(&ov->i2c_lock); return rc; } @@ -906,7 +906,7 @@ i2c_r_slave(struct usb_ov511 *ov, { int rc; - down(&ov->i2c_lock); + mutex_lock(&ov->i2c_lock); /* Set new slave IDs */ rc = i2c_set_slave_internal(ov, slave); @@ -923,7 +923,7 @@ out: if (i2c_set_slave_internal(ov, ov->primary_i2c_slave) < 0) err("Couldn't restore primary I2C slave"); - up(&ov->i2c_lock); + mutex_unlock(&ov->i2c_lock); return rc; } @@ -933,7 +933,7 @@ ov51x_set_slave_ids(struct usb_ov511 *ov, unsigned char sid) { int rc; - down(&ov->i2c_lock); + mutex_lock(&ov->i2c_lock); rc = i2c_set_slave_internal(ov, sid); if (rc < 0) @@ -942,7 +942,7 @@ ov51x_set_slave_ids(struct usb_ov511 *ov, unsigned char sid) // FIXME: Is this actually necessary? rc = ov51x_reset(ov, OV511_RESET_NOREGS); out: - up(&ov->i2c_lock); + mutex_unlock(&ov->i2c_lock); return rc; } @@ -3832,7 +3832,7 @@ ov51x_alloc(struct usb_ov511 *ov) const int raw_bufsize = OV511_NUMFRAMES * MAX_RAW_DATA_SIZE(w, h); PDEBUG(4, "entered"); - down(&ov->buf_lock); + mutex_lock(&ov->buf_lock); if (ov->buf_state == BUF_ALLOCATED) goto out; @@ -3879,12 +3879,12 @@ ov51x_alloc(struct usb_ov511 *ov) ov->buf_state = BUF_ALLOCATED; out: - up(&ov->buf_lock); + mutex_unlock(&ov->buf_lock); PDEBUG(4, "leaving"); return 0; error: ov51x_do_dealloc(ov); - up(&ov->buf_lock); + mutex_unlock(&ov->buf_lock); PDEBUG(4, "errored"); return -ENOMEM; } @@ -3893,9 +3893,9 @@ static void ov51x_dealloc(struct usb_ov511 *ov) { PDEBUG(4, "entered"); - down(&ov->buf_lock); + mutex_lock(&ov->buf_lock); ov51x_do_dealloc(ov); - up(&ov->buf_lock); + mutex_unlock(&ov->buf_lock); PDEBUG(4, "leaving"); } @@ -3914,7 +3914,7 @@ ov51x_v4l1_open(struct inode *inode, struct file *file) PDEBUG(4, "opening"); - down(&ov->lock); + mutex_lock(&ov->lock); err = -EBUSY; if (ov->user) @@ -3958,7 +3958,7 @@ ov51x_v4l1_open(struct inode *inode, struct file *file) ov51x_led_control(ov, 1); out: - up(&ov->lock); + mutex_unlock(&ov->lock); return err; } @@ -3970,7 +3970,7 @@ ov51x_v4l1_close(struct inode *inode, struct file *file) PDEBUG(4, "ov511_close"); - down(&ov->lock); + mutex_lock(&ov->lock); ov->user--; ov51x_stop_isoc(ov); @@ -3981,15 +3981,15 @@ ov51x_v4l1_close(struct inode *inode, struct file *file) if (ov->dev) ov51x_dealloc(ov); - up(&ov->lock); + mutex_unlock(&ov->lock); /* Device unplugged while open. Only a minimum of unregistration is done * here; the disconnect callback already did the rest. */ if (!ov->dev) { - down(&ov->cbuf_lock); + mutex_lock(&ov->cbuf_lock); kfree(ov->cbuf); ov->cbuf = NULL; - up(&ov->cbuf_lock); + mutex_unlock(&ov->cbuf_lock); ov51x_dealloc(ov); kfree(ov); @@ -4449,12 +4449,12 @@ ov51x_v4l1_ioctl(struct inode *inode, struct file *file, struct usb_ov511 *ov = video_get_drvdata(vdev); int rc; - if (down_interruptible(&ov->lock)) + if (mutex_lock_interruptible(&ov->lock)) return -EINTR; rc = video_usercopy(inode, file, cmd, arg, ov51x_v4l1_ioctl_internal); - up(&ov->lock); + mutex_unlock(&ov->lock); return rc; } @@ -4468,7 +4468,7 @@ ov51x_v4l1_read(struct file *file, char __user *buf, size_t cnt, loff_t *ppos) int i, rc = 0, frmx = -1; struct ov511_frame *frame; - if (down_interruptible(&ov->lock)) + if (mutex_lock_interruptible(&ov->lock)) return -EINTR; PDEBUG(4, "%ld bytes, noblock=%d", count, noblock); @@ -4604,11 +4604,11 @@ restart: PDEBUG(4, "read finished, returning %ld (sweet)", count); - up(&ov->lock); + mutex_unlock(&ov->lock); return count; error: - up(&ov->lock); + mutex_unlock(&ov->lock); return rc; } @@ -4631,14 +4631,14 @@ ov51x_v4l1_mmap(struct file *file, struct vm_area_struct *vma) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))) return -EINVAL; - if (down_interruptible(&ov->lock)) + if (mutex_lock_interruptible(&ov->lock)) return -EINTR; pos = (unsigned long)ov->fbuf; while (size > 0) { page = vmalloc_to_pfn((void *)pos); if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { - up(&ov->lock); + mutex_unlock(&ov->lock); return -EAGAIN; } start += PAGE_SIZE; @@ -4649,7 +4649,7 @@ ov51x_v4l1_mmap(struct file *file, struct vm_area_struct *vma) size = 0; } - up(&ov->lock); + mutex_unlock(&ov->lock); return 0; } @@ -5738,11 +5738,10 @@ ov51x_probe(struct usb_interface *intf, const struct usb_device_id *id) init_waitqueue_head(&ov->wq); - init_MUTEX(&ov->lock); /* to 1 == available */ - init_MUTEX(&ov->buf_lock); - init_MUTEX(&ov->param_lock); - init_MUTEX(&ov->i2c_lock); - init_MUTEX(&ov->cbuf_lock); + mutex_init(&ov->lock); /* to 1 == available */ + mutex_init(&ov->buf_lock); + mutex_init(&ov->i2c_lock); + mutex_init(&ov->cbuf_lock); ov->buf_state = BUF_NOT_ALLOCATED; @@ -5833,10 +5832,10 @@ error: } if (ov->cbuf) { - down(&ov->cbuf_lock); + mutex_lock(&ov->cbuf_lock); kfree(ov->cbuf); ov->cbuf = NULL; - up(&ov->cbuf_lock); + mutex_unlock(&ov->cbuf_lock); } kfree(ov); @@ -5881,10 +5880,10 @@ ov51x_disconnect(struct usb_interface *intf) /* Free the memory */ if (ov && !ov->user) { - down(&ov->cbuf_lock); + mutex_lock(&ov->cbuf_lock); kfree(ov->cbuf); ov->cbuf = NULL; - up(&ov->cbuf_lock); + mutex_unlock(&ov->cbuf_lock); ov51x_dealloc(ov); kfree(ov); diff --git a/drivers/usb/media/ov511.h b/drivers/usb/media/ov511.h index 086509a137c6..bce9b3633889 100644 --- a/drivers/usb/media/ov511.h +++ b/drivers/usb/media/ov511.h @@ -5,6 +5,7 @@ #include #include #include +#include #define OV511_DEBUG /* Turn on debug messages */ @@ -435,7 +436,7 @@ struct usb_ov511 { int led_policy; /* LED: off|on|auto; OV511+ only */ - struct semaphore lock; /* Serializes user-accessible operations */ + struct mutex lock; /* Serializes user-accessible operations */ int user; /* user count for exclusive use */ int streaming; /* Are we streaming Isochronous? */ @@ -473,11 +474,9 @@ struct usb_ov511 { int packet_size; /* Frame size per isoc desc */ int packet_numbering; /* Is ISO frame numbering enabled? */ - struct semaphore param_lock; /* params lock for this camera */ - /* Framebuffer/sbuf management */ int buf_state; - struct semaphore buf_lock; + struct mutex buf_lock; struct ov51x_decomp_ops *decomp_ops; @@ -494,12 +493,12 @@ struct usb_ov511 { int pal; /* Device is designed for PAL resolution */ /* I2C interface */ - struct semaphore i2c_lock; /* Protect I2C controller regs */ + struct mutex i2c_lock; /* Protect I2C controller regs */ unsigned char primary_i2c_slave; /* I2C write id of sensor */ /* Control transaction stuff */ unsigned char *cbuf; /* Buffer for payload */ - struct semaphore cbuf_lock; + struct mutex cbuf_lock; }; /* Used to represent a list of values and their respective symbolic names */ diff --git a/drivers/usb/media/se401.c b/drivers/usb/media/se401.c index 2ba562285fda..8c3b1ad8a8fd 100644 --- a/drivers/usb/media/se401.c +++ b/drivers/usb/media/se401.c @@ -1157,21 +1157,21 @@ static int se401_mmap(struct file *file, struct vm_area_struct *vma) unsigned long size = vma->vm_end-vma->vm_start; unsigned long page, pos; - down(&se401->lock); + mutex_lock(&se401->lock); if (se401->dev == NULL) { - up(&se401->lock); + mutex_unlock(&se401->lock); return -EIO; } if (size > (((SE401_NUMFRAMES * se401->maxframesize) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) { - up(&se401->lock); + mutex_unlock(&se401->lock); return -EINVAL; } pos = (unsigned long)se401->fbuf; while (size > 0) { page = vmalloc_to_pfn((void *)pos); if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { - up(&se401->lock); + mutex_unlock(&se401->lock); return -EAGAIN; } start += PAGE_SIZE; @@ -1181,7 +1181,7 @@ static int se401_mmap(struct file *file, struct vm_area_struct *vma) else size = 0; } - up(&se401->lock); + mutex_unlock(&se401->lock); return 0; } @@ -1366,7 +1366,7 @@ static int se401_probe(struct usb_interface *intf, memcpy(&se401->vdev, &se401_template, sizeof(se401_template)); memcpy(se401->vdev.name, se401->camera_name, strlen(se401->camera_name)); init_waitqueue_head(&se401->wq); - init_MUTEX(&se401->lock); + mutex_init(&se401->lock); wmb(); if (video_register_device(&se401->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { diff --git a/drivers/usb/media/se401.h b/drivers/usb/media/se401.h index 2e5846f1eb20..e88a40d4c86a 100644 --- a/drivers/usb/media/se401.h +++ b/drivers/usb/media/se401.h @@ -5,6 +5,7 @@ #include #include #include +#include #define se401_DEBUG /* Turn on debug messages */ @@ -189,7 +190,7 @@ struct usb_se401 { int maxframesize; int cframesize; /* current framesize */ - struct semaphore lock; + struct mutex lock; int user; /* user count for exclusive use */ int removed; /* device disconnected */ diff --git a/drivers/usb/media/sn9c102.h b/drivers/usb/media/sn9c102.h index 17d60c1eea7e..59e44be27cb4 100644 --- a/drivers/usb/media/sn9c102.h +++ b/drivers/usb/media/sn9c102.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include "sn9c102_sensor.h" @@ -109,7 +110,7 @@ struct sn9c102_module_param { u8 force_munmap; }; -static DECLARE_MUTEX(sn9c102_sysfs_lock); +static DEFINE_MUTEX(sn9c102_sysfs_lock); static DECLARE_RWSEM(sn9c102_disconnect); struct sn9c102_device { @@ -141,7 +142,7 @@ struct sn9c102_device { enum sn9c102_dev_state state; u8 users; - struct semaphore dev_sem, fileop_sem; + struct mutex dev_mutex, fileop_mutex; spinlock_t queue_lock; wait_queue_head_t open, wait_frame, wait_stream; }; diff --git a/drivers/usb/media/sn9c102_core.c b/drivers/usb/media/sn9c102_core.c index c81397e4714b..30119080871b 100644 --- a/drivers/usb/media/sn9c102_core.c +++ b/drivers/usb/media/sn9c102_core.c @@ -866,18 +866,18 @@ static ssize_t sn9c102_show_reg(struct class_device* cd, char* buf) struct sn9c102_device* cam; ssize_t count; - if (down_interruptible(&sn9c102_sysfs_lock)) + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; cam = video_get_drvdata(to_video_device(cd)); if (!cam) { - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; } count = sprintf(buf, "%u\n", cam->sysfs.reg); - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); return count; } @@ -890,18 +890,18 @@ sn9c102_store_reg(struct class_device* cd, const char* buf, size_t len) u8 index; ssize_t count; - if (down_interruptible(&sn9c102_sysfs_lock)) + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; cam = video_get_drvdata(to_video_device(cd)); if (!cam) { - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; } index = sn9c102_strtou8(buf, len, &count); if (index > 0x1f || !count) { - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); return -EINVAL; } @@ -910,7 +910,7 @@ sn9c102_store_reg(struct class_device* cd, const char* buf, size_t len) DBG(2, "Moved SN9C10X register index to 0x%02X", cam->sysfs.reg); DBG(3, "Written bytes: %zd", count); - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); return count; } @@ -922,17 +922,17 @@ static ssize_t sn9c102_show_val(struct class_device* cd, char* buf) ssize_t count; int val; - if (down_interruptible(&sn9c102_sysfs_lock)) + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; cam = video_get_drvdata(to_video_device(cd)); if (!cam) { - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; } if ((val = sn9c102_read_reg(cam, cam->sysfs.reg)) < 0) { - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); return -EIO; } @@ -940,7 +940,7 @@ static ssize_t sn9c102_show_val(struct class_device* cd, char* buf) DBG(3, "Read bytes: %zd", count); - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); return count; } @@ -954,24 +954,24 @@ sn9c102_store_val(struct class_device* cd, const char* buf, size_t len) ssize_t count; int err; - if (down_interruptible(&sn9c102_sysfs_lock)) + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; cam = video_get_drvdata(to_video_device(cd)); if (!cam) { - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; } value = sn9c102_strtou8(buf, len, &count); if (!count) { - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); return -EINVAL; } err = sn9c102_write_reg(cam, value, cam->sysfs.reg); if (err) { - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); return -EIO; } @@ -979,7 +979,7 @@ sn9c102_store_val(struct class_device* cd, const char* buf, size_t len) cam->sysfs.reg, value); DBG(3, "Written bytes: %zd", count); - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); return count; } @@ -990,12 +990,12 @@ static ssize_t sn9c102_show_i2c_reg(struct class_device* cd, char* buf) struct sn9c102_device* cam; ssize_t count; - if (down_interruptible(&sn9c102_sysfs_lock)) + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; cam = video_get_drvdata(to_video_device(cd)); if (!cam) { - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; } @@ -1003,7 +1003,7 @@ static ssize_t sn9c102_show_i2c_reg(struct class_device* cd, char* buf) DBG(3, "Read bytes: %zd", count); - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); return count; } @@ -1016,18 +1016,18 @@ sn9c102_store_i2c_reg(struct class_device* cd, const char* buf, size_t len) u8 index; ssize_t count; - if (down_interruptible(&sn9c102_sysfs_lock)) + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; cam = video_get_drvdata(to_video_device(cd)); if (!cam) { - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; } index = sn9c102_strtou8(buf, len, &count); if (!count) { - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); return -EINVAL; } @@ -1036,7 +1036,7 @@ sn9c102_store_i2c_reg(struct class_device* cd, const char* buf, size_t len) DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg); DBG(3, "Written bytes: %zd", count); - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); return count; } @@ -1048,22 +1048,22 @@ static ssize_t sn9c102_show_i2c_val(struct class_device* cd, char* buf) ssize_t count; int val; - if (down_interruptible(&sn9c102_sysfs_lock)) + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; cam = video_get_drvdata(to_video_device(cd)); if (!cam) { - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; } if (!(cam->sensor->sysfs_ops & SN9C102_I2C_READ)) { - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); return -ENOSYS; } if ((val = sn9c102_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) { - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); return -EIO; } @@ -1071,7 +1071,7 @@ static ssize_t sn9c102_show_i2c_val(struct class_device* cd, char* buf) DBG(3, "Read bytes: %zd", count); - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); return count; } @@ -1085,29 +1085,29 @@ sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len) ssize_t count; int err; - if (down_interruptible(&sn9c102_sysfs_lock)) + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; cam = video_get_drvdata(to_video_device(cd)); if (!cam) { - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; } if (!(cam->sensor->sysfs_ops & SN9C102_I2C_WRITE)) { - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); return -ENOSYS; } value = sn9c102_strtou8(buf, len, &count); if (!count) { - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); return -EINVAL; } err = sn9c102_i2c_write(cam, cam->sysfs.i2c_reg, value); if (err) { - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); return -EIO; } @@ -1115,7 +1115,7 @@ sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len) cam->sysfs.i2c_reg, value); DBG(3, "Written bytes: %zd", count); - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); return count; } @@ -1130,18 +1130,18 @@ sn9c102_store_green(struct class_device* cd, const char* buf, size_t len) u8 value; ssize_t count; - if (down_interruptible(&sn9c102_sysfs_lock)) + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; cam = video_get_drvdata(to_video_device(cd)); if (!cam) { - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; } bridge = cam->bridge; - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); value = sn9c102_strtou8(buf, len, &count); if (!count) @@ -1404,7 +1404,7 @@ static int sn9c102_init(struct sn9c102_device* cam) } if (!(cam->state & DEV_INITIALIZED)) { - init_MUTEX(&cam->fileop_sem); + mutex_init(&cam->fileop_mutex); spin_lock_init(&cam->queue_lock); init_waitqueue_head(&cam->wait_frame); init_waitqueue_head(&cam->wait_stream); @@ -1422,13 +1422,13 @@ static int sn9c102_init(struct sn9c102_device* cam) static void sn9c102_release_resources(struct sn9c102_device* cam) { - down(&sn9c102_sysfs_lock); + mutex_lock(&sn9c102_sysfs_lock); DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor); video_set_drvdata(cam->v4ldev, NULL); video_unregister_device(cam->v4ldev); - up(&sn9c102_sysfs_lock); + mutex_unlock(&sn9c102_sysfs_lock); kfree(cam->control_buffer); } @@ -1449,7 +1449,7 @@ static int sn9c102_open(struct inode* inode, struct file* filp) cam = video_get_drvdata(video_devdata(filp)); - if (down_interruptible(&cam->dev_sem)) { + if (mutex_lock_interruptible(&cam->dev_mutex)) { up_read(&sn9c102_disconnect); return -ERESTARTSYS; } @@ -1461,7 +1461,7 @@ static int sn9c102_open(struct inode* inode, struct file* filp) err = -EWOULDBLOCK; goto out; } - up(&cam->dev_sem); + mutex_unlock(&cam->dev_mutex); err = wait_event_interruptible_exclusive(cam->open, cam->state & DEV_DISCONNECTED || !cam->users); @@ -1473,7 +1473,7 @@ static int sn9c102_open(struct inode* inode, struct file* filp) up_read(&sn9c102_disconnect); return -ENODEV; } - down(&cam->dev_sem); + mutex_lock(&cam->dev_mutex); } @@ -1501,7 +1501,7 @@ static int sn9c102_open(struct inode* inode, struct file* filp) DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor); out: - up(&cam->dev_sem); + mutex_unlock(&cam->dev_mutex); up_read(&sn9c102_disconnect); return err; } @@ -1511,7 +1511,7 @@ static int sn9c102_release(struct inode* inode, struct file* filp) { struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); - down(&cam->dev_sem); /* prevent disconnect() to be called */ + mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */ sn9c102_stop_transfer(cam); @@ -1519,7 +1519,7 @@ static int sn9c102_release(struct inode* inode, struct file* filp) if (cam->state & DEV_DISCONNECTED) { sn9c102_release_resources(cam); - up(&cam->dev_sem); + mutex_unlock(&cam->dev_mutex); kfree(cam); return 0; } @@ -1529,7 +1529,7 @@ static int sn9c102_release(struct inode* inode, struct file* filp) DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor); - up(&cam->dev_sem); + mutex_unlock(&cam->dev_mutex); return 0; } @@ -1543,33 +1543,33 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) unsigned long lock_flags; int err = 0; - if (down_interruptible(&cam->fileop_sem)) + if (mutex_lock_interruptible(&cam->fileop_mutex)) return -ERESTARTSYS; if (cam->state & DEV_DISCONNECTED) { DBG(1, "Device not present"); - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -ENODEV; } if (cam->state & DEV_MISCONFIGURED) { DBG(1, "The camera is misconfigured. Close and open it " "again."); - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -EIO; } if (cam->io == IO_MMAP) { DBG(3, "Close and open the device again to choose " "the read method"); - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -EINVAL; } if (cam->io == IO_NONE) { if (!sn9c102_request_buffers(cam,cam->nreadbuffers, IO_READ)) { DBG(1, "read() failed, not enough memory"); - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -ENOMEM; } cam->io = IO_READ; @@ -1583,13 +1583,13 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) } if (!count) { - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return 0; } if (list_empty(&cam->outqueue)) { if (filp->f_flags & O_NONBLOCK) { - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -EAGAIN; } err = wait_event_interruptible @@ -1598,15 +1598,15 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) (cam->state & DEV_DISCONNECTED) || (cam->state & DEV_MISCONFIGURED) ); if (err) { - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return err; } if (cam->state & DEV_DISCONNECTED) { - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -ENODEV; } if (cam->state & DEV_MISCONFIGURED) { - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -EIO; } } @@ -1634,7 +1634,7 @@ exit: PDBGG("Frame #%lu, bytes read: %zu", (unsigned long)f->buf.index, count); - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return count; } @@ -1647,7 +1647,7 @@ static unsigned int sn9c102_poll(struct file *filp, poll_table *wait) unsigned long lock_flags; unsigned int mask = 0; - if (down_interruptible(&cam->fileop_sem)) + if (mutex_lock_interruptible(&cam->fileop_mutex)) return POLLERR; if (cam->state & DEV_DISCONNECTED) { @@ -1685,12 +1685,12 @@ static unsigned int sn9c102_poll(struct file *filp, poll_table *wait) if (!list_empty(&cam->outqueue)) mask |= POLLIN | POLLRDNORM; - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return mask; error: - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return POLLERR; } @@ -1724,25 +1724,25 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma) void *pos; u32 i; - if (down_interruptible(&cam->fileop_sem)) + if (mutex_lock_interruptible(&cam->fileop_mutex)) return -ERESTARTSYS; if (cam->state & DEV_DISCONNECTED) { DBG(1, "Device not present"); - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -ENODEV; } if (cam->state & DEV_MISCONFIGURED) { DBG(1, "The camera is misconfigured. Close and open it " "again."); - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -EIO; } if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) || size != PAGE_ALIGN(cam->frame[0].buf.length)) { - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -EINVAL; } @@ -1751,7 +1751,7 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma) break; } if (i == cam->nbuffers) { - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -EINVAL; } @@ -1761,7 +1761,7 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma) pos = cam->frame[i].bufmem; while (size > 0) { /* size is page-aligned */ if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -EAGAIN; } start += PAGE_SIZE; @@ -1774,7 +1774,7 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma) sn9c102_vm_open(vma); - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return 0; } @@ -2655,19 +2655,19 @@ static int sn9c102_ioctl(struct inode* inode, struct file* filp, struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); int err = 0; - if (down_interruptible(&cam->fileop_sem)) + if (mutex_lock_interruptible(&cam->fileop_mutex)) return -ERESTARTSYS; if (cam->state & DEV_DISCONNECTED) { DBG(1, "Device not present"); - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -ENODEV; } if (cam->state & DEV_MISCONFIGURED) { DBG(1, "The camera is misconfigured. Close and open it " "again."); - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -EIO; } @@ -2675,7 +2675,7 @@ static int sn9c102_ioctl(struct inode* inode, struct file* filp, err = sn9c102_ioctl_v4l2(inode, filp, cmd, (void __user *)arg); - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return err; } @@ -2722,7 +2722,7 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) goto fail; } - init_MUTEX(&cam->dev_sem); + mutex_init(&cam->dev_mutex); r = sn9c102_read_reg(cam, 0x00); if (r < 0 || r != 0x10) { @@ -2776,7 +2776,7 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) cam->v4ldev->release = video_device_release; video_set_drvdata(cam->v4ldev, cam); - down(&cam->dev_sem); + mutex_lock(&cam->dev_mutex); err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, video_nr[dev_nr]); @@ -2786,7 +2786,7 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) DBG(1, "Free /dev/videoX node not found"); video_nr[dev_nr] = -1; dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0; - up(&cam->dev_sem); + mutex_unlock(&cam->dev_mutex); goto fail; } @@ -2803,7 +2803,7 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) usb_set_intfdata(intf, cam); - up(&cam->dev_sem); + mutex_unlock(&cam->dev_mutex); return 0; @@ -2827,7 +2827,7 @@ static void sn9c102_usb_disconnect(struct usb_interface* intf) down_write(&sn9c102_disconnect); - down(&cam->dev_sem); + mutex_lock(&cam->dev_mutex); DBG(2, "Disconnecting %s...", cam->v4ldev->name); @@ -2847,7 +2847,7 @@ static void sn9c102_usb_disconnect(struct usb_interface* intf) sn9c102_release_resources(cam); } - up(&cam->dev_sem); + mutex_unlock(&cam->dev_mutex); if (!cam->users) kfree(cam); diff --git a/drivers/usb/media/stv680.c b/drivers/usb/media/stv680.c index b497a6a0a206..b1a6be2958ed 100644 --- a/drivers/usb/media/stv680.c +++ b/drivers/usb/media/stv680.c @@ -67,6 +67,7 @@ #include #include #include +#include #include "stv680.h" @@ -1258,22 +1259,22 @@ static int stv680_mmap (struct file *file, struct vm_area_struct *vma) unsigned long size = vma->vm_end-vma->vm_start; unsigned long page, pos; - down (&stv680->lock); + mutex_lock(&stv680->lock); if (stv680->udev == NULL) { - up (&stv680->lock); + mutex_unlock(&stv680->lock); return -EIO; } if (size > (((STV680_NUMFRAMES * stv680->maxframesize) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) { - up (&stv680->lock); + mutex_unlock(&stv680->lock); return -EINVAL; } pos = (unsigned long) stv680->fbuf; while (size > 0) { page = vmalloc_to_pfn((void *)pos); if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { - up (&stv680->lock); + mutex_unlock(&stv680->lock); return -EAGAIN; } start += PAGE_SIZE; @@ -1283,7 +1284,7 @@ static int stv680_mmap (struct file *file, struct vm_area_struct *vma) else size = 0; } - up (&stv680->lock); + mutex_unlock(&stv680->lock); return 0; } @@ -1409,7 +1410,7 @@ static int stv680_probe (struct usb_interface *intf, const struct usb_device_id memcpy (stv680->vdev->name, stv680->camera_name, strlen (stv680->camera_name)); init_waitqueue_head (&stv680->wq); - init_MUTEX (&stv680->lock); + mutex_init (&stv680->lock); wmb (); if (video_register_device (stv680->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { diff --git a/drivers/usb/media/stv680.h b/drivers/usb/media/stv680.h index b0551cdb280b..ea46e0001e6d 100644 --- a/drivers/usb/media/stv680.h +++ b/drivers/usb/media/stv680.h @@ -118,7 +118,7 @@ struct usb_stv { int origGain; int origMode; /* original camera mode */ - struct semaphore lock; /* to lock the structure */ + struct mutex lock; /* to lock the structure */ int user; /* user count for exclusive use */ int removed; /* device disconnected */ int streaming; /* Are we streaming video? */ diff --git a/drivers/usb/media/usbvideo.c b/drivers/usb/media/usbvideo.c index 63d279ec722b..0b51fae720a9 100644 --- a/drivers/usb/media/usbvideo.c +++ b/drivers/usb/media/usbvideo.c @@ -714,7 +714,7 @@ int usbvideo_register( cams->md_module = md; if (cams->md_module == NULL) warn("%s: module == NULL!", __FUNCTION__); - init_MUTEX(&cams->lock); /* to 1 == available */ + mutex_init(&cams->lock); /* to 1 == available */ for (i = 0; i < num_cams; i++) { struct uvd *up = &cams->cam[i]; @@ -862,7 +862,7 @@ static void usbvideo_Disconnect(struct usb_interface *intf) if (uvd->debug > 0) info("%s(%p.)", __FUNCTION__, intf); - down(&uvd->lock); + mutex_lock(&uvd->lock); uvd->remove_pending = 1; /* Now all ISO data will be ignored */ /* At this time we ask to cancel outstanding URBs */ @@ -882,7 +882,7 @@ static void usbvideo_Disconnect(struct usb_interface *intf) info("%s: In use, disconnect pending.", __FUNCTION__); else usbvideo_CameraRelease(uvd); - up(&uvd->lock); + mutex_unlock(&uvd->lock); info("USB camera disconnected."); usbvideo_ClientDecModCount(uvd); @@ -929,19 +929,19 @@ static int usbvideo_find_struct(struct usbvideo *cams) err("No usbvideo handle?"); return -1; } - down(&cams->lock); + mutex_lock(&cams->lock); for (u = 0; u < cams->num_cameras; u++) { struct uvd *uvd = &cams->cam[u]; if (!uvd->uvd_used) /* This one is free */ { uvd->uvd_used = 1; /* In use now */ - init_MUTEX(&uvd->lock); /* to 1 == available */ + mutex_init(&uvd->lock); /* to 1 == available */ uvd->dev = NULL; rv = u; break; } } - up(&cams->lock); + mutex_unlock(&cams->lock); return rv; } @@ -983,7 +983,7 @@ struct uvd *usbvideo_AllocateDevice(struct usbvideo *cams) /* Not relying upon caller we increase module counter ourselves */ usbvideo_ClientIncModCount(uvd); - down(&uvd->lock); + mutex_lock(&uvd->lock); for (i=0; i < USBVIDEO_NUMSBUF; i++) { uvd->sbuf[i].urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); if (uvd->sbuf[i].urb == NULL) { @@ -1006,7 +1006,7 @@ struct uvd *usbvideo_AllocateDevice(struct usbvideo *cams) * return control to the client's probe function right now. */ allocate_done: - up (&uvd->lock); + mutex_unlock(&uvd->lock); usbvideo_ClientDecModCount(uvd); return uvd; } @@ -1120,7 +1120,7 @@ static int usbvideo_v4l_open(struct inode *inode, struct file *file) info("%s($%p)", __FUNCTION__, dev); usbvideo_ClientIncModCount(uvd); - down(&uvd->lock); + mutex_lock(&uvd->lock); if (uvd->user) { err("%s: Someone tried to open an already opened device!", __FUNCTION__); @@ -1201,7 +1201,7 @@ static int usbvideo_v4l_open(struct inode *inode, struct file *file) } } } - up(&uvd->lock); + mutex_unlock(&uvd->lock); if (errCode != 0) usbvideo_ClientDecModCount(uvd); if (uvd->debug > 0) @@ -1230,7 +1230,7 @@ static int usbvideo_v4l_close(struct inode *inode, struct file *file) if (uvd->debug > 1) info("%s($%p)", __FUNCTION__, dev); - down(&uvd->lock); + mutex_lock(&uvd->lock); GET_CALLBACK(uvd, stopDataPump)(uvd); usbvideo_rvfree(uvd->fbuf, uvd->fbuf_size); uvd->fbuf = NULL; @@ -1251,7 +1251,7 @@ static int usbvideo_v4l_close(struct inode *inode, struct file *file) info("usbvideo_v4l_close: Final disconnect."); usbvideo_CameraRelease(uvd); } - up(&uvd->lock); + mutex_unlock(&uvd->lock); usbvideo_ClientDecModCount(uvd); if (uvd->debug > 1) @@ -1511,7 +1511,7 @@ static ssize_t usbvideo_v4l_read(struct file *file, char __user *buf, if (uvd->debug >= 1) info("%s: %Zd. bytes, noblock=%d.", __FUNCTION__, count, noblock); - down(&uvd->lock); + mutex_lock(&uvd->lock); /* See if a frame is completed, then use it. */ for(i = 0; i < USBVIDEO_NUMFRAMES; i++) { @@ -1643,7 +1643,7 @@ static ssize_t usbvideo_v4l_read(struct file *file, char __user *buf, } } read_done: - up(&uvd->lock); + mutex_unlock(&uvd->lock); return count; } diff --git a/drivers/usb/media/usbvideo.h b/drivers/usb/media/usbvideo.h index 6c390a1f981b..135433c2680a 100644 --- a/drivers/usb/media/usbvideo.h +++ b/drivers/usb/media/usbvideo.h @@ -19,6 +19,7 @@ #include #include #include +#include /* Most helpful debugging aid */ #define assert(expr) ((void) ((expr) ? 0 : (err("assert failed at line %d",__LINE__)))) @@ -213,7 +214,7 @@ struct uvd { unsigned long flags; /* FLAGS_USBVIDEO_xxx */ unsigned long paletteBits; /* Which palettes we accept? */ unsigned short defaultPalette; /* What palette to use for read() */ - struct semaphore lock; + struct mutex lock; int user; /* user count for exclusive use */ videosize_t videosize; /* Current setting */ @@ -272,7 +273,7 @@ struct usbvideo { int num_cameras; /* As allocated */ struct usb_driver usbdrv; /* Interface to the USB stack */ char drvName[80]; /* Driver name */ - struct semaphore lock; /* Mutex protecting camera structures */ + struct mutex lock; /* Mutex protecting camera structures */ struct usbvideo_cb cb; /* Table of callbacks (virtual methods) */ struct video_device vdt; /* Video device template */ struct uvd *cam; /* Array of camera structures */ diff --git a/drivers/usb/media/vicam.c b/drivers/usb/media/vicam.c index 5df144073871..8cfc47b831fb 100644 --- a/drivers/usb/media/vicam.c +++ b/drivers/usb/media/vicam.c @@ -42,6 +42,7 @@ #include #include #include +#include #include "usbvideo.h" // #define VICAM_DEBUG @@ -407,7 +408,7 @@ struct vicam_camera { struct usb_device *udev; // usb device /* guard against simultaneous accesses to the camera */ - struct semaphore cam_lock; + struct mutex cam_lock; int is_initialized; u8 open_count; @@ -461,12 +462,12 @@ static int send_control_msg(struct vicam_camera *cam, u16 size) { int status = -ENODEV; - down(&cam->cam_lock); + mutex_lock(&cam->cam_lock); if (cam->udev) { status = __send_control_msg(cam, request, value, index, cp, size); } - up(&cam->cam_lock); + mutex_unlock(&cam->cam_lock); return status; } static int @@ -831,13 +832,13 @@ vicam_close(struct inode *inode, struct file *file) rvfree(cam->framebuf, VICAM_MAX_FRAME_SIZE * VICAM_FRAMES); kfree(cam->cntrlbuf); - down(&cam->cam_lock); + mutex_lock(&cam->cam_lock); cam->open_count--; open_count = cam->open_count; udev = cam->udev; - up(&cam->cam_lock); + mutex_unlock(&cam->cam_lock); if (!open_count && !udev) { kfree(cam); @@ -960,7 +961,7 @@ read_frame(struct vicam_camera *cam, int framenum) request[8] = 0; // bytes 9-15 do not seem to affect exposure or image quality - down(&cam->cam_lock); + mutex_lock(&cam->cam_lock); if (!cam->udev) { goto done; @@ -985,7 +986,7 @@ read_frame(struct vicam_camera *cam, int framenum) } done: - up(&cam->cam_lock); + mutex_unlock(&cam->cam_lock); } static ssize_t @@ -1309,7 +1310,7 @@ vicam_probe( struct usb_interface *intf, const struct usb_device_id *id) cam->shutter_speed = 15; - init_MUTEX(&cam->cam_lock); + mutex_init(&cam->cam_lock); memcpy(&cam->vdev, &vicam_template, sizeof (vicam_template)); @@ -1351,7 +1352,7 @@ vicam_disconnect(struct usb_interface *intf) /* stop the camera from being used */ - down(&cam->cam_lock); + mutex_lock(&cam->cam_lock); /* mark the camera as gone */ @@ -1368,7 +1369,7 @@ vicam_disconnect(struct usb_interface *intf) open_count = cam->open_count; - up(&cam->cam_lock); + mutex_unlock(&cam->cam_lock); if (!open_count) { kfree(cam); diff --git a/drivers/usb/media/w9968cf.c b/drivers/usb/media/w9968cf.c index 78cd1a577d26..b57dec3782e0 100644 --- a/drivers/usb/media/w9968cf.c +++ b/drivers/usb/media/w9968cf.c @@ -47,6 +47,13 @@ #include "w9968cf.h" #include "w9968cf_decoder.h" +static struct w9968cf_vpp_t* w9968cf_vpp; +static DECLARE_WAIT_QUEUE_HEAD(w9968cf_vppmod_wait); + +static LIST_HEAD(w9968cf_dev_list); /* head of V4L registered cameras list */ +static DEFINE_MUTEX(w9968cf_devlist_mutex); /* semaphore for list traversal */ + +static DECLARE_RWSEM(w9968cf_disconnect); /* prevent races with open() */ /**************************************************************************** @@ -2418,7 +2425,7 @@ w9968cf_configure_camera(struct w9968cf_device* cam, enum w9968cf_model_id mod_id, const unsigned short dev_nr) { - init_MUTEX(&cam->fileop_sem); + mutex_init(&cam->fileop_mutex); init_waitqueue_head(&cam->open); spin_lock_init(&cam->urb_lock); spin_lock_init(&cam->flist_lock); @@ -2646,7 +2653,7 @@ static void w9968cf_adjust_configuration(struct w9968cf_device* cam) --------------------------------------------------------------------------*/ static void w9968cf_release_resources(struct w9968cf_device* cam) { - down(&w9968cf_devlist_sem); + mutex_lock(&w9968cf_devlist_mutex); DBG(2, "V4L device deregistered: /dev/video%d", cam->v4ldev->minor) @@ -2657,7 +2664,7 @@ static void w9968cf_release_resources(struct w9968cf_device* cam) kfree(cam->control_buffer); kfree(cam->data_buffer); - up(&w9968cf_devlist_sem); + mutex_unlock(&w9968cf_devlist_mutex); } @@ -2677,14 +2684,14 @@ static int w9968cf_open(struct inode* inode, struct file* filp) cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp)); - down(&cam->dev_sem); + mutex_lock(&cam->dev_mutex); if (cam->sensor == CC_UNKNOWN) { DBG(2, "No supported image sensor has been detected by the " "'ovcamchip' module for the %s (/dev/video%d). Make " "sure it is loaded *before* (re)connecting the camera.", symbolic(camlist, cam->id), cam->v4ldev->minor) - up(&cam->dev_sem); + mutex_unlock(&cam->dev_mutex); up_read(&w9968cf_disconnect); return -ENODEV; } @@ -2693,11 +2700,11 @@ static int w9968cf_open(struct inode* inode, struct file* filp) DBG(2, "%s (/dev/video%d) has been already occupied by '%s'", symbolic(camlist, cam->id),cam->v4ldev->minor,cam->command) if ((filp->f_flags & O_NONBLOCK)||(filp->f_flags & O_NDELAY)) { - up(&cam->dev_sem); + mutex_unlock(&cam->dev_mutex); up_read(&w9968cf_disconnect); return -EWOULDBLOCK; } - up(&cam->dev_sem); + mutex_unlock(&cam->dev_mutex); err = wait_event_interruptible_exclusive(cam->open, cam->disconnected || !cam->users); @@ -2709,7 +2716,7 @@ static int w9968cf_open(struct inode* inode, struct file* filp) up_read(&w9968cf_disconnect); return -ENODEV; } - down(&cam->dev_sem); + mutex_lock(&cam->dev_mutex); } DBG(5, "Opening '%s', /dev/video%d ...", @@ -2738,7 +2745,7 @@ static int w9968cf_open(struct inode* inode, struct file* filp) DBG(5, "Video device is open") - up(&cam->dev_sem); + mutex_unlock(&cam->dev_mutex); up_read(&w9968cf_disconnect); return 0; @@ -2746,7 +2753,7 @@ static int w9968cf_open(struct inode* inode, struct file* filp) deallocate_memory: w9968cf_deallocate_memory(cam); DBG(2, "Failed to open the video device") - up(&cam->dev_sem); + mutex_unlock(&cam->dev_mutex); up_read(&w9968cf_disconnect); return err; } @@ -2758,13 +2765,13 @@ static int w9968cf_release(struct inode* inode, struct file* filp) cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp)); - down(&cam->dev_sem); /* prevent disconnect() to be called */ + mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */ w9968cf_stop_transfer(cam); if (cam->disconnected) { w9968cf_release_resources(cam); - up(&cam->dev_sem); + mutex_unlock(&cam->dev_mutex); kfree(cam); return 0; } @@ -2774,7 +2781,7 @@ static int w9968cf_release(struct inode* inode, struct file* filp) wake_up_interruptible_nr(&cam->open, 1); DBG(5, "Video device closed") - up(&cam->dev_sem); + mutex_unlock(&cam->dev_mutex); return 0; } @@ -2791,18 +2798,18 @@ w9968cf_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) if (filp->f_flags & O_NONBLOCK) return -EWOULDBLOCK; - if (down_interruptible(&cam->fileop_sem)) + if (mutex_lock_interruptible(&cam->fileop_mutex)) return -ERESTARTSYS; if (cam->disconnected) { DBG(2, "Device not present") - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -ENODEV; } if (cam->misconfigured) { DBG(2, "The camera is misconfigured. Close and open it again.") - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -EIO; } @@ -2817,11 +2824,11 @@ w9968cf_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) cam->frame[1].status == F_READY || cam->disconnected); if (err) { - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return err; } if (cam->disconnected) { - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -ENODEV; } @@ -2835,7 +2842,7 @@ w9968cf_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) if (copy_to_user(buf, fr->buffer, count)) { fr->status = F_UNUSED; - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -EFAULT; } *f_pos += count; @@ -2844,7 +2851,7 @@ w9968cf_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) DBG(5, "%zu bytes read", count) - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return count; } @@ -2898,24 +2905,24 @@ w9968cf_ioctl(struct inode* inode, struct file* filp, cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp)); - if (down_interruptible(&cam->fileop_sem)) + if (mutex_lock_interruptible(&cam->fileop_mutex)) return -ERESTARTSYS; if (cam->disconnected) { DBG(2, "Device not present") - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -ENODEV; } if (cam->misconfigured) { DBG(2, "The camera is misconfigured. Close and open it again.") - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -EIO; } err = w9968cf_v4l_ioctl(inode, filp, cmd, (void __user *)arg); - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return err; } @@ -3502,8 +3509,8 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) if (!cam) return -ENOMEM; - init_MUTEX(&cam->dev_sem); - down(&cam->dev_sem); + mutex_init(&cam->dev_mutex); + mutex_lock(&cam->dev_mutex); cam->usbdev = udev; /* NOTE: a local copy is used to avoid possible race conditions */ @@ -3515,10 +3522,10 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) simcams = W9968CF_SIMCAMS; /* How many cameras are connected ? */ - down(&w9968cf_devlist_sem); + mutex_lock(&w9968cf_devlist_mutex); list_for_each(ptr, &w9968cf_dev_list) sc++; - up(&w9968cf_devlist_sem); + mutex_unlock(&w9968cf_devlist_mutex); if (sc >= simcams) { DBG(2, "Device rejected: too many connected cameras " @@ -3578,9 +3585,9 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) w9968cf_configure_camera(cam, udev, mod_id, dev_nr); /* Add a new entry into the list of V4L registered devices */ - down(&w9968cf_devlist_sem); + mutex_lock(&w9968cf_devlist_mutex); list_add(&cam->v4llist, &w9968cf_dev_list); - up(&w9968cf_devlist_sem); + mutex_unlock(&w9968cf_devlist_mutex); dev_nr = (dev_nr < W9968CF_MAX_DEVICES-1) ? dev_nr+1 : 0; w9968cf_turn_on_led(cam); @@ -3588,7 +3595,7 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) w9968cf_i2c_init(cam); usb_set_intfdata(intf, cam); - up(&cam->dev_sem); + mutex_unlock(&cam->dev_mutex); return 0; fail: /* Free unused memory */ @@ -3596,7 +3603,7 @@ fail: /* Free unused memory */ kfree(cam->data_buffer); if (cam->v4ldev) video_device_release(cam->v4ldev); - up(&cam->dev_sem); + mutex_unlock(&cam->dev_mutex); kfree(cam); return err; } @@ -3611,7 +3618,7 @@ static void w9968cf_usb_disconnect(struct usb_interface* intf) if (cam) { /* Prevent concurrent accesses to data */ - down(&cam->dev_sem); + mutex_lock(&cam->dev_mutex); cam->disconnected = 1; @@ -3630,7 +3637,7 @@ static void w9968cf_usb_disconnect(struct usb_interface* intf) } else w9968cf_release_resources(cam); - up(&cam->dev_sem); + mutex_unlock(&cam->dev_mutex); if (!cam->users) kfree(cam); diff --git a/drivers/usb/media/w9968cf.h b/drivers/usb/media/w9968cf.h index 47a6ff794171..a87be719a281 100644 --- a/drivers/usb/media/w9968cf.h +++ b/drivers/usb/media/w9968cf.h @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include @@ -194,14 +194,6 @@ enum w9968cf_vpp_flag { VPP_UYVY_TO_RGBX = 0x08, }; -static struct w9968cf_vpp_t* w9968cf_vpp; -static DECLARE_WAIT_QUEUE_HEAD(w9968cf_vppmod_wait); - -static LIST_HEAD(w9968cf_dev_list); /* head of V4L registered cameras list */ -static DECLARE_MUTEX(w9968cf_devlist_sem); /* semaphore for list traversal */ - -static DECLARE_RWSEM(w9968cf_disconnect); /* prevent races with open() */ - /* Main device driver structure */ struct w9968cf_device { struct device dev; /* device structure */ @@ -277,8 +269,8 @@ struct w9968cf_device { struct i2c_client* sensor_client; /* Locks */ - struct semaphore dev_sem, /* for probe, disconnect,open and close */ - fileop_sem; /* for read and ioctl */ + struct mutex dev_mutex, /* for probe, disconnect,open and close */ + fileop_mutex; /* for read and ioctl */ spinlock_t urb_lock, /* for submit_urb() and unlink_urb() */ flist_lock; /* for requested frame list accesses */ wait_queue_head_t open, wait_queue; diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index b3aca5124339..d0b167256699 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -121,7 +122,7 @@ static struct usb_driver idmouse_driver = { }; /* prevent races between open() and disconnect() */ -static DECLARE_MUTEX(disconnect_sem); +static DEFINE_MUTEX(disconnect_mutex); static int idmouse_create_image(struct usb_idmouse *dev) { @@ -213,18 +214,18 @@ static int idmouse_open(struct inode *inode, struct file *file) int result = 0; /* prevent disconnects */ - down(&disconnect_sem); + mutex_lock(&disconnect_mutex); /* get the interface from minor number and driver information */ interface = usb_find_interface (&idmouse_driver, iminor (inode)); if (!interface) { - up(&disconnect_sem); + mutex_unlock(&disconnect_mutex); return -ENODEV; } /* get the device information block from the interface */ dev = usb_get_intfdata(interface); if (!dev) { - up(&disconnect_sem); + mutex_unlock(&disconnect_mutex); return -ENODEV; } @@ -258,7 +259,7 @@ error: up(&dev->sem); /* unlock the disconnect semaphore */ - up(&disconnect_sem); + mutex_unlock(&disconnect_mutex); return result; } @@ -267,12 +268,12 @@ static int idmouse_release(struct inode *inode, struct file *file) struct usb_idmouse *dev; /* prevent a race condition with open() */ - down(&disconnect_sem); + mutex_lock(&disconnect_mutex); dev = (struct usb_idmouse *) file->private_data; if (dev == NULL) { - up(&disconnect_sem); + mutex_unlock(&disconnect_mutex); return -ENODEV; } @@ -282,7 +283,7 @@ static int idmouse_release(struct inode *inode, struct file *file) /* are we really open? */ if (dev->open <= 0) { up(&dev->sem); - up(&disconnect_sem); + mutex_unlock(&disconnect_mutex); return -ENODEV; } @@ -292,12 +293,12 @@ static int idmouse_release(struct inode *inode, struct file *file) /* the device was unplugged before the file was released */ up(&dev->sem); idmouse_delete(dev); - up(&disconnect_sem); + mutex_unlock(&disconnect_mutex); return 0; } up(&dev->sem); - up(&disconnect_sem); + mutex_unlock(&disconnect_mutex); return 0; } @@ -399,7 +400,7 @@ static void idmouse_disconnect(struct usb_interface *interface) struct usb_idmouse *dev; /* prevent races with open() */ - down(&disconnect_sem); + mutex_lock(&disconnect_mutex); /* get device structure */ dev = usb_get_intfdata(interface); @@ -421,7 +422,7 @@ static void idmouse_disconnect(struct usb_interface *interface) if (!dev->open) idmouse_delete(dev); - up(&disconnect_sem); + mutex_unlock(&disconnect_mutex); info("%s disconnected", DRIVER_DESC); } diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index f6bdaf08a80f..966acb474f67 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -172,7 +173,7 @@ struct ld_usb { }; /* prevent races between open() and disconnect() */ -static DECLARE_MUTEX(disconnect_sem); +static DEFINE_MUTEX(disconnect_mutex); static struct usb_driver ld_usb_driver; @@ -293,7 +294,7 @@ static int ld_usb_open(struct inode *inode, struct file *file) nonseekable_open(inode, file); subminor = iminor(inode); - down(&disconnect_sem); + mutex_lock(&disconnect_mutex); interface = usb_find_interface(&ld_usb_driver, subminor); @@ -355,7 +356,7 @@ unlock_exit: up(&dev->sem); unlock_disconnect_exit: - up(&disconnect_sem); + mutex_unlock(&disconnect_mutex); return retval; } @@ -740,7 +741,7 @@ static void ld_usb_disconnect(struct usb_interface *intf) struct ld_usb *dev; int minor; - down(&disconnect_sem); + mutex_lock(&disconnect_mutex); dev = usb_get_intfdata(intf); usb_set_intfdata(intf, NULL); @@ -761,7 +762,7 @@ static void ld_usb_disconnect(struct usb_interface *intf) up(&dev->sem); } - up(&disconnect_sem); + mutex_unlock(&disconnect_mutex); dev_info(&intf->dev, "LD USB Device #%d now disconnected\n", (minor - USB_LD_MINOR_BASE)); diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 1336745b8f55..779bcf0373ad 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -83,6 +83,7 @@ #include #include #include +#include #include #include #include @@ -256,7 +257,7 @@ static void tower_disconnect (struct usb_interface *interface); /* prevent races between open() and disconnect */ -static DECLARE_MUTEX (disconnect_sem); +static DEFINE_MUTEX (disconnect_mutex); /* file operations needed when we register this driver */ static struct file_operations tower_fops = { @@ -349,7 +350,7 @@ static int tower_open (struct inode *inode, struct file *file) nonseekable_open(inode, file); subminor = iminor(inode); - down (&disconnect_sem); + mutex_lock (&disconnect_mutex); interface = usb_find_interface (&tower_driver, subminor); @@ -427,7 +428,7 @@ unlock_exit: up (&dev->sem); unlock_disconnect_exit: - up (&disconnect_sem); + mutex_unlock (&disconnect_mutex); dbg(2, "%s: leave, return value %d ", __FUNCTION__, retval); @@ -1005,7 +1006,7 @@ static void tower_disconnect (struct usb_interface *interface) dbg(2, "%s: enter", __FUNCTION__); - down (&disconnect_sem); + mutex_lock (&disconnect_mutex); dev = usb_get_intfdata (interface); usb_set_intfdata (interface, NULL); @@ -1027,7 +1028,7 @@ static void tower_disconnect (struct usb_interface *interface) up (&dev->sem); } - up (&disconnect_sem); + mutex_unlock (&disconnect_mutex); info("LEGO USB Tower #%d now disconnected", (minor - LEGO_USB_TOWER_MINOR_BASE)); diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c index c34944c75047..b03e346e33f1 100644 --- a/drivers/usb/mon/mon_main.c +++ b/drivers/usb/mon/mon_main.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "usb_mon.h" #include "../core/hcd.h" @@ -23,7 +24,7 @@ static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus); static void mon_bus_drop(struct kref *r); static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus); -DECLARE_MUTEX(mon_lock); +DEFINE_MUTEX(mon_lock); static struct dentry *mon_dir; /* /dbg/usbmon */ static LIST_HEAD(mon_buses); /* All buses we know: struct mon_bus */ @@ -196,14 +197,14 @@ static void mon_bus_remove(struct usb_bus *ubus) { struct mon_bus *mbus = ubus->mon_bus; - down(&mon_lock); + mutex_lock(&mon_lock); list_del(&mbus->bus_link); debugfs_remove(mbus->dent_t); debugfs_remove(mbus->dent_s); mon_dissolve(mbus, ubus); kref_put(&mbus->ref, mon_bus_drop); - up(&mon_lock); + mutex_unlock(&mon_lock); } static int mon_notify(struct notifier_block *self, unsigned long action, @@ -307,9 +308,9 @@ static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus) goto err_create_s; mbus->dent_s = d; - down(&mon_lock); + mutex_lock(&mon_lock); list_add_tail(&mbus->bus_link, &mon_buses); - up(&mon_lock); + mutex_unlock(&mon_lock); return; err_create_s: @@ -347,11 +348,11 @@ static int __init mon_init(void) usb_register_notify(&mon_nb); - down(&usb_bus_list_lock); + mutex_lock(&usb_bus_list_lock); list_for_each_entry (ubus, &usb_bus_list, bus_list) { mon_bus_init(mondir, ubus); } - up(&usb_bus_list_lock); + mutex_unlock(&usb_bus_list_lock); return 0; } @@ -363,7 +364,7 @@ static void __exit mon_exit(void) usb_unregister_notify(&mon_nb); usb_mon_deregister(); - down(&mon_lock); + mutex_lock(&mon_lock); while (!list_empty(&mon_buses)) { p = mon_buses.next; mbus = list_entry(p, struct mon_bus, bus_link); @@ -387,7 +388,7 @@ static void __exit mon_exit(void) mon_dissolve(mbus, mbus->u_bus); kref_put(&mbus->ref, mon_bus_drop); } - up(&mon_lock); + mutex_unlock(&mon_lock); debugfs_remove(mon_dir); } diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c index 611612146ae9..59089e8b7e5e 100644 --- a/drivers/usb/mon/mon_text.c +++ b/drivers/usb/mon/mon_text.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "usb_mon.h" @@ -54,7 +55,7 @@ struct mon_reader_text { wait_queue_head_t wait; int printf_size; char *printf_buf; - struct semaphore printf_lock; + struct mutex printf_lock; char slab_name[SLAB_NAME_SZ]; }; @@ -208,7 +209,7 @@ static int mon_text_open(struct inode *inode, struct file *file) struct mon_reader_text *rp; int rc; - down(&mon_lock); + mutex_lock(&mon_lock); mbus = inode->u.generic_ip; ubus = mbus->u_bus; @@ -220,7 +221,7 @@ static int mon_text_open(struct inode *inode, struct file *file) memset(rp, 0, sizeof(struct mon_reader_text)); INIT_LIST_HEAD(&rp->e_list); init_waitqueue_head(&rp->wait); - init_MUTEX(&rp->printf_lock); + mutex_init(&rp->printf_lock); rp->printf_size = PRINTF_DFL; rp->printf_buf = kmalloc(rp->printf_size, GFP_KERNEL); @@ -247,7 +248,7 @@ static int mon_text_open(struct inode *inode, struct file *file) mon_reader_add(mbus, &rp->r); file->private_data = rp; - up(&mon_lock); + mutex_unlock(&mon_lock); return 0; // err_busy: @@ -257,7 +258,7 @@ err_slab: err_alloc_pr: kfree(rp); err_alloc: - up(&mon_lock); + mutex_unlock(&mon_lock); return rc; } @@ -301,7 +302,7 @@ static ssize_t mon_text_read(struct file *file, char __user *buf, set_current_state(TASK_RUNNING); remove_wait_queue(&rp->wait, &waita); - down(&rp->printf_lock); + mutex_lock(&rp->printf_lock); cnt = 0; pbuf = rp->printf_buf; limit = rp->printf_size; @@ -358,7 +359,7 @@ static ssize_t mon_text_read(struct file *file, char __user *buf, if (copy_to_user(buf, rp->printf_buf, cnt)) cnt = -EFAULT; - up(&rp->printf_lock); + mutex_unlock(&rp->printf_lock); kmem_cache_free(rp->e_slab, ep); return cnt; } @@ -371,12 +372,12 @@ static int mon_text_release(struct inode *inode, struct file *file) struct list_head *p; struct mon_event_text *ep; - down(&mon_lock); + mutex_lock(&mon_lock); mbus = inode->u.generic_ip; if (mbus->nreaders <= 0) { printk(KERN_ERR TAG ": consistency error on close\n"); - up(&mon_lock); + mutex_unlock(&mon_lock); return 0; } mon_reader_del(mbus, &rp->r); @@ -402,7 +403,7 @@ static int mon_text_release(struct inode *inode, struct file *file) kfree(rp->printf_buf); kfree(rp); - up(&mon_lock); + mutex_unlock(&mon_lock); return 0; } diff --git a/drivers/usb/mon/usb_mon.h b/drivers/usb/mon/usb_mon.h index 4be0f9346071..8e0613c350cc 100644 --- a/drivers/usb/mon/usb_mon.h +++ b/drivers/usb/mon/usb_mon.h @@ -49,7 +49,7 @@ void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r); */ extern char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len); -extern struct semaphore mon_lock; +extern struct mutex mon_lock; extern struct file_operations mon_fops_text; extern struct file_operations mon_fops_stat; diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 4ef5527028c5..5f11e19eaae3 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -47,6 +47,7 @@ #include #include +#include #include #include @@ -271,9 +272,9 @@ static int device_reset(struct scsi_cmnd *srb) US_DEBUGP("%s called\n", __FUNCTION__); /* lock the device pointers and do the reset */ - down(&(us->dev_semaphore)); + mutex_lock(&(us->dev_mutex)); result = us->transport_reset(us); - up(&(us->dev_semaphore)); + mutex_unlock(&us->dev_mutex); return result < 0 ? FAILED : SUCCESS; } @@ -286,9 +287,9 @@ static int bus_reset(struct scsi_cmnd *srb) US_DEBUGP("%s called\n", __FUNCTION__); - down(&(us->dev_semaphore)); + mutex_lock(&(us->dev_mutex)); result = usb_stor_port_reset(us); - up(&(us->dev_semaphore)); + mutex_unlock(&us->dev_mutex); return result < 0 ? FAILED : SUCCESS; } diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index dbcf23980ff1..dd108634348e 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -55,6 +55,7 @@ #include #include #include +#include #include #include @@ -188,7 +189,7 @@ static int storage_suspend(struct usb_interface *iface, pm_message_t message) struct us_data *us = usb_get_intfdata(iface); /* Wait until no command is running */ - down(&us->dev_semaphore); + mutex_lock(&us->dev_mutex); US_DEBUGP("%s\n", __FUNCTION__); if (us->suspend_resume_hook) @@ -198,7 +199,7 @@ static int storage_suspend(struct usb_interface *iface, pm_message_t message) /* When runtime PM is working, we'll set a flag to indicate * whether we should autoresume when a SCSI request arrives. */ - up(&us->dev_semaphore); + mutex_unlock(&us->dev_mutex); return 0; } @@ -206,14 +207,14 @@ static int storage_resume(struct usb_interface *iface) { struct us_data *us = usb_get_intfdata(iface); - down(&us->dev_semaphore); + mutex_lock(&us->dev_mutex); US_DEBUGP("%s\n", __FUNCTION__); if (us->suspend_resume_hook) (us->suspend_resume_hook)(us, US_RESUME); iface->dev.power.power_state.event = PM_EVENT_ON; - up(&us->dev_semaphore); + mutex_unlock(&us->dev_mutex); return 0; } @@ -276,12 +277,12 @@ static int usb_stor_control_thread(void * __us) US_DEBUGP("*** thread awakened.\n"); /* lock the device pointers */ - down(&(us->dev_semaphore)); + mutex_lock(&(us->dev_mutex)); /* if the device has disconnected, we are free to exit */ if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) { US_DEBUGP("-- exiting\n"); - up(&(us->dev_semaphore)); + mutex_unlock(&us->dev_mutex); break; } @@ -370,7 +371,7 @@ SkipForAbort: scsi_unlock(host); /* unlock the device pointers */ - up(&(us->dev_semaphore)); + mutex_unlock(&us->dev_mutex); } /* for (;;) */ scsi_host_put(host); @@ -815,8 +816,8 @@ static void quiesce_and_remove_host(struct us_data *us) * The thread will exit when it sees the DISCONNECTING flag. */ /* Wait for the current command to finish, then remove the host */ - down(&us->dev_semaphore); - up(&us->dev_semaphore); + mutex_lock(&us->dev_mutex); + mutex_unlock(&us->dev_mutex); /* queuecommand won't accept any new commands and the control * thread won't execute a previously-queued command. If there @@ -870,9 +871,9 @@ retry: /* For bulk-only devices, determine the max LUN value */ if (us->protocol == US_PR_BULK && !(us->flags & US_FL_SINGLE_LUN)) { - down(&us->dev_semaphore); + mutex_lock(&us->dev_mutex); us->max_lun = usb_stor_Bulk_max_lun(us); - up(&us->dev_semaphore); + mutex_unlock(&us->dev_mutex); } scsi_scan_host(us_to_host(us)); printk(KERN_DEBUG "usb-storage: device scan complete\n"); @@ -912,7 +913,7 @@ static int storage_probe(struct usb_interface *intf, us = host_to_us(host); memset(us, 0, sizeof(struct us_data)); - init_MUTEX(&(us->dev_semaphore)); + mutex_init(&(us->dev_mutex)); init_MUTEX_LOCKED(&(us->sema)); init_completion(&(us->notify)); init_waitqueue_head(&us->delay_wait); diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index 7259fd1f6b0d..009fb0953a56 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -49,6 +49,7 @@ #include #include #include +#include #include struct us_data; @@ -103,9 +104,9 @@ typedef void (*pm_hook)(struct us_data *, int); /* power management hook */ struct us_data { /* The device we're working with * It's important to note: - * (o) you must hold dev_semaphore to change pusb_dev + * (o) you must hold dev_mutex to change pusb_dev */ - struct semaphore dev_semaphore; /* protect pusb_dev */ + struct mutex dev_mutex; /* protect pusb_dev */ struct usb_device *pusb_dev; /* this usb_device */ struct usb_interface *pusb_intf; /* this interface */ struct us_unusual_dev *unusual_dev; /* device-filter entry */ -- cgit v1.2.3-59-g8ed1b From f8aeb3bb8657b207895aa10f75e63f2c48d08985 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Fri, 20 Jan 2006 13:55:14 -0800 Subject: [PATCH] USB: EHCI and NF2 quirk This teaches the EHCI driver about a quirk seen in older NForce2 chips, adding a workaround to ignore selective suspend requests. Bus-wide (so-called "global") suspend still works, as does USB wakeup of a root hub that's globally suspended. There's still a hole in this support though. Strictly speaking, this should _fail_ selective suspend requests, rather than ignoring them, since doing it this way means that devices which should be able to issue remote wakeup are not going to be able to do that. For now, we'll just live with that problem ... since usbcore expects to do selective suspend on the way towards a full bus suspend, and usbcore needs to be able to do full bus suspend. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hub.c | 4 ++++ drivers/usb/host/ehci-pci.c | 25 ++++++++++++++++++++++++- drivers/usb/host/ehci.h | 3 ++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 69b0b9be7a64..d03e3cad5ca8 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -359,6 +359,8 @@ static int ehci_hub_control ( case USB_PORT_FEAT_SUSPEND: if (temp & PORT_RESET) goto error; + if (ehci->no_selective_suspend) + break; if (temp & PORT_SUSPEND) { if ((temp & PORT_PE) == 0) goto error; @@ -514,6 +516,8 @@ static int ehci_hub_control ( temp &= ~PORT_RWC_BITS; switch (wValue) { case USB_PORT_FEAT_SUSPEND: + if (ehci->no_selective_suspend) + break; if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) goto error; diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 3a6687df5594..1e03f1a5a5fd 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -106,11 +106,11 @@ static int ehci_pci_setup(struct usb_hcd *hcd) } break; case PCI_VENDOR_ID_NVIDIA: + switch (pdev->device) { /* NVidia reports that certain chips don't handle * QH, ITD, or SITD addresses above 2GB. (But TD, * data buffer, and periodic schedule are normal.) */ - switch (pdev->device) { case 0x003c: /* MCP04 */ case 0x005b: /* CK804 */ case 0x00d8: /* CK8 */ @@ -120,6 +120,14 @@ static int ehci_pci_setup(struct usb_hcd *hcd) ehci_warn(ehci, "can't enable NVidia " "workaround for >2GB RAM\n"); break; + /* Some NForce2 chips have problems with selective suspend; + * fixed in newer silicon. + */ + case 0x0068: + pci_read_config_dword(pdev, PCI_REVISION_ID, &temp); + if ((temp & 0xff) < 0xa4) + ehci->no_selective_suspend = 1; + break; } break; } @@ -163,6 +171,21 @@ static int ehci_pci_setup(struct usb_hcd *hcd) device_init_wakeup(&pdev->dev, 1); } +#ifdef CONFIG_USB_SUSPEND + /* REVISIT: the controller works fine for wakeup iff the root hub + * itself is "globally" suspended, but usbcore currently doesn't + * understand such things. + * + * System suspend currently expects to be able to suspend the entire + * device tree, device-at-a-time. If we failed selective suspend + * reports, system suspend would fail; so the root hub code must claim + * success. That's lying to usbcore, and it matters for for runtime + * PM scenarios with selective suspend and remote wakeup... + */ + if (ehci->no_selective_suspend && device_can_wakeup(&pdev->dev)) + ehci_warn(ehci, "selective suspend/wakeup unavailable\n"); +#endif + retval = ehci_pci_reinit(ehci, pdev); done: return retval; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 18e257c2bdb5..86af41c41de1 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -89,6 +89,8 @@ struct ehci_hcd { /* one per controller */ u32 command; unsigned is_tdi_rh_tt:1; /* TDI roothub with TT */ + unsigned no_selective_suspend:1; + u8 sbrn; /* packed release number */ /* irq statistics */ #ifdef EHCI_STATS @@ -97,7 +99,6 @@ struct ehci_hcd { /* one per controller */ #else # define COUNT(x) do {} while (0) #endif - u8 sbrn; /* packed release number */ }; /* convert between an HCD pointer and the corresponding EHCI_HCD */ -- cgit v1.2.3-59-g8ed1b From 469d02293d494d30dba81895cd3d34b0a3a6d51a Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Fri, 20 Jan 2006 13:49:10 -0800 Subject: [PATCH] USB: EHCI full speed ISO bugfixes This patch replaces the split ISO raw_mask calculation code in the iso_stream_init() function that computed incorrect numbers of high speed transactions for both input and output transfers. In the output case, it added a superfluous start-split transaction for all maxmimum packet sizes that are a multiple of 188. In the input case, it forgot to add complete-split transactions for all microframes covered by the full speed transaction, and the additional complete-split transaction needed for the case when full speed data starts arriving near the end of a microframe. These changes don't affect the lack of full speed bandwidth, but at least it removes the MMF errors that the HC raised with some input streams. Signed-off-by: Clemens Ladisch Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-sched.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index ebcca9700671..88419c6823a8 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -707,6 +707,7 @@ iso_stream_init ( } else { u32 addr; int think_time; + int hs_transfers; addr = dev->ttport << 24; if (!ehci_is_TDI(ehci) @@ -719,6 +720,7 @@ iso_stream_init ( think_time = dev->tt ? dev->tt->think_time : 0; stream->tt_usecs = NS_TO_US (think_time + usb_calc_bus_time ( dev->speed, is_input, 1, maxp)); + hs_transfers = max (1u, (maxp + 187) / 188); if (is_input) { u32 tmp; @@ -727,12 +729,11 @@ iso_stream_init ( stream->usecs = HS_USECS_ISO (1); stream->raw_mask = 1; - /* pessimistic c-mask */ - tmp = usb_calc_bus_time (USB_SPEED_FULL, 1, 0, maxp) - / (125 * 1000); - stream->raw_mask |= 3 << (tmp + 9); + /* c-mask as specified in USB 2.0 11.18.4 3.c */ + tmp = (1 << (hs_transfers + 2)) - 1; + stream->raw_mask |= tmp << (8 + 2); } else - stream->raw_mask = smask_out [maxp / 188]; + stream->raw_mask = smask_out [hs_transfers - 1]; bandwidth = stream->usecs + stream->c_usecs; bandwidth /= 1 << (interval + 2); -- cgit v1.2.3-59-g8ed1b From 80cb9aee01245b38325dd84f1359b14a3f01f10d Mon Sep 17 00:00:00 2001 From: Randy Vinson Date: Fri, 20 Jan 2006 13:53:38 -0800 Subject: [PATCH] USB: EHCI for Freescale 83xx Adding a Host Mode USB driver for the Freescale 83xx. This driver supports both the Dual-Role (DR) controller and the Multi-Port-Host (MPH) controller present in the Freescale MPC8349. It has been tested with the MPC8349CDS reference system. This driver depends on platform support code for setting up the pins on the device package in a manner appropriate for the board in use. Note that this patch requires selecting the EHCI controller option under the USB Host menu. Signed-off-by: Randy Vinson Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Kconfig | 1 + drivers/usb/host/Kconfig | 2 +- drivers/usb/host/ehci-fsl.c | 357 ++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/ehci-fsl.h | 37 +++++ drivers/usb/host/ehci-hcd.c | 8 +- include/linux/fsl_devices.h | 27 ++++ 6 files changed, 430 insertions(+), 2 deletions(-) create mode 100644 drivers/usb/host/ehci-fsl.c create mode 100644 drivers/usb/host/ehci-fsl.h diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 85dacc92545a..7772e28a303f 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -11,6 +11,7 @@ config USB_ARCH_HAS_HCD boolean default y if USB_ARCH_HAS_OHCI default y if ARM # SL-811 + default y if PPC_83xx default PCI # many non-PCI SOC chips embed OHCI diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index be3fd9bce573..a45293673906 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -6,7 +6,7 @@ comment "USB Host Controller Drivers" config USB_EHCI_HCD tristate "EHCI HCD (USB 2.0) support" - depends on USB && PCI + depends on USB && (PCI || PPC_83xx) ---help--- The Enhanced Host Controller Interface (EHCI) is standard for USB 2.0 "high speed" (480 Mbit/sec, 60 Mbyte/sec) host controller hardware. diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c new file mode 100644 index 000000000000..c6012d6cd527 --- /dev/null +++ b/drivers/usb/host/ehci-fsl.c @@ -0,0 +1,357 @@ +/* + * (C) Copyright David Brownell 2000-2002 + * Copyright (c) 2005 MontaVista Software + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Ported to 834x by Randy Vinson using code provided + * by Hunter Wu. + */ + +#include +#include + +#include "ehci-fsl.h" + +/* FIXME: Power Managment is un-ported so temporarily disable it */ +#undef CONFIG_PM + +/* PCI-based HCs are common, but plenty of non-PCI HCs are used too */ + +/* configure so an HC device and id are always provided */ +/* always called with process context; sleeping is OK */ + +/** + * usb_hcd_fsl_probe - initialize FSL-based HCDs + * @drvier: Driver to be used for this HCD + * @pdev: USB Host Controller being probed + * Context: !in_interrupt() + * + * Allocates basic resources for this USB host controller. + * + */ +int usb_hcd_fsl_probe(const struct hc_driver *driver, + struct platform_device *pdev) +{ + struct fsl_usb2_platform_data *pdata; + struct usb_hcd *hcd; + struct resource *res; + int irq; + int retval; + unsigned int temp; + + pr_debug("initializing FSL-SOC USB Controller\n"); + + /* Need platform data for setup */ + pdata = (struct fsl_usb2_platform_data *)pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, + "No platform data for %s.\n", pdev->dev.bus_id); + return -ENODEV; + } + + /* + * This is a host mode driver, verify that we're supposed to be + * in host mode. + */ + if (!((pdata->operating_mode == FSL_USB2_DR_HOST) || + (pdata->operating_mode == FSL_USB2_MPH_HOST))) { + dev_err(&pdev->dev, + "Non Host Mode configured for %s. Wrong driver linked.\n", + pdev->dev.bus_id); + return -ENODEV; + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(&pdev->dev, + "Found HC with no IRQ. Check %s setup!\n", + pdev->dev.bus_id); + return -ENODEV; + } + irq = res->start; + + hcd = usb_create_hcd(driver, &pdev->dev, pdev->dev.bus_id); + if (!hcd) { + retval = -ENOMEM; + goto err1; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, + "Found HC with no register addr. Check %s setup!\n", + pdev->dev.bus_id); + retval = -ENODEV; + goto err2; + } + hcd->rsrc_start = res->start; + hcd->rsrc_len = res->end - res->start + 1; + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, + driver->description)) { + dev_dbg(&pdev->dev, "controller already in use\n"); + retval = -EBUSY; + goto err2; + } + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + + if (hcd->regs == NULL) { + dev_dbg(&pdev->dev, "error mapping memory\n"); + retval = -EFAULT; + goto err3; + } + + /* Enable USB controller */ + temp = in_be32(hcd->regs + 0x500); + out_be32(hcd->regs + 0x500, temp | 0x4); + + /* Set to Host mode */ + temp = in_le32(hcd->regs + 0x1a8); + out_le32(hcd->regs + 0x1a8, temp | 0x3); + + retval = usb_add_hcd(hcd, irq, SA_SHIRQ); + if (retval != 0) + goto err4; + return retval; + + err4: + iounmap(hcd->regs); + err3: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + err2: + usb_put_hcd(hcd); + err1: + dev_err(&pdev->dev, "init %s fail, %d\n", pdev->dev.bus_id, retval); + return retval; +} + +/* may be called without controller electrically present */ +/* may be called with controller, bus, and devices active */ + +/** + * usb_hcd_fsl_remove - shutdown processing for FSL-based HCDs + * @dev: USB Host Controller being removed + * Context: !in_interrupt() + * + * Reverses the effect of usb_hcd_fsl_probe(). + * + */ +void usb_hcd_fsl_remove(struct usb_hcd *hcd, struct platform_device *pdev) +{ + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); +} + +static void mpc83xx_setup_phy(struct ehci_hcd *ehci, + enum fsl_usb2_phy_modes phy_mode, + unsigned int port_offset) +{ + u32 portsc = readl(&ehci->regs->port_status[port_offset]); + portsc &= ~PORT_PTS_MSK; + switch (phy_mode) { + case FSL_USB2_PHY_ULPI: + portsc |= PORT_PTS_ULPI; + break; + case FSL_USB2_PHY_SERIAL: + portsc |= PORT_PTS_SERIAL; + break; + case FSL_USB2_PHY_UTMI_WIDE: + portsc |= PORT_PTS_PTW; + /* fall through */ + case FSL_USB2_PHY_UTMI: + portsc |= PORT_PTS_UTMI; + break; + case FSL_USB2_PHY_NONE: + break; + } + writel(portsc, &ehci->regs->port_status[port_offset]); +} + +static void mpc83xx_usb_setup(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct fsl_usb2_platform_data *pdata; + void __iomem *non_ehci = hcd->regs; + + pdata = + (struct fsl_usb2_platform_data *)hcd->self.controller-> + platform_data; + /* Enable PHY interface in the control reg. */ + out_be32(non_ehci + FSL_SOC_USB_CTRL, 0x00000004); + out_be32(non_ehci + FSL_SOC_USB_SNOOP1, 0x0000001b); + + if (pdata->operating_mode == FSL_USB2_DR_HOST) + mpc83xx_setup_phy(ehci, pdata->phy_mode, 0); + + if (pdata->operating_mode == FSL_USB2_MPH_HOST) { + if (pdata->port_enables & FSL_USB2_PORT0_ENABLED) + mpc83xx_setup_phy(ehci, pdata->phy_mode, 0); + if (pdata->port_enables & FSL_USB2_PORT1_ENABLED) + mpc83xx_setup_phy(ehci, pdata->phy_mode, 1); + } + + /* put controller in host mode. */ + writel(0x00000003, non_ehci + FSL_SOC_USB_USBMODE); + out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x0000000c); + out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000040); + out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001); +} + +/* called after powerup, by probe or system-pm "wakeup" */ +static int ehci_fsl_reinit(struct ehci_hcd *ehci) +{ + mpc83xx_usb_setup(ehci_to_hcd(ehci)); + ehci_port_power(ehci, 0); + + return 0; +} + +/* called during probe() after chip reset completes */ +static int ehci_fsl_setup(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval; + + /* EHCI registers start at offset 0x100 */ + ehci->caps = hcd->regs + 0x100; + ehci->regs = hcd->regs + 0x100 + + HC_LENGTH(readl(&ehci->caps->hc_capbase)); + dbg_hcs_params(ehci, "reset"); + dbg_hcc_params(ehci, "reset"); + + /* cache this readonly data; minimize chip reads */ + ehci->hcs_params = readl(&ehci->caps->hcs_params); + + retval = ehci_halt(ehci); + if (retval) + return retval; + + /* data structure init */ + retval = ehci_init(hcd); + if (retval) + return retval; + + ehci->is_tdi_rh_tt = 1; + + ehci->sbrn = 0x20; + + ehci_reset(ehci); + + retval = ehci_fsl_reinit(ehci); + return retval; +} + +static const struct hc_driver ehci_fsl_hc_driver = { + .description = hcd_name, + .product_desc = "Freescale On-Chip EHCI Host Controller", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_USB2, + + /* + * basic lifecycle operations + */ + .reset = ehci_fsl_setup, + .start = ehci_run, +#ifdef CONFIG_PM + .suspend = ehci_bus_suspend, + .resume = ehci_bus_resume, +#endif + .stop = ehci_stop, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, +}; + +static int ehci_fsl_drv_probe(struct platform_device *pdev) +{ + if (usb_disabled()) + return -ENODEV; + + return usb_hcd_fsl_probe(&ehci_fsl_hc_driver, pdev); +} + +static int ehci_fsl_drv_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + usb_hcd_fsl_remove(hcd, pdev); + + return 0; +} + +static struct platform_driver ehci_fsl_dr_driver = { + .probe = ehci_fsl_drv_probe, + .remove = ehci_fsl_drv_remove, + .driver = { + .name = "fsl-usb2-dr", + }, +}; + +static struct platform_driver ehci_fsl_mph_driver = { + .probe = ehci_fsl_drv_probe, + .remove = ehci_fsl_drv_remove, + .driver = { + .name = "fsl-usb2-mph", + }, +}; + +static int __init ehci_fsl_init(void) +{ + int retval; + + pr_debug("%s: block sizes: qh %Zd qtd %Zd itd %Zd sitd %Zd\n", + hcd_name, + sizeof(struct ehci_qh), sizeof(struct ehci_qtd), + sizeof(struct ehci_itd), sizeof(struct ehci_sitd)); + + retval = platform_driver_register(&ehci_fsl_dr_driver); + if (retval) + return retval; + + return platform_driver_register(&ehci_fsl_mph_driver); +} + +static void __exit ehci_fsl_cleanup(void) +{ + platform_driver_unregister(&ehci_fsl_mph_driver); + platform_driver_unregister(&ehci_fsl_dr_driver); +} + +module_init(ehci_fsl_init); +module_exit(ehci_fsl_cleanup); diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h new file mode 100644 index 000000000000..caac0d1967d0 --- /dev/null +++ b/drivers/usb/host/ehci-fsl.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2005 freescale semiconductor + * Copyright (c) 2005 MontaVista Software + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef _EHCI_FSL_H +#define _EHCI_FSL_H + +/* offsets for the non-ehci registers in the FSL SOC USB controller */ +#define FSL_SOC_USB_ULPIVP 0x170 +#define FSL_SOC_USB_PORTSC1 0x184 +#define PORT_PTS_MSK (3<<30) +#define PORT_PTS_UTMI (0<<30) +#define PORT_PTS_ULPI (2<<30) +#define PORT_PTS_SERIAL (3<<30) +#define PORT_PTS_PTW (1<<28) +#define FSL_SOC_USB_PORTSC2 0x188 +#define FSL_SOC_USB_USBMODE 0x1a8 +#define FSL_SOC_USB_SNOOP1 0x400 /* NOTE: big-endian */ +#define FSL_SOC_USB_SNOOP2 0x404 /* NOTE: big-endian */ +#define FSL_SOC_USB_AGECNTTHRSH 0x408 /* NOTE: big-endian */ +#define FSL_SOC_USB_SICTRL 0x40c /* NOTE: big-endian */ +#define FSL_SOC_USB_PRICTRL 0x410 /* NOTE: big-endian */ +#define FSL_SOC_USB_CTRL 0x500 /* NOTE: big-endian */ +#endif /* _EHCI_FSL_H */ diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 9dd3d14c64f3..8730babb5771 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -891,6 +891,12 @@ MODULE_LICENSE ("GPL"); #include "ehci-pci.c" #endif -#if !defined(CONFIG_PCI) +#ifdef CONFIG_PPC_83xx +#include "ehci-fsl.c" +#endif + +#if !(defined(CONFIG_PCI) || \ + defined(CONFIG_PPC_83xx) \ + ) #error "missing bus glue for ehci-hcd" #endif diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index a9f1cfd096ff..a3a0e078f79d 100644 --- a/include/linux/fsl_devices.h +++ b/include/linux/fsl_devices.h @@ -83,5 +83,32 @@ struct fsl_i2c_platform_data { #define FSL_I2C_DEV_SEPARATE_DFSRR 0x00000001 #define FSL_I2C_DEV_CLOCK_5200 0x00000002 + +enum fsl_usb2_operating_modes { + FSL_USB2_MPH_HOST, + FSL_USB2_DR_HOST, + FSL_USB2_DR_DEVICE, + FSL_USB2_DR_OTG, +}; + +enum fsl_usb2_phy_modes { + FSL_USB2_PHY_NONE, + FSL_USB2_PHY_ULPI, + FSL_USB2_PHY_UTMI, + FSL_USB2_PHY_UTMI_WIDE, + FSL_USB2_PHY_SERIAL, +}; + +struct fsl_usb2_platform_data { + /* board specific information */ + enum fsl_usb2_operating_modes operating_mode; + enum fsl_usb2_phy_modes phy_mode; + unsigned int port_enables; +}; + +/* Flags in fsl_usb2_mph_platform_data */ +#define FSL_USB2_PORT0_ENABLED 0x00000001 +#define FSL_USB2_PORT1_ENABLED 0x00000002 + #endif /* _FSL_DEVICE_H_ */ #endif /* __KERNEL__ */ -- cgit v1.2.3-59-g8ed1b From 8cd42e97bf451bbbb2f54dc571366ae5a72faaea Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Fri, 20 Jan 2006 13:57:52 -0800 Subject: [PATCH] USB: EHCI and Freescale 83xx quirk On the MPC834x processors the multiport host (MPH) EHCI controller has an erratum in which the port number in the queue head expects to be 0..N-1 instead of 1..N. If we are on one of these chips we subtract one from the port number before putting it into the queue head. Signed-off-by: Kumar Gala Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-fsl.c | 10 ++++++++++ drivers/usb/host/ehci-q.c | 9 ++++++++- drivers/usb/host/ehci.h | 15 +++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index c6012d6cd527..59f90f76ee31 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -198,6 +198,16 @@ static void mpc83xx_usb_setup(struct usb_hcd *hcd) mpc83xx_setup_phy(ehci, pdata->phy_mode, 0); if (pdata->operating_mode == FSL_USB2_MPH_HOST) { + unsigned int chip, rev, svr; + + svr = mfspr(SPRN_SVR); + chip = svr >> 16; + rev = (svr >> 4) & 0xf; + + /* Deal with USB Erratum #14 on MPC834x Rev 1.0 & 1.1 chips */ + if ((rev == 1) && (chip >= 0x8050) && (chip <= 0x8055)) + ehci->has_fsl_port_bug = 1; + if (pdata->port_enables & FSL_USB2_PORT0_ENABLED) mpc83xx_setup_phy(ehci, pdata->phy_mode, 0); if (pdata->port_enables & FSL_USB2_PORT1_ENABLED) diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 9b13bf2fa98d..6e28e593c044 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -721,7 +721,14 @@ qh_make ( info1 |= maxp << 16; info2 |= (EHCI_TUNE_MULT_TT << 30); - info2 |= urb->dev->ttport << 23; + + /* Some Freescale processors have an erratum in which the + * port number in the queue head was 0..N-1 instead of 1..N. + */ + if (ehci_has_fsl_portno_bug(ehci)) + info2 |= (urb->dev->ttport-1) << 23; + else + info2 |= urb->dev->ttport << 23; /* set the address of the TT; for TDI's integrated * root hub tt, leave it zeroed. diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 86af41c41de1..679c1cdcc915 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -88,8 +88,11 @@ struct ehci_hcd { /* one per controller */ unsigned long next_statechange; u32 command; + /* SILICON QUIRKS */ unsigned is_tdi_rh_tt:1; /* TDI roothub with TT */ unsigned no_selective_suspend:1; + unsigned has_fsl_port_bug:1; /* FreeScale */ + u8 sbrn; /* packed release number */ /* irq statistics */ @@ -637,6 +640,18 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc) #define ehci_port_speed(ehci, portsc) (1<has_fsl_port_bug) +#else +#define ehci_has_fsl_portno_bug(e) (0) +#endif + + /*-------------------------------------------------------------------------*/ #ifndef DEBUG -- cgit v1.2.3-59-g8ed1b From 76fa9a240de4294a097235c9ddd470c21eb3449e Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Fri, 20 Jan 2006 14:06:09 -0800 Subject: [PATCH] USB: EHCI for AU1200 ALCHEMY: Add EHCI support for AU1200 Updated by removing the OHCI support Signed-off-by: Jordan Crouse Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- arch/mips/au1000/common/cputable.c | 2 +- arch/mips/au1000/common/platform.c | 2 - drivers/usb/Kconfig | 9 +- drivers/usb/host/Kconfig | 2 +- drivers/usb/host/ehci-au1xxx.c | 297 +++++++++++++++++++++ drivers/usb/host/ehci-hcd.c | 11 +- include/asm-mips/mach-mips/cpu-feature-overrides.h | 4 + 7 files changed, 319 insertions(+), 8 deletions(-) create mode 100644 drivers/usb/host/ehci-au1xxx.c diff --git a/arch/mips/au1000/common/cputable.c b/arch/mips/au1000/common/cputable.c index 4dbde82c8215..d8df5fdb045f 100644 --- a/arch/mips/au1000/common/cputable.c +++ b/arch/mips/au1000/common/cputable.c @@ -38,7 +38,7 @@ struct cpu_spec cpu_specs[] = { { 0xffffffff, 0x02030204, "Au1100 BE", 0, 1 }, { 0xffffffff, 0x03030200, "Au1550 AA", 0, 1 }, { 0xffffffff, 0x04030200, "Au1200 AB", 0, 0 }, - { 0xffffffff, 0x04030201, "Au1200 AC", 0, 1 }, + { 0xffffffff, 0x04030201, "Au1200 AC", 1, 0 }, { 0x00000000, 0x00000000, "Unknown Au1xxx", 1, 0 }, }; diff --git a/arch/mips/au1000/common/platform.c b/arch/mips/au1000/common/platform.c index 48d3f54f88f8..1fb3dbdb93be 100644 --- a/arch/mips/au1000/common/platform.c +++ b/arch/mips/au1000/common/platform.c @@ -278,9 +278,7 @@ static struct platform_device *au1xxx_platform_devices[] __initdata = { &au1100_lcd_device, #endif #ifdef CONFIG_SOC_AU1200 -#if 0 /* fixme */ &au1xxx_usb_ehci_device, -#endif &au1xxx_usb_gdt_device, &au1xxx_usb_otg_device, &au1200_lcd_device, diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 7772e28a303f..0e865975899f 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -10,8 +10,8 @@ menu "USB support" config USB_ARCH_HAS_HCD boolean default y if USB_ARCH_HAS_OHCI + default y if USB_ARCH_HAS_EHCI default y if ARM # SL-811 - default y if PPC_83xx default PCI # many non-PCI SOC chips embed OHCI @@ -31,6 +31,13 @@ config USB_ARCH_HAS_OHCI # more: default PCI +# some non-PCI hcds implement EHCI +config USB_ARCH_HAS_EHCI + boolean + default y if PPC_83xx + default y if SOC_AU1200 + default PCI + # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. config USB tristate "Support for Host-side USB" diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index a45293673906..e27b79a3c05f 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -6,7 +6,7 @@ comment "USB Host Controller Drivers" config USB_EHCI_HCD tristate "EHCI HCD (USB 2.0) support" - depends on USB && (PCI || PPC_83xx) + depends on USB && USB_ARCH_HAS_EHCI ---help--- The Enhanced Host Controller Interface (EHCI) is standard for USB 2.0 "high speed" (480 Mbit/sec, 60 Mbyte/sec) host controller hardware. diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c new file mode 100644 index 000000000000..63eadeec1324 --- /dev/null +++ b/drivers/usb/host/ehci-au1xxx.c @@ -0,0 +1,297 @@ +/* + * EHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 2000-2004 David Brownell + * + * Bus Glue for AMD Alchemy Au1xxx + * + * Based on "ohci-au1xxx.c" by Matt Porter + * + * Modified for AMD Alchemy Au1200 EHC + * by K.Boge + * + * This file is licenced under the GPL. + */ + +#include +#include + +#ifndef CONFIG_SOC_AU1200 +#error "this Alchemy chip doesn't have EHCI" +#else /* Au1200 */ + +#define USB_HOST_CONFIG (USB_MSR_BASE + USB_MSR_MCFG) +#define USB_MCFG_PFEN (1<<31) +#define USB_MCFG_RDCOMB (1<<30) +#define USB_MCFG_SSDEN (1<<23) +#define USB_MCFG_PHYPLLEN (1<<19) +#define USB_MCFG_EHCCLKEN (1<<17) +#define USB_MCFG_UCAM (1<<7) +#define USB_MCFG_EBMEN (1<<3) +#define USB_MCFG_EMEMEN (1<<2) + +#define USBH_ENABLE_CE (USB_MCFG_PHYPLLEN | USB_MCFG_EHCCLKEN) + +#ifdef CONFIG_DMA_COHERENT +#define USBH_ENABLE_INIT (USBH_ENABLE_CE \ + | USB_MCFG_PFEN | USB_MCFG_RDCOMB \ + | USB_MCFG_SSDEN | USB_MCFG_UCAM \ + | USB_MCFG_EBMEN | USB_MCFG_EMEMEN) +#else +#define USBH_ENABLE_INIT (USBH_ENABLE_CE \ + | USB_MCFG_PFEN | USB_MCFG_RDCOMB \ + | USB_MCFG_SSDEN \ + | USB_MCFG_EBMEN | USB_MCFG_EMEMEN) +#endif +#define USBH_DISABLE (USB_MCFG_EBMEN | USB_MCFG_EMEMEN) + +#endif /* Au1200 */ + +extern int usb_disabled(void); + +/*-------------------------------------------------------------------------*/ + +static void au1xxx_start_ehc(struct platform_device *dev) +{ + pr_debug(__FILE__ ": starting Au1xxx EHCI USB Controller\n"); + + /* write HW defaults again in case Yamon cleared them */ + if (au_readl(USB_HOST_CONFIG) == 0) { + au_writel(0x00d02000, USB_HOST_CONFIG); + au_readl(USB_HOST_CONFIG); + udelay(1000); + } + /* enable host controller */ + au_writel(USBH_ENABLE_CE | au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG); + au_readl(USB_HOST_CONFIG); + udelay(1000); + au_writel(USBH_ENABLE_INIT | au_readl(USB_HOST_CONFIG), + USB_HOST_CONFIG); + au_readl(USB_HOST_CONFIG); + udelay(1000); + + pr_debug(__FILE__ ": Clock to USB host has been enabled\n"); +} + +static void au1xxx_stop_ehc(struct platform_device *dev) +{ + pr_debug(__FILE__ ": stopping Au1xxx EHCI USB Controller\n"); + + /* Disable mem */ + au_writel(~USBH_DISABLE & au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG); + udelay(1000); + /* Disable clock */ + au_writel(~USB_MCFG_EHCCLKEN & au_readl(USB_HOST_CONFIG), + USB_HOST_CONFIG); + au_readl(USB_HOST_CONFIG); +} + +/*-------------------------------------------------------------------------*/ + +/* configure so an HC device and id are always provided */ +/* always called with process context; sleeping is OK */ + +/** + * usb_ehci_au1xxx_probe - initialize Au1xxx-based HCDs + * Context: !in_interrupt() + * + * Allocates basic resources for this USB host controller, and + * then invokes the start() method for the HCD associated with it + * through the hotplug entry's driver_data. + * + */ +int usb_ehci_au1xxx_probe(const struct hc_driver *driver, + struct usb_hcd **hcd_out, struct platform_device *dev) +{ + int retval; + struct usb_hcd *hcd; + struct ehci_hcd *ehci; + +#if defined(CONFIG_SOC_AU1200) && defined(CONFIG_DMA_COHERENT) + + /* Au1200 AB USB does not support coherent memory */ + if (!(read_c0_prid() & 0xff)) { + pr_info("%s: this is chip revision AB!\n", dev->dev.name); + pr_info("%s: update your board or re-configure the kernel\n", + dev->dev.name); + return -ENODEV; + } +#endif + + au1xxx_start_ehc(dev); + + if (dev->resource[1].flags != IORESOURCE_IRQ) { + pr_debug("resource[1] is not IORESOURCE_IRQ"); + retval = -ENOMEM; + } + hcd = usb_create_hcd(driver, &dev->dev, "Au1xxx"); + if (!hcd) + return -ENOMEM; + hcd->rsrc_start = dev->resource[0].start; + hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + pr_debug("request_mem_region failed"); + retval = -EBUSY; + goto err1; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + pr_debug("ioremap failed"); + retval = -ENOMEM; + goto err2; + } + + ehci = hcd_to_ehci(hcd); + ehci->caps = hcd->regs; + ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase)); + /* cache this readonly data; minimize chip reads */ + ehci->hcs_params = readl(&ehci->caps->hcs_params); + + /* ehci_hcd_init(hcd_to_ehci(hcd)); */ + + retval = + usb_add_hcd(hcd, dev->resource[1].start, SA_INTERRUPT | SA_SHIRQ); + if (retval == 0) + return retval; + + au1xxx_stop_ehc(dev); + iounmap(hcd->regs); +err2: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err1: + usb_put_hcd(hcd); + return retval; +} + +/* may be called without controller electrically present */ +/* may be called with controller, bus, and devices active */ + +/** + * usb_ehci_hcd_au1xxx_remove - shutdown processing for Au1xxx-based HCDs + * @dev: USB Host Controller being removed + * Context: !in_interrupt() + * + * Reverses the effect of usb_ehci_hcd_au1xxx_probe(), first invoking + * the HCD's stop() method. It is always called from a thread + * context, normally "rmmod", "apmd", or something similar. + * + */ +void usb_ehci_au1xxx_remove(struct usb_hcd *hcd, struct platform_device *dev) +{ + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + au1xxx_stop_ehc(dev); +} + +/*-------------------------------------------------------------------------*/ + +static const struct hc_driver ehci_au1xxx_hc_driver = { + .description = hcd_name, + .product_desc = "Au1xxx EHCI", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * basic lifecycle operations + */ + .reset = ehci_init, + .start = ehci_run, + .stop = ehci_stop, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, +#ifdef CONFIG_PM + .hub_suspend = ehci_hub_suspend, + .hub_resume = ehci_hub_resume, +#endif +}; + +/*-------------------------------------------------------------------------*/ + +static int ehci_hcd_au1xxx_drv_probe(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct usb_hcd *hcd = NULL; + int ret; + + pr_debug("In ehci_hcd_au1xxx_drv_probe\n"); + + if (usb_disabled()) + return -ENODEV; + + ret = usb_ehci_au1xxx_probe(&ehci_au1xxx_hc_driver, &hcd, pdev); + return ret; +} + +static int ehci_hcd_au1xxx_drv_remove(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct usb_hcd *hcd = dev_get_drvdata(dev); + + usb_ehci_au1xxx_remove(hcd, pdev); + return 0; +} + + /*TBD*/ +/*static int ehci_hcd_au1xxx_drv_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct usb_hcd *hcd = dev_get_drvdata(dev); + + return 0; +} +static int ehci_hcd_au1xxx_drv_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct usb_hcd *hcd = dev_get_drvdata(dev); + + return 0; +} +*/ +static struct device_driver ehci_hcd_au1xxx_driver = { + .name = "au1xxx-ehci", + .bus = &platform_bus_type, + .probe = ehci_hcd_au1xxx_drv_probe, + .remove = ehci_hcd_au1xxx_drv_remove, + /*.suspend = ehci_hcd_au1xxx_drv_suspend, */ + /*.resume = ehci_hcd_au1xxx_drv_resume, */ +}; + +static int __init ehci_hcd_au1xxx_init(void) +{ + pr_debug(DRIVER_INFO " (Au1xxx)\n"); + + return driver_register(&ehci_hcd_au1xxx_driver); +} + +static void __exit ehci_hcd_au1xxx_cleanup(void) +{ + driver_unregister(&ehci_hcd_au1xxx_driver); +} + +module_init(ehci_hcd_au1xxx_init); +module_exit(ehci_hcd_au1xxx_cleanup); diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 8730babb5771..79f2d8b9bfb6 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -889,14 +889,19 @@ MODULE_LICENSE ("GPL"); #ifdef CONFIG_PCI #include "ehci-pci.c" +#define EHCI_BUS_GLUED #endif #ifdef CONFIG_PPC_83xx #include "ehci-fsl.c" +#define EHCI_BUS_GLUED #endif -#if !(defined(CONFIG_PCI) || \ - defined(CONFIG_PPC_83xx) \ - ) +#ifdef CONFIG_SOC_AU1X00 +#include "ehci-au1xxx.c" +#define EHCI_BUS_GLUED +#endif + +#ifndef EHCI_BUS_GLUED #error "missing bus glue for ehci-hcd" #endif diff --git a/include/asm-mips/mach-mips/cpu-feature-overrides.h b/include/asm-mips/mach-mips/cpu-feature-overrides.h index 9f92aed17754..e06af6c86f86 100644 --- a/include/asm-mips/mach-mips/cpu-feature-overrides.h +++ b/include/asm-mips/mach-mips/cpu-feature-overrides.h @@ -29,7 +29,11 @@ /* #define cpu_has_prefetch ? */ #define cpu_has_mcheck 1 /* #define cpu_has_ejtag ? */ +#ifdef CONFIG_CPU_HAS_LLSC #define cpu_has_llsc 1 +#else +#define cpu_has_llsc 0 +#endif /* #define cpu_has_vtag_icache ? */ /* #define cpu_has_dc_aliases ? */ /* #define cpu_has_ic_fills_f_dc ? */ -- cgit v1.2.3-59-g8ed1b From d5fb7f1b5b832946eaf450b2a695ec3e7fd2d351 Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Fri, 20 Jan 2006 14:09:54 -0800 Subject: [PATCH] USB: OHCI for AU1200 ALCHEMY: Add OHCI support for AU1200 Updated by moving the OHCI support out of the EHCI patch. Signed-off-by: Jordan Crouse Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- arch/mips/au1000/common/platform.c | 2 +- drivers/usb/host/ohci-au1xxx.c | 102 +++++++++++++++++++++++++++++++------ 2 files changed, 88 insertions(+), 16 deletions(-) diff --git a/arch/mips/au1000/common/platform.c b/arch/mips/au1000/common/platform.c index 1fb3dbdb93be..dbb4ee7ed42d 100644 --- a/arch/mips/au1000/common/platform.c +++ b/arch/mips/au1000/common/platform.c @@ -20,7 +20,7 @@ static struct resource au1xxx_usb_ohci_resources[] = { [0] = { .start = USB_OHCI_BASE, - .end = USB_OHCI_BASE + USB_OHCI_LEN, + .end = USB_OHCI_BASE + USB_OHCI_LEN - 1, .flags = IORESOURCE_MEM, }, [1] = { diff --git a/drivers/usb/host/ohci-au1xxx.c b/drivers/usb/host/ohci-au1xxx.c index db280ca7b7a0..a1c8b3b2fcc7 100644 --- a/drivers/usb/host/ohci-au1xxx.c +++ b/drivers/usb/host/ohci-au1xxx.c @@ -23,6 +23,8 @@ #include +#ifndef CONFIG_SOC_AU1200 + #define USBH_ENABLE_BE (1<<0) #define USBH_ENABLE_C (1<<1) #define USBH_ENABLE_E (1<<2) @@ -37,21 +39,68 @@ #error not byte order defined #endif +#else /* Au1200 */ + +#define USB_HOST_CONFIG (USB_MSR_BASE + USB_MSR_MCFG) +#define USB_MCFG_PFEN (1<<31) +#define USB_MCFG_RDCOMB (1<<30) +#define USB_MCFG_SSDEN (1<<23) +#define USB_MCFG_OHCCLKEN (1<<16) +#define USB_MCFG_UCAM (1<<7) +#define USB_MCFG_OBMEN (1<<1) +#define USB_MCFG_OMEMEN (1<<0) + +#define USBH_ENABLE_CE USB_MCFG_OHCCLKEN +#ifdef CONFIG_DMA_COHERENT +#define USBH_ENABLE_INIT (USB_MCFG_OHCCLKEN \ + | USB_MCFG_PFEN | USB_MCFG_RDCOMB \ + | USB_MCFG_SSDEN | USB_MCFG_UCAM \ + | USB_MCFG_OBMEN | USB_MCFG_OMEMEN) +#else +#define USBH_ENABLE_INIT (USB_MCFG_OHCCLKEN \ + | USB_MCFG_PFEN | USB_MCFG_RDCOMB \ + | USB_MCFG_SSDEN \ + | USB_MCFG_OBMEN | USB_MCFG_OMEMEN) +#endif +#define USBH_DISABLE (USB_MCFG_OBMEN | USB_MCFG_OMEMEN) + +#endif /* Au1200 */ + extern int usb_disabled(void); /*-------------------------------------------------------------------------*/ -static void au1xxx_start_hc(struct platform_device *dev) +static void au1xxx_start_ohc(struct platform_device *dev) { printk(KERN_DEBUG __FILE__ ": starting Au1xxx OHCI USB Controller\n"); /* enable host controller */ + +#ifndef CONFIG_SOC_AU1200 + au_writel(USBH_ENABLE_CE, USB_HOST_CONFIG); udelay(1000); au_writel(USBH_ENABLE_INIT, USB_HOST_CONFIG); udelay(1000); +#else /* Au1200 */ + + /* write HW defaults again in case Yamon cleared them */ + if (au_readl(USB_HOST_CONFIG) == 0) { + au_writel(0x00d02000, USB_HOST_CONFIG); + au_readl(USB_HOST_CONFIG); + udelay(1000); + } + au_writel(USBH_ENABLE_CE | au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG); + au_readl(USB_HOST_CONFIG); + udelay(1000); + au_writel(USBH_ENABLE_INIT | au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG); + au_readl(USB_HOST_CONFIG); + udelay(1000); + +#endif /* Au1200 */ + /* wait for reset complete (read register twice; see au1500 errata) */ while (au_readl(USB_HOST_CONFIG), !(au_readl(USB_HOST_CONFIG) & USBH_ENABLE_RD)) @@ -61,13 +110,25 @@ static void au1xxx_start_hc(struct platform_device *dev) ": Clock to USB host has been enabled \n"); } -static void au1xxx_stop_hc(struct platform_device *dev) +static void au1xxx_stop_ohc(struct platform_device *dev) { printk(KERN_DEBUG __FILE__ ": stopping Au1xxx OHCI USB Controller\n"); +#ifndef CONFIG_SOC_AU1200 + /* Disable clock */ au_writel(au_readl(USB_HOST_CONFIG) & ~USBH_ENABLE_CE, USB_HOST_CONFIG); + +#else /* Au1200 */ + + /* Disable mem */ + au_writel(~USBH_DISABLE & au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG); + udelay(1000); + /* Disable clock */ + au_writel(~USBH_ENABLE_CE & au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG); + au_readl(USB_HOST_CONFIG); +#endif /* Au1200 */ } @@ -78,7 +139,7 @@ static void au1xxx_stop_hc(struct platform_device *dev) /** - * usb_hcd_au1xxx_probe - initialize Au1xxx-based HCDs + * usb_ohci_au1xxx_probe - initialize Au1xxx-based HCDs * Context: !in_interrupt() * * Allocates basic resources for this USB host controller, and @@ -86,14 +147,25 @@ static void au1xxx_stop_hc(struct platform_device *dev) * through the hotplug entry's driver_data. * */ -int usb_hcd_au1xxx_probe (const struct hc_driver *driver, +static int usb_ohci_au1xxx_probe(const struct hc_driver *driver, struct platform_device *dev) { int retval; struct usb_hcd *hcd; - if(dev->resource[1].flags != IORESOURCE_IRQ) { - pr_debug ("resource[1] is not IORESOURCE_IRQ"); +#if defined(CONFIG_SOC_AU1200) && defined(CONFIG_DMA_COHERENT) + /* Au1200 AB USB does not support coherent memory */ + if (!(read_c0_prid() & 0xff)) { + pr_info("%s: this is chip revision AB !!\n", + dev->dev.name); + pr_info("%s: update your board or re-configure the kernel\n", + dev->dev.name); + return -ENODEV; + } +#endif + + if (dev->resource[1].flags != IORESOURCE_IRQ) { + pr_debug("resource[1] is not IORESOURCE_IRQ\n"); return -ENOMEM; } @@ -104,26 +176,26 @@ int usb_hcd_au1xxx_probe (const struct hc_driver *driver, hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1; if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { - pr_debug("request_mem_region failed"); + pr_debug("request_mem_region failed\n"); retval = -EBUSY; goto err1; } hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); if (!hcd->regs) { - pr_debug("ioremap failed"); + pr_debug("ioremap failed\n"); retval = -ENOMEM; goto err2; } - au1xxx_start_hc(dev); + au1xxx_start_ohc(dev); ohci_hcd_init(hcd_to_ohci(hcd)); - retval = usb_add_hcd(hcd, dev->resource[1].start, SA_INTERRUPT); + retval = usb_add_hcd(hcd, dev->resource[1].start, SA_INTERRUPT | SA_SHIRQ); if (retval == 0) return retval; - au1xxx_stop_hc(dev); + au1xxx_stop_ohc(dev); iounmap(hcd->regs); err2: release_mem_region(hcd->rsrc_start, hcd->rsrc_len); @@ -146,10 +218,10 @@ int usb_hcd_au1xxx_probe (const struct hc_driver *driver, * context, normally "rmmod", "apmd", or something similar. * */ -void usb_hcd_au1xxx_remove (struct usb_hcd *hcd, struct platform_device *dev) +static void usb_ohci_au1xxx_remove(struct usb_hcd *hcd, struct platform_device *dev) { usb_remove_hcd(hcd); - au1xxx_stop_hc(dev); + au1xxx_stop_ohc(dev); iounmap(hcd->regs); release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); @@ -235,7 +307,7 @@ static int ohci_hcd_au1xxx_drv_probe(struct platform_device *pdev) if (usb_disabled()) return -ENODEV; - ret = usb_hcd_au1xxx_probe(&ohci_au1xxx_hc_driver, pdev); + ret = usb_ohci_au1xxx_probe(&ohci_au1xxx_hc_driver, pdev); return ret; } @@ -243,7 +315,7 @@ static int ohci_hcd_au1xxx_drv_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); - usb_hcd_au1xxx_remove(hcd, pdev); + usb_ohci_au1xxx_remove(hcd, pdev); return 0; } /*TBD*/ -- cgit v1.2.3-59-g8ed1b From d0852299381326c5d8eb67771aa98108050e6901 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Fri, 20 Jan 2006 14:35:55 -0800 Subject: [PATCH] USB: EHCI unlink tweaks This patch modifies the behavior of the EHCI driver in an unlink path that seems to be causing various issues on some systems. Those problems have included issues with disconnection, driver unbinding, and similar cases where urb unlinking would just not work right. This patch should help avoid those problems by not turning off the async (control/bulk) schedule until it's not expecting an "async advance" IRQ, which comes from the processing passing the schedule head. Whether the driver attempts to do such things is dependent on system timings, so many folk would never have seen these problems. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-q.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 6e28e593c044..5ada7cd5698b 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1022,12 +1022,14 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) /* stop async schedule right now? */ if (unlikely (qh == ehci->async)) { /* can't get here without STS_ASS set */ - if (ehci_to_hcd(ehci)->state != HC_STATE_HALT) { + if (ehci_to_hcd(ehci)->state != HC_STATE_HALT + && !ehci->reclaim) { + /* ... and CMD_IAAD clear */ writel (cmd & ~CMD_ASE, &ehci->regs->command); wmb (); // handshake later, if we need to + timer_action_done (ehci, TIMER_ASYNC_OFF); } - timer_action_done (ehci, TIMER_ASYNC_OFF); return; } -- cgit v1.2.3-59-g8ed1b From 39a269c09f854d3d98cbb755b3568175f04efa10 Mon Sep 17 00:00:00 2001 From: Andrew Victor Date: Sun, 22 Jan 2006 10:32:13 -0800 Subject: [PATCH] USB: add support for OCHI on AT91rm9200 This adds support for OHCI on AT91rm9200 based boards. Possibly of interest here is the way this uses to gate clocks on/off during system pm state transitions. That's typical for non-PCI systems. Some can go further; Mini-A host side connectors enable ID-pin sensing. From: Andrew Victor Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Kconfig | 1 + drivers/usb/Makefile | 1 + drivers/usb/host/ohci-at91.c | 306 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/ohci-hcd.c | 5 + 4 files changed, 313 insertions(+) create mode 100644 drivers/usb/host/ohci-at91.c diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 0e865975899f..b1222cd4aa46 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -23,6 +23,7 @@ config USB_ARCH_HAS_OHCI default y if ARCH_LH7A404 default y if ARCH_S3C2410 default y if PXA27x + default y if ARCH_AT91RM9200 # PPC: default y if STB03xxx default y if PPC_MPC52xx diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 7796ecfc0765..bb36a1c1dbb3 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_USB_OHCI_HCD) += host/ obj-$(CONFIG_USB_UHCI_HCD) += host/ obj-$(CONFIG_USB_SL811_HCD) += host/ obj-$(CONFIG_ETRAX_USB_HOST) += host/ +obj-$(CONFIG_USB_OHCI_AT91) += host/ obj-$(CONFIG_USB_ACM) += class/ obj-$(CONFIG_USB_PRINTER) += class/ diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c new file mode 100644 index 000000000000..980030d684d5 --- /dev/null +++ b/drivers/usb/host/ohci-at91.c @@ -0,0 +1,306 @@ +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * Copyright (C) 2004 SAN People (Pty) Ltd. + * Copyright (C) 2005 Thibaut VARENE + * + * AT91RM9200 Bus Glue + * + * Based on fragments of 2.4 driver by Rick Bronson. + * Based on ohci-omap.c + * + * This file is licenced under the GPL. + */ + +#include +#include + +#include +#include +#include + +#ifndef CONFIG_ARCH_AT91RM9200 +#error "This file is AT91RM9200 bus glue. CONFIG_ARCH_AT91RM9200 must be defined." +#endif + +/* interface and function clocks */ +static struct clk *iclk, *fclk; + +extern int usb_disabled(void); + +/*-------------------------------------------------------------------------*/ + +static void at91_start_hc(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct ohci_regs __iomem *regs = hcd->regs; + + dev_dbg(&pdev->dev, "starting AT91RM9200 OHCI USB Controller\n"); + + /* + * Start the USB clocks. + */ + clk_enable(iclk); + clk_enable(fclk); + + /* + * The USB host controller must remain in reset. + */ + writel(0, ®s->control); +} + +static void at91_stop_hc(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct ohci_regs __iomem *regs = hcd->regs; + + dev_dbg(&pdev->dev, "stopping AT91RM9200 OHCI USB Controller\n"); + + /* + * Put the USB host controller into reset. + */ + writel(0, ®s->control); + + /* + * Stop the USB clocks. + */ + clk_disable(fclk); + clk_disable(iclk); +} + + +/*-------------------------------------------------------------------------*/ + +static int usb_hcd_at91_remove (struct usb_hcd *, struct platform_device *); + +/* configure so an HC device and id are always provided */ +/* always called with process context; sleeping is OK */ + + +/** + * usb_hcd_at91_probe - initialize AT91RM9200-based HCDs + * Context: !in_interrupt() + * + * Allocates basic resources for this USB host controller, and + * then invokes the start() method for the HCD associated with it + * through the hotplug entry's driver_data. + * + * Store this function in the HCD's struct pci_driver as probe(). + */ +int usb_hcd_at91_probe (const struct hc_driver *driver, struct platform_device *pdev) +{ + int retval; + struct usb_hcd *hcd = NULL; + + if (pdev->num_resources != 2) { + pr_debug("hcd probe: invalid num_resources"); + return -ENODEV; + } + + if ((pdev->resource[0].flags != IORESOURCE_MEM) || (pdev->resource[1].flags != IORESOURCE_IRQ)) { + pr_debug("hcd probe: invalid resource type\n"); + return -ENODEV; + } + + hcd = usb_create_hcd(driver, &pdev->dev, "at91rm9200"); + if (!hcd) + return -ENOMEM; + hcd->rsrc_start = pdev->resource[0].start; + hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + pr_debug("request_mem_region failed\n"); + retval = -EBUSY; + goto err1; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + pr_debug("ioremap failed\n"); + retval = -EIO; + goto err2; + } + + iclk = clk_get(&pdev->dev, "ohci_clk"); + fclk = clk_get(&pdev->dev, "uhpck"); + + at91_start_hc(pdev); + ohci_hcd_init(hcd_to_ohci(hcd)); + + retval = usb_add_hcd(hcd, pdev->resource[1].start, SA_INTERRUPT); + if (retval == 0) + return retval; + + /* Error handling */ + at91_stop_hc(pdev); + + clk_put(fclk); + clk_put(iclk); + + iounmap(hcd->regs); + + err2: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + + err1: + usb_put_hcd(hcd); + return retval; +} + + +/* may be called without controller electrically present */ +/* may be called with controller, bus, and devices active */ + +/** + * usb_hcd_at91_remove - shutdown processing for AT91RM9200-based HCDs + * @dev: USB Host Controller being removed + * Context: !in_interrupt() + * + * Reverses the effect of usb_hcd_at91_probe(), first invoking + * the HCD's stop() method. It is always called from a thread + * context, normally "rmmod", "apmd", or something similar. + * + */ +static int usb_hcd_at91_remove (struct usb_hcd *hcd, struct platform_device *pdev) +{ + usb_remove_hcd(hcd); + at91_stop_hc(pdev); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + + clk_put(fclk); + clk_put(iclk); + fclk = iclk = NULL; + + dev_set_drvdata(&pdev->dev, NULL); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int __devinit +ohci_at91_start (struct usb_hcd *hcd) +{ +// struct at91_ohci_data *board = hcd->self.controller->platform_data; + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + int ret; + + if ((ret = ohci_init(ohci)) < 0) + return ret; + + if ((ret = ohci_run(ohci)) < 0) { + err("can't start %s", hcd->self.bus_name); + ohci_stop(hcd); + return ret; + } +// hcd->self.root_hub->maxchild = board->ports; + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static const struct hc_driver ohci_at91_hc_driver = { + .description = hcd_name, + .product_desc = "AT91RM9200 OHCI", + .hcd_priv_size = sizeof(struct ohci_hcd), + + /* + * generic hardware linkage + */ + .irq = ohci_irq, + .flags = HCD_USB11 | HCD_MEMORY, + + /* + * basic lifecycle operations + */ + .start = ohci_at91_start, + .stop = ohci_stop, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ohci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, + +#ifdef CONFIG_PM + .hub_suspend = ohci_hub_suspend, + .hub_resume = ohci_hub_resume, +#endif + .start_port_reset = ohci_start_port_reset, +}; + +/*-------------------------------------------------------------------------*/ + +static int ohci_hcd_at91_drv_probe(struct platform_device *dev) +{ + return usb_hcd_at91_probe(&ohci_at91_hc_driver, dev); +} + +static int ohci_hcd_at91_drv_remove(struct platform_device *dev) +{ + return usb_hcd_at91_remove(platform_get_drvdata(dev), dev); +} + +#ifdef CONFIG_PM +static int ohci_hcd_at91_drv_suspend(struct platform_device *dev, u32 state, u32 level) +{ + printk("%s(%s:%d): not implemented yet\n", + __func__, __FILE__, __LINE__); + + clk_disable(fclk); + + return 0; +} + +static int ohci_hcd_at91_drv_resume(struct platform_device *dev, u32 state) +{ + printk("%s(%s:%d): not implemented yet\n", + __func__, __FILE__, __LINE__); + + clk_enable(fclk); + + return 0; +} +#else +#define ohci_hcd_at91_drv_suspend NULL +#define ohci_hcd_at91_drv_resume NULL +#endif + +static struct platform_driver ohci_hcd_at91_driver = { + .probe = ohci_hcd_at91_drv_probe, + .remove = ohci_hcd_at91_drv_remove, + .suspend = ohci_hcd_at91_drv_suspend, + .resume = ohci_hcd_at91_drv_resume, + .driver = { + .name = "at91rm9200-ohci", + .owner = THIS_MODULE, + }, +}; + +static int __init ohci_hcd_at91_init (void) +{ + if (usb_disabled()) + return -ENODEV; + + return platform_driver_register(&ohci_hcd_at91_driver); +} + +static void __exit ohci_hcd_at91_cleanup (void) +{ + platform_driver_unregister(&ohci_hcd_at91_driver); +} + +module_init (ohci_hcd_at91_init); +module_exit (ohci_hcd_at91_cleanup); diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index a4b12404ae08..2990be269000 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -905,6 +905,10 @@ MODULE_LICENSE ("GPL"); #include "ohci-ppc-soc.c" #endif +#ifdef CONFIG_ARCH_AT91RM9200 +#include "ohci-at91.c" +#endif + #if !(defined(CONFIG_PCI) \ || defined(CONFIG_SA1111) \ || defined(CONFIG_ARCH_S3C2410) \ @@ -913,6 +917,7 @@ MODULE_LICENSE ("GPL"); || defined (CONFIG_PXA27x) \ || defined (CONFIG_SOC_AU1X00) \ || defined (CONFIG_USB_OHCI_HCD_PPC_SOC) \ + || defined (CONFIG_ARCH_AT91RM9200) \ ) #error "missing bus glue for ohci-hcd" #endif -- cgit v1.2.3-59-g8ed1b From bae4bd848dc0b7e6defc7a5d62834a35d1eed06d Mon Sep 17 00:00:00 2001 From: David Brownell Date: Sun, 22 Jan 2006 10:32:37 -0800 Subject: [PATCH] USB: add support for AT91 gadget This adds support for the USB peripheral controller on AT91 (rm9200, eventually also sam9261 or uClinux) platforms. More SOC support for Linux-USB ... an uncomplicated pure PIO driver. It'd be worth using this as a model, if you're starting a driver for some other peripheral controller. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/Kconfig | 17 + drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/at91_udc.c | 1773 +++++++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/at91_udc.h | 181 +++++ 4 files changed, 1972 insertions(+) create mode 100644 drivers/usb/gadget/at91_udc.c create mode 100644 drivers/usb/gadget/at91_udc.h diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index ff075a53c8d6..d80f71877d68 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -187,6 +187,23 @@ config USB_OTG Select this only if your OMAP board has a Mini-AB connector. +config USB_GADGET_AT91 + boolean "AT91 USB Device Port" + depends on ARCH_AT91RM9200 + select USB_GADGET_SELECTED + help + Many Atmel AT91 processors (such as the AT91RM2000) have a + full speed USB Device Port with support for five configurable + endpoints (plus endpoint zero). + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "at91_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_AT91 + tristate + depends on USB_GADGET_AT91 + default USB_GADGET config USB_GADGET_DUMMY_HCD boolean "Dummy HCD (DEVELOPMENT)" diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index d5fd04d886e6..5a28e61392ec 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_USB_PXA2XX) += pxa2xx_udc.o obj-$(CONFIG_USB_GOKU) += goku_udc.o obj-$(CONFIG_USB_OMAP) += omap_udc.o obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o +obj-$(CONFIG_USB_AT91) += at91_udc.o # # USB gadget drivers diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c new file mode 100644 index 000000000000..865858cfd1c2 --- /dev/null +++ b/drivers/usb/gadget/at91_udc.c @@ -0,0 +1,1773 @@ +/* + * at91_udc -- driver for at91-series USB peripheral controller + * + * Copyright (C) 2004 by Thomas Rathbone + * Copyright (C) 2005 by HP Labs + * Copyright (C) 2005 by David Brownell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#undef DEBUG +#undef VERBOSE +#undef PACKET_TRACE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "at91_udc.h" + + +/* + * This controller is simple and PIO-only. It's used in many AT91-series + * ARMv4T controllers, including the at91rm9200 (arm920T, with MMU), + * at91sam9261 (arm926ejs, with MMU), and several no-mmu versions. + * + * This driver expects the board has been wired with two GPIOs suppporting + * a VBUS sensing IRQ, and a D+ pullup. (They may be omitted, but the + * testing hasn't covered such cases.) The pullup is most important; it + * provides software control over whether the host enumerates the device. + * The VBUS sensing helps during enumeration, and allows both USB clocks + * (and the transceiver) to stay gated off until they're necessary, saving + * power. During USB suspend, the 48 MHz clock is gated off. + */ + +#define DRIVER_VERSION "8 March 2005" + +static const char driver_name [] = "at91_udc"; +static const char ep0name[] = "ep0"; + +/*-------------------------------------------------------------------------*/ + +/* + * Read from a UDP register. + */ +static inline unsigned long at91_udp_read(unsigned int reg) +{ + void __iomem *udp_base = (void __iomem *)AT91_VA_BASE_UDP; + + return __raw_readl(udp_base + reg); +} + +/* + * Write to a UDP register. + */ +static inline void at91_udp_write(unsigned int reg, unsigned long value) +{ + void __iomem *udp_base = (void __iomem *)AT91_VA_BASE_UDP; + + __raw_writel(value, udp_base + reg); +} + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + +#include + +static const char debug_filename[] = "driver/udc"; + +#define FOURBITS "%s%s%s%s" +#define EIGHTBITS FOURBITS FOURBITS + +static void proc_ep_show(struct seq_file *s, struct at91_ep *ep) +{ + static char *types[] = { + "control", "out-iso", "out-bulk", "out-int", + "BOGUS", "in-iso", "in-bulk", "in-int"}; + + u32 csr; + struct at91_request *req; + unsigned long flags; + + local_irq_save(flags); + + csr = __raw_readl(ep->creg); + + /* NOTE: not collecting per-endpoint irq statistics... */ + + seq_printf(s, "\n"); + seq_printf(s, "%s, maxpacket %d %s%s %s%s\n", + ep->ep.name, ep->ep.maxpacket, + ep->is_in ? "in" : "out", + ep->is_iso ? " iso" : "", + ep->is_pingpong + ? (ep->fifo_bank ? "pong" : "ping") + : "", + ep->stopped ? " stopped" : ""); + seq_printf(s, "csr %08x rxbytes=%d %s %s %s" EIGHTBITS "\n", + csr, + (csr & 0x07ff0000) >> 16, + (csr & (1 << 15)) ? "enabled" : "disabled", + (csr & (1 << 11)) ? "DATA1" : "DATA0", + types[(csr & 0x700) >> 8], + + /* iff type is control then print current direction */ + (!(csr & 0x700)) + ? ((csr & (1 << 7)) ? " IN" : " OUT") + : "", + (csr & (1 << 6)) ? " rxdatabk1" : "", + (csr & (1 << 5)) ? " forcestall" : "", + (csr & (1 << 4)) ? " txpktrdy" : "", + + (csr & (1 << 3)) ? " stallsent" : "", + (csr & (1 << 2)) ? " rxsetup" : "", + (csr & (1 << 1)) ? " rxdatabk0" : "", + (csr & (1 << 0)) ? " txcomp" : ""); + if (list_empty (&ep->queue)) + seq_printf(s, "\t(queue empty)\n"); + + else list_for_each_entry (req, &ep->queue, queue) { + unsigned length = req->req.actual; + + seq_printf(s, "\treq %p len %d/%d buf %p\n", + &req->req, length, + req->req.length, req->req.buf); + } + local_irq_restore(flags); +} + +static void proc_irq_show(struct seq_file *s, const char *label, u32 mask) +{ + int i; + + seq_printf(s, "%s %04x:%s%s" FOURBITS, label, mask, + (mask & (1 << 13)) ? " wakeup" : "", + (mask & (1 << 12)) ? " endbusres" : "", + + (mask & (1 << 11)) ? " sofint" : "", + (mask & (1 << 10)) ? " extrsm" : "", + (mask & (1 << 9)) ? " rxrsm" : "", + (mask & (1 << 8)) ? " rxsusp" : ""); + for (i = 0; i < 8; i++) { + if (mask & (1 << i)) + seq_printf(s, " ep%d", i); + } + seq_printf(s, "\n"); +} + +static int proc_udc_show(struct seq_file *s, void *unused) +{ + struct at91_udc *udc = s->private; + struct at91_ep *ep; + u32 tmp; + + seq_printf(s, "%s: version %s\n", driver_name, DRIVER_VERSION); + + seq_printf(s, "vbus %s, pullup %s, %s powered%s, gadget %s\n\n", + udc->vbus ? "present" : "off", + udc->enabled + ? (udc->vbus ? "active" : "enabled") + : "disabled", + udc->selfpowered ? "self" : "VBUS", + udc->suspended ? ", suspended" : "", + udc->driver ? udc->driver->driver.name : "(none)"); + + /* don't access registers when interface isn't clocked */ + if (!udc->clocked) { + seq_printf(s, "(not clocked)\n"); + return 0; + } + + tmp = at91_udp_read(AT91_UDP_FRM_NUM); + seq_printf(s, "frame %05x:%s%s frame=%d\n", tmp, + (tmp & AT91_UDP_FRM_OK) ? " ok" : "", + (tmp & AT91_UDP_FRM_ERR) ? " err" : "", + (tmp & AT91_UDP_NUM)); + + tmp = at91_udp_read(AT91_UDP_GLB_STAT); + seq_printf(s, "glbstate %02x:%s" FOURBITS "\n", tmp, + (tmp & AT91_UDP_RMWUPE) ? " rmwupe" : "", + (tmp & AT91_UDP_RSMINPR) ? " rsminpr" : "", + (tmp & AT91_UDP_ESR) ? " esr" : "", + (tmp & AT91_UDP_CONFG) ? " confg" : "", + (tmp & AT91_UDP_FADDEN) ? " fadden" : ""); + + tmp = at91_udp_read(AT91_UDP_FADDR); + seq_printf(s, "faddr %03x:%s fadd=%d\n", tmp, + (tmp & AT91_UDP_FEN) ? " fen" : "", + (tmp & AT91_UDP_FADD)); + + proc_irq_show(s, "imr ", at91_udp_read(AT91_UDP_IMR)); + proc_irq_show(s, "isr ", at91_udp_read(AT91_UDP_ISR)); + + if (udc->enabled && udc->vbus) { + proc_ep_show(s, &udc->ep[0]); + list_for_each_entry (ep, &udc->gadget.ep_list, ep.ep_list) { + if (ep->desc) + proc_ep_show(s, ep); + } + } + return 0; +} + +static int proc_udc_open(struct inode *inode, struct file *file) +{ + return single_open(file, proc_udc_show, PDE(inode)->data); +} + +static struct file_operations proc_ops = { + .open = proc_udc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void create_debug_file(struct at91_udc *udc) +{ + struct proc_dir_entry *pde; + + pde = create_proc_entry (debug_filename, 0, NULL); + udc->pde = pde; + if (pde == NULL) + return; + + pde->proc_fops = &proc_ops; + pde->data = udc; +} + +static void remove_debug_file(struct at91_udc *udc) +{ + if (udc->pde) + remove_proc_entry(debug_filename, NULL); +} + +#else + +static inline void create_debug_file(struct at91_udc *udc) {} +static inline void remove_debug_file(struct at91_udc *udc) {} + +#endif + + +/*-------------------------------------------------------------------------*/ + +static void done(struct at91_ep *ep, struct at91_request *req, int status) +{ + unsigned stopped = ep->stopped; + + list_del_init(&req->queue); + if (req->req.status == -EINPROGRESS) + req->req.status = status; + else + status = req->req.status; + if (status && status != -ESHUTDOWN) + VDBG("%s done %p, status %d\n", ep->ep.name, req, status); + + ep->stopped = 1; + req->req.complete(&ep->ep, &req->req); + ep->stopped = stopped; + + /* ep0 is always ready; other endpoints need a non-empty queue */ + if (list_empty(&ep->queue) && ep->int_mask != (1 << 0)) + at91_udp_write(AT91_UDP_IDR, ep->int_mask); +} + +/*-------------------------------------------------------------------------*/ + +/* bits indicating OUT fifo has data ready */ +#define RX_DATA_READY (AT91_UDP_RX_DATA_BK0 | AT91_UDP_RX_DATA_BK1) + +/* + * Endpoint FIFO CSR bits have a mix of bits, making it unsafe to just write + * back most of the value you just read (because of side effects, including + * bits that may change after reading and before writing). + * + * Except when changing a specific bit, always write values which: + * - clear SET_FX bits (setting them could change something) + * - set CLR_FX bits (clearing them could change something) + * + * There are also state bits like FORCESTALL, EPEDS, DIR, and EPTYPE + * that shouldn't normally be changed. + */ +#define SET_FX (AT91_UDP_TXPKTRDY) +#define CLR_FX (RX_DATA_READY | AT91_UDP_RXSETUP | AT91_UDP_STALLSENT | AT91_UDP_TXCOMP) + +/* pull OUT packet data from the endpoint's fifo */ +static int read_fifo (struct at91_ep *ep, struct at91_request *req) +{ + u32 __iomem *creg = ep->creg; + u8 __iomem *dreg = ep->creg + (AT91_UDP_FDR(0) - AT91_UDP_CSR(0)); + u32 csr; + u8 *buf; + unsigned int count, bufferspace, is_done; + + buf = req->req.buf + req->req.actual; + bufferspace = req->req.length - req->req.actual; + + /* + * there might be nothing to read if ep_queue() calls us, + * or if we already emptied both pingpong buffers + */ +rescan: + csr = __raw_readl(creg); + if ((csr & RX_DATA_READY) == 0) + return 0; + + count = (csr & AT91_UDP_RXBYTECNT) >> 16; + if (count > ep->ep.maxpacket) + count = ep->ep.maxpacket; + if (count > bufferspace) { + DBG("%s buffer overflow\n", ep->ep.name); + req->req.status = -EOVERFLOW; + count = bufferspace; + } + __raw_readsb(dreg, buf, count); + + /* release and swap pingpong mem bank */ + csr |= CLR_FX; + if (ep->is_pingpong) { + if (ep->fifo_bank == 0) { + csr &= ~(SET_FX | AT91_UDP_RX_DATA_BK0); + ep->fifo_bank = 1; + } else { + csr &= ~(SET_FX | AT91_UDP_RX_DATA_BK1); + ep->fifo_bank = 0; + } + } else + csr &= ~(SET_FX | AT91_UDP_RX_DATA_BK0); + __raw_writel(csr, creg); + + req->req.actual += count; + is_done = (count < ep->ep.maxpacket); + if (count == bufferspace) + is_done = 1; + + PACKET("%s %p out/%d%s\n", ep->ep.name, &req->req, count, + is_done ? " (done)" : ""); + + /* + * avoid extra trips through IRQ logic for packets already in + * the fifo ... maybe preventing an extra (expensive) OUT-NAK + */ + if (is_done) + done(ep, req, 0); + else if (ep->is_pingpong) { + bufferspace -= count; + buf += count; + goto rescan; + } + + return is_done; +} + +/* load fifo for an IN packet */ +static int write_fifo(struct at91_ep *ep, struct at91_request *req) +{ + u32 __iomem *creg = ep->creg; + u32 csr = __raw_readl(creg); + u8 __iomem *dreg = ep->creg + (AT91_UDP_FDR(0) - AT91_UDP_CSR(0)); + unsigned total, count, is_last; + + /* + * TODO: allow for writing two packets to the fifo ... that'll + * reduce the amount of IN-NAKing, but probably won't affect + * throughput much. (Unlike preventing OUT-NAKing!) + */ + + /* + * If ep_queue() calls us, the queue is empty and possibly in + * odd states like TXCOMP not yet cleared (we do it, saving at + * least one IRQ) or the fifo not yet being free. Those aren't + * issues normally (IRQ handler fast path). + */ + if (unlikely(csr & (AT91_UDP_TXCOMP | AT91_UDP_TXPKTRDY))) { + if (csr & AT91_UDP_TXCOMP) { + csr |= CLR_FX; + csr &= ~(SET_FX | AT91_UDP_TXCOMP); + __raw_writel(csr, creg); + csr = __raw_readl(creg); + } + if (csr & AT91_UDP_TXPKTRDY) + return 0; + } + + total = req->req.length - req->req.actual; + if (ep->ep.maxpacket < total) { + count = ep->ep.maxpacket; + is_last = 0; + } else { + count = total; + is_last = (count < ep->ep.maxpacket) || !req->req.zero; + } + + /* + * Write the packet, maybe it's a ZLP. + * + * NOTE: incrementing req->actual before we receive the ACK means + * gadget driver IN bytecounts can be wrong in fault cases. That's + * fixable with PIO drivers like this one (save "count" here, and + * do the increment later on TX irq), but not for most DMA hardware. + * + * So all gadget drivers must accept that potential error. Some + * hardware supports precise fifo status reporting, letting them + * recover when the actual bytecount matters (e.g. for USB Test + * and Measurement Class devices). + */ + __raw_writesb(dreg, req->req.buf + req->req.actual, count); + csr &= ~SET_FX; + csr |= CLR_FX | AT91_UDP_TXPKTRDY; + __raw_writel(csr, creg); + req->req.actual += count; + + PACKET("%s %p in/%d%s\n", ep->ep.name, &req->req, count, + is_last ? " (done)" : ""); + if (is_last) + done(ep, req, 0); + return is_last; +} + +static void nuke(struct at91_ep *ep, int status) +{ + struct at91_request *req; + + // terminer chaque requete dans la queue + ep->stopped = 1; + if (list_empty(&ep->queue)) + return; + + VDBG("%s %s\n", __FUNCTION__, ep->ep.name); + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct at91_request, queue); + done(ep, req, status); + } +} + +/*-------------------------------------------------------------------------*/ + +static int at91_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) +{ + struct at91_ep *ep = container_of(_ep, struct at91_ep, ep); + struct at91_udc *dev = ep->udc; + u16 maxpacket; + u32 tmp; + unsigned long flags; + + if (!_ep || !ep + || !desc || ep->desc + || _ep->name == ep0name + || desc->bDescriptorType != USB_DT_ENDPOINT + || (maxpacket = le16_to_cpu(desc->wMaxPacketSize)) == 0 + || maxpacket > ep->maxpacket) { + DBG("bad ep or descriptor\n"); + return -EINVAL; + } + + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { + DBG("bogus device state\n"); + return -ESHUTDOWN; + } + + tmp = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + switch (tmp) { + case USB_ENDPOINT_XFER_CONTROL: + DBG("only one control endpoint\n"); + return -EINVAL; + case USB_ENDPOINT_XFER_INT: + if (maxpacket > 64) + goto bogus_max; + break; + case USB_ENDPOINT_XFER_BULK: + switch (maxpacket) { + case 8: + case 16: + case 32: + case 64: + goto ok; + } +bogus_max: + DBG("bogus maxpacket %d\n", maxpacket); + return -EINVAL; + case USB_ENDPOINT_XFER_ISOC: + if (!ep->is_pingpong) { + DBG("iso requires double buffering\n"); + return -EINVAL; + } + break; + } + +ok: + local_irq_save(flags); + + /* initialize endpoint to match this descriptor */ + ep->is_in = (desc->bEndpointAddress & USB_DIR_IN) != 0; + ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC); + ep->stopped = 0; + if (ep->is_in) + tmp |= 0x04; + tmp <<= 8; + tmp |= AT91_UDP_EPEDS; + __raw_writel(tmp, ep->creg); + + ep->desc = desc; + ep->ep.maxpacket = maxpacket; + + /* + * reset/init endpoint fifo. NOTE: leaves fifo_bank alone, + * since endpoint resets don't reset hw pingpong state. + */ + at91_udp_write(AT91_UDP_RST_EP, ep->int_mask); + at91_udp_write(AT91_UDP_RST_EP, 0); + + local_irq_restore(flags); + return 0; +} + +static int at91_ep_disable (struct usb_ep * _ep) +{ + struct at91_ep *ep = container_of(_ep, struct at91_ep, ep); + unsigned long flags; + + if (ep == &ep->udc->ep[0]) + return -EINVAL; + + local_irq_save(flags); + + nuke(ep, -ESHUTDOWN); + + /* restore the endpoint's pristine config */ + ep->desc = NULL; + ep->ep.maxpacket = ep->maxpacket; + + /* reset fifos and endpoint */ + if (ep->udc->clocked) { + at91_udp_write(AT91_UDP_RST_EP, ep->int_mask); + at91_udp_write(AT91_UDP_RST_EP, 0); + __raw_writel(0, ep->creg); + } + + local_irq_restore(flags); + return 0; +} + +/* + * this is a PIO-only driver, so there's nothing + * interesting for request or buffer allocation. + */ + +static struct usb_request *at91_ep_alloc_request (struct usb_ep *_ep, unsigned int gfp_flags) +{ + struct at91_request *req; + + req = kcalloc(1, sizeof (struct at91_request), SLAB_KERNEL); + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->queue); + return &req->req; +} + +static void at91_ep_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct at91_request *req; + + req = container_of(_req, struct at91_request, req); + BUG_ON(!list_empty(&req->queue)); + kfree(req); +} + +static void *at91_ep_alloc_buffer( + struct usb_ep *_ep, + unsigned bytes, + dma_addr_t *dma, + gfp_t gfp_flags) +{ + *dma = ~0; + return kmalloc(bytes, gfp_flags); +} + +static void at91_ep_free_buffer( + struct usb_ep *ep, + void *buf, + dma_addr_t dma, + unsigned bytes) +{ + kfree(buf); +} + +static int at91_ep_queue(struct usb_ep *_ep, + struct usb_request *_req, gfp_t gfp_flags) +{ + struct at91_request *req; + struct at91_ep *ep; + struct at91_udc *dev; + int status; + unsigned long flags; + + req = container_of(_req, struct at91_request, req); + ep = container_of(_ep, struct at91_ep, ep); + + if (!_req || !_req->complete + || !_req->buf || !list_empty(&req->queue)) { + DBG("invalid request\n"); + return -EINVAL; + } + + if (!_ep || (!ep->desc && ep->ep.name != ep0name)) { + DBG("invalid ep\n"); + return -EINVAL; + } + + dev = ep->udc; + + if (!dev || !dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { + DBG("invalid device\n"); + return -EINVAL; + } + + _req->status = -EINPROGRESS; + _req->actual = 0; + + local_irq_save(flags); + + /* try to kickstart any empty and idle queue */ + if (list_empty(&ep->queue) && !ep->stopped) { + int is_ep0; + + /* + * If this control request has a non-empty DATA stage, this + * will start that stage. It works just like a non-control + * request (until the status stage starts, maybe early). + * + * If the data stage is empty, then this starts a successful + * IN/STATUS stage. (Unsuccessful ones use set_halt.) + */ + is_ep0 = (ep->ep.name == ep0name); + if (is_ep0) { + u32 tmp; + + if (!dev->req_pending) { + status = -EINVAL; + goto done; + } + + /* + * defer changing CONFG until after the gadget driver + * reconfigures the endpoints. + */ + if (dev->wait_for_config_ack) { + tmp = at91_udp_read(AT91_UDP_GLB_STAT); + tmp ^= AT91_UDP_CONFG; + VDBG("toggle config\n"); + at91_udp_write(AT91_UDP_GLB_STAT, tmp); + } + if (req->req.length == 0) { +ep0_in_status: + PACKET("ep0 in/status\n"); + status = 0; + tmp = __raw_readl(ep->creg); + tmp &= ~SET_FX; + tmp |= CLR_FX | AT91_UDP_TXPKTRDY; + __raw_writel(tmp, ep->creg); + dev->req_pending = 0; + goto done; + } + } + + if (ep->is_in) + status = write_fifo(ep, req); + else { + status = read_fifo(ep, req); + + /* IN/STATUS stage is otherwise triggered by irq */ + if (status && is_ep0) + goto ep0_in_status; + } + } else + status = 0; + + if (req && !status) { + list_add_tail (&req->queue, &ep->queue); + at91_udp_write(AT91_UDP_IER, ep->int_mask); + } +done: + local_irq_restore(flags); + return (status < 0) ? status : 0; +} + +static int at91_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct at91_ep *ep; + struct at91_request *req; + + ep = container_of(_ep, struct at91_ep, ep); + if (!_ep || ep->ep.name == ep0name) + return -EINVAL; + + /* 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) + return -EINVAL; + + done(ep, req, -ECONNRESET); + return 0; +} + +static int at91_ep_set_halt(struct usb_ep *_ep, int value) +{ + struct at91_ep *ep = container_of(_ep, struct at91_ep, ep); + u32 __iomem *creg; + u32 csr; + unsigned long flags; + int status = 0; + + if (!_ep || ep->is_iso || !ep->udc->clocked) + return -EINVAL; + + creg = ep->creg; + local_irq_save(flags); + + csr = __raw_readl(creg); + + /* + * fail with still-busy IN endpoints, ensuring correct sequencing + * of data tx then stall. note that the fifo rx bytecount isn't + * completely accurate as a tx bytecount. + */ + if (ep->is_in && (!list_empty(&ep->queue) || (csr >> 16) != 0)) + status = -EAGAIN; + else { + csr |= CLR_FX; + csr &= ~SET_FX; + if (value) { + csr |= AT91_UDP_FORCESTALL; + VDBG("halt %s\n", ep->ep.name); + } else { + at91_udp_write(AT91_UDP_RST_EP, ep->int_mask); + at91_udp_write(AT91_UDP_RST_EP, 0); + csr &= ~AT91_UDP_FORCESTALL; + } + __raw_writel(csr, creg); + } + + local_irq_restore(flags); + return status; +} + +static struct usb_ep_ops at91_ep_ops = { + .enable = at91_ep_enable, + .disable = at91_ep_disable, + .alloc_request = at91_ep_alloc_request, + .free_request = at91_ep_free_request, + .alloc_buffer = at91_ep_alloc_buffer, + .free_buffer = at91_ep_free_buffer, + .queue = at91_ep_queue, + .dequeue = at91_ep_dequeue, + .set_halt = at91_ep_set_halt, + // there's only imprecise fifo status reporting +}; + +/*-------------------------------------------------------------------------*/ + +static int at91_get_frame(struct usb_gadget *gadget) +{ + if (!to_udc(gadget)->clocked) + return -EINVAL; + return at91_udp_read(AT91_UDP_FRM_NUM) & AT91_UDP_NUM; +} + +static int at91_wakeup(struct usb_gadget *gadget) +{ + struct at91_udc *udc = to_udc(gadget); + u32 glbstate; + int status = -EINVAL; + unsigned long flags; + + DBG("%s\n", __FUNCTION__ ); + local_irq_save(flags); + + if (!udc->clocked || !udc->suspended) + goto done; + + /* NOTE: some "early versions" handle ESR differently ... */ + + glbstate = at91_udp_read(AT91_UDP_GLB_STAT); + if (!(glbstate & AT91_UDP_ESR)) + goto done; + glbstate |= AT91_UDP_ESR; + at91_udp_write(AT91_UDP_GLB_STAT, glbstate); + +done: + local_irq_restore(flags); + return status; +} + +/* reinit == restore inital software state */ +static void udc_reinit(struct at91_udc *udc) +{ + u32 i; + + INIT_LIST_HEAD(&udc->gadget.ep_list); + INIT_LIST_HEAD(&udc->gadget.ep0->ep_list); + + for (i = 0; i < NUM_ENDPOINTS; i++) { + struct at91_ep *ep = &udc->ep[i]; + + if (i != 0) + list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); + ep->desc = NULL; + ep->stopped = 0; + ep->fifo_bank = 0; + ep->ep.maxpacket = ep->maxpacket; + // initialiser une queue par endpoint + INIT_LIST_HEAD(&ep->queue); + } +} + +static void stop_activity(struct at91_udc *udc) +{ + struct usb_gadget_driver *driver = udc->driver; + int i; + + if (udc->gadget.speed == USB_SPEED_UNKNOWN) + driver = NULL; + udc->gadget.speed = USB_SPEED_UNKNOWN; + + for (i = 0; i < NUM_ENDPOINTS; i++) { + struct at91_ep *ep = &udc->ep[i]; + ep->stopped = 1; + nuke(ep, -ESHUTDOWN); + } + if (driver) + driver->disconnect(&udc->gadget); + + udc_reinit(udc); +} + +static void clk_on(struct at91_udc *udc) +{ + if (udc->clocked) + return; + udc->clocked = 1; + clk_enable(udc->iclk); + clk_enable(udc->fclk); +} + +static void clk_off(struct at91_udc *udc) +{ + if (!udc->clocked) + return; + udc->clocked = 0; + udc->gadget.speed = USB_SPEED_UNKNOWN; + clk_disable(udc->iclk); + clk_disable(udc->fclk); +} + +/* + * activate/deactivate link with host; minimize power usage for + * inactive links by cutting clocks and transceiver power. + */ +static void pullup(struct at91_udc *udc, int is_on) +{ + if (!udc->enabled || !udc->vbus) + is_on = 0; + DBG("%sactive\n", is_on ? "" : "in"); + if (is_on) { + clk_on(udc); + at91_udp_write(AT91_UDP_TXVC, 0); + at91_set_gpio_value(udc->board.pullup_pin, 1); + } else { + stop_activity(udc); + at91_udp_write(AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS); + at91_set_gpio_value(udc->board.pullup_pin, 0); + clk_off(udc); + + // REVISIT: with transceiver disabled, will D- float + // so that a host would falsely detect a device? + } +} + +/* vbus is here! turn everything on that's ready */ +static int at91_vbus_session(struct usb_gadget *gadget, int is_active) +{ + struct at91_udc *udc = to_udc(gadget); + unsigned long flags; + + // VDBG("vbus %s\n", is_active ? "on" : "off"); + local_irq_save(flags); + udc->vbus = (is_active != 0); + pullup(udc, is_active); + local_irq_restore(flags); + return 0; +} + +static int at91_pullup(struct usb_gadget *gadget, int is_on) +{ + struct at91_udc *udc = to_udc(gadget); + unsigned long flags; + + local_irq_save(flags); + udc->enabled = is_on = !!is_on; + pullup(udc, is_on); + local_irq_restore(flags); + return 0; +} + +static int at91_set_selfpowered(struct usb_gadget *gadget, int is_on) +{ + struct at91_udc *udc = to_udc(gadget); + unsigned long flags; + + local_irq_save(flags); + udc->selfpowered = (is_on != 0); + local_irq_restore(flags); + return 0; +} + +static const struct usb_gadget_ops at91_udc_ops = { + .get_frame = at91_get_frame, + .wakeup = at91_wakeup, + .set_selfpowered = at91_set_selfpowered, + .vbus_session = at91_vbus_session, + .pullup = at91_pullup, + + /* + * VBUS-powered devices may also also want to support bigger + * power budgets after an appropriate SET_CONFIGURATION. + */ + // .vbus_power = at91_vbus_power, +}; + +/*-------------------------------------------------------------------------*/ + +static int handle_ep(struct at91_ep *ep) +{ + struct at91_request *req; + u32 __iomem *creg = ep->creg; + u32 csr = __raw_readl(creg); + + if (!list_empty(&ep->queue)) + req = list_entry(ep->queue.next, + struct at91_request, queue); + else + req = NULL; + + if (ep->is_in) { + if (csr & (AT91_UDP_STALLSENT | AT91_UDP_TXCOMP)) { + csr |= CLR_FX; + csr &= ~(SET_FX | AT91_UDP_STALLSENT | AT91_UDP_TXCOMP); + __raw_writel(csr, creg); + } + if (req) + return write_fifo(ep, req); + + } else { + if (csr & AT91_UDP_STALLSENT) { + /* STALLSENT bit == ISOERR */ + if (ep->is_iso && req) + req->req.status = -EILSEQ; + csr |= CLR_FX; + csr &= ~(SET_FX | AT91_UDP_STALLSENT); + __raw_writel(csr, creg); + csr = __raw_readl(creg); + } + if (req && (csr & RX_DATA_READY)) + return read_fifo(ep, req); + } + return 0; +} + +union setup { + u8 raw[8]; + struct usb_ctrlrequest r; +}; + +static void handle_setup(struct at91_udc *udc, struct at91_ep *ep, u32 csr) +{ + u32 __iomem *creg = ep->creg; + u8 __iomem *dreg = ep->creg + (AT91_UDP_FDR(0) - AT91_UDP_CSR(0)); + unsigned rxcount, i = 0; + u32 tmp; + union setup pkt; + int status = 0; + + /* read and ack SETUP; hard-fail for bogus packets */ + rxcount = (csr & AT91_UDP_RXBYTECNT) >> 16; + if (likely(rxcount == 8)) { + while (rxcount--) + pkt.raw[i++] = __raw_readb(dreg); + if (pkt.r.bRequestType & USB_DIR_IN) { + csr |= AT91_UDP_DIR; + ep->is_in = 1; + } else { + csr &= ~AT91_UDP_DIR; + ep->is_in = 0; + } + } else { + // REVISIT this happens sometimes under load; why?? + ERR("SETUP len %d, csr %08x\n", rxcount, csr); + status = -EINVAL; + } + csr |= CLR_FX; + csr &= ~(SET_FX | AT91_UDP_RXSETUP); + __raw_writel(csr, creg); + udc->wait_for_addr_ack = 0; + udc->wait_for_config_ack = 0; + ep->stopped = 0; + if (unlikely(status != 0)) + goto stall; + +#define w_index le16_to_cpu(pkt.r.wIndex) +#define w_value le16_to_cpu(pkt.r.wValue) +#define w_length le16_to_cpu(pkt.r.wLength) + + VDBG("SETUP %02x.%02x v%04x i%04x l%04x\n", + pkt.r.bRequestType, pkt.r.bRequest, + w_value, w_index, w_length); + + /* + * A few standard requests get handled here, ones that touch + * hardware ... notably for device and endpoint features. + */ + udc->req_pending = 1; + csr = __raw_readl(creg); + csr |= CLR_FX; + csr &= ~SET_FX; + switch ((pkt.r.bRequestType << 8) | pkt.r.bRequest) { + + case ((USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8) + | USB_REQ_SET_ADDRESS: + __raw_writel(csr | AT91_UDP_TXPKTRDY, creg); + udc->addr = w_value; + udc->wait_for_addr_ack = 1; + udc->req_pending = 0; + /* FADDR is set later, when we ack host STATUS */ + return; + + case ((USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8) + | USB_REQ_SET_CONFIGURATION: + tmp = at91_udp_read(AT91_UDP_GLB_STAT) & AT91_UDP_CONFG; + if (pkt.r.wValue) + udc->wait_for_config_ack = (tmp == 0); + else + udc->wait_for_config_ack = (tmp != 0); + if (udc->wait_for_config_ack) + VDBG("wait for config\n"); + /* CONFG is toggled later, if gadget driver succeeds */ + break; + + /* + * Hosts may set or clear remote wakeup status, and + * devices may report they're VBUS powered. + */ + case ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8) + | USB_REQ_GET_STATUS: + tmp = (udc->selfpowered << USB_DEVICE_SELF_POWERED); + if (at91_udp_read(AT91_UDP_GLB_STAT) & AT91_UDP_ESR) + tmp |= (1 << USB_DEVICE_REMOTE_WAKEUP); + PACKET("get device status\n"); + __raw_writeb(tmp, dreg); + __raw_writeb(0, dreg); + goto write_in; + /* then STATUS starts later, automatically */ + case ((USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8) + | USB_REQ_SET_FEATURE: + if (w_value != USB_DEVICE_REMOTE_WAKEUP) + goto stall; + tmp = at91_udp_read(AT91_UDP_GLB_STAT); + tmp |= AT91_UDP_ESR; + at91_udp_write(AT91_UDP_GLB_STAT, tmp); + goto succeed; + case ((USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8) + | USB_REQ_CLEAR_FEATURE: + if (w_value != USB_DEVICE_REMOTE_WAKEUP) + goto stall; + tmp = at91_udp_read(AT91_UDP_GLB_STAT); + tmp &= ~AT91_UDP_ESR; + at91_udp_write(AT91_UDP_GLB_STAT, tmp); + goto succeed; + + /* + * Interfaces have no feature settings; this is pretty useless. + * we won't even insist the interface exists... + */ + case ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE) << 8) + | USB_REQ_GET_STATUS: + PACKET("get interface status\n"); + __raw_writeb(0, dreg); + __raw_writeb(0, dreg); + goto write_in; + /* then STATUS starts later, automatically */ + case ((USB_TYPE_STANDARD|USB_RECIP_INTERFACE) << 8) + | USB_REQ_SET_FEATURE: + case ((USB_TYPE_STANDARD|USB_RECIP_INTERFACE) << 8) + | USB_REQ_CLEAR_FEATURE: + goto stall; + + /* + * Hosts may clear bulk/intr endpoint halt after the gadget + * driver sets it (not widely used); or set it (for testing) + */ + case ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT) << 8) + | USB_REQ_GET_STATUS: + tmp = w_index & USB_ENDPOINT_NUMBER_MASK; + ep = &udc->ep[tmp]; + if (tmp > NUM_ENDPOINTS || (tmp && !ep->desc)) + goto stall; + + if (tmp) { + if ((w_index & USB_DIR_IN)) { + if (!ep->is_in) + goto stall; + } else if (ep->is_in) + goto stall; + } + PACKET("get %s status\n", ep->ep.name); + if (__raw_readl(ep->creg) & AT91_UDP_FORCESTALL) + tmp = (1 << USB_ENDPOINT_HALT); + else + tmp = 0; + __raw_writeb(tmp, dreg); + __raw_writeb(0, dreg); + goto write_in; + /* then STATUS starts later, automatically */ + case ((USB_TYPE_STANDARD|USB_RECIP_ENDPOINT) << 8) + | USB_REQ_SET_FEATURE: + tmp = w_index & USB_ENDPOINT_NUMBER_MASK; + ep = &udc->ep[tmp]; + if (w_value != USB_ENDPOINT_HALT || tmp > NUM_ENDPOINTS) + goto stall; + if (!ep->desc || ep->is_iso) + goto stall; + if ((w_index & USB_DIR_IN)) { + if (!ep->is_in) + goto stall; + } else if (ep->is_in) + goto stall; + + tmp = __raw_readl(ep->creg); + tmp &= ~SET_FX; + tmp |= CLR_FX | AT91_UDP_FORCESTALL; + __raw_writel(tmp, ep->creg); + goto succeed; + case ((USB_TYPE_STANDARD|USB_RECIP_ENDPOINT) << 8) + | USB_REQ_CLEAR_FEATURE: + tmp = w_index & USB_ENDPOINT_NUMBER_MASK; + ep = &udc->ep[tmp]; + if (w_value != USB_ENDPOINT_HALT || tmp > NUM_ENDPOINTS) + goto stall; + if (tmp == 0) + goto succeed; + if (!ep->desc || ep->is_iso) + goto stall; + if ((w_index & USB_DIR_IN)) { + if (!ep->is_in) + goto stall; + } else if (ep->is_in) + goto stall; + + at91_udp_write(AT91_UDP_RST_EP, ep->int_mask); + at91_udp_write(AT91_UDP_RST_EP, 0); + tmp = __raw_readl(ep->creg); + tmp |= CLR_FX; + tmp &= ~(SET_FX | AT91_UDP_FORCESTALL); + __raw_writel(tmp, ep->creg); + if (!list_empty(&ep->queue)) + handle_ep(ep); + goto succeed; + } + +#undef w_value +#undef w_index +#undef w_length + + /* pass request up to the gadget driver */ + status = udc->driver->setup(&udc->gadget, &pkt.r); + if (status < 0) { +stall: + VDBG("req %02x.%02x protocol STALL; stat %d\n", + pkt.r.bRequestType, pkt.r.bRequest, status); + csr |= AT91_UDP_FORCESTALL; + __raw_writel(csr, creg); + udc->req_pending = 0; + } + return; + +succeed: + /* immediate successful (IN) STATUS after zero length DATA */ + PACKET("ep0 in/status\n"); +write_in: + csr |= AT91_UDP_TXPKTRDY; + __raw_writel(csr, creg); + udc->req_pending = 0; + return; +} + +static void handle_ep0(struct at91_udc *udc) +{ + struct at91_ep *ep0 = &udc->ep[0]; + u32 __iomem *creg = ep0->creg; + u32 csr = __raw_readl(creg); + struct at91_request *req; + + if (unlikely(csr & AT91_UDP_STALLSENT)) { + nuke(ep0, -EPROTO); + udc->req_pending = 0; + csr |= CLR_FX; + csr &= ~(SET_FX | AT91_UDP_STALLSENT | AT91_UDP_FORCESTALL); + __raw_writel(csr, creg); + VDBG("ep0 stalled\n"); + csr = __raw_readl(creg); + } + if (csr & AT91_UDP_RXSETUP) { + nuke(ep0, 0); + udc->req_pending = 0; + handle_setup(udc, ep0, csr); + return; + } + + if (list_empty(&ep0->queue)) + req = NULL; + else + req = list_entry(ep0->queue.next, struct at91_request, queue); + + /* host ACKed an IN packet that we sent */ + if (csr & AT91_UDP_TXCOMP) { + csr |= CLR_FX; + csr &= ~(SET_FX | AT91_UDP_TXCOMP); + + /* write more IN DATA? */ + if (req && ep0->is_in) { + if (handle_ep(ep0)) + udc->req_pending = 0; + + /* + * Ack after: + * - last IN DATA packet (including GET_STATUS) + * - IN/STATUS for OUT DATA + * - IN/STATUS for any zero-length DATA stage + * except for the IN DATA case, the host should send + * an OUT status later, which we'll ack. + */ + } else { + udc->req_pending = 0; + __raw_writel(csr, creg); + + /* + * SET_ADDRESS takes effect only after the STATUS + * (to the original address) gets acked. + */ + if (udc->wait_for_addr_ack) { + u32 tmp; + + at91_udp_write(AT91_UDP_FADDR, AT91_UDP_FEN | udc->addr); + tmp = at91_udp_read(AT91_UDP_GLB_STAT); + tmp &= ~AT91_UDP_FADDEN; + if (udc->addr) + tmp |= AT91_UDP_FADDEN; + at91_udp_write(AT91_UDP_GLB_STAT, tmp); + + udc->wait_for_addr_ack = 0; + VDBG("address %d\n", udc->addr); + } + } + } + + /* OUT packet arrived ... */ + else if (csr & AT91_UDP_RX_DATA_BK0) { + csr |= CLR_FX; + csr &= ~(SET_FX | AT91_UDP_RX_DATA_BK0); + + /* OUT DATA stage */ + if (!ep0->is_in) { + if (req) { + if (handle_ep(ep0)) { + /* send IN/STATUS */ + PACKET("ep0 in/status\n"); + csr = __raw_readl(creg); + csr &= ~SET_FX; + csr |= CLR_FX | AT91_UDP_TXPKTRDY; + __raw_writel(csr, creg); + udc->req_pending = 0; + } + } else if (udc->req_pending) { + /* + * AT91 hardware has a hard time with this + * "deferred response" mode for control-OUT + * transfers. (For control-IN it's fine.) + * + * The normal solution leaves OUT data in the + * fifo until the gadget driver is ready. + * We couldn't do that here without disabling + * the IRQ that tells about SETUP packets, + * e.g. when the host gets impatient... + * + * Working around it by copying into a buffer + * would almost be a non-deferred response, + * except that it wouldn't permit reliable + * stalling of the request. Instead, demand + * that gadget drivers not use this mode. + */ + DBG("no control-OUT deferred responses!\n"); + __raw_writel(csr | AT91_UDP_FORCESTALL, creg); + udc->req_pending = 0; + } + + /* STATUS stage for control-IN; ack. */ + } else { + PACKET("ep0 out/status ACK\n"); + __raw_writel(csr, creg); + + /* "early" status stage */ + if (req) + done(ep0, req, 0); + } + } +} + +static irqreturn_t at91_udc_irq (int irq, void *_udc, struct pt_regs *r) +{ + struct at91_udc *udc = _udc; + u32 rescans = 5; + + while (rescans--) { + u32 status = at91_udp_read(AT91_UDP_ISR); + + status &= at91_udp_read(AT91_UDP_IMR); + if (!status) + break; + + /* USB reset irq: not maskable */ + if (status & AT91_UDP_ENDBUSRES) { + at91_udp_write(AT91_UDP_IDR, ~MINIMUS_INTERRUPTUS); + at91_udp_write(AT91_UDP_IER, MINIMUS_INTERRUPTUS); + /* Atmel code clears this irq twice */ + at91_udp_write(AT91_UDP_ICR, AT91_UDP_ENDBUSRES); + at91_udp_write(AT91_UDP_ICR, AT91_UDP_ENDBUSRES); + VDBG("end bus reset\n"); + udc->addr = 0; + stop_activity(udc); + + /* enable ep0 */ + at91_udp_write(AT91_UDP_CSR(0), AT91_UDP_EPEDS | AT91_UDP_EPTYPE_CTRL); + udc->gadget.speed = USB_SPEED_FULL; + udc->suspended = 0; + at91_udp_write(AT91_UDP_IER, AT91_UDP_EP(0)); + + /* + * NOTE: this driver keeps clocks off unless the + * USB host is present. That saves power, and also + * eliminates IRQs (reset, resume, suspend) that can + * otherwise flood from the controller. If your + * board doesn't support VBUS detection, suspend and + * resume irq logic may need more attention... + */ + + /* host initiated suspend (3+ms bus idle) */ + } else if (status & AT91_UDP_RXSUSP) { + at91_udp_write(AT91_UDP_IDR, AT91_UDP_RXSUSP); + at91_udp_write(AT91_UDP_IER, AT91_UDP_RXRSM); + at91_udp_write(AT91_UDP_ICR, AT91_UDP_RXSUSP); + // VDBG("bus suspend\n"); + if (udc->suspended) + continue; + udc->suspended = 1; + + /* + * NOTE: when suspending a VBUS-powered device, the + * gadget driver should switch into slow clock mode + * and then into standby to avoid drawing more than + * 500uA power (2500uA for some high-power configs). + */ + if (udc->driver && udc->driver->suspend) + udc->driver->suspend(&udc->gadget); + + /* host initiated resume */ + } else if (status & AT91_UDP_RXRSM) { + at91_udp_write(AT91_UDP_IDR, AT91_UDP_RXRSM); + at91_udp_write(AT91_UDP_IER, AT91_UDP_RXSUSP); + at91_udp_write(AT91_UDP_ICR, AT91_UDP_RXRSM); + // VDBG("bus resume\n"); + if (!udc->suspended) + continue; + udc->suspended = 0; + + /* + * NOTE: for a VBUS-powered device, the gadget driver + * would normally want to switch out of slow clock + * mode into normal mode. + */ + if (udc->driver && udc->driver->resume) + udc->driver->resume(&udc->gadget); + + /* endpoint IRQs are cleared by handling them */ + } else { + int i; + unsigned mask = 1; + struct at91_ep *ep = &udc->ep[1]; + + if (status & mask) + handle_ep0(udc); + for (i = 1; i < NUM_ENDPOINTS; i++) { + mask <<= 1; + if (status & mask) + handle_ep(ep); + ep++; + } + } + } + + return IRQ_HANDLED; +} + +/*-------------------------------------------------------------------------*/ + +static struct at91_udc controller = { + .gadget = { + .ops = &at91_udc_ops, + .ep0 = &controller.ep[0].ep, + .name = driver_name, + .dev = { + .bus_id = "gadget" + } + }, + .ep[0] = { + .ep = { + .name = ep0name, + .ops = &at91_ep_ops, + }, + .udc = &controller, + .maxpacket = 8, + .creg = (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(0)), + .int_mask = 1 << 0, + }, + .ep[1] = { + .ep = { + .name = "ep1", + .ops = &at91_ep_ops, + }, + .udc = &controller, + .is_pingpong = 1, + .maxpacket = 64, + .creg = (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(1)), + .int_mask = 1 << 1, + }, + .ep[2] = { + .ep = { + .name = "ep2", + .ops = &at91_ep_ops, + }, + .udc = &controller, + .is_pingpong = 1, + .maxpacket = 64, + .creg = (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(2)), + .int_mask = 1 << 2, + }, + .ep[3] = { + .ep = { + /* could actually do bulk too */ + .name = "ep3-int", + .ops = &at91_ep_ops, + }, + .udc = &controller, + .maxpacket = 8, + .creg = (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(3)), + .int_mask = 1 << 3, + }, + .ep[4] = { + .ep = { + .name = "ep4", + .ops = &at91_ep_ops, + }, + .udc = &controller, + .is_pingpong = 1, + .maxpacket = 256, + .creg = (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(4)), + .int_mask = 1 << 4, + }, + .ep[5] = { + .ep = { + .name = "ep5", + .ops = &at91_ep_ops, + }, + .udc = &controller, + .is_pingpong = 1, + .maxpacket = 256, + .creg = (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(5)), + .int_mask = 1 << 5, + }, + /* ep6 and ep7 are also reserved */ +}; + +static irqreturn_t at91_vbus_irq(int irq, void *_udc, struct pt_regs *r) +{ + struct at91_udc *udc = _udc; + unsigned value; + + /* vbus needs at least brief debouncing */ + udelay(10); + value = at91_get_gpio_value(udc->board.vbus_pin); + if (value != udc->vbus) + at91_vbus_session(&udc->gadget, value); + + return IRQ_HANDLED; +} + +int usb_gadget_register_driver (struct usb_gadget_driver *driver) +{ + struct at91_udc *udc = &controller; + int retval; + + if (!driver + || driver->speed != USB_SPEED_FULL + || !driver->bind + || !driver->unbind + || !driver->setup) { + DBG("bad parameter.\n"); + return -EINVAL; + } + + if (udc->driver) { + DBG("UDC already has a gadget driver\n"); + return -EBUSY; + } + + udc->driver = driver; + udc->gadget.dev.driver = &driver->driver; + udc->gadget.dev.driver_data = &driver->driver; + udc->enabled = 1; + udc->selfpowered = 1; + + retval = driver->bind(&udc->gadget); + if (retval) { + DBG("driver->bind() returned %d\n", retval); + udc->driver = NULL; + return retval; + } + + local_irq_disable(); + pullup(udc, 1); + local_irq_enable(); + + DBG("bound to %s\n", driver->driver.name); + return 0; +} +EXPORT_SYMBOL (usb_gadget_register_driver); + +int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) +{ + struct at91_udc *udc = &controller; + + if (!driver || driver != udc->driver) + return -EINVAL; + + local_irq_disable(); + udc->enabled = 0; + pullup(udc, 0); + local_irq_enable(); + + driver->unbind(&udc->gadget); + udc->driver = NULL; + + DBG("unbound from %s\n", driver->driver.name); + return 0; +} +EXPORT_SYMBOL (usb_gadget_unregister_driver); + +/*-------------------------------------------------------------------------*/ + +static void at91udc_shutdown(struct platform_device *dev) +{ + /* force disconnect on reboot */ + pullup(platform_get_drvdata(dev), 0); +} + +static int __devinit at91udc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct at91_udc *udc; + int retval; + + if (!dev->platform_data) { + /* small (so we copy it) but critical! */ + DBG("missing platform_data\n"); + return -ENODEV; + } + + if (!request_mem_region(AT91_BASE_UDP, SZ_16K, driver_name)) { + DBG("someone's using UDC memory\n"); + return -EBUSY; + } + + /* init software state */ + udc = &controller; + udc->gadget.dev.parent = dev; + udc->board = *(struct at91_udc_data *) dev->platform_data; + udc->pdev = pdev; + udc_reinit(udc); + udc->enabled = 0; + + /* get interface and function clocks */ + udc->iclk = clk_get(dev, "udc_clk"); + udc->fclk = clk_get(dev, "udpck"); + if (IS_ERR(udc->iclk) || IS_ERR(udc->fclk)) { + DBG("clocks missing\n"); + return -ENODEV; + } + + retval = device_register(&udc->gadget.dev); + if (retval < 0) + goto fail0; + + /* disable everything until there's a gadget driver and vbus */ + pullup(udc, 0); + + /* request UDC and maybe VBUS irqs */ + if (request_irq(AT91_ID_UDP, at91_udc_irq, SA_INTERRUPT, driver_name, udc)) { + DBG("request irq %d failed\n", AT91_ID_UDP); + retval = -EBUSY; + goto fail1; + } + if (udc->board.vbus_pin > 0) { + if (request_irq(udc->board.vbus_pin, at91_vbus_irq, SA_INTERRUPT, driver_name, udc)) { + DBG("request vbus irq %d failed\n", udc->board.vbus_pin); + free_irq(AT91_ID_UDP, udc); + retval = -EBUSY; + goto fail1; + } + } else { + DBG("no VBUS detection, assuming always-on\n"); + udc->vbus = 1; + } + dev_set_drvdata(dev, udc); + create_debug_file(udc); + + INFO("%s version %s\n", driver_name, DRIVER_VERSION); + return 0; + +fail1: + device_unregister(&udc->gadget.dev); +fail0: + release_mem_region(AT91_VA_BASE_UDP, SZ_16K); + DBG("%s probe failed, %d\n", driver_name, retval); + return retval; +} + +static int __devexit at91udc_remove(struct platform_device *dev) +{ + struct at91_udc *udc = platform_get_drvdata(dev); + + DBG("remove\n"); + + pullup(udc, 0); + + if (udc->driver != 0) + usb_gadget_unregister_driver(udc->driver); + + remove_debug_file(udc); + if (udc->board.vbus_pin > 0) + free_irq(udc->board.vbus_pin, udc); + free_irq(AT91_ID_UDP, udc); + device_unregister(&udc->gadget.dev); + release_mem_region(AT91_BASE_UDP, SZ_16K); + + clk_put(udc->iclk); + clk_put(udc->fclk); + + return 0; +} + +#ifdef CONFIG_PM +static int at91udc_suspend(struct platform_device *dev, u32 state, u32 level) +{ + struct at91_udc *udc = platform_get_drvdata(dev); + + /* + * The "safe" suspend transitions are opportunistic ... e.g. when + * the USB link is suspended (48MHz clock autogated off), or when + * it's disconnected (programmatically gated off, elsewhere). + * Then we can suspend, and the chip can enter slow clock mode. + * + * The problem case is some component (user mode?) suspending this + * device while it's active, with the 48 MHz clock in use. There + * are two basic approaches: (a) veto suspend levels involving slow + * clock mode, (b) disconnect, so 48 MHz will no longer be in use + * and we can enter slow clock mode. This uses (b) for now, since + * it's simplest until AT91 PM exists and supports the other option. + */ + if (udc->vbus && !udc->suspended) + pullup(udc, 0); + return 0; +} + +static int at91udc_resume(struct platform_device *dev, u32 level) +{ + struct at91_udc *udc = platform_get_drvdata(dev); + + /* maybe reconnect to host; if so, clocks on */ + pullup(udc, 1); + return 0; +} +#else +#define at91udc_suspend NULL +#define at91udc_resume NULL +#endif + +static struct platform_driver at91_udc = { + .probe = at91udc_probe, + .remove = __devexit_p(at91udc_remove), + .shutdown = at91udc_shutdown, + .suspend = at91udc_suspend, + .resume = at91udc_resume, + .driver = { + .name = (char *) driver_name, + .owner = THIS_MODULE, + }, +}; + +static int __devinit udc_init_module(void) +{ + return platform_driver_register(&at91_udc); +} +module_init(udc_init_module); + +static void __devexit udc_exit_module(void) +{ + platform_driver_unregister(&at91_udc); +} +module_exit(udc_exit_module); + +MODULE_DESCRIPTION("AT91RM9200 udc driver"); +MODULE_AUTHOR("Thomas Rathbone, David Brownell"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/at91_udc.h b/drivers/usb/gadget/at91_udc.h new file mode 100644 index 000000000000..5a4799cedd19 --- /dev/null +++ b/drivers/usb/gadget/at91_udc.h @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2004 by Thomas Rathbone, HP Labs + * Copyright (C) 2005 by Ivan Kokshaysky + * Copyright (C) 2006 by SAN People + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef AT91_UDC_H +#define AT91_UDC_H + +/* + * USB Device Port (UDP) registers. + * Based on AT91RM9200 datasheet revision E. + */ + +#define AT91_UDP_FRM_NUM 0x00 /* Frame Number Register */ +#define AT91_UDP_NUM (0x7ff << 0) /* Frame Number */ +#define AT91_UDP_FRM_ERR (1 << 16) /* Frame Error */ +#define AT91_UDP_FRM_OK (1 << 17) /* Frame OK */ + +#define AT91_UDP_GLB_STAT 0x04 /* Global State Register */ +#define AT91_UDP_FADDEN (1 << 0) /* Function Address Enable */ +#define AT91_UDP_CONFG (1 << 1) /* Configured */ +#define AT91_UDP_ESR (1 << 2) /* Enable Send Resume */ +#define AT91_UDP_RSMINPR (1 << 3) /* Resume has been sent */ +#define AT91_UDP_RMWUPE (1 << 4) /* Remote Wake Up Enable */ + +#define AT91_UDP_FADDR 0x08 /* Function Address Register */ +#define AT91_UDP_FADD (0x7f << 0) /* Function Address Value */ +#define AT91_UDP_FEN (1 << 8) /* Function Enable */ + +#define AT91_UDP_IER 0x10 /* Interrupt Enable Register */ +#define AT91_UDP_IDR 0x14 /* Interrupt Disable Register */ +#define AT91_UDP_IMR 0x18 /* Interrupt Mask Register */ + +#define AT91_UDP_ISR 0x1c /* Interrupt Status Register */ +#define AT91_UDP_EP(n) (1 << (n)) /* Endpoint Interrupt Status */ +#define AT91_UDP_RXSUSP (1 << 8) /* USB Suspend Interrupt Status */ +#define AT91_UDP_RXRSM (1 << 9) /* USB Resume Interrupt Status */ +#define AT91_UDP_EXTRSM (1 << 10) /* External Resume Interrupt Status */ +#define AT91_UDP_SOFINT (1 << 11) /* Start of Frame Interrupt Status */ +#define AT91_UDP_ENDBUSRES (1 << 12) /* End of Bus Reset Interrpt Status */ +#define AT91_UDP_WAKEUP (1 << 13) /* USB Wakeup Interrupt Status */ + +#define AT91_UDP_ICR 0x20 /* Interrupt Clear Register */ +#define AT91_UDP_RST_EP 0x28 /* Reset Endpoint Register */ + +#define AT91_UDP_CSR(n) (0x30+((n)*4)) /* Endpoint Control/Status Registers 0-7 */ +#define AT91_UDP_TXCOMP (1 << 0) /* Generates IN packet with data previously written in DPR */ +#define AT91_UDP_RX_DATA_BK0 (1 << 1) /* Receive Data Bank 0 */ +#define AT91_UDP_RXSETUP (1 << 2) /* Send STALL to the host */ +#define AT91_UDP_STALLSENT (1 << 3) /* Stall Sent / Isochronous error (Isochronous endpoints) */ +#define AT91_UDP_TXPKTRDY (1 << 4) /* Transmit Packet Ready */ +#define AT91_UDP_FORCESTALL (1 << 5) /* Force Stall */ +#define AT91_UDP_RX_DATA_BK1 (1 << 6) /* Receive Data Bank 1 */ +#define AT91_UDP_DIR (1 << 7) /* Transfer Direction */ +#define AT91_UDP_EPTYPE (7 << 8) /* Endpoint Type */ +#define AT91_UDP_EPTYPE_CTRL (0 << 8) +#define AT91_UDP_EPTYPE_ISO_OUT (1 << 8) +#define AT91_UDP_EPTYPE_BULK_OUT (2 << 8) +#define AT91_UDP_EPTYPE_INT_OUT (3 << 8) +#define AT91_UDP_EPTYPE_ISO_IN (5 << 8) +#define AT91_UDP_EPTYPE_BULK_IN (6 << 8) +#define AT91_UDP_EPTYPE_INT_IN (7 << 8) +#define AT91_UDP_DTGLE (1 << 11) /* Data Toggle */ +#define AT91_UDP_EPEDS (1 << 15) /* Endpoint Enable/Disable */ +#define AT91_UDP_RXBYTECNT (0x7ff << 16) /* Number of bytes in FIFO */ + +#define AT91_UDP_FDR(n) (0x50+((n)*4)) /* Endpoint FIFO Data Registers 0-7 */ + +#define AT91_UDP_TXVC 0x74 /* Transceiver Control Register */ +#define AT91_UDP_TXVC_TXVDIS (1 << 8) /* Transceiver Disable */ + + +/*-------------------------------------------------------------------------*/ + +/* + * controller driver data structures + */ + +#define NUM_ENDPOINTS 6 + +/* + * hardware won't disable bus reset, or resume while the controller + * is suspended ... watching suspend helps keep the logic symmetric. + */ +#define MINIMUS_INTERRUPTUS \ + (AT91_UDP_ENDBUSRES | AT91_UDP_RXRSM | AT91_UDP_RXSUSP) + +struct at91_ep { + struct usb_ep ep; + struct list_head queue; + struct at91_udc *udc; + void __iomem *creg; + + unsigned maxpacket:16; + u8 int_mask; + unsigned is_pingpong:1; + + unsigned stopped:1; + unsigned is_in:1; + unsigned is_iso:1; + unsigned fifo_bank:1; + + const struct usb_endpoint_descriptor + *desc; +}; + +/* + * driver is non-SMP, and just blocks IRQs whenever it needs + * access protection for chip registers or driver state + */ +struct at91_udc { + struct usb_gadget gadget; + struct at91_ep ep[NUM_ENDPOINTS]; + struct usb_gadget_driver *driver; + unsigned vbus:1; + unsigned enabled:1; + unsigned clocked:1; + unsigned suspended:1; + unsigned req_pending:1; + unsigned wait_for_addr_ack:1; + unsigned wait_for_config_ack:1; + unsigned selfpowered:1; + u8 addr; + struct at91_udc_data board; + struct clk *iclk, *fclk; + struct platform_device *pdev; + struct proc_dir_entry *pde; +}; + +static inline struct at91_udc *to_udc(struct usb_gadget *g) +{ + return container_of(g, struct at91_udc, gadget); +} + +struct at91_request { + struct usb_request req; + struct list_head queue; +}; + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +#define DBG(stuff...) printk(KERN_DEBUG "udc: " stuff) +#else +#define DBG(stuff...) do{}while(0) +#endif + +#ifdef VERBOSE +# define VDBG DBG +#else +# define VDBG(stuff...) do{}while(0) +#endif + +#ifdef PACKET_TRACE +# define PACKET VDBG +#else +# define PACKET(stuff...) do{}while(0) +#endif + +#define ERR(stuff...) printk(KERN_ERR "udc: " stuff) +#define WARN(stuff...) printk(KERN_WARNING "udc: " stuff) +#define INFO(stuff...) printk(KERN_INFO "udc: " stuff) + +#endif + -- cgit v1.2.3-59-g8ed1b From 7802ac5c29d135345db1b06f9167075cd9f2d675 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Sun, 22 Jan 2006 10:33:27 -0800 Subject: [PATCH] USB: minor gadget/rndis tweak Resove a minor FIXME: don't change MTU while RNDIS link is active, the other end won't expect such things... Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/ether.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index afc84cfb61f9..04b9743c1c1f 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -1549,7 +1549,8 @@ static int eth_change_mtu (struct net_device *net, int new_mtu) { struct eth_dev *dev = netdev_priv(net); - // FIXME if rndis, don't change while link's live + if (dev->rndis) + return -EBUSY; if (new_mtu <= ETH_HLEN || new_mtu > ETH_FRAME_LEN) return -ERANGE; -- cgit v1.2.3-59-g8ed1b From 1c05ad4447e4ecbd61647c102fb6f2f5a6634ff3 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 25 Jan 2006 08:45:59 -0800 Subject: [PATCH] recognize three more usb peripheral controllers This adds declarations for three USB peripheral controllers: - Two high speed USB cores that can be licensed from Mentor Graphics to be integrated into silicon: * "musbhsfc" is for peripherals only, as found in for example the IBM/AMCC 44EP processors. * "musbhdrc" is OTG-capable (dual role), and is found in various products including OMAP 2430 and the new DaVinci SOCs. The "musbh" standing for "Mentor USB Highspeed", the rest standing for "Function Controller" or "Dual Role Controller" (OTG-capable). - The full speed controller on the FreeScale MPC8272. Adding these definitions just allows gadget driver code to handle any controller-specific logic; controller drivers are quite separate. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/ether.c | 8 ++++++++ drivers/usb/gadget/gadget_chips.h | 30 ++++++++++++++++++++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 04b9743c1c1f..3d2603e31808 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -253,6 +253,14 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); #define DEV_CONFIG_CDC #endif +#ifdef CONFIG_USB_GADGET_MUSBHSFC +#define DEV_CONFIG_CDC +#endif + +#ifdef CONFIG_USB_GADGET_MUSBHDRC +#define DEV_CONFIG_CDC +#endif + /* For CDC-incapable hardware, choose the simple cdc subset. * Anything that talks bulk (without notable bugs) can do this. diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index 8cbae21d84b9..c4081407171f 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -3,9 +3,9 @@ * gadget drivers or other code that needs to deal with them, and which * autoconfigures instead of using early binding to the hardware. * - * This could eventually work like the ARM mach_is_*() stuff, driven by + * This SHOULD eventually work like the ARM mach_is_*() stuff, driven by * some config file that gets updated as new hardware is supported. - * (And avoiding the runtime comparisons in typical one-choice cases.) + * (And avoiding all runtime comparisons in typical one-choice configs!) * * NOTE: some of these controller drivers may not be available yet. */ @@ -93,6 +93,26 @@ #define gadget_is_imx(g) 0 #endif +/* Mentor high speed function controller */ +#ifdef CONFIG_USB_GADGET_MUSBHSFC +#define gadget_is_musbhsfc(g) !strcmp("musbhsfc_udc", (g)->name) +#else +#define gadget_is_musbhsfc(g) 0 +#endif + +/* Mentor high speed "dual role" controller, peripheral mode */ +#ifdef CONFIG_USB_GADGET_MUSBHDRC +#define gadget_is_musbhdrc(g) !strcmp("musbhdrc_udc", (g)->name) +#else +#define gadget_is_musbhdrc(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_MPC8272 +#define gadget_is_mpc8272(g) !strcmp("mpc8272_udc", (g)->name) +#else +#define gadget_is_mpc8272(g) 0 +#endif + // CONFIG_USB_GADGET_SX2 // CONFIG_USB_GADGET_AU1X00 // ... @@ -143,5 +163,11 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x13; else if (gadget_is_imx(gadget)) return 0x14; + else if (gadget_is_musbhsfc(gadget)) + return 0x15; + else if (gadget_is_musbhdrc(gadget)) + return 0x16; + else if (gadget_is_mpc8272(gadget)) + return 0x17; return -ENOENT; } -- cgit v1.2.3-59-g8ed1b From b1e8f0a6a8805c971857cd10a65cf8caa4c1a672 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Mon, 23 Jan 2006 15:25:40 -0800 Subject: [PATCH] USB: usbcore sets up root hubs earlier Make the HCD initialization sequence more sane ... notably, setting up root hubs before HCDs are asked to do their one-time init. Among other things, that lets the HCDs do custom root hub init along with all the other one-time initialization done in the (now misnamed) reset() method. This also copies the controller wakeup flags into the root hub; it's done a bit later than would be ideal, but that'll be necessary until the PCI code initializes them correctly. (The PCI patch breaks on PPC due to how it sequences PCI initialization.) Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 104 ++++++++++++++++++++++++++----------------------- 1 file changed, 55 insertions(+), 49 deletions(-) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 9223f2869674..6368562d73ca 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -823,18 +823,17 @@ static void usb_deregister_bus (struct usb_bus *bus) /** * register_root_hub - called by usb_add_hcd() to register a root hub - * @usb_dev: the usb root hub device to be registered. * @hcd: host controller for this root hub * * This function registers the root hub with the USB subsystem. It sets up - * the device properly in the device tree and stores the root_hub pointer - * in the bus structure, then calls usb_new_device() to register the usb - * device. It also assigns the root hub's USB address (always 1). + * the device properly in the device tree and then calls usb_new_device() + * to register the usb device. It also assigns the root hub's USB address + * (always 1). */ -static int register_root_hub (struct usb_device *usb_dev, - struct usb_hcd *hcd) +static int register_root_hub(struct usb_hcd *hcd) { struct device *parent_dev = hcd->self.controller; + struct usb_device *usb_dev = hcd->self.root_hub; const int devnum = 1; int retval; @@ -846,12 +845,10 @@ static int register_root_hub (struct usb_device *usb_dev, usb_set_device_state(usb_dev, USB_STATE_ADDRESS); mutex_lock(&usb_bus_list_lock); - usb_dev->bus->root_hub = usb_dev; usb_dev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64); retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE); if (retval != sizeof usb_dev->descriptor) { - usb_dev->bus->root_hub = NULL; mutex_unlock(&usb_bus_list_lock); dev_dbg (parent_dev, "can't read %s device descriptor %d\n", usb_dev->dev.bus_id, retval); @@ -860,7 +857,6 @@ static int register_root_hub (struct usb_device *usb_dev, retval = usb_new_device (usb_dev); if (retval) { - usb_dev->bus->root_hub = NULL; dev_err (parent_dev, "can't register root hub for %s, %d\n", usb_dev->dev.bus_id, retval); } @@ -1772,12 +1768,10 @@ int usb_add_hcd(struct usb_hcd *hcd, set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - /* till now HC has been in an indeterminate state ... */ - if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) { - dev_err(hcd->self.controller, "can't reset\n"); - return retval; - } - + /* HC is in reset state, but accessible. Now do the one-time init, + * bottom up so that hcds can customize the root hubs before khubd + * starts talking to them. (Note, bus id is assigned early too.) + */ if ((retval = hcd_buffer_create(hcd)) != 0) { dev_dbg(hcd->self.controller, "pool alloc failed\n"); return retval; @@ -1786,6 +1780,42 @@ int usb_add_hcd(struct usb_hcd *hcd, if ((retval = usb_register_bus(&hcd->self)) < 0) goto err_register_bus; + if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) { + dev_err(hcd->self.controller, "unable to allocate root hub\n"); + retval = -ENOMEM; + goto err_allocate_root_hub; + } + rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH : + USB_SPEED_FULL; + hcd->self.root_hub = rhdev; + + /* "reset" is misnamed; its role is now one-time init. the controller + * should already have been reset (and boot firmware kicked off etc). + */ + if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) { + dev_err(hcd->self.controller, "can't setup\n"); + goto err_hcd_driver_setup; + } + + /* wakeup flag init is in transition; for now we can't rely on PCI to + * initialize these bits properly, so we let reset() override it. + * This init should _precede_ the reset() once PCI behaves. + */ + device_init_wakeup(&rhdev->dev, + device_can_wakeup(hcd->self.controller)); + + // ... all these hcd->*_wakeup flags will vanish + hcd->can_wakeup = device_can_wakeup(hcd->self.controller); + + /* hcd->driver->reset() reported can_wakeup, probably with + * assistance from board's boot firmware. + * NOTE: normal devices won't enable wakeup by default. + */ + if (hcd->can_wakeup) + dev_dbg(hcd->self.controller, "supports USB remote wakeup\n"); + hcd->remote_wakeup = hcd->can_wakeup; + + /* enable irqs just before we start the controller */ if (hcd->driver->irq) { char buf[8], *bufp = buf; @@ -1817,56 +1847,32 @@ int usb_add_hcd(struct usb_hcd *hcd, (unsigned long long)hcd->rsrc_start); } - /* Allocate the root hub before calling hcd->driver->start(), - * but don't register it until afterward so that the hardware - * is running. - */ - if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) { - dev_err(hcd->self.controller, "unable to allocate root hub\n"); - retval = -ENOMEM; - goto err_allocate_root_hub; - } - - /* Although in principle hcd->driver->start() might need to use rhdev, - * none of the current drivers do. - */ if ((retval = hcd->driver->start(hcd)) < 0) { dev_err(hcd->self.controller, "startup error %d\n", retval); goto err_hcd_driver_start; } - /* hcd->driver->start() reported can_wakeup, probably with - * assistance from board's boot firmware. - * NOTE: normal devices won't enable wakeup by default. - */ - if (hcd->can_wakeup) - dev_dbg(hcd->self.controller, "supports USB remote wakeup\n"); - hcd->remote_wakeup = hcd->can_wakeup; - - rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH : - USB_SPEED_FULL; + /* starting here, usbcore will pay attention to this root hub */ rhdev->bus_mA = min(500u, hcd->power_budget); - if ((retval = register_root_hub(rhdev, hcd)) != 0) + if ((retval = register_root_hub(hcd)) != 0) goto err_register_root_hub; if (hcd->uses_new_polling && hcd->poll_rh) usb_hcd_poll_rh_status(hcd); return retval; - err_register_root_hub: +err_register_root_hub: hcd->driver->stop(hcd); - - err_hcd_driver_start: - usb_put_dev(rhdev); - - err_allocate_root_hub: +err_hcd_driver_start: if (hcd->irq >= 0) free_irq(irqnum, hcd); - - err_request_irq: +err_request_irq: +err_hcd_driver_setup: + hcd->self.root_hub = NULL; + usb_put_dev(rhdev); +err_allocate_root_hub: usb_deregister_bus(&hcd->self); - - err_register_bus: +err_register_bus: hcd_buffer_destroy(hcd); return retval; } -- cgit v1.2.3-59-g8ed1b From 6a9062f393fa48125df23c5491543828a21e1ae0 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Mon, 23 Jan 2006 15:28:07 -0800 Subject: [PATCH] USB: ohci uses driver model wakeup flags This makes OHCI use the driver model wakeup control bits for its root hub (e.g. disable on amd756, because of chip erratum) and for the controller itself. It no longer uses the hcd glue bits with those roles, and depends on the previous patch making the root hub available earlier. Note that on most platforms (boot code properly setting the RWC bit) this gives a partial workaround for the way PCI isn't currently flagging devices that support PME# signals. (Because of odd PCI init sequencing on PPC.) That's because many OHCI controllers support "legacy PCI PM" ... without involving any PCI PM capability. USB wakeup from STR, if it works on your system, may still involve tweaking things by hand in /proc/acpi/wakeup. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-hcd.c | 49 +++++++++++++++++++++++++-------------------- drivers/usb/host/ohci-hub.c | 12 +++++------ drivers/usb/host/ohci-pci.c | 15 ++++++++++++-- 3 files changed, 46 insertions(+), 30 deletions(-) diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 2990be269000..544f7589912f 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -443,11 +443,16 @@ ohci_reboot (struct notifier_block *block, unsigned long code, void *null) static int ohci_init (struct ohci_hcd *ohci) { int ret; + struct usb_hcd *hcd = ohci_to_hcd(ohci); disable (ohci); - ohci->regs = ohci_to_hcd(ohci)->regs; + ohci->regs = hcd->regs; ohci->next_statechange = jiffies; + /* REVISIT this BIOS handshake is now moved into PCI "quirks", and + * was never needed for most non-PCI systems ... remove the code? + */ + #ifndef IR_DISABLE /* SMM owns the HC? not for long! */ if (!no_handshake && ohci_readl (ohci, @@ -478,8 +483,10 @@ static int ohci_init (struct ohci_hcd *ohci) /* Disable HC interrupts */ ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); - // flush the writes - (void) ohci_readl (ohci, &ohci->regs->control); + + /* flush the writes, and save key bits like RWC */ + if (ohci_readl (ohci, &ohci->regs->control) & OHCI_CTRL_RWC) + ohci->hc_control |= OHCI_CTRL_RWC; /* Read the number of ports unless overridden */ if (ohci->num_ports == 0) @@ -488,16 +495,19 @@ static int ohci_init (struct ohci_hcd *ohci) if (ohci->hcca) return 0; - ohci->hcca = dma_alloc_coherent (ohci_to_hcd(ohci)->self.controller, + ohci->hcca = dma_alloc_coherent (hcd->self.controller, sizeof *ohci->hcca, &ohci->hcca_dma, 0); if (!ohci->hcca) return -ENOMEM; if ((ret = ohci_mem_init (ohci)) < 0) - ohci_stop (ohci_to_hcd(ohci)); + ohci_stop (hcd); + else { + register_reboot_notifier (&ohci->reboot_notifier); + create_debug_files (ohci); + } return ret; - } /*-------------------------------------------------------------------------*/ @@ -510,6 +520,7 @@ static int ohci_run (struct ohci_hcd *ohci) { u32 mask, temp; int first = ohci->fminterval == 0; + struct usb_hcd *hcd = ohci_to_hcd(ohci); disable (ohci); @@ -525,18 +536,17 @@ static int ohci_run (struct ohci_hcd *ohci) /* also: power/overcurrent flags in roothub.a */ } - /* Reset USB nearly "by the book". RemoteWakeupConnected - * saved if boot firmware (BIOS/SMM/...) told us it's connected - * (for OHCI integrated on mainboard, it normally is) + /* Reset USB nearly "by the book". RemoteWakeupConnected was + * saved if boot firmware (BIOS/SMM/...) told us it's connected, + * or if bus glue did the same (e.g. for PCI add-in cards with + * PCI PM support). */ - ohci->hc_control = ohci_readl (ohci, &ohci->regs->control); ohci_dbg (ohci, "resetting from state '%s', control = 0x%x\n", hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS), - ohci->hc_control); - - if (ohci->hc_control & OHCI_CTRL_RWC - && !(ohci->flags & OHCI_QUIRK_AMD756)) - ohci_to_hcd(ohci)->can_wakeup = 1; + ohci_readl (ohci, &ohci->regs->control)); + if ((ohci->hc_control & OHCI_CTRL_RWC) != 0 + && !device_may_wakeup(hcd->self.controller)) + device_init_wakeup(hcd->self.controller, 1); switch (ohci->hc_control & OHCI_CTRL_HCFS) { case OHCI_USB_OPER: @@ -632,7 +642,7 @@ retry: ohci->hc_control &= OHCI_CTRL_RWC; ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER; ohci_writel (ohci, ohci->hc_control, &ohci->regs->control); - ohci_to_hcd(ohci)->state = HC_STATE_RUNNING; + hcd->state = HC_STATE_RUNNING; /* wake on ConnectStatusChange, matching external hubs */ ohci_writel (ohci, RH_HS_DRWE, &ohci->regs->roothub.status); @@ -667,15 +677,10 @@ retry: // POTPGT delay is bits 24-31, in 2 ms units. mdelay ((temp >> 23) & 0x1fe); - ohci_to_hcd(ohci)->state = HC_STATE_RUNNING; + hcd->state = HC_STATE_RUNNING; ohci_dump (ohci, 1); - if (ohci_to_hcd(ohci)->self.root_hub == NULL) { - register_reboot_notifier (&ohci->reboot_notifier); - create_debug_files (ohci); - } - return 0; } diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 4b2226d77b34..0bb972b58336 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -107,7 +107,7 @@ static int ohci_bus_suspend (struct usb_hcd *hcd) &ohci->regs->intrstatus); /* maybe resume can wake root hub */ - if (hcd->remote_wakeup) + if (device_may_wakeup(&ohci_to_hcd(ohci)->self.root_hub->dev)) ohci->hc_control |= OHCI_CTRL_RWE; else ohci->hc_control &= ~OHCI_CTRL_RWE; @@ -246,9 +246,9 @@ static int ohci_bus_resume (struct usb_hcd *hcd) (void) ohci_readl (ohci, &ohci->regs->control); msleep (3); - temp = OHCI_CONTROL_INIT | OHCI_USB_OPER; - if (hcd->can_wakeup) - temp |= OHCI_CTRL_RWC; + temp = ohci->hc_control; + temp &= OHCI_CTRL_RWC; + temp |= OHCI_CONTROL_INIT | OHCI_USB_OPER; ohci->hc_control = temp; ohci_writel (ohci, temp, &ohci->regs->control); (void) ohci_readl (ohci, &ohci->regs->control); @@ -302,7 +302,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); int i, changed = 0, length = 1; - int can_suspend = hcd->can_wakeup; + int can_suspend = device_may_wakeup(&hcd->self.root_hub->dev); unsigned long flags; spin_lock_irqsave (&ohci->lock, flags); @@ -354,7 +354,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) */ if (!(status & RH_PS_CCS)) continue; - if ((status & RH_PS_PSS) && hcd->remote_wakeup) + if ((status & RH_PS_PSS) && can_suspend) continue; can_suspend = 0; } diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 1b09dde068e1..1bfe96f4d045 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -35,7 +35,10 @@ ohci_pci_start (struct usb_hcd *hcd) struct ohci_hcd *ohci = hcd_to_ohci (hcd); int ret; - if(hcd->self.controller && hcd->self.controller->bus == &pci_bus_type) { + /* REVISIT this whole block should move to reset(), which handles + * all the other one-time init. + */ + if (hcd->self.controller) { struct pci_dev *pdev = to_pci_dev(hcd->self.controller); /* AMD 756, for most chips (early revs), corrupts register @@ -45,7 +48,8 @@ ohci_pci_start (struct usb_hcd *hcd) && pdev->device == 0x740c) { ohci->flags = OHCI_QUIRK_AMD756; ohci_dbg (ohci, "AMD756 erratum 4 workaround\n"); - // also somewhat erratum 10 (suspend/resume issues) + /* also erratum 10 (suspend/resume issues) */ + device_init_wakeup(&hcd->self.root_hub->dev, 0); } /* FIXME for some of the early AMD 760 southbridges, OHCI @@ -88,6 +92,13 @@ ohci_pci_start (struct usb_hcd *hcd) ohci_dbg (ohci, "enabled Compaq ZFMicro chipset quirk\n"); } + + /* RWC may not be set for add-in PCI cards, since boot + * firmware probably ignored them. This transfers PCI + * PM wakeup capabilities (once the PCI layer is fixed). + */ + if (device_may_wakeup(&pdev->dev)) + ohci->hc_control |= OHCI_CTRL_RWC; } /* NOTE: there may have already been a first reset, to -- cgit v1.2.3-59-g8ed1b From fb669cc01ed778c4926f395e44a9b61644597d38 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 24 Jan 2006 08:40:27 -0800 Subject: [PATCH] USB: remove usbcore-specific wakeup flags This makes usbcore use the driver model wakeup flags for host controllers and for their root hubs. Since previous patches have removed all users of the HCD flags they replace, this converts the last users of those flags. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd-pci.c | 11 ++++++++--- drivers/usb/core/hcd.c | 40 ++++++++++++++++++++++++++-------------- drivers/usb/core/hcd.h | 2 -- drivers/usb/core/hub.c | 22 ++++++++++++++-------- 4 files changed, 48 insertions(+), 27 deletions(-) diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 29b5b2a6e183..e0afb5ad29e5 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -264,14 +264,19 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message) */ retval = pci_set_power_state (dev, PCI_D3hot); if (retval == 0) { - dev_dbg (hcd->self.controller, "--> PCI D3\n"); + int wake = device_can_wakeup(&hcd->self.root_hub->dev); + + wake = wake && device_may_wakeup(hcd->self.controller); + + dev_dbg (hcd->self.controller, "--> PCI D3%s\n", + wake ? "/wakeup" : ""); /* Ignore these return values. We rely on pci code to * reject requests the hardware can't implement, rather * than coding the same thing. */ - (void) pci_enable_wake (dev, PCI_D3hot, hcd->remote_wakeup); - (void) pci_enable_wake (dev, PCI_D3cold, hcd->remote_wakeup); + (void) pci_enable_wake (dev, PCI_D3hot, wake); + (void) pci_enable_wake (dev, PCI_D3cold, wake); } else { dev_dbg (&dev->dev, "PCI D3 suspend fail, %d\n", retval); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 6368562d73ca..a98d978d76e2 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -367,21 +367,39 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) /* DEVICE REQUESTS */ + /* The root hub's remote wakeup enable bit is implemented using + * driver model wakeup flags. If this system supports wakeup + * through USB, userspace may change the default "allow wakeup" + * policy through sysfs or these calls. + * + * Most root hubs support wakeup from downstream devices, for + * runtime power management (disabling USB clocks and reducing + * VBUS power usage). However, not all of them do so; silicon, + * board, and BIOS bugs here are not uncommon, so these can't + * be treated quite like external hubs. + * + * Likewise, not all root hubs will pass wakeup events upstream, + * to wake up the whole system. So don't assume root hub and + * controller capabilities are identical. + */ + case DeviceRequest | USB_REQ_GET_STATUS: - tbuf [0] = (hcd->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP) + tbuf [0] = (device_may_wakeup(&hcd->self.root_hub->dev) + << USB_DEVICE_REMOTE_WAKEUP) | (1 << USB_DEVICE_SELF_POWERED); tbuf [1] = 0; len = 2; break; case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: if (wValue == USB_DEVICE_REMOTE_WAKEUP) - hcd->remote_wakeup = 0; + device_set_wakeup_enable(&hcd->self.root_hub->dev, 0); else goto error; break; case DeviceOutRequest | USB_REQ_SET_FEATURE: - if (hcd->can_wakeup && wValue == USB_DEVICE_REMOTE_WAKEUP) - hcd->remote_wakeup = 1; + if (device_can_wakeup(&hcd->self.root_hub->dev) + && wValue == USB_DEVICE_REMOTE_WAKEUP) + device_set_wakeup_enable(&hcd->self.root_hub->dev, 1); else goto error; break; @@ -410,7 +428,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) bufp = fs_rh_config_descriptor; len = sizeof fs_rh_config_descriptor; } - if (hcd->can_wakeup) + if (device_can_wakeup(&hcd->self.root_hub->dev)) patch_wakeup = 1; break; case USB_DT_STRING << 8: @@ -1804,16 +1822,10 @@ int usb_add_hcd(struct usb_hcd *hcd, device_init_wakeup(&rhdev->dev, device_can_wakeup(hcd->self.controller)); - // ... all these hcd->*_wakeup flags will vanish - hcd->can_wakeup = device_can_wakeup(hcd->self.controller); - - /* hcd->driver->reset() reported can_wakeup, probably with - * assistance from board's boot firmware. - * NOTE: normal devices won't enable wakeup by default. - */ - if (hcd->can_wakeup) + /* NOTE: root hub and controller capabilities may not be the same */ + if (device_can_wakeup(hcd->self.controller) + && device_can_wakeup(&hcd->self.root_hub->dev)) dev_dbg(hcd->self.controller, "supports USB remote wakeup\n"); - hcd->remote_wakeup = hcd->can_wakeup; /* enable irqs just before we start the controller */ if (hcd->driver->irq) { diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index f44a2fe62a9d..7022aafb2ae8 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -78,8 +78,6 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */ #define HCD_FLAG_HW_ACCESSIBLE 0x00000001 #define HCD_FLAG_SAW_IRQ 0x00000002 - unsigned can_wakeup:1; /* hw supports wakeup? */ - unsigned remote_wakeup:1;/* sw should use wakeup? */ unsigned rh_registered:1;/* is root hub registered? */ /* The next flag is a stopgap, to be removed when all the HCDs diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 867fa8130238..f1d64d4bbf5f 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1006,12 +1006,18 @@ void usb_set_device_state(struct usb_device *udev, ; /* do nothing */ else if (new_state != USB_STATE_NOTATTACHED) { udev->state = new_state; - if (new_state == USB_STATE_CONFIGURED) - device_init_wakeup(&udev->dev, - (udev->actconfig->desc.bmAttributes - & USB_CONFIG_ATT_WAKEUP)); - else if (new_state != USB_STATE_SUSPENDED) - device_init_wakeup(&udev->dev, 0); + + /* root hub wakeup capabilities are managed out-of-band + * and may involve silicon errata ... ignore them here. + */ + if (udev->parent) { + if (new_state == USB_STATE_CONFIGURED) + device_init_wakeup(&udev->dev, + (udev->actconfig->desc.bmAttributes + & USB_CONFIG_ATT_WAKEUP)); + else if (new_state != USB_STATE_SUSPENDED) + device_init_wakeup(&udev->dev, 0); + } } else recursively_mark_NOTATTACHED(udev); spin_unlock_irqrestore(&device_state_lock, flags); @@ -1877,9 +1883,9 @@ int usb_resume_device(struct usb_device *udev) if (udev->state == USB_STATE_NOTATTACHED) return -ENODEV; -#ifdef CONFIG_USB_SUSPEND /* selective resume of one downstream hub-to-device port */ if (udev->parent) { +#ifdef CONFIG_USB_SUSPEND if (udev->state == USB_STATE_SUSPENDED) { // NOTE swsusp may bork us, device state being wrong... // NOTE this fails if parent is also suspended... @@ -1887,8 +1893,8 @@ int usb_resume_device(struct usb_device *udev) udev->portnum, udev); } else status = 0; - } else #endif + } else status = finish_device_resume(udev); if (status < 0) dev_dbg(&udev->dev, "can't resume, status %d\n", -- cgit v1.2.3-59-g8ed1b From aef4e266964bc15861b5835c1f5b9d2ebc155c2a Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 31 Jan 2006 12:58:38 -0500 Subject: [PATCH] usbhid: add error handling This patch (as628c) adds error handling to the USB HID core. When an error is reported for an interrupt URB, the driver will do delayed retries, at increasing intervals, for up to one second. If that doesn't work, it will try to reset the device. Testing by users has shown that both the retries and the resets end up getting used. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/input/hid-core.c | 153 +++++++++++++++++++++++++++++++++++++------ drivers/usb/input/hid.h | 10 +++ 2 files changed, 142 insertions(+), 21 deletions(-) diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c index f187a719f17e..6b8a51cad375 100644 --- a/drivers/usb/input/hid-core.c +++ b/drivers/usb/input/hid-core.c @@ -902,6 +902,99 @@ static int hid_input_report(int type, struct urb *urb, int interrupt, struct pt_ return 0; } +/* + * Input submission and I/O error handler. + */ + +static void hid_io_error(struct hid_device *hid); + +/* Start up the input URB */ +static int hid_start_in(struct hid_device *hid) +{ + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&hid->inlock, flags); + if (hid->open > 0 && !test_bit(HID_SUSPENDED, &hid->iofl) && + !test_and_set_bit(HID_IN_RUNNING, &hid->iofl)) { + rc = usb_submit_urb(hid->urbin, GFP_ATOMIC); + if (rc != 0) + clear_bit(HID_IN_RUNNING, &hid->iofl); + } + spin_unlock_irqrestore(&hid->inlock, flags); + return rc; +} + +/* I/O retry timer routine */ +static void hid_retry_timeout(unsigned long _hid) +{ + struct hid_device *hid = (struct hid_device *) _hid; + + dev_dbg(&hid->intf->dev, "retrying intr urb\n"); + if (hid_start_in(hid)) + hid_io_error(hid); +} + +/* Workqueue routine to reset the device */ +static void hid_reset(void *_hid) +{ + struct hid_device *hid = (struct hid_device *) _hid; + int rc_lock, rc; + + dev_dbg(&hid->intf->dev, "resetting device\n"); + rc = rc_lock = usb_lock_device_for_reset(hid->dev, hid->intf); + if (rc_lock >= 0) { + rc = usb_reset_device(hid->dev); + if (rc_lock) + usb_unlock_device(hid->dev); + } + clear_bit(HID_RESET_PENDING, &hid->iofl); + + if (rc == 0) { + hid->retry_delay = 0; + if (hid_start_in(hid)) + hid_io_error(hid); + } else if (!(rc == -ENODEV || rc == -EHOSTUNREACH || rc == -EINTR)) + err("can't reset device, %s-%s/input%d, status %d", + hid->dev->bus->bus_name, + hid->dev->devpath, + hid->ifnum, rc); +} + +/* Main I/O error handler */ +static void hid_io_error(struct hid_device *hid) +{ + unsigned long flags; + + spin_lock_irqsave(&hid->inlock, flags); + + /* Stop when disconnected */ + if (usb_get_intfdata(hid->intf) == NULL) + goto done; + + /* When an error occurs, retry at increasing intervals */ + if (hid->retry_delay == 0) { + hid->retry_delay = 13; /* Then 26, 52, 104, 104, ... */ + hid->stop_retry = jiffies + msecs_to_jiffies(1000); + } else if (hid->retry_delay < 100) + hid->retry_delay *= 2; + + if (time_after(jiffies, hid->stop_retry)) { + + /* Retries failed, so do a port reset */ + if (!test_and_set_bit(HID_RESET_PENDING, &hid->iofl)) { + if (schedule_work(&hid->reset_work)) + goto done; + clear_bit(HID_RESET_PENDING, &hid->iofl); + } + } + + mod_timer(&hid->io_retry, + jiffies + msecs_to_jiffies(hid->retry_delay)); +done: + spin_unlock_irqrestore(&hid->inlock, flags); +} + /* * Input interrupt completion handler. */ @@ -913,25 +1006,35 @@ static void hid_irq_in(struct urb *urb, struct pt_regs *regs) switch (urb->status) { case 0: /* success */ + hid->retry_delay = 0; hid_input_report(HID_INPUT_REPORT, urb, 1, regs); break; case -ECONNRESET: /* unlink */ case -ENOENT: - case -EPERM: case -ESHUTDOWN: /* unplug */ - case -EILSEQ: /* unplug timeout on uhci */ + clear_bit(HID_IN_RUNNING, &hid->iofl); return; + case -EILSEQ: /* protocol error or unplug */ + case -EPROTO: /* protocol error or unplug */ case -ETIMEDOUT: /* NAK */ - break; + clear_bit(HID_IN_RUNNING, &hid->iofl); + hid_io_error(hid); + return; default: /* error */ warn("input irq status %d received", urb->status); } status = usb_submit_urb(urb, SLAB_ATOMIC); - if (status) - err("can't resubmit intr, %s-%s/input%d, status %d", - hid->dev->bus->bus_name, hid->dev->devpath, - hid->ifnum, status); + if (status) { + clear_bit(HID_IN_RUNNING, &hid->iofl); + if (status != -EPERM) { + err("can't resubmit intr, %s-%s/input%d, status %d", + hid->dev->bus->bus_name, + hid->dev->devpath, + hid->ifnum, status); + hid_io_error(hid); + } + } } /* @@ -1093,8 +1196,9 @@ static void hid_irq_out(struct urb *urb, struct pt_regs *regs) case 0: /* success */ break; case -ESHUTDOWN: /* unplug */ - case -EILSEQ: /* unplug timeout on uhci */ unplug = 1; + case -EILSEQ: /* protocol error or unplug */ + case -EPROTO: /* protocol error or unplug */ case -ECONNRESET: /* unlink */ case -ENOENT: break; @@ -1141,8 +1245,9 @@ static void hid_ctrl(struct urb *urb, struct pt_regs *regs) hid_input_report(hid->ctrl[hid->ctrltail].report->type, urb, 0, regs); break; case -ESHUTDOWN: /* unplug */ - case -EILSEQ: /* unplug timectrl on uhci */ unplug = 1; + case -EILSEQ: /* protocol error or unplug */ + case -EPROTO: /* protocol error or unplug */ case -ECONNRESET: /* unlink */ case -ENOENT: case -EPIPE: /* report not available */ @@ -1255,14 +1360,9 @@ static int hid_get_class_descriptor(struct usb_device *dev, int ifnum, int hid_open(struct hid_device *hid) { - if (hid->open++) - return 0; - - hid->urbin->dev = hid->dev; - - if (usb_submit_urb(hid->urbin, GFP_KERNEL)) - return -EIO; - + ++hid->open; + if (hid_start_in(hid)) + hid_io_error(hid); return 0; } @@ -1787,6 +1887,10 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) init_waitqueue_head(&hid->wait); + INIT_WORK(&hid->reset_work, hid_reset, hid); + setup_timer(&hid->io_retry, hid_retry_timeout, (unsigned long) hid); + + spin_lock_init(&hid->inlock); spin_lock_init(&hid->outlock); spin_lock_init(&hid->ctrllock); @@ -1855,11 +1959,16 @@ static void hid_disconnect(struct usb_interface *intf) if (!hid) return; + spin_lock_irq(&hid->inlock); /* Sync with error handler */ usb_set_intfdata(intf, NULL); + spin_unlock_irq(&hid->inlock); usb_kill_urb(hid->urbin); usb_kill_urb(hid->urbout); usb_kill_urb(hid->urbctrl); + del_timer_sync(&hid->io_retry); + flush_scheduled_work(); + if (hid->claimed & HID_CLAIMED_INPUT) hidinput_disconnect(hid); if (hid->claimed & HID_CLAIMED_HIDDEV) @@ -1934,6 +2043,10 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message) { struct hid_device *hid = usb_get_intfdata (intf); + spin_lock_irq(&hid->inlock); /* Sync with error handler */ + set_bit(HID_SUSPENDED, &hid->iofl); + spin_unlock_irq(&hid->inlock); + del_timer(&hid->io_retry); usb_kill_urb(hid->urbin); dev_dbg(&intf->dev, "suspend\n"); return 0; @@ -1944,10 +2057,8 @@ static int hid_resume(struct usb_interface *intf) struct hid_device *hid = usb_get_intfdata (intf); int status; - if (hid->open) - status = usb_submit_urb(hid->urbin, GFP_NOIO); - else - status = 0; + clear_bit(HID_SUSPENDED, &hid->iofl); + status = hid_start_in(hid); dev_dbg(&intf->dev, "resume status %d\n", status); return status; } diff --git a/drivers/usb/input/hid.h b/drivers/usb/input/hid.h index 8b0d4346ce9c..4e1b784fe527 100644 --- a/drivers/usb/input/hid.h +++ b/drivers/usb/input/hid.h @@ -31,6 +31,8 @@ #include #include #include +#include +#include /* * USB HID (Human Interface Device) interface class code @@ -370,6 +372,9 @@ struct hid_control_fifo { #define HID_CTRL_RUNNING 1 #define HID_OUT_RUNNING 2 +#define HID_IN_RUNNING 3 +#define HID_RESET_PENDING 4 +#define HID_SUSPENDED 5 struct hid_input { struct list_head list; @@ -393,12 +398,17 @@ struct hid_device { /* device report descriptor */ int ifnum; /* USB interface number */ unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */ + struct timer_list io_retry; /* Retry timer */ + unsigned long stop_retry; /* Time to give up, in jiffies */ + unsigned int retry_delay; /* Delay length in ms */ + struct work_struct reset_work; /* Task context for resets */ unsigned int bufsize; /* URB buffer size */ struct urb *urbin; /* Input URB */ char *inbuf; /* Input buffer */ dma_addr_t inbuf_dma; /* Input buffer dma */ + spinlock_t inlock; /* Input fifo spinlock */ struct urb *urbctrl; /* Control URB */ struct usb_ctrlrequest *cr; /* Control request struct */ -- cgit v1.2.3-59-g8ed1b From efafe6fb72b2bbab40080a08f7946f1eadb9bad9 Mon Sep 17 00:00:00 2001 From: Malte Doersam Date: Sat, 28 Jan 2006 17:48:33 +0100 Subject: [PATCH] USB: Pegasus: Linksys USBVPN1 support + cleanup This patch adds a second linksys vendor-id (077b) and the product id of the pegasus based adapter USBVPN1 http://www1.linksys.com/Products/product.asp?prid=3D543&scid=3D30 Furthermore it replaces all LINKSYS_GPIO_RESET with DEFAULT_GPIO_RESET as both are declared like this: #define DEFAULT_GPIO_RESET 0x24 #define LINKSYS_GPIO_RESET 0x24 This is misleading and confusing. The check is now done via the VENDOR_ID in pegasus.c: if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS Signed-off-by: Malte Doersam Signed-off-by: Greg Kroah-Hartman --- drivers/usb/net/pegasus.c | 1 + drivers/usb/net/pegasus.h | 26 ++++++++++++++------------ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c index 156a2f1cb39a..5b6675684567 100644 --- a/drivers/usb/net/pegasus.c +++ b/drivers/usb/net/pegasus.c @@ -524,6 +524,7 @@ static int enable_net_traffic(struct net_device *dev, struct usb_device *usb) ret = set_registers(pegasus, EthCtrl0, 3, data); if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS || + usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS2 || usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) { u16 auxmode; read_mii_word(pegasus, 0, 0x1b, &auxmode); diff --git a/drivers/usb/net/pegasus.h b/drivers/usb/net/pegasus.h index 9fbd59b55cb6..a54752ce1493 100644 --- a/drivers/usb/net/pegasus.h +++ b/drivers/usb/net/pegasus.h @@ -25,7 +25,6 @@ #define PHY_READ 0x40 #define PHY_WRITE 0x20 #define DEFAULT_GPIO_RESET 0x24 -#define LINKSYS_GPIO_RESET 0x24 #define DEFAULT_GPIO_SET 0x26 #define PEGASUS_PRESENT 0x00000001 @@ -140,6 +139,7 @@ struct usb_eth_dev { #define VENDOR_KINGSTON 0x0951 #define VENDOR_LANEED 0x056e #define VENDOR_LINKSYS 0x066b +#define VENDOR_LINKSYS2 0x077b #define VENDOR_MELCO 0x0411 #define VENDOR_MICROSOFT 0x045e #define VENDOR_MOBILITY 0x1342 @@ -218,15 +218,15 @@ PEGASUS_DEV( "Corega FEter USB-TX", VENDOR_COREGA, 0x0004, PEGASUS_DEV( "Corega FEter USB-TXS", VENDOR_COREGA, 0x000d, DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK, 0x4001, - LINKSYS_GPIO_RESET ) + DEFAULT_GPIO_RESET ) PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK, 0x4002, - LINKSYS_GPIO_RESET ) + DEFAULT_GPIO_RESET ) PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK, 0x4102, - LINKSYS_GPIO_RESET | PEGASUS_II ) + DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK, 0x400b, - LINKSYS_GPIO_RESET | PEGASUS_II ) + DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK, 0x200c, - LINKSYS_GPIO_RESET | PEGASUS_II ) + DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "D-Link DSB-650TX(PNA)", VENDOR_DLINK, 0x4003, DEFAULT_GPIO_RESET | HAS_HOME_PNA ) PEGASUS_DEV( "D-Link DSB-650", VENDOR_DLINK, 0xabc1, @@ -260,17 +260,19 @@ PEGASUS_DEV( "LANEED USB Ethernet LD-USB/T", VENDOR_LANEED, 0xabc1, PEGASUS_DEV( "LANEED USB Ethernet LD-USB/TX", VENDOR_LANEED, 0x200c, DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "Linksys USB10TX", VENDOR_LINKSYS, 0x2202, - LINKSYS_GPIO_RESET ) + DEFAULT_GPIO_RESET ) PEGASUS_DEV( "Linksys USB100TX", VENDOR_LINKSYS, 0x2203, - LINKSYS_GPIO_RESET ) + DEFAULT_GPIO_RESET ) PEGASUS_DEV( "Linksys USB100TX", VENDOR_LINKSYS, 0x2204, - LINKSYS_GPIO_RESET | HAS_HOME_PNA ) + DEFAULT_GPIO_RESET | HAS_HOME_PNA ) PEGASUS_DEV( "Linksys USB10T Ethernet Adapter", VENDOR_LINKSYS, 0x2206, - LINKSYS_GPIO_RESET | PEGASUS_II) + DEFAULT_GPIO_RESET | PEGASUS_II) +PEGASUS_DEV( "Linksys USBVPN1", VENDOR_LINKSYS2, 0x08b4, + DEFAULT_GPIO_RESET ) PEGASUS_DEV( "Linksys USB USB100TX", VENDOR_LINKSYS, 0x400b, - LINKSYS_GPIO_RESET | PEGASUS_II ) + DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "Linksys USB10TX", VENDOR_LINKSYS, 0x200c, - LINKSYS_GPIO_RESET | PEGASUS_II ) + DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "MELCO/BUFFALO LUA-TX", VENDOR_MELCO, 0x0001, DEFAULT_GPIO_RESET ) PEGASUS_DEV( "MELCO/BUFFALO LUA-TX", VENDOR_MELCO, 0x0005, -- cgit v1.2.3-59-g8ed1b From 0eb8c7cafe8d4fa9a90ea8680200e3ae3d57b1bd Mon Sep 17 00:00:00 2001 From: Franck Bui-Huu Date: Tue, 7 Feb 2006 09:06:17 +0100 Subject: [PATCH] USB: Zero driver: Removed duplicated code Signed-off-by: Franck Bui-Huu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/zero.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index ae7a1c0f5748..5e9fe8a70543 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -1224,12 +1224,6 @@ autoconf_fail: loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - if (gadget->is_otg) { - otg_descriptor.bmAttributes |= USB_OTG_HNP, - source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; - loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; - } - usb_gadget_set_selfpowered (gadget); init_timer (&dev->resume); -- cgit v1.2.3-59-g8ed1b From 499003e815344304c7b0c93aad923ddf644d24e0 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Tue, 24 Jan 2006 08:11:27 -0800 Subject: [PATCH] USB: Fix masking bug initialization of Freescale EHCI controller In setting up the of PHY we masked off too many bits, instead just initialize PORTSC for the type of PHY we are using. Signed-off-by: Kumar Gala Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-fsl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 59f90f76ee31..f985f121a245 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -160,8 +160,7 @@ static void mpc83xx_setup_phy(struct ehci_hcd *ehci, enum fsl_usb2_phy_modes phy_mode, unsigned int port_offset) { - u32 portsc = readl(&ehci->regs->port_status[port_offset]); - portsc &= ~PORT_PTS_MSK; + u32 portsc = 0; switch (phy_mode) { case FSL_USB2_PHY_ULPI: portsc |= PORT_PTS_ULPI; -- cgit v1.2.3-59-g8ed1b From dccf4a48d47120a42382ba526f1a0848c13ba2a4 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sat, 17 Dec 2005 17:58:46 -0500 Subject: [PATCH] UHCI: use one QH per endpoint, not per URB This patch (as623) changes the uhci-hcd driver to make it use one QH per device endpoint, instead of a QH per URB as it does now. Numerous areas of the code are affected by this. For example, the distinction between "queued" URBs and non-"queued" URBs no longer exists; all URBs belong to a queue and some just happen to be at the queue's head. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-debug.c | 320 ++++---------- drivers/usb/host/uhci-hcd.c | 65 ++- drivers/usb/host/uhci-hcd.h | 177 ++++---- drivers/usb/host/uhci-q.c | 985 +++++++++++++++++++----------------------- 4 files changed, 685 insertions(+), 862 deletions(-) diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c index 5832953086f8..3faccbd68547 100644 --- a/drivers/usb/host/uhci-debug.c +++ b/drivers/usb/host/uhci-debug.c @@ -90,13 +90,60 @@ static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space) return out - buf; } -static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space) +static int uhci_show_urbp(struct urb_priv *urbp, char *buf, int len, int space) { char *out = buf; - struct urb_priv *urbp; - struct list_head *head, *tmp; struct uhci_td *td; - int i = 0, checked = 0, prevactive = 0; + int i, nactive, ninactive; + + if (len < 200) + return 0; + + out += sprintf(out, "urb_priv [%p] ", urbp); + out += sprintf(out, "urb [%p] ", urbp->urb); + out += sprintf(out, "qh [%p] ", urbp->qh); + out += sprintf(out, "Dev=%d ", usb_pipedevice(urbp->urb->pipe)); + out += sprintf(out, "EP=%x(%s) ", usb_pipeendpoint(urbp->urb->pipe), + (usb_pipein(urbp->urb->pipe) ? "IN" : "OUT")); + + switch (usb_pipetype(urbp->urb->pipe)) { + case PIPE_ISOCHRONOUS: out += sprintf(out, "ISO"); break; + case PIPE_INTERRUPT: out += sprintf(out, "INT"); break; + case PIPE_BULK: out += sprintf(out, "BLK"); break; + case PIPE_CONTROL: out += sprintf(out, "CTL"); break; + } + + out += sprintf(out, "%s", (urbp->fsbr ? " FSBR" : "")); + out += sprintf(out, "%s", (urbp->fsbr_timeout ? " FSBR_TO" : "")); + + if (urbp->urb->status != -EINPROGRESS) + out += sprintf(out, " Status=%d", urbp->urb->status); + out += sprintf(out, "\n"); + + i = nactive = ninactive = 0; + list_for_each_entry(td, &urbp->td_list, list) { + if (++i <= 10 || debug > 2) { + out += sprintf(out, "%*s%d: ", space + 2, "", i); + out += uhci_show_td(td, out, len - (out - buf), 0); + } else { + if (td_status(td) & TD_CTRL_ACTIVE) + ++nactive; + else + ++ninactive; + } + } + if (nactive + ninactive > 0) + out += sprintf(out, "%*s[skipped %d inactive and %d active " + "TDs]\n", + space, "", ninactive, nactive); + + return out - buf; +} + +static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space) +{ + char *out = buf; + int i, nurbs; __le32 element = qh_element(qh); /* Try to make sure there's enough memory */ @@ -118,86 +165,36 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space) if (!(element & ~(UHCI_PTR_QH | UHCI_PTR_DEPTH))) out += sprintf(out, "%*s Element is NULL (bug?)\n", space, ""); - if (!qh->urbp) { - out += sprintf(out, "%*s urbp == NULL\n", space, ""); - goto out; - } - - urbp = qh->urbp; - - head = &urbp->td_list; - tmp = head->next; - - td = list_entry(tmp, struct uhci_td, list); - - if (cpu_to_le32(td->dma_handle) != (element & ~UHCI_PTR_BITS)) - out += sprintf(out, "%*s Element != First TD\n", space, ""); - - while (tmp != head) { - struct uhci_td *td = list_entry(tmp, struct uhci_td, list); - - tmp = tmp->next; - - out += sprintf(out, "%*s%d: ", space + 2, "", i++); - out += uhci_show_td(td, out, len - (out - buf), 0); - - if (i > 10 && !checked && prevactive && tmp != head && - debug <= 2) { - struct list_head *ntmp = tmp; - struct uhci_td *ntd = td; - int active = 1, ni = i; - - checked = 1; - - while (ntmp != head && ntmp->next != head && active) { - ntd = list_entry(ntmp, struct uhci_td, list); - - ntmp = ntmp->next; - - active = td_status(ntd) & TD_CTRL_ACTIVE; - - ni++; - } - - if (active && ni > i) { - out += sprintf(out, "%*s[skipped %d active TDs]\n", space, "", ni - i); - tmp = ntmp; - td = ntd; - i = ni; - } + if (list_empty(&qh->queue)) { + out += sprintf(out, "%*s queue is empty\n", space, ""); + } else { + struct urb_priv *urbp = list_entry(qh->queue.next, + struct urb_priv, node); + struct uhci_td *td = list_entry(urbp->td_list.next, + struct uhci_td, list); + + if (cpu_to_le32(td->dma_handle) != (element & ~UHCI_PTR_BITS)) + out += sprintf(out, "%*s Element != First TD\n", + space, ""); + i = nurbs = 0; + list_for_each_entry(urbp, &qh->queue, node) { + if (++i <= 10) + out += uhci_show_urbp(urbp, out, + len - (out - buf), space + 2); + else + ++nurbs; } - - prevactive = td_status(td) & TD_CTRL_ACTIVE; - } - - if (list_empty(&urbp->queue_list) || urbp->queued) - goto out; - - out += sprintf(out, "%*sQueued QHs:\n", -space, "--"); - - head = &urbp->queue_list; - tmp = head->next; - - while (tmp != head) { - struct urb_priv *nurbp = list_entry(tmp, struct urb_priv, - queue_list); - tmp = tmp->next; - - out += uhci_show_qh(nurbp->qh, out, len - (out - buf), space); + if (nurbs > 0) + out += sprintf(out, "%*s Skipped %d URBs\n", + space, "", nurbs); } -out: return out - buf; } -#define show_frame_num() \ - if (!shown) { \ - shown = 1; \ - out += sprintf(out, "- Frame %d\n", i); \ - } - #ifdef CONFIG_PROC_FS static const char * const qh_names[] = { + "skel_unlink_qh", "skel_iso_qh", "skel_int128_qh", "skel_int64_qh", "skel_int32_qh", "skel_int16_qh", "skel_int8_qh", "skel_int4_qh", @@ -206,12 +203,6 @@ static const char * const qh_names[] = { "skel_bulk_qh", "skel_term_qh" }; -#define show_qh_name() \ - if (!shown) { \ - shown = 1; \ - out += sprintf(out, "- %s\n", qh_names[i]); \ - } - static int uhci_show_sc(int port, unsigned short status, char *buf, int len) { char *out = buf; @@ -321,139 +312,29 @@ static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len) return out - buf; } -static int uhci_show_urbp(struct uhci_hcd *uhci, struct urb_priv *urbp, char *buf, int len) -{ - struct list_head *tmp; - char *out = buf; - int count = 0; - - if (len < 200) - return 0; - - out += sprintf(out, "urb_priv [%p] ", urbp); - out += sprintf(out, "urb [%p] ", urbp->urb); - out += sprintf(out, "qh [%p] ", urbp->qh); - out += sprintf(out, "Dev=%d ", usb_pipedevice(urbp->urb->pipe)); - out += sprintf(out, "EP=%x(%s) ", usb_pipeendpoint(urbp->urb->pipe), (usb_pipein(urbp->urb->pipe) ? "IN" : "OUT")); - - switch (usb_pipetype(urbp->urb->pipe)) { - case PIPE_ISOCHRONOUS: out += sprintf(out, "ISO "); break; - case PIPE_INTERRUPT: out += sprintf(out, "INT "); break; - case PIPE_BULK: out += sprintf(out, "BLK "); break; - case PIPE_CONTROL: out += sprintf(out, "CTL "); break; - } - - out += sprintf(out, "%s", (urbp->fsbr ? "FSBR " : "")); - out += sprintf(out, "%s", (urbp->fsbr_timeout ? "FSBR_TO " : "")); - - if (urbp->urb->status != -EINPROGRESS) - out += sprintf(out, "Status=%d ", urbp->urb->status); - //out += sprintf(out, "FSBRtime=%lx ",urbp->fsbrtime); - - count = 0; - list_for_each(tmp, &urbp->td_list) - count++; - out += sprintf(out, "TDs=%d ",count); - - if (urbp->queued) - out += sprintf(out, "queued\n"); - else { - count = 0; - list_for_each(tmp, &urbp->queue_list) - count++; - out += sprintf(out, "queued URBs=%d\n", count); - } - - return out - buf; -} - -static int uhci_show_lists(struct uhci_hcd *uhci, char *buf, int len) -{ - char *out = buf; - struct list_head *head, *tmp; - int count; - - out += sprintf(out, "Main list URBs:"); - if (list_empty(&uhci->urb_list)) - out += sprintf(out, " Empty\n"); - else { - out += sprintf(out, "\n"); - count = 0; - head = &uhci->urb_list; - tmp = head->next; - while (tmp != head) { - struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list); - - out += sprintf(out, " %d: ", ++count); - out += uhci_show_urbp(uhci, urbp, out, len - (out - buf)); - tmp = tmp->next; - } - } - - out += sprintf(out, "Remove list URBs:"); - if (list_empty(&uhci->urb_remove_list)) - out += sprintf(out, " Empty\n"); - else { - out += sprintf(out, "\n"); - count = 0; - head = &uhci->urb_remove_list; - tmp = head->next; - while (tmp != head) { - struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list); - - out += sprintf(out, " %d: ", ++count); - out += uhci_show_urbp(uhci, urbp, out, len - (out - buf)); - tmp = tmp->next; - } - } - - out += sprintf(out, "Complete list URBs:"); - if (list_empty(&uhci->complete_list)) - out += sprintf(out, " Empty\n"); - else { - out += sprintf(out, "\n"); - count = 0; - head = &uhci->complete_list; - tmp = head->next; - while (tmp != head) { - struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list); - - out += sprintf(out, " %d: ", ++count); - out += uhci_show_urbp(uhci, urbp, out, len - (out - buf)); - tmp = tmp->next; - } - } - - return out - buf; -} - static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) { - unsigned long flags; char *out = buf; int i, j; struct uhci_qh *qh; struct uhci_td *td; struct list_head *tmp, *head; - spin_lock_irqsave(&uhci->lock, flags); - out += uhci_show_root_hub_state(uhci, out, len - (out - buf)); out += sprintf(out, "HC status\n"); out += uhci_show_status(uhci, out, len - (out - buf)); + if (debug <= 1) + return out - buf; out += sprintf(out, "Frame List\n"); for (i = 0; i < UHCI_NUMFRAMES; ++i) { - int shown = 0; td = uhci->frame_cpu[i]; if (!td) continue; - if (td->dma_handle != (dma_addr_t)uhci->frame[i]) { - show_frame_num(); + out += sprintf(out, "- Frame %d\n", i); \ + if (td->dma_handle != (dma_addr_t)uhci->frame[i]) out += sprintf(out, " frame list does not match td->dma_handle!\n"); - } - show_frame_num(); head = &td->fl_list; tmp = head; @@ -467,14 +348,11 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) out += sprintf(out, "Skeleton QHs\n"); for (i = 0; i < UHCI_NUM_SKELQH; ++i) { - int shown = 0; + int cnt = 0; qh = uhci->skelqh[i]; - - if (debug > 1) { - show_qh_name(); - out += uhci_show_qh(qh, out, len - (out - buf), 4); - } + out += sprintf(out, "- %s\n", qh_names[i]); \ + out += uhci_show_qh(qh, out, len - (out - buf), 4); /* Last QH is the Terminating QH, it's different */ if (i == UHCI_NUM_SKELQH - 1) { @@ -487,44 +365,27 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) continue; } - j = (i < 7) ? 7 : i+1; /* Next skeleton */ - if (list_empty(&qh->list)) { - if (i < UHCI_NUM_SKELQH - 1) { - if (qh->link != - (cpu_to_le32(uhci->skelqh[j]->dma_handle) | UHCI_PTR_QH)) { - show_qh_name(); - out += sprintf(out, " skeleton QH not linked to next skeleton QH!\n"); - } - } - - continue; - } - - show_qh_name(); - - head = &qh->list; + j = (i < 9) ? 9 : i+1; /* Next skeleton */ + head = &qh->node; tmp = head->next; while (tmp != head) { - qh = list_entry(tmp, struct uhci_qh, list); - + qh = list_entry(tmp, struct uhci_qh, node); tmp = tmp->next; - - out += uhci_show_qh(qh, out, len - (out - buf), 4); + if (++cnt <= 10) + out += uhci_show_qh(qh, out, + len - (out - buf), 4); } + if ((cnt -= 10) > 0) + out += sprintf(out, " Skipped %d QHs\n", cnt); - if (i < UHCI_NUM_SKELQH - 1) { + if (i > 1 && i < UHCI_NUM_SKELQH - 1) { if (qh->link != (cpu_to_le32(uhci->skelqh[j]->dma_handle) | UHCI_PTR_QH)) out += sprintf(out, " last QH not linked to next skeleton!\n"); } } - if (debug > 2) - out += uhci_show_lists(uhci, out, len - (out - buf)); - - spin_unlock_irqrestore(&uhci->lock, flags); - return out - buf; } @@ -541,6 +402,7 @@ static int uhci_debug_open(struct inode *inode, struct file *file) struct uhci_hcd *uhci = inode->u.generic_ip; struct uhci_debug *up; int ret = -ENOMEM; + unsigned long flags; lock_kernel(); up = kmalloc(sizeof(*up), GFP_KERNEL); @@ -553,7 +415,9 @@ static int uhci_debug_open(struct inode *inode, struct file *file) goto out; } + spin_lock_irqsave(&uhci->lock, flags); up->size = uhci_sprint_schedule(uhci, up->data, MAX_OUTPUT); + spin_unlock_irqrestore(&uhci->lock, flags); file->private_data = up; diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index dfe121d35887..1ff4b8806372 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -54,7 +54,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v2.3" +#define DRIVER_VERSION "v3.0" #define DRIVER_AUTHOR "Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, \ Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, \ Alan Stern" @@ -489,15 +489,11 @@ static int uhci_start(struct usb_hcd *hcd) uhci->fsbrtimeout = 0; spin_lock_init(&uhci->lock); - INIT_LIST_HEAD(&uhci->qh_remove_list); INIT_LIST_HEAD(&uhci->td_remove_list); - - INIT_LIST_HEAD(&uhci->urb_remove_list); - INIT_LIST_HEAD(&uhci->urb_list); - INIT_LIST_HEAD(&uhci->complete_list); + INIT_LIST_HEAD(&uhci->idle_qh_list); init_waitqueue_head(&uhci->waitqh); @@ -540,7 +536,7 @@ static int uhci_start(struct usb_hcd *hcd) } for (i = 0; i < UHCI_NUM_SKELQH; i++) { - uhci->skelqh[i] = uhci_alloc_qh(uhci); + uhci->skelqh[i] = uhci_alloc_qh(uhci, NULL, NULL); if (!uhci->skelqh[i]) { dev_err(uhci_dev(uhci), "unable to allocate QH\n"); goto err_alloc_skelqh; @@ -557,13 +553,17 @@ static int uhci_start(struct usb_hcd *hcd) uhci->skel_int16_qh->link = uhci->skel_int8_qh->link = uhci->skel_int4_qh->link = - uhci->skel_int2_qh->link = - cpu_to_le32(uhci->skel_int1_qh->dma_handle) | UHCI_PTR_QH; - uhci->skel_int1_qh->link = cpu_to_le32(uhci->skel_ls_control_qh->dma_handle) | UHCI_PTR_QH; - - uhci->skel_ls_control_qh->link = cpu_to_le32(uhci->skel_fs_control_qh->dma_handle) | UHCI_PTR_QH; - uhci->skel_fs_control_qh->link = cpu_to_le32(uhci->skel_bulk_qh->dma_handle) | UHCI_PTR_QH; - uhci->skel_bulk_qh->link = cpu_to_le32(uhci->skel_term_qh->dma_handle) | UHCI_PTR_QH; + uhci->skel_int2_qh->link = UHCI_PTR_QH | + cpu_to_le32(uhci->skel_int1_qh->dma_handle); + + uhci->skel_int1_qh->link = UHCI_PTR_QH | + cpu_to_le32(uhci->skel_ls_control_qh->dma_handle); + uhci->skel_ls_control_qh->link = UHCI_PTR_QH | + cpu_to_le32(uhci->skel_fs_control_qh->dma_handle); + uhci->skel_fs_control_qh->link = UHCI_PTR_QH | + cpu_to_le32(uhci->skel_bulk_qh->dma_handle); + uhci->skel_bulk_qh->link = UHCI_PTR_QH | + cpu_to_le32(uhci->skel_term_qh->dma_handle); /* This dummy TD is to work around a bug in Intel PIIX controllers */ uhci_fill_td(uhci->term_td, 0, uhci_explen(0) | @@ -589,15 +589,15 @@ static int uhci_start(struct usb_hcd *hcd) /* * ffs (Find First bit Set) does exactly what we need: - * 1,3,5,... => ffs = 0 => use skel_int2_qh = skelqh[6], - * 2,6,10,... => ffs = 1 => use skel_int4_qh = skelqh[5], etc. - * ffs > 6 => not on any high-period queue, so use - * skel_int1_qh = skelqh[7]. + * 1,3,5,... => ffs = 0 => use skel_int2_qh = skelqh[8], + * 2,6,10,... => ffs = 1 => use skel_int4_qh = skelqh[7], etc. + * ffs >= 7 => not on any high-period queue, so use + * skel_int1_qh = skelqh[9]. * Add UHCI_NUMFRAMES to insure at least one bit is set. */ - irq = 6 - (int) __ffs(i + UHCI_NUMFRAMES); - if (irq < 0) - irq = 7; + irq = 8 - (int) __ffs(i + UHCI_NUMFRAMES); + if (irq <= 1) + irq = 9; /* Only place we don't use the frame list routines */ uhci->frame[i] = UHCI_PTR_QH | @@ -767,13 +767,30 @@ static int uhci_resume(struct usb_hcd *hcd) } #endif -/* Wait until all the URBs for a particular device/endpoint are gone */ +/* Wait until a particular device/endpoint's QH is idle, and free it */ static void uhci_hcd_endpoint_disable(struct usb_hcd *hcd, - struct usb_host_endpoint *ep) + struct usb_host_endpoint *hep) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); + struct uhci_qh *qh; + + spin_lock_irq(&uhci->lock); + qh = (struct uhci_qh *) hep->hcpriv; + if (qh == NULL) + goto done; - wait_event_interruptible(uhci->waitqh, list_empty(&ep->urb_list)); + while (qh->state != QH_STATE_IDLE) { + ++uhci->num_waiting; + spin_unlock_irq(&uhci->lock); + wait_event_interruptible(uhci->waitqh, + qh->state == QH_STATE_IDLE); + spin_lock_irq(&uhci->lock); + --uhci->num_waiting; + } + + uhci_free_qh(uhci, qh); +done: + spin_unlock_irq(&uhci->lock); } static int uhci_hcd_get_frame_number(struct usb_hcd *hcd) diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index 8b4b887a7d41..7a9481c09a05 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -28,8 +28,9 @@ #define USBSTS_USBINT 0x0001 /* Interrupt due to IOC */ #define USBSTS_ERROR 0x0002 /* Interrupt due to error */ #define USBSTS_RD 0x0004 /* Resume Detect */ -#define USBSTS_HSE 0x0008 /* Host System Error - basically PCI problems */ -#define USBSTS_HCPE 0x0010 /* Host Controller Process Error - the scripts were buggy */ +#define USBSTS_HSE 0x0008 /* Host System Error: PCI problems */ +#define USBSTS_HCPE 0x0010 /* Host Controller Process Error: + * the schedule is buggy */ #define USBSTS_HCH 0x0020 /* HC Halted */ /* Interrupt enable register */ @@ -47,7 +48,8 @@ /* USB port status and control registers */ #define USBPORTSC1 16 #define USBPORTSC2 18 -#define USBPORTSC_CCS 0x0001 /* Current Connect Status ("device present") */ +#define USBPORTSC_CCS 0x0001 /* Current Connect Status + * ("device present") */ #define USBPORTSC_CSC 0x0002 /* Connect Status Change */ #define USBPORTSC_PE 0x0004 /* Port Enable */ #define USBPORTSC_PEC 0x0008 /* Port Enable Change */ @@ -71,15 +73,16 @@ #define USBLEGSUP_RWC 0x8f00 /* the R/WC bits */ #define USBLEGSUP_RO 0x5040 /* R/O and reserved bits */ -#define UHCI_PTR_BITS cpu_to_le32(0x000F) -#define UHCI_PTR_TERM cpu_to_le32(0x0001) -#define UHCI_PTR_QH cpu_to_le32(0x0002) -#define UHCI_PTR_DEPTH cpu_to_le32(0x0004) -#define UHCI_PTR_BREADTH cpu_to_le32(0x0000) +#define UHCI_PTR_BITS __constant_cpu_to_le32(0x000F) +#define UHCI_PTR_TERM __constant_cpu_to_le32(0x0001) +#define UHCI_PTR_QH __constant_cpu_to_le32(0x0002) +#define UHCI_PTR_DEPTH __constant_cpu_to_le32(0x0004) +#define UHCI_PTR_BREADTH __constant_cpu_to_le32(0x0000) #define UHCI_NUMFRAMES 1024 /* in the frame list [array] */ #define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */ -#define CAN_SCHEDULE_FRAMES 1000 /* how far future frames can be scheduled */ +#define CAN_SCHEDULE_FRAMES 1000 /* how far in the future frames + * can be scheduled */ /* @@ -87,38 +90,54 @@ */ /* - * One role of a QH is to hold a queue of TDs for some endpoint. Each QH is - * used with one URB, and qh->element (updated by the HC) is either: - * - the next unprocessed TD for the URB, or - * - UHCI_PTR_TERM (when there's no more traffic for this endpoint), or - * - the QH for the next URB queued to the same endpoint. + * One role of a QH is to hold a queue of TDs for some endpoint. One QH goes + * with each endpoint, and qh->element (updated by the HC) is either: + * - the next unprocessed TD in the endpoint's queue, or + * - UHCI_PTR_TERM (when there's no more traffic for this endpoint). * * The other role of a QH is to serve as a "skeleton" framelist entry, so we * can easily splice a QH for some endpoint into the schedule at the right * place. Then qh->element is UHCI_PTR_TERM. * - * In the frame list, qh->link maintains a list of QHs seen by the HC: + * In the schedule, qh->link maintains a list of QHs seen by the HC: * skel1 --> ep1-qh --> ep2-qh --> ... --> skel2 --> ... + * + * qh->node is the software equivalent of qh->link. The differences + * are that the software list is doubly-linked and QHs in the UNLINKING + * state are on the software list but not the hardware schedule. + * + * For bookkeeping purposes we maintain QHs even for Isochronous endpoints, + * but they never get added to the hardware schedule. */ +#define QH_STATE_IDLE 1 /* QH is not being used */ +#define QH_STATE_UNLINKING 2 /* QH has been removed from the + * schedule but the hardware may + * still be using it */ +#define QH_STATE_ACTIVE 3 /* QH is on the schedule */ + struct uhci_qh { /* Hardware fields */ - __le32 link; /* Next queue */ - __le32 element; /* Queue element pointer */ + __le32 link; /* Next QH in the schedule */ + __le32 element; /* Queue element (TD) pointer */ /* Software fields */ dma_addr_t dma_handle; - struct urb_priv *urbp; + struct list_head node; /* Node in the list of QHs */ + struct usb_host_endpoint *hep; /* Endpoint information */ + struct usb_device *udev; + struct list_head queue; /* Queue of urbps for this QH */ + struct uhci_qh *skel; /* Skeleton for this QH */ - struct list_head list; - struct list_head remove_list; + unsigned int unlink_frame; /* When the QH was unlinked */ + int state; /* QH_STATE_xxx; see above */ } __attribute__((aligned(16))); /* * We need a special accessor for the element pointer because it is * subject to asynchronous updates by the controller. */ -static __le32 inline qh_element(struct uhci_qh *qh) { +static inline __le32 qh_element(struct uhci_qh *qh) { __le32 element = qh->element; barrier(); @@ -149,11 +168,13 @@ static __le32 inline qh_element(struct uhci_qh *qh) { #define TD_CTRL_ACTLEN_MASK 0x7FF /* actual length, encoded as n - 1 */ #define TD_CTRL_ANY_ERROR (TD_CTRL_STALLED | TD_CTRL_DBUFERR | \ - TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF) + TD_CTRL_BABBLE | TD_CTRL_CRCTIME | \ + TD_CTRL_BITSTUFF) #define uhci_maxerr(err) ((err) << TD_CTRL_C_ERR_SHIFT) #define uhci_status_bits(ctrl_sts) ((ctrl_sts) & 0xF60000) -#define uhci_actual_length(ctrl_sts) (((ctrl_sts) + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */ +#define uhci_actual_length(ctrl_sts) (((ctrl_sts) + 1) & \ + TD_CTRL_ACTLEN_MASK) /* 1-based */ /* * for TD : (a.k.a. Token) @@ -163,7 +184,7 @@ static __le32 inline qh_element(struct uhci_qh *qh) { #define TD_TOKEN_TOGGLE_SHIFT 19 #define TD_TOKEN_TOGGLE (1 << 19) #define TD_TOKEN_EXPLEN_SHIFT 21 -#define TD_TOKEN_EXPLEN_MASK 0x7FF /* expected length, encoded as n - 1 */ +#define TD_TOKEN_EXPLEN_MASK 0x7FF /* expected length, encoded as n-1 */ #define TD_TOKEN_PID_MASK 0xFF #define uhci_explen(len) ((((len) - 1) & TD_TOKEN_EXPLEN_MASK) << \ @@ -187,7 +208,7 @@ static __le32 inline qh_element(struct uhci_qh *qh) { * sw space after the TD entry. * * td->link points to either another TD (not necessarily for the same urb or - * even the same endpoint), or nothing (PTR_TERM), or a QH (for queued urbs). + * even the same endpoint), or nothing (PTR_TERM), or a QH. */ struct uhci_td { /* Hardware fields */ @@ -210,7 +231,7 @@ struct uhci_td { * We need a special accessor for the control/status word because it is * subject to asynchronous updates by the controller. */ -static u32 inline td_status(struct uhci_td *td) { +static inline u32 td_status(struct uhci_td *td) { __le32 status = td->status; barrier(); @@ -223,17 +244,14 @@ static u32 inline td_status(struct uhci_td *td) { */ /* - * The UHCI driver places Interrupt, Control and Bulk into QHs both - * to group together TDs for one transfer, and also to facilitate queuing - * of URBs. To make it easy to insert entries into the schedule, we have - * a skeleton of QHs for each predefined Interrupt latency, low-speed - * control, full-speed control and terminating QH (see explanation for - * the terminating QH below). + * The UHCI driver uses QHs with Interrupt, Control and Bulk URBs for + * automatic queuing. To make it easy to insert entries into the schedule, + * we have a skeleton of QHs for each predefined Interrupt latency, + * low-speed control, full-speed control, bulk, and terminating QH + * (see explanation for the terminating QH below). * * When we want to add a new QH, we add it to the end of the list for the - * skeleton QH. - * - * For instance, the queue can look like this: + * skeleton QH. For instance, the schedule list can look like this: * * skel int128 QH * dev 1 interrupt QH @@ -256,26 +274,31 @@ static u32 inline td_status(struct uhci_td *td) { * - To loop back to the full-speed control queue for full-speed bandwidth * reclamation. * - * Isochronous transfers are stored before the start of the skeleton - * schedule and don't use QHs. While the UHCI spec doesn't forbid the - * use of QHs for Isochronous, it doesn't use them either. And the spec - * says that queues never advance on an error completion status, which - * makes them totally unsuitable for Isochronous transfers. + * There's a special skeleton QH for Isochronous QHs. It never appears + * on the schedule, and Isochronous TDs go on the schedule before the + * the skeleton QHs. The hardware accesses them directly rather than + * through their QH, which is used only for bookkeeping purposes. + * While the UHCI spec doesn't forbid the use of QHs for Isochronous, + * it doesn't use them either. And the spec says that queues never + * advance on an error completion status, which makes them totally + * unsuitable for Isochronous transfers. */ -#define UHCI_NUM_SKELQH 12 -#define skel_int128_qh skelqh[0] -#define skel_int64_qh skelqh[1] -#define skel_int32_qh skelqh[2] -#define skel_int16_qh skelqh[3] -#define skel_int8_qh skelqh[4] -#define skel_int4_qh skelqh[5] -#define skel_int2_qh skelqh[6] -#define skel_int1_qh skelqh[7] -#define skel_ls_control_qh skelqh[8] -#define skel_fs_control_qh skelqh[9] -#define skel_bulk_qh skelqh[10] -#define skel_term_qh skelqh[11] +#define UHCI_NUM_SKELQH 14 +#define skel_unlink_qh skelqh[0] +#define skel_iso_qh skelqh[1] +#define skel_int128_qh skelqh[2] +#define skel_int64_qh skelqh[3] +#define skel_int32_qh skelqh[4] +#define skel_int16_qh skelqh[5] +#define skel_int8_qh skelqh[6] +#define skel_int4_qh skelqh[7] +#define skel_int2_qh skelqh[8] +#define skel_int1_qh skelqh[9] +#define skel_ls_control_qh skelqh[10] +#define skel_fs_control_qh skelqh[11] +#define skel_bulk_qh skelqh[12] +#define skel_term_qh skelqh[13] /* * Search tree for determining where fits in the skelqh[] @@ -293,21 +316,21 @@ static inline int __interval_to_skel(int interval) if (interval < 16) { if (interval < 4) { if (interval < 2) - return 7; /* int1 for 0-1 ms */ - return 6; /* int2 for 2-3 ms */ + return 9; /* int1 for 0-1 ms */ + return 8; /* int2 for 2-3 ms */ } if (interval < 8) - return 5; /* int4 for 4-7 ms */ - return 4; /* int8 for 8-15 ms */ + return 7; /* int4 for 4-7 ms */ + return 6; /* int8 for 8-15 ms */ } if (interval < 64) { if (interval < 32) - return 3; /* int16 for 16-31 ms */ - return 2; /* int32 for 32-63 ms */ + return 5; /* int16 for 16-31 ms */ + return 4; /* int32 for 32-63 ms */ } if (interval < 128) - return 1; /* int64 for 64-127 ms */ - return 0; /* int128 for 128-255 ms (Max.) */ + return 3; /* int64 for 64-127 ms */ + return 2; /* int128 for 128-255 ms (Max.) */ } @@ -363,12 +386,12 @@ struct uhci_hcd { spinlock_t lock; - dma_addr_t frame_dma_handle; /* Hardware frame list */ + dma_addr_t frame_dma_handle; /* Hardware frame list */ __le32 *frame; - void **frame_cpu; /* CPU's frame list */ + void **frame_cpu; /* CPU's frame list */ - int fsbr; /* Full-speed bandwidth reclamation */ - unsigned long fsbrtimeout; /* FSBR delay */ + int fsbr; /* Full-speed bandwidth reclamation */ + unsigned long fsbrtimeout; /* FSBR delay */ enum uhci_rh_state rh_state; unsigned long auto_stop_time; /* When to AUTO_STOP */ @@ -392,24 +415,19 @@ struct uhci_hcd { /* Main list of URBs currently controlled by this HC */ struct list_head urb_list; - /* List of QHs that are done, but waiting to be unlinked (race) */ - struct list_head qh_remove_list; - unsigned int qh_remove_age; /* Age in frames */ - /* List of TDs that are done, but waiting to be freed (race) */ struct list_head td_remove_list; unsigned int td_remove_age; /* Age in frames */ - /* List of asynchronously unlinked URBs */ - struct list_head urb_remove_list; - unsigned int urb_remove_age; /* Age in frames */ - /* List of URBs awaiting completion callback */ struct list_head complete_list; + struct list_head idle_qh_list; /* Where the idle QHs live */ + int rh_numports; /* Number of root-hub ports */ wait_queue_head_t waitqh; /* endpoint_disable waiters */ + int num_waiting; /* Number of waiters */ }; /* Convert between a usb_hcd pointer and the corresponding uhci_hcd */ @@ -430,22 +448,19 @@ static inline struct usb_hcd *uhci_to_hcd(struct uhci_hcd *uhci) */ struct urb_priv { struct list_head urb_list; + struct list_head node; /* Node in the QH's urbp list */ struct urb *urb; struct uhci_qh *qh; /* QH for this URB */ struct list_head td_list; - unsigned fsbr : 1; /* URB turned on FSBR */ - unsigned fsbr_timeout : 1; /* URB timed out on FSBR */ - unsigned queued : 1; /* QH was queued (not linked in) */ - unsigned short_control_packet : 1; /* If we get a short packet during */ - /* a control transfer, retrigger */ - /* the status phase */ - unsigned long fsbrtime; /* In jiffies */ - struct list_head queue_list; + unsigned fsbr : 1; /* URB turned on FSBR */ + unsigned fsbr_timeout : 1; /* URB timed out on FSBR */ + unsigned short_transfer : 1; /* URB got a short transfer, no + * need to rescan */ }; diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 782398045f9f..b1b551a3d14e 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -13,13 +13,9 @@ * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface * support from usb-ohci.c by Adam Richter, adam@yggdrasil.com). * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c) - * (C) Copyright 2004 Alan Stern, stern@rowland.harvard.edu + * (C) Copyright 2004-2005 Alan Stern, stern@rowland.harvard.edu */ -static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb); -static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb); -static void uhci_remove_pending_urbps(struct uhci_hcd *uhci); -static void uhci_free_pending_qhs(struct uhci_hcd *uhci); static void uhci_free_pending_tds(struct uhci_hcd *uhci); /* @@ -30,7 +26,7 @@ static void uhci_free_pending_tds(struct uhci_hcd *uhci); * games with the FSBR code to make sure we get the correct order in all * the cases. I don't think it's worth the effort */ -static inline void uhci_set_next_interrupt(struct uhci_hcd *uhci) +static void uhci_set_next_interrupt(struct uhci_hcd *uhci) { if (uhci->is_stopped) mod_timer(&uhci_to_hcd(uhci)->rh_timer, jiffies); @@ -42,12 +38,6 @@ static inline void uhci_clear_next_interrupt(struct uhci_hcd *uhci) uhci->term_td->status &= ~cpu_to_le32(TD_CTRL_IOC); } -static inline void uhci_moveto_complete(struct uhci_hcd *uhci, - struct urb_priv *urbp) -{ - list_move_tail(&urbp->urb_list, &uhci->complete_list); -} - static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci) { dma_addr_t dma_handle; @@ -71,6 +61,18 @@ static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci) return td; } +static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td) +{ + if (!list_empty(&td->list)) + dev_warn(uhci_dev(uhci), "td %p still in list!\n", td); + if (!list_empty(&td->remove_list)) + dev_warn(uhci_dev(uhci), "td %p still in remove_list!\n", td); + if (!list_empty(&td->fl_list)) + dev_warn(uhci_dev(uhci), "td %p still in fl_list!\n", td); + + dma_pool_free(uhci->td_pool, td, td->dma_handle); +} + static inline void uhci_fill_td(struct uhci_td *td, u32 status, u32 token, u32 buffer) { @@ -82,7 +84,8 @@ static inline void uhci_fill_td(struct uhci_td *td, u32 status, /* * We insert Isochronous URBs directly into the frame list at the beginning */ -static void uhci_insert_td_frame_list(struct uhci_hcd *uhci, struct uhci_td *td, unsigned framenum) +static inline void uhci_insert_td_in_frame_list(struct uhci_hcd *uhci, + struct uhci_td *td, unsigned framenum) { framenum &= (UHCI_NUMFRAMES - 1); @@ -108,7 +111,7 @@ static void uhci_insert_td_frame_list(struct uhci_hcd *uhci, struct uhci_td *td, } } -static inline void uhci_remove_td_frame_list(struct uhci_hcd *uhci, +static inline void uhci_remove_td_from_frame_list(struct uhci_hcd *uhci, struct uhci_td *td) { /* If it's not inserted, don't remove it */ @@ -139,48 +142,68 @@ static inline void uhci_remove_td_frame_list(struct uhci_hcd *uhci, td->frame = -1; } -static void unlink_isochronous_tds(struct uhci_hcd *uhci, struct urb *urb) +/* + * Remove all the TDs for an Isochronous URB from the frame list + */ +static void uhci_unlink_isochronous_tds(struct uhci_hcd *uhci, struct urb *urb) { struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv; struct uhci_td *td; list_for_each_entry(td, &urbp->td_list, list) - uhci_remove_td_frame_list(uhci, td); + uhci_remove_td_from_frame_list(uhci, td); wmb(); } /* - * Inserts a td list into qh. + * Remove an URB's TDs from the hardware schedule */ -static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, __le32 breadth) +static void uhci_remove_tds_from_schedule(struct uhci_hcd *uhci, + struct urb *urb, int status) { - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct uhci_td *td; - __le32 *plink; + struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv; - /* Ordering isn't important here yet since the QH hasn't been */ - /* inserted into the schedule yet */ - plink = &qh->element; - list_for_each_entry(td, &urbp->td_list, list) { - *plink = cpu_to_le32(td->dma_handle) | breadth; - plink = &td->link; + /* Isochronous TDs get unlinked directly from the frame list */ + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + uhci_unlink_isochronous_tds(uhci, urb); + return; } - *plink = UHCI_PTR_TERM; -} -static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td) -{ - if (!list_empty(&td->list)) - dev_warn(uhci_dev(uhci), "td %p still in list!\n", td); - if (!list_empty(&td->remove_list)) - dev_warn(uhci_dev(uhci), "td %p still in remove_list!\n", td); - if (!list_empty(&td->fl_list)) - dev_warn(uhci_dev(uhci), "td %p still in fl_list!\n", td); + /* If the URB isn't first on its queue, adjust the link pointer + * of the last TD in the previous URB. */ + if (urbp->node.prev != &urbp->qh->queue) { + struct urb_priv *purbp; + struct uhci_td *ptd, *ltd; + + if (status == -EINPROGRESS) + status = 0; + purbp = list_entry(urbp->node.prev, struct urb_priv, node); + ptd = list_entry(purbp->td_list.prev, struct uhci_td, + list); + ltd = list_entry(urbp->td_list.prev, struct uhci_td, + list); + ptd->link = ltd->link; + } - dma_pool_free(uhci->td_pool, td, td->dma_handle); + /* If the URB completed with an error, then the QH element certainly + * points to one of the URB's TDs. If it completed normally then + * the QH element has certainly moved on to the next URB. And if + * the URB is still in progress then it must have been dequeued. + * The QH element either hasn't reached it yet or is somewhere in + * the middle. If the URB wasn't first we can assume that it + * hasn't started yet (see above): Otherwise all the preceding URBs + * would have completed and been removed from the queue, so this one + * _would_ be first. + * + * If the QH element is inside this URB, clear it. It will be + * set properly when the QH is activated. + */ + if (status < 0) + urbp->qh->element = UHCI_PTR_TERM; } -static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci) +static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, + struct usb_device *udev, struct usb_host_endpoint *hep) { dma_addr_t dma_handle; struct uhci_qh *qh; @@ -194,256 +217,120 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci) qh->element = UHCI_PTR_TERM; qh->link = UHCI_PTR_TERM; - qh->urbp = NULL; + INIT_LIST_HEAD(&qh->queue); + INIT_LIST_HEAD(&qh->node); - INIT_LIST_HEAD(&qh->list); - INIT_LIST_HEAD(&qh->remove_list); + if (udev) { /* Normal QH */ + qh->state = QH_STATE_IDLE; + qh->hep = hep; + qh->udev = udev; + hep->hcpriv = qh; + usb_get_dev(udev); + } else { /* Skeleton QH */ + qh->state = QH_STATE_ACTIVE; + qh->udev = NULL; + } return qh; } static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) { - if (!list_empty(&qh->list)) + WARN_ON(qh->state != QH_STATE_IDLE && qh->udev); + if (!list_empty(&qh->queue)) dev_warn(uhci_dev(uhci), "qh %p list not empty!\n", qh); - if (!list_empty(&qh->remove_list)) - dev_warn(uhci_dev(uhci), "qh %p still in remove_list!\n", qh); + list_del(&qh->node); + if (qh->udev) { + qh->hep->hcpriv = NULL; + usb_put_dev(qh->udev); + } dma_pool_free(uhci->qh_pool, qh, qh->dma_handle); } /* - * Append this urb's qh after the last qh in skelqh->list - * - * Note that urb_priv.queue_list doesn't have a separate queue head; - * it's a ring with every element "live". + * Put a QH on the schedule in both hardware and software */ -static void uhci_insert_qh(struct uhci_hcd *uhci, struct uhci_qh *skelqh, struct urb *urb) +static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) { - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct urb_priv *turbp; - struct uhci_qh *lqh; + struct uhci_qh *pqh; - /* Grab the last QH */ - lqh = list_entry(skelqh->list.prev, struct uhci_qh, list); + WARN_ON(list_empty(&qh->queue)); - /* Point to the next skelqh */ - urbp->qh->link = lqh->link; - wmb(); /* Ordering is important */ + /* Set the element pointer if it isn't set already. + * This isn't needed for Isochronous queues, but it doesn't hurt. */ + if (qh_element(qh) == UHCI_PTR_TERM) { + struct urb_priv *urbp = list_entry(qh->queue.next, + struct urb_priv, node); + struct uhci_td *td = list_entry(urbp->td_list.next, + struct uhci_td, list); - /* - * Patch QHs for previous endpoint's queued URBs? HC goes - * here next, not to the next skelqh it now points to. - * - * lqh --> td ... --> qh ... --> td --> qh ... --> td - * | | | - * v v v - * +<----------------+-----------------+ - * v - * newqh --> td ... --> td - * | - * v - * ... - * - * The HC could see (and use!) any of these as we write them. - */ - lqh->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH; - if (lqh->urbp) { - list_for_each_entry(turbp, &lqh->urbp->queue_list, queue_list) - turbp->qh->link = lqh->link; + qh->element = cpu_to_le32(td->dma_handle); } - list_add_tail(&urbp->qh->list, &skelqh->list); + if (qh->state == QH_STATE_ACTIVE) + return; + qh->state = QH_STATE_ACTIVE; + + /* Move the QH from its old list to the end of the appropriate + * skeleton's list */ + list_move_tail(&qh->node, &qh->skel->node); + + /* Link it into the schedule */ + pqh = list_entry(qh->node.prev, struct uhci_qh, node); + qh->link = pqh->link; + wmb(); + pqh->link = UHCI_PTR_QH | cpu_to_le32(qh->dma_handle); } /* - * Start removal of QH from schedule; it finishes next frame. - * TDs should be unlinked before this is called. + * Take a QH off the hardware schedule */ -static void uhci_remove_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) +static void uhci_unlink_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) { struct uhci_qh *pqh; - __le32 newlink; - if (!qh) + if (qh->state == QH_STATE_UNLINKING) return; + WARN_ON(qh->state != QH_STATE_ACTIVE || !qh->udev); + qh->state = QH_STATE_UNLINKING; - /* - * Only go through the hoops if it's actually linked in - */ - if (!list_empty(&qh->list)) { - - /* If our queue is nonempty, make the next URB the head */ - if (!list_empty(&qh->urbp->queue_list)) { - struct urb_priv *nurbp; - - nurbp = list_entry(qh->urbp->queue_list.next, - struct urb_priv, queue_list); - nurbp->queued = 0; - list_add(&nurbp->qh->list, &qh->list); - newlink = cpu_to_le32(nurbp->qh->dma_handle) | UHCI_PTR_QH; - } else - newlink = qh->link; - - /* Fix up the previous QH's queue to link to either - * the new head of this queue or the start of the - * next endpoint's queue. */ - pqh = list_entry(qh->list.prev, struct uhci_qh, list); - pqh->link = newlink; - if (pqh->urbp) { - struct urb_priv *turbp; - - list_for_each_entry(turbp, &pqh->urbp->queue_list, - queue_list) - turbp->qh->link = newlink; - } - wmb(); - - /* Leave qh->link in case the HC is on the QH now, it will */ - /* continue the rest of the schedule */ - qh->element = UHCI_PTR_TERM; - - list_del_init(&qh->list); - } - - list_del_init(&qh->urbp->queue_list); - qh->urbp = NULL; + /* Unlink the QH from the schedule and record when we did it */ + pqh = list_entry(qh->node.prev, struct uhci_qh, node); + pqh->link = qh->link; + mb(); uhci_get_current_frame_number(uhci); - if (uhci->frame_number + uhci->is_stopped != uhci->qh_remove_age) { - uhci_free_pending_qhs(uhci); - uhci->qh_remove_age = uhci->frame_number; - } + qh->unlink_frame = uhci->frame_number; - /* Check to see if the remove list is empty. Set the IOC bit */ - /* to force an interrupt so we can remove the QH */ - if (list_empty(&uhci->qh_remove_list)) + /* Force an interrupt so we know when the QH is fully unlinked */ + if (list_empty(&uhci->skel_unlink_qh->node)) uhci_set_next_interrupt(uhci); - list_add(&qh->remove_list, &uhci->qh_remove_list); + /* Move the QH from its old list to the end of the unlinking list */ + list_move_tail(&qh->node, &uhci->skel_unlink_qh->node); } -static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle) -{ - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct uhci_td *td; - - list_for_each_entry(td, &urbp->td_list, list) { - if (toggle) - td->token |= cpu_to_le32(TD_TOKEN_TOGGLE); - else - td->token &= ~cpu_to_le32(TD_TOKEN_TOGGLE); - - toggle ^= 1; - } - - return toggle; -} - -/* This function will append one URB's QH to another URB's QH. This is for */ -/* queuing interrupt, control or bulk transfers */ -static void uhci_append_queued_urb(struct uhci_hcd *uhci, struct urb *eurb, struct urb *urb) -{ - struct urb_priv *eurbp, *urbp, *furbp, *lurbp; - struct uhci_td *lltd; - - eurbp = eurb->hcpriv; - urbp = urb->hcpriv; - - /* Find the first URB in the queue */ - furbp = eurbp; - if (eurbp->queued) { - list_for_each_entry(furbp, &eurbp->queue_list, queue_list) - if (!furbp->queued) - break; - } - - lurbp = list_entry(furbp->queue_list.prev, struct urb_priv, queue_list); - - lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list); - - /* Control transfers always start with toggle 0 */ - if (!usb_pipecontrol(urb->pipe)) - usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe), - uhci_fixup_toggle(urb, - uhci_toggle(td_token(lltd)) ^ 1)); - - /* All qhs in the queue need to link to the next queue */ - urbp->qh->link = eurbp->qh->link; - - wmb(); /* Make sure we flush everything */ - - lltd->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH; - - list_add_tail(&urbp->queue_list, &furbp->queue_list); - - urbp->queued = 1; -} - -static void uhci_delete_queued_urb(struct uhci_hcd *uhci, struct urb *urb) +/* + * When we and the controller are through with a QH, it becomes IDLE. + * This happens when a QH has been off the schedule (on the unlinking + * list) for more than one frame, or when an error occurs while adding + * the first URB onto a new QH. + */ +static void uhci_make_qh_idle(struct uhci_hcd *uhci, struct uhci_qh *qh) { - struct urb_priv *urbp, *nurbp, *purbp, *turbp; - struct uhci_td *pltd; - unsigned int toggle; - - urbp = urb->hcpriv; - - if (list_empty(&urbp->queue_list)) - return; - - nurbp = list_entry(urbp->queue_list.next, struct urb_priv, queue_list); - - /* - * Fix up the toggle for the following URBs in the queue. - * Only needed for bulk and interrupt: control and isochronous - * endpoints don't propagate toggles between messages. - */ - if (usb_pipebulk(urb->pipe) || usb_pipeint(urb->pipe)) { - if (!urbp->queued) - /* We just set the toggle in uhci_unlink_generic */ - toggle = usb_gettoggle(urb->dev, - usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe)); - else { - /* If we're in the middle of the queue, grab the */ - /* toggle from the TD previous to us */ - purbp = list_entry(urbp->queue_list.prev, - struct urb_priv, queue_list); - pltd = list_entry(purbp->td_list.prev, - struct uhci_td, list); - toggle = uhci_toggle(td_token(pltd)) ^ 1; - } - - list_for_each_entry(turbp, &urbp->queue_list, queue_list) { - if (!turbp->queued) - break; - toggle = uhci_fixup_toggle(turbp->urb, toggle); - } + WARN_ON(qh->state == QH_STATE_ACTIVE); - usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe), toggle); - } - - if (urbp->queued) { - /* We're somewhere in the middle (or end). The case where - * we're at the head is handled in uhci_remove_qh(). */ - purbp = list_entry(urbp->queue_list.prev, struct urb_priv, - queue_list); - - pltd = list_entry(purbp->td_list.prev, struct uhci_td, list); - if (nurbp->queued) - pltd->link = cpu_to_le32(nurbp->qh->dma_handle) | UHCI_PTR_QH; - else - /* The next URB happens to be the beginning, so */ - /* we're the last, end the chain */ - pltd->link = UHCI_PTR_TERM; - } + list_move(&qh->node, &uhci->idle_qh_list); + qh->state = QH_STATE_IDLE; - /* urbp->queue_list is handled in uhci_remove_qh() */ + /* If anyone is waiting for a QH to become idle, wake them up */ + if (uhci->num_waiting) + wake_up_all(&uhci->waitqh); } -static struct urb_priv *uhci_alloc_urb_priv(struct uhci_hcd *uhci, struct urb *urb) +static inline struct urb_priv *uhci_alloc_urb_priv(struct uhci_hcd *uhci, + struct urb *urb) { struct urb_priv *urbp; @@ -453,17 +340,14 @@ static struct urb_priv *uhci_alloc_urb_priv(struct uhci_hcd *uhci, struct urb *u memset((void *)urbp, 0, sizeof(*urbp)); - urbp->fsbrtime = jiffies; urbp->urb = urb; + urb->hcpriv = urbp; + urbp->fsbrtime = jiffies; + INIT_LIST_HEAD(&urbp->node); INIT_LIST_HEAD(&urbp->td_list); - INIT_LIST_HEAD(&urbp->queue_list); INIT_LIST_HEAD(&urbp->urb_list); - list_add_tail(&urbp->urb_list, &uhci->urb_list); - - urb->hcpriv = urbp; - return urbp; } @@ -482,18 +366,17 @@ static void uhci_remove_td_from_urb(struct uhci_td *td) list_del_init(&td->list); } -static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb) +static void uhci_free_urb_priv(struct uhci_hcd *uhci, + struct urb_priv *urbp) { struct uhci_td *td, *tmp; - struct urb_priv *urbp; - - urbp = (struct urb_priv *)urb->hcpriv; - if (!urbp) - return; if (!list_empty(&urbp->urb_list)) - dev_warn(uhci_dev(uhci), "urb %p still on uhci->urb_list " - "or uhci->remove_list!\n", urb); + dev_warn(uhci_dev(uhci), "urb %p still on uhci->urb_list!\n", + urbp->urb); + if (!list_empty(&urbp->node)) + dev_warn(uhci_dev(uhci), "urb %p still on QH's list!\n", + urbp->urb); uhci_get_current_frame_number(uhci); if (uhci->frame_number + uhci->is_stopped != uhci->td_remove_age) { @@ -502,7 +385,7 @@ static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb) } /* Check to see if the remove list is empty. Set the IOC bit */ - /* to force an interrupt so we can remove the TDs*/ + /* to force an interrupt so we can remove the TDs. */ if (list_empty(&uhci->td_remove_list)) uhci_set_next_interrupt(uhci); @@ -511,7 +394,7 @@ static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb) list_add(&td->remove_list, &uhci->td_remove_list); } - urb->hcpriv = NULL; + urbp->urb->hcpriv = NULL; kmem_cache_free(uhci_up_cachep, urbp); } @@ -567,18 +450,83 @@ static int uhci_map_status(int status, int dir_out) return 0; } +/* + * Fix up the data toggles for URBs in a queue, when one of them + * terminates early (short transfer, error, or dequeued). + */ +static void uhci_fixup_toggles(struct urb *urb) +{ + struct list_head *head; + struct uhci_td *td; + struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv; + int prevactive = 0; + unsigned int toggle = 0; + struct urb_priv *turbp, *list_end; + + /* + * We need to find out what the last successful toggle was so + * we can update the data toggles for the following transfers. + * + * There are 2 ways the last successful completed TD is found: + * + * 1) The TD is NOT active and the actual length < expected length + * 2) The TD is NOT active and it's the last TD in the chain + * + * and a third way the first uncompleted TD is found: + * + * 3) The TD is active and the previous TD is NOT active + */ + head = &urbp->td_list; + list_for_each_entry(td, head, list) { + unsigned int ctrlstat = td_status(td); + + if (!(ctrlstat & TD_CTRL_ACTIVE) && + (uhci_actual_length(ctrlstat) < + uhci_expected_length(td_token(td)) || + td->list.next == head)) + toggle = uhci_toggle(td_token(td)) ^ 1; + else if ((ctrlstat & TD_CTRL_ACTIVE) && !prevactive) + toggle = uhci_toggle(td_token(td)); + + prevactive = ctrlstat & TD_CTRL_ACTIVE; + } + + /* + * Fix up the toggle for the following URBs in the queue. + * + * We can stop as soon as we find an URB with toggles set correctly, + * because then all the following URBs will be correct also. + */ + list_end = list_entry(&urbp->qh->queue, struct urb_priv, node); + turbp = urbp; + while ((turbp = list_entry(turbp->node.next, struct urb_priv, node)) + != list_end) { + td = list_entry(turbp->td_list.next, struct uhci_td, list); + if (uhci_toggle(td_token(td)) == toggle) + return; + + list_for_each_entry(td, &turbp->td_list, list) { + td->token ^= __constant_cpu_to_le32(TD_TOKEN_TOGGLE); + toggle ^= 1; + } + } + + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe), toggle); +} + /* * Control transfers */ -static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb) +static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, + struct uhci_qh *qh) { - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; struct uhci_td *td; - struct uhci_qh *qh, *skelqh; unsigned long destination, status; - int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); + int maxsze = le16_to_cpu(qh->hep->desc.wMaxPacketSize); int len = urb->transfer_buffer_length; dma_addr_t data = urb->transfer_dma; + __le32 *plink; /* The "pipe" thing contains the destination in bits 8--18 */ destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; @@ -597,7 +545,8 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status, destination | uhci_explen(8), - urb->setup_dma); + urb->setup_dma); + plink = &td->link; /* * If direction is "send", change the packet ID from SETUP (0x2D) @@ -615,21 +564,20 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur * Build the DATA TDs */ while (len > 0) { - int pktsze = len; - - if (pktsze > maxsze) - pktsze = maxsze; + int pktsze = min(len, maxsze); td = uhci_alloc_td(uhci); if (!td) return -ENOMEM; + *plink = cpu_to_le32(td->dma_handle); /* Alternate Data0/1 (start with Data1) */ destination ^= TD_TOKEN_TOGGLE; uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status, destination | uhci_explen(pktsze), - data); + data); + plink = &td->link; data += pktsze; len -= pktsze; @@ -641,6 +589,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur td = uhci_alloc_td(uhci); if (!td) return -ENOMEM; + *plink = cpu_to_le32(td->dma_handle); /* * It's IN if the pipe is an output pipe or we're not expecting @@ -658,16 +607,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status | TD_CTRL_IOC, - destination | uhci_explen(0), 0); - - qh = uhci_alloc_qh(uhci); - if (!qh) - return -ENOMEM; - - urbp->qh = qh; - qh->urbp = urbp; - - uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH); + destination | uhci_explen(0), 0); /* Low-speed transfers get a different queue, and won't hog the bus. * Also, some devices enumerate better without FSBR; the easiest way @@ -675,18 +615,13 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur * isn't in the CONFIGURED state. */ if (urb->dev->speed == USB_SPEED_LOW || urb->dev->state != USB_STATE_CONFIGURED) - skelqh = uhci->skel_ls_control_qh; + qh->skel = uhci->skel_ls_control_qh; else { - skelqh = uhci->skel_fs_control_qh; + qh->skel = uhci->skel_fs_control_qh; uhci_inc_fsbr(uhci, urb); } - if (eurb) - uhci_append_queued_urb(uhci, eurb, urb); - else - uhci_insert_qh(uhci, skelqh, urb); - - return -EINPROGRESS; + return 0; } /* @@ -703,7 +638,7 @@ static int usb_control_retrigger_status(struct uhci_hcd *uhci, struct urb *urb) struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; struct uhci_td *td; - urbp->short_control_packet = 1; + urbp->short_transfer = 1; td = list_entry(urbp->td_list.prev, struct uhci_td, list); urbp->qh->element = cpu_to_le32(td->dma_handle); @@ -720,16 +655,14 @@ static int uhci_result_control(struct uhci_hcd *uhci, struct urb *urb) unsigned int status; int ret = 0; - if (list_empty(&urbp->td_list)) - return -EINVAL; - head = &urbp->td_list; - - if (urbp->short_control_packet) { + if (urbp->short_transfer) { tmp = head->prev; goto status_stage; } + urb->actual_length = 0; + tmp = head->next; td = list_entry(tmp, struct uhci_td, list); @@ -742,8 +675,6 @@ static int uhci_result_control(struct uhci_hcd *uhci, struct urb *urb) if (status) goto td_error; - urb->actual_length = 0; - /* The rest of the TDs (but the last) are data */ tmp = tmp->next; while (tmp != head && tmp->next != head) { @@ -770,10 +701,7 @@ static int uhci_result_control(struct uhci_hcd *uhci, struct urb *urb) goto err; } - if (uhci_packetid(td_token(td)) == USB_PID_IN) - return usb_control_retrigger_status(uhci, urb); - else - return 0; + return usb_control_retrigger_status(uhci, urb); } } @@ -825,15 +753,15 @@ err: /* * Common submit for bulk and interrupt */ -static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb, struct uhci_qh *skelqh) +static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, + struct uhci_qh *qh) { struct uhci_td *td; - struct uhci_qh *qh; unsigned long destination, status; - int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); + int maxsze = le16_to_cpu(qh->hep->desc.wMaxPacketSize); int len = urb->transfer_buffer_length; - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; dma_addr_t data = urb->transfer_dma; + __le32 *plink, fake_link; if (len < 0) return -EINVAL; @@ -841,7 +769,8 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb /* The "pipe" thing contains the destination in bits 8--18 */ destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); - status = uhci_maxerr(3) | TD_CTRL_ACTIVE; + /* 3 errors */ + status = TD_CTRL_ACTIVE | uhci_maxerr(3); if (urb->dev->speed == USB_SPEED_LOW) status |= TD_CTRL_LS; if (usb_pipein(urb->pipe)) @@ -850,10 +779,11 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb /* * Build the DATA TDs */ + plink = &fake_link; do { /* Allow zero length packets */ int pktsze = maxsze; - if (pktsze >= len) { + if (len <= pktsze) { /* The last packet */ pktsze = len; if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) status &= ~TD_CTRL_SPD; @@ -862,12 +792,15 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb td = uhci_alloc_td(uhci); if (!td) return -ENOMEM; + *plink = cpu_to_le32(td->dma_handle); uhci_add_td_to_urb(urb, td); - uhci_fill_td(td, status, destination | uhci_explen(pktsze) | + uhci_fill_td(td, status, + destination | uhci_explen(pktsze) | (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT), data); + plink = &td->link; data += pktsze; len -= maxsze; @@ -883,11 +816,13 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb * however, if transfer_length == 0, the zero packet was already * prepared above. */ - if (usb_pipeout(urb->pipe) && (urb->transfer_flags & URB_ZERO_PACKET) && - !len && urb->transfer_buffer_length) { + if ((urb->transfer_flags & URB_ZERO_PACKET) && + usb_pipeout(urb->pipe) && len == 0 && + urb->transfer_buffer_length > 0) { td = uhci_alloc_td(uhci); if (!td) return -ENOMEM; + *plink = cpu_to_le32(td->dma_handle); uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status, destination | uhci_explen(0) | @@ -905,24 +840,9 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb * fast side but not enough to justify delaying an interrupt * more than 2 or 3 URBs, so we will ignore the URB_NO_INTERRUPT * flag setting. */ - td->status |= cpu_to_le32(TD_CTRL_IOC); - - qh = uhci_alloc_qh(uhci); - if (!qh) - return -ENOMEM; - - urbp->qh = qh; - qh->urbp = urbp; + td->status |= __constant_cpu_to_le32(TD_CTRL_IOC); - /* Always breadth first */ - uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH); - - if (eurb) - uhci_append_queued_urb(uhci, eurb, urb); - else - uhci_insert_qh(uhci, skelqh, urb); - - return -EINPROGRESS; + return 0; } /* @@ -954,8 +874,24 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb) if (urb->transfer_flags & URB_SHORT_NOT_OK) { ret = -EREMOTEIO; goto err; - } else - return 0; + } + + /* + * This URB stopped short of its end. We have to + * fix up the toggles of the following URBs on the + * queue and restart the queue. + * + * Do this only the first time we encounter the + * short URB. + */ + if (!urbp->short_transfer) { + urbp->short_transfer = 1; + uhci_fixup_toggles(urb); + td = list_entry(urbp->td_list.prev, + struct uhci_td, list); + urbp->qh->element = td->link; + } + break; } } @@ -988,7 +924,8 @@ err: return ret; } -static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb) +static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, + struct uhci_qh *qh) { int ret; @@ -996,21 +933,22 @@ static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, struc if (urb->dev->speed == USB_SPEED_LOW) return -EINVAL; - ret = uhci_submit_common(uhci, urb, eurb, uhci->skel_bulk_qh); - if (ret == -EINPROGRESS) + qh->skel = uhci->skel_bulk_qh; + ret = uhci_submit_common(uhci, urb, qh); + if (ret == 0) uhci_inc_fsbr(uhci, urb); - return ret; } -static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb) +static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb, + struct uhci_qh *qh) { - /* USB 1.1 interrupt transfers only involve one packet per interval; - * that's the uhci_submit_common() "breadth first" policy. Drivers - * can submit urbs of any length, but longer ones might need many - * intervals to complete. + /* USB 1.1 interrupt transfers only involve one packet per interval. + * Drivers can submit URBs of any length, but longer ones will need + * multiple intervals to complete. */ - return uhci_submit_common(uhci, urb, eurb, uhci->skelqh[__interval_to_skel(urb->interval)]); + qh->skel = uhci->skelqh[__interval_to_skel(urb->interval)]; + return uhci_submit_common(uhci, urb, qh); } /* @@ -1072,11 +1010,12 @@ static int isochronous_find_start(struct uhci_hcd *uhci, struct urb *urb) /* * Isochronous transfers */ -static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb) +static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb, + struct uhci_qh *qh) { - struct uhci_td *td; + struct uhci_td *td = NULL; /* Since urb->number_of_packets > 0 */ int i, ret, frame; - int status, destination; + unsigned long destination, status; struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv; status = TD_CTRL_ACTIVE | TD_CTRL_IOS; @@ -1092,20 +1031,25 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb) return -ENOMEM; uhci_add_td_to_urb(urb, td); - uhci_fill_td(td, status, destination | uhci_explen(urb->iso_frame_desc[i].length), - urb->transfer_dma + urb->iso_frame_desc[i].offset); - - if (i + 1 >= urb->number_of_packets) - td->status |= cpu_to_le32(TD_CTRL_IOC); + uhci_fill_td(td, status, destination | + uhci_explen(urb->iso_frame_desc[i].length), + urb->transfer_dma + + urb->iso_frame_desc[i].offset); } + /* Set the interrupt-on-completion flag on the last packet. */ + td->status |= __constant_cpu_to_le32(TD_CTRL_IOC); + + qh->skel = uhci->skel_iso_qh; + + /* Add the TDs to the frame list */ frame = urb->start_frame; list_for_each_entry(td, &urbp->td_list, list) { - uhci_insert_td_frame_list(uhci, td, frame); + uhci_insert_td_in_frame_list(uhci, td, frame); frame += urb->interval; } - return -EINPROGRESS; + return 0; } static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb) @@ -1139,80 +1083,67 @@ static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb) i++; } - unlink_isochronous_tds(uhci, urb); return ret; } -static struct urb *uhci_find_urb_ep(struct uhci_hcd *uhci, struct urb *urb) -{ - struct urb_priv *up; - - /* We don't match Isoc transfers since they are special */ - if (usb_pipeisoc(urb->pipe)) - return NULL; - - list_for_each_entry(up, &uhci->urb_list, urb_list) { - struct urb *u = up->urb; - - if (u->dev == urb->dev && u->status == -EINPROGRESS) { - /* For control, ignore the direction */ - if (usb_pipecontrol(urb->pipe) && - (u->pipe & ~USB_DIR_IN) == (urb->pipe & ~USB_DIR_IN)) - return u; - else if (u->pipe == urb->pipe) - return u; - } - } - - return NULL; -} - static int uhci_urb_enqueue(struct usb_hcd *hcd, - struct usb_host_endpoint *ep, + struct usb_host_endpoint *hep, struct urb *urb, gfp_t mem_flags) { int ret; struct uhci_hcd *uhci = hcd_to_uhci(hcd); unsigned long flags; - struct urb *eurb; + struct urb_priv *urbp; + struct uhci_qh *qh; int bustime; spin_lock_irqsave(&uhci->lock, flags); ret = urb->status; if (ret != -EINPROGRESS) /* URB already unlinked! */ - goto out; + goto done; - eurb = uhci_find_urb_ep(uhci, urb); + ret = -ENOMEM; + urbp = uhci_alloc_urb_priv(uhci, urb); + if (!urbp) + goto done; - if (!uhci_alloc_urb_priv(uhci, urb)) { - ret = -ENOMEM; - goto out; + if (hep->hcpriv) + qh = (struct uhci_qh *) hep->hcpriv; + else { + qh = uhci_alloc_qh(uhci, urb->dev, hep); + if (!qh) + goto err_no_qh; } + urbp->qh = qh; switch (usb_pipetype(urb->pipe)) { case PIPE_CONTROL: - ret = uhci_submit_control(uhci, urb, eurb); + ret = uhci_submit_control(uhci, urb, qh); + break; + case PIPE_BULK: + ret = uhci_submit_bulk(uhci, urb, qh); break; case PIPE_INTERRUPT: - if (!eurb) { + if (list_empty(&qh->queue)) { bustime = usb_check_bandwidth(urb->dev, urb); if (bustime < 0) ret = bustime; else { - ret = uhci_submit_interrupt(uhci, urb, eurb); - if (ret == -EINPROGRESS) + ret = uhci_submit_interrupt(uhci, urb, qh); + if (ret == 0) usb_claim_bandwidth(urb->dev, urb, bustime, 0); } } else { /* inherit from parent */ - urb->bandwidth = eurb->bandwidth; - ret = uhci_submit_interrupt(uhci, urb, eurb); + struct urb_priv *eurbp; + + eurbp = list_entry(qh->queue.prev, struct urb_priv, + node); + urb->bandwidth = eurbp->urb->bandwidth; + ret = uhci_submit_interrupt(uhci, urb, qh); } break; - case PIPE_BULK: - ret = uhci_submit_bulk(uhci, urb, eurb); - break; case PIPE_ISOCHRONOUS: bustime = usb_check_bandwidth(urb->dev, urb); if (bustime < 0) { @@ -1220,22 +1151,59 @@ static int uhci_urb_enqueue(struct usb_hcd *hcd, break; } - ret = uhci_submit_isochronous(uhci, urb); - if (ret == -EINPROGRESS) + ret = uhci_submit_isochronous(uhci, urb, qh); + if (ret == 0) usb_claim_bandwidth(urb->dev, urb, bustime, 1); break; } + if (ret != 0) + goto err_submit_failed; - if (ret != -EINPROGRESS) { - /* Submit failed, so delete it from the urb_list */ - struct urb_priv *urbp = urb->hcpriv; + /* Add this URB to the QH */ + urbp->qh = qh; + list_add_tail(&urbp->node, &qh->queue); + list_add_tail(&urbp->urb_list, &uhci->urb_list); - list_del_init(&urbp->urb_list); - uhci_destroy_urb_priv(uhci, urb); - } else - ret = 0; + /* If the new URB is the first and only one on this QH then either + * the QH is new and idle or else it's unlinked and waiting to + * become idle, so we can activate it right away. */ + if (qh->queue.next == &urbp->node) + uhci_activate_qh(uhci, qh); + + /* If the QH is already active, we have a race with the hardware. + * This won't get fixed until dummy TDs are added. */ + else if (qh->state == QH_STATE_ACTIVE) { + + /* If the URB isn't first on its queue, adjust the link pointer + * of the last TD in the previous URB. */ + if (urbp->node.prev != &urbp->qh->queue) { + struct urb_priv *purbp = list_entry(urbp->node.prev, + struct urb_priv, node); + struct uhci_td *ptd = list_entry(purbp->td_list.prev, + struct uhci_td, list); + struct uhci_td *td = list_entry(urbp->td_list.next, + struct uhci_td, list); + + ptd->link = cpu_to_le32(td->dma_handle); + + } + if (qh_element(qh) == UHCI_PTR_TERM) { + struct uhci_td *td = list_entry(urbp->td_list.next, + struct uhci_td, list); + + qh->element = cpu_to_le32(td->dma_handle); + } + } + goto done; + +err_submit_failed: + if (qh->state == QH_STATE_IDLE) + uhci_make_qh_idle(uhci, qh); /* Reclaim unused QH */ -out: +err_no_qh: + uhci_free_urb_priv(uhci, urbp); + +done: spin_unlock_irqrestore(&uhci->lock, flags); return ret; } @@ -1245,119 +1213,115 @@ out: */ static void uhci_transfer_result(struct uhci_hcd *uhci, struct urb *urb) { - int ret = -EINPROGRESS; - struct urb_priv *urbp; - - spin_lock(&urb->lock); - - urbp = (struct urb_priv *)urb->hcpriv; - - if (urb->status != -EINPROGRESS) /* URB already dequeued */ - goto out; + int status; + int okay_to_giveback = 0; + struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv; switch (usb_pipetype(urb->pipe)) { case PIPE_CONTROL: - ret = uhci_result_control(uhci, urb); - break; - case PIPE_BULK: - case PIPE_INTERRUPT: - ret = uhci_result_common(uhci, urb); + status = uhci_result_control(uhci, urb); break; case PIPE_ISOCHRONOUS: - ret = uhci_result_isochronous(uhci, urb); + status = uhci_result_isochronous(uhci, urb); + break; + default: /* PIPE_BULK or PIPE_INTERRUPT */ + status = uhci_result_common(uhci, urb); break; } - if (ret == -EINPROGRESS) - goto out; - urb->status = ret; + spin_lock(&urb->lock); + if (urb->status == -EINPROGRESS) { /* Not yet dequeued */ + if (status != -EINPROGRESS) { /* URB has completed */ + urb->status = status; + + /* If the URB got a real error (as opposed to + * simply being dequeued), we don't have to + * unlink the QH. Fix this later... */ + if (status < 0) + uhci_unlink_qh(uhci, urbp->qh); + else + okay_to_giveback = 1; + } + } else { /* Already dequeued */ + if (urbp->qh->state == QH_STATE_UNLINKING && + uhci->frame_number + uhci->is_stopped != + urbp->qh->unlink_frame) + okay_to_giveback = 1; + } + spin_unlock(&urb->lock); + if (!okay_to_giveback) + return; switch (usb_pipetype(urb->pipe)) { - case PIPE_CONTROL: - case PIPE_BULK: case PIPE_ISOCHRONOUS: /* Release bandwidth for Interrupt or Isoc. transfers */ if (urb->bandwidth) usb_release_bandwidth(urb->dev, urb, 1); - uhci_unlink_generic(uhci, urb); break; case PIPE_INTERRUPT: /* Release bandwidth for Interrupt or Isoc. transfers */ /* Make sure we don't release if we have a queued URB */ - if (list_empty(&urbp->queue_list) && urb->bandwidth) + if (list_empty(&urbp->qh->queue) && urb->bandwidth) usb_release_bandwidth(urb->dev, urb, 0); else /* bandwidth was passed on to queued URB, */ /* so don't let usb_unlink_urb() release it */ urb->bandwidth = 0; - uhci_unlink_generic(uhci, urb); + /* Falls through */ + case PIPE_BULK: + if (status < 0) + uhci_fixup_toggles(urb); + break; + default: /* PIPE_CONTROL */ break; - default: - dev_info(uhci_dev(uhci), "%s: unknown pipe type %d " - "for urb %p\n", - __FUNCTION__, usb_pipetype(urb->pipe), urb); } - /* Move it from uhci->urb_list to uhci->complete_list */ - uhci_moveto_complete(uhci, urbp); + /* Take the URB's TDs off the hardware schedule */ + uhci_remove_tds_from_schedule(uhci, urb, status); -out: - spin_unlock(&urb->lock); -} - -static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb) -{ - struct list_head *head; - struct uhci_td *td; - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - int prevactive = 0; + /* Take the URB off the QH's queue and see if the QH is now unused */ + list_del_init(&urbp->node); + if (list_empty(&urbp->qh->queue)) + uhci_unlink_qh(uhci, urbp->qh); uhci_dec_fsbr(uhci, urb); /* Safe since it checks */ - /* - * Now we need to find out what the last successful toggle was - * so we can update the local data toggle for the next transfer - * - * There are 2 ways the last successful completed TD is found: - * - * 1) The TD is NOT active and the actual length < expected length - * 2) The TD is NOT active and it's the last TD in the chain - * - * and a third way the first uncompleted TD is found: - * - * 3) The TD is active and the previous TD is NOT active - * - * Control and Isochronous ignore the toggle, so this is safe - * for all types - * - * FIXME: The toggle fixups won't be 100% reliable until we - * change over to using a single queue for each endpoint and - * stop the queue before unlinking. - */ - head = &urbp->td_list; - list_for_each_entry(td, head, list) { - unsigned int ctrlstat = td_status(td); + /* Queue it for giving back */ + list_move_tail(&urbp->urb_list, &uhci->complete_list); +} - if (!(ctrlstat & TD_CTRL_ACTIVE) && - (uhci_actual_length(ctrlstat) < - uhci_expected_length(td_token(td)) || - td->list.next == head)) - usb_settoggle(urb->dev, uhci_endpoint(td_token(td)), - uhci_packetout(td_token(td)), - uhci_toggle(td_token(td)) ^ 1); - else if ((ctrlstat & TD_CTRL_ACTIVE) && !prevactive) - usb_settoggle(urb->dev, uhci_endpoint(td_token(td)), - uhci_packetout(td_token(td)), - uhci_toggle(td_token(td))); +/* + * Check out the QHs waiting to be fully unlinked + */ +static void uhci_scan_unlinking_qhs(struct uhci_hcd *uhci) +{ + struct uhci_qh *qh, *tmp; - prevactive = ctrlstat & TD_CTRL_ACTIVE; - } + list_for_each_entry_safe(qh, tmp, &uhci->skel_unlink_qh->node, node) { - uhci_delete_queued_urb(uhci, urb); + /* If the queue is empty and the QH is fully unlinked then + * it can become IDLE. */ + if (list_empty(&qh->queue)) { + if (uhci->frame_number + uhci->is_stopped != + qh->unlink_frame) + uhci_make_qh_idle(uhci, qh); - /* The interrupt loop will reclaim the QHs */ - uhci_remove_qh(uhci, urbp->qh); - urbp->qh = NULL; + /* If none of the QH's URBs have been dequeued then the QH + * should be re-activated. */ + } else { + struct urb_priv *urbp; + int any_dequeued = 0; + + list_for_each_entry(urbp, &qh->queue, node) { + if (urbp->urb->status != -EINPROGRESS) { + any_dequeued = 1; + break; + } + } + if (!any_dequeued) + uhci_activate_qh(uhci, qh); + } + } } static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) @@ -1370,22 +1334,11 @@ static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) urbp = urb->hcpriv; if (!urbp) /* URB was never linked! */ goto done; - list_del_init(&urbp->urb_list); + /* Remove Isochronous TDs from the frame list ASAP */ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) - unlink_isochronous_tds(uhci, urb); - uhci_unlink_generic(uhci, urb); - - uhci_get_current_frame_number(uhci); - if (uhci->frame_number + uhci->is_stopped != uhci->urb_remove_age) { - uhci_remove_pending_urbps(uhci); - uhci->urb_remove_age = uhci->frame_number; - } - - /* If we're the first, set the next interrupt bit */ - if (list_empty(&uhci->urb_remove_list)) - uhci_set_next_interrupt(uhci); - list_add_tail(&urbp->urb_list, &uhci->urb_remove_list); + uhci_unlink_isochronous_tds(uhci, urb); + uhci_unlink_qh(uhci, urbp->qh); done: spin_unlock_irqrestore(&uhci->lock, flags); @@ -1426,17 +1379,6 @@ static int uhci_fsbr_timeout(struct uhci_hcd *uhci, struct urb *urb) return 0; } -static void uhci_free_pending_qhs(struct uhci_hcd *uhci) -{ - struct uhci_qh *qh, *tmp; - - list_for_each_entry_safe(qh, tmp, &uhci->qh_remove_list, remove_list) { - list_del_init(&qh->remove_list); - - uhci_free_qh(uhci, qh); - } -} - static void uhci_free_pending_tds(struct uhci_hcd *uhci) { struct uhci_td *td, *tmp; @@ -1455,7 +1397,7 @@ __acquires(uhci->lock) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); - uhci_destroy_urb_priv(uhci, urb); + uhci_free_urb_priv(uhci, (struct urb_priv *) (urb->hcpriv)); spin_unlock(&uhci->lock); usb_hcd_giveback_urb(hcd, urb, regs); @@ -1474,13 +1416,6 @@ static void uhci_finish_completion(struct uhci_hcd *uhci, struct pt_regs *regs) } } -static void uhci_remove_pending_urbps(struct uhci_hcd *uhci) -{ - - /* Splice the urb_remove_list onto the end of the complete_list */ - list_splice_init(&uhci->urb_remove_list, uhci->complete_list.prev); -} - /* Process events in the schedule, but only in one thread at a time */ static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs) { @@ -1498,12 +1433,8 @@ static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs) uhci_clear_next_interrupt(uhci); uhci_get_current_frame_number(uhci); - if (uhci->frame_number + uhci->is_stopped != uhci->qh_remove_age) - uhci_free_pending_qhs(uhci); if (uhci->frame_number + uhci->is_stopped != uhci->td_remove_age) uhci_free_pending_tds(uhci); - if (uhci->frame_number + uhci->is_stopped != uhci->urb_remove_age) - uhci_remove_pending_urbps(uhci); /* Walk the list of pending URBs to see which ones completed * (must be _safe because uhci_transfer_result() dequeues URBs) */ @@ -1516,25 +1447,21 @@ static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs) uhci_finish_completion(uhci, regs); /* If the controller is stopped, we can finish these off right now */ - if (uhci->is_stopped) { - uhci_free_pending_qhs(uhci); + if (uhci->is_stopped) uhci_free_pending_tds(uhci); - uhci_remove_pending_urbps(uhci); - } if (uhci->need_rescan) goto rescan; uhci->scan_in_progress = 0; - if (list_empty(&uhci->urb_remove_list) && - list_empty(&uhci->td_remove_list) && - list_empty(&uhci->qh_remove_list)) + /* Check out the QHs waiting for unlinking */ + uhci_scan_unlinking_qhs(uhci); + + if (list_empty(&uhci->td_remove_list) && + list_empty(&uhci->skel_unlink_qh->node)) uhci_clear_next_interrupt(uhci); else uhci_set_next_interrupt(uhci); - - /* Wake up anyone waiting for an URB to complete */ - wake_up_all(&uhci->waitqh); } static void check_fsbr(struct uhci_hcd *uhci) -- cgit v1.2.3-59-g8ed1b From af0bb5998abe8ed28ee354dd4c71689cacdc91e9 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sat, 17 Dec 2005 18:00:12 -0500 Subject: [PATCH] UHCI: use dummy TDs This patch (as624) fixes a hardware race in uhci-hcd by adding a dummy TD to the end of each endpoint's queue. Without the dummy the host controller will effectively turn off the queue when it reaches the end, which happens asynchronously. This leads to a potential problem when new transfer descriptors are added to the end of the queue; they may never get used. With a dummy TD present the controller never turns off the queue; instead it just stops at the dummy and leaves the queue on but inactive. When new TDs are added to the end of the queue, the first new one gets written over the dummy. Thus there's never any question about whether the queue is running or needs to be restarted. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-debug.c | 5 ++ drivers/usb/host/uhci-hcd.h | 1 + drivers/usb/host/uhci-q.c | 138 +++++++++++++++++++++++------------------- 3 files changed, 83 insertions(+), 61 deletions(-) diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c index 3faccbd68547..6814783adf91 100644 --- a/drivers/usb/host/uhci-debug.c +++ b/drivers/usb/host/uhci-debug.c @@ -189,6 +189,11 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space) space, "", nurbs); } + if (qh->udev) { + out += sprintf(out, "%*s Dummy TD\n", space, ""); + out += uhci_show_td(qh->dummy_td, out, len - (out - buf), 0); + } + return out - buf; } diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index 7a9481c09a05..c057956667b5 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -128,6 +128,7 @@ struct uhci_qh { struct usb_device *udev; struct list_head queue; /* Queue of urbps for this QH */ struct uhci_qh *skel; /* Skeleton for this QH */ + struct uhci_td *dummy_td; /* Dummy TD to end the queue */ unsigned int unlink_frame; /* When the QH was unlinked */ int state; /* QH_STATE_xxx; see above */ diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index b1b551a3d14e..c4194182dcc4 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -48,10 +48,6 @@ static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci) return NULL; td->dma_handle = dma_handle; - - td->link = UHCI_PTR_TERM; - td->buffer = 0; - td->frame = -1; INIT_LIST_HEAD(&td->list); @@ -221,6 +217,11 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, INIT_LIST_HEAD(&qh->node); if (udev) { /* Normal QH */ + qh->dummy_td = uhci_alloc_td(uhci); + if (!qh->dummy_td) { + dma_pool_free(uhci->qh_pool, qh, dma_handle); + return NULL; + } qh->state = QH_STATE_IDLE; qh->hep = hep; qh->udev = udev; @@ -244,6 +245,7 @@ static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) if (qh->udev) { qh->hep->hcpriv = NULL; usb_put_dev(qh->udev); + uhci_free_td(uhci, qh->dummy_td); } dma_pool_free(uhci->qh_pool, qh, qh->dma_handle); } @@ -531,22 +533,20 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, /* The "pipe" thing contains the destination in bits 8--18 */ destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; - /* 3 errors */ - status = TD_CTRL_ACTIVE | uhci_maxerr(3); + /* 3 errors, dummy TD remains inactive */ + status = uhci_maxerr(3); if (urb->dev->speed == USB_SPEED_LOW) status |= TD_CTRL_LS; /* * Build the TD for the control request setup packet */ - td = uhci_alloc_td(uhci); - if (!td) - return -ENOMEM; - + td = qh->dummy_td; uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status, destination | uhci_explen(8), urb->setup_dma); plink = &td->link; + status |= TD_CTRL_ACTIVE; /* * If direction is "send", change the packet ID from SETUP (0x2D) @@ -568,7 +568,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, td = uhci_alloc_td(uhci); if (!td) - return -ENOMEM; + goto nomem; *plink = cpu_to_le32(td->dma_handle); /* Alternate Data0/1 (start with Data1) */ @@ -588,7 +588,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, */ td = uhci_alloc_td(uhci); if (!td) - return -ENOMEM; + goto nomem; *plink = cpu_to_le32(td->dma_handle); /* @@ -608,6 +608,20 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status | TD_CTRL_IOC, destination | uhci_explen(0), 0); + plink = &td->link; + + /* + * Build the new dummy TD and activate the old one + */ + td = uhci_alloc_td(uhci); + if (!td) + goto nomem; + *plink = cpu_to_le32(td->dma_handle); + + uhci_fill_td(td, 0, USB_PID_OUT | uhci_explen(0), 0); + wmb(); + qh->dummy_td->status |= __constant_cpu_to_le32(TD_CTRL_ACTIVE); + qh->dummy_td = td; /* Low-speed transfers get a different queue, and won't hog the bus. * Also, some devices enumerate better without FSBR; the easiest way @@ -620,8 +634,12 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, qh->skel = uhci->skel_fs_control_qh; uhci_inc_fsbr(uhci, urb); } - return 0; + +nomem: + /* Remove the dummy TD from the td_list so it doesn't get freed */ + uhci_remove_td_from_urb(qh->dummy_td); + return -ENOMEM; } /* @@ -761,16 +779,19 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, int maxsze = le16_to_cpu(qh->hep->desc.wMaxPacketSize); int len = urb->transfer_buffer_length; dma_addr_t data = urb->transfer_dma; - __le32 *plink, fake_link; + __le32 *plink; + unsigned int toggle; if (len < 0) return -EINVAL; /* The "pipe" thing contains the destination in bits 8--18 */ destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); + toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe)); - /* 3 errors */ - status = TD_CTRL_ACTIVE | uhci_maxerr(3); + /* 3 errors, dummy TD remains inactive */ + status = uhci_maxerr(3); if (urb->dev->speed == USB_SPEED_LOW) status |= TD_CTRL_LS; if (usb_pipein(urb->pipe)) @@ -779,7 +800,8 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, /* * Build the DATA TDs */ - plink = &fake_link; + plink = NULL; + td = qh->dummy_td; do { /* Allow zero length packets */ int pktsze = maxsze; @@ -789,24 +811,23 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, status &= ~TD_CTRL_SPD; } - td = uhci_alloc_td(uhci); - if (!td) - return -ENOMEM; - *plink = cpu_to_le32(td->dma_handle); - + if (plink) { + td = uhci_alloc_td(uhci); + if (!td) + goto nomem; + *plink = cpu_to_le32(td->dma_handle); + } uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status, - destination | uhci_explen(pktsze) | - (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT), - data); + destination | uhci_explen(pktsze) | + (toggle << TD_TOKEN_TOGGLE_SHIFT), + data); plink = &td->link; + status |= TD_CTRL_ACTIVE; data += pktsze; len -= maxsze; - - usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe)); + toggle ^= 1; } while (len > 0); /* @@ -821,17 +842,17 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, urb->transfer_buffer_length > 0) { td = uhci_alloc_td(uhci); if (!td) - return -ENOMEM; + goto nomem; *plink = cpu_to_le32(td->dma_handle); uhci_add_td_to_urb(urb, td); - uhci_fill_td(td, status, destination | uhci_explen(0) | - (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT), - data); + uhci_fill_td(td, status, + destination | uhci_explen(0) | + (toggle << TD_TOKEN_TOGGLE_SHIFT), + data); + plink = &td->link; - usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe)); + toggle ^= 1; } /* Set the interrupt-on-completion flag on the last packet. @@ -842,7 +863,27 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, * flag setting. */ td->status |= __constant_cpu_to_le32(TD_CTRL_IOC); + /* + * Build the new dummy TD and activate the old one + */ + td = uhci_alloc_td(uhci); + if (!td) + goto nomem; + *plink = cpu_to_le32(td->dma_handle); + + uhci_fill_td(td, 0, USB_PID_OUT | uhci_explen(0), 0); + wmb(); + qh->dummy_td->status |= __constant_cpu_to_le32(TD_CTRL_ACTIVE); + qh->dummy_td = td; + + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe), toggle); return 0; + +nomem: + /* Remove the dummy TD from the td_list so it doesn't get freed */ + uhci_remove_td_from_urb(qh->dummy_td); + return -ENOMEM; } /* @@ -1169,31 +1210,6 @@ static int uhci_urb_enqueue(struct usb_hcd *hcd, * become idle, so we can activate it right away. */ if (qh->queue.next == &urbp->node) uhci_activate_qh(uhci, qh); - - /* If the QH is already active, we have a race with the hardware. - * This won't get fixed until dummy TDs are added. */ - else if (qh->state == QH_STATE_ACTIVE) { - - /* If the URB isn't first on its queue, adjust the link pointer - * of the last TD in the previous URB. */ - if (urbp->node.prev != &urbp->qh->queue) { - struct urb_priv *purbp = list_entry(urbp->node.prev, - struct urb_priv, node); - struct uhci_td *ptd = list_entry(purbp->td_list.prev, - struct uhci_td, list); - struct uhci_td *td = list_entry(urbp->td_list.next, - struct uhci_td, list); - - ptd->link = cpu_to_le32(td->dma_handle); - - } - if (qh_element(qh) == UHCI_PTR_TERM) { - struct uhci_td *td = list_entry(urbp->td_list.next, - struct uhci_td, list); - - qh->element = cpu_to_le32(td->dma_handle); - } - } goto done; err_submit_failed: -- cgit v1.2.3-59-g8ed1b From 0ed8fee1c1d38a62e981025ba40b5eba30c4ce2a Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sat, 17 Dec 2005 18:02:38 -0500 Subject: [PATCH] UHCI: remove main list of URBs As part of reorienting uhci-hcd away from URBs and toward endpoint queues, this patch (as625) eliminates the driver's main list of URBs. The list wsa used mainly in checking for URB completions; now the driver goes through the list of active endpoints and checks the members of the queues. As a side effect, I had to remove the code that looks for FSBR timeouts. For now, FSBR will remain on so long as any URBs on a full-speed control or bulk queue request it, even if the queue isn't advancing. A later patch can add more intelligent handling. This isn't a huge drawback; it's pretty rare for an URB to get stuck for more than a fraction of a second. (And it will help the people trying to use those insane HP USB devices.) Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-debug.c | 1 - drivers/usb/host/uhci-hcd.c | 2 - drivers/usb/host/uhci-hcd.h | 15 +- drivers/usb/host/uhci-q.c | 631 ++++++++++++++++++------------------------ 4 files changed, 279 insertions(+), 370 deletions(-) diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c index 6814783adf91..f2f5f8ce1715 100644 --- a/drivers/usb/host/uhci-debug.c +++ b/drivers/usb/host/uhci-debug.c @@ -114,7 +114,6 @@ static int uhci_show_urbp(struct urb_priv *urbp, char *buf, int len, int space) } out += sprintf(out, "%s", (urbp->fsbr ? " FSBR" : "")); - out += sprintf(out, "%s", (urbp->fsbr_timeout ? " FSBR_TO" : "")); if (urbp->urb->status != -EINPROGRESS) out += sprintf(out, " Status=%d", urbp->urb->status); diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 1ff4b8806372..9865f303d3fc 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -491,8 +491,6 @@ static int uhci_start(struct usb_hcd *hcd) spin_lock_init(&uhci->lock); INIT_LIST_HEAD(&uhci->td_remove_list); - INIT_LIST_HEAD(&uhci->urb_list); - INIT_LIST_HEAD(&uhci->complete_list); INIT_LIST_HEAD(&uhci->idle_qh_list); init_waitqueue_head(&uhci->waitqh); diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index c057956667b5..7e96bef2e88f 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -132,6 +132,10 @@ struct uhci_qh { unsigned int unlink_frame; /* When the QH was unlinked */ int state; /* QH_STATE_xxx; see above */ + + unsigned int initial_toggle:1; /* Endpoint's current toggle value */ + unsigned int needs_fixup:1; /* Must fix the TD toggle values */ + unsigned int is_stopped:1; /* Queue was stopped by an error */ } __attribute__((aligned(16))); /* @@ -384,6 +388,7 @@ struct uhci_hcd { struct uhci_td *term_td; /* Terminating TD, see UHCI bug */ struct uhci_qh *skelqh[UHCI_NUM_SKELQH]; /* Skeleton QHs */ + struct uhci_qh *next_qh; /* Next QH to scan */ spinlock_t lock; @@ -413,16 +418,10 @@ struct uhci_hcd { unsigned long resuming_ports; unsigned long ports_timeout; /* Time to stop signalling */ - /* Main list of URBs currently controlled by this HC */ - struct list_head urb_list; - /* List of TDs that are done, but waiting to be freed (race) */ struct list_head td_remove_list; unsigned int td_remove_age; /* Age in frames */ - /* List of URBs awaiting completion callback */ - struct list_head complete_list; - struct list_head idle_qh_list; /* Where the idle QHs live */ int rh_numports; /* Number of root-hub ports */ @@ -448,7 +447,6 @@ static inline struct usb_hcd *uhci_to_hcd(struct uhci_hcd *uhci) * Private per-URB data */ struct urb_priv { - struct list_head urb_list; struct list_head node; /* Node in the QH's urbp list */ struct urb *urb; @@ -456,10 +454,7 @@ struct urb_priv { struct uhci_qh *qh; /* QH for this URB */ struct list_head td_list; - unsigned long fsbrtime; /* In jiffies */ - unsigned fsbr : 1; /* URB turned on FSBR */ - unsigned fsbr_timeout : 1; /* URB timed out on FSBR */ unsigned short_transfer : 1; /* URB got a short transfer, no * need to rescan */ }; diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index c4194182dcc4..44bba9a6d196 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -151,53 +151,6 @@ static void uhci_unlink_isochronous_tds(struct uhci_hcd *uhci, struct urb *urb) wmb(); } -/* - * Remove an URB's TDs from the hardware schedule - */ -static void uhci_remove_tds_from_schedule(struct uhci_hcd *uhci, - struct urb *urb, int status) -{ - struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv; - - /* Isochronous TDs get unlinked directly from the frame list */ - if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { - uhci_unlink_isochronous_tds(uhci, urb); - return; - } - - /* If the URB isn't first on its queue, adjust the link pointer - * of the last TD in the previous URB. */ - if (urbp->node.prev != &urbp->qh->queue) { - struct urb_priv *purbp; - struct uhci_td *ptd, *ltd; - - if (status == -EINPROGRESS) - status = 0; - purbp = list_entry(urbp->node.prev, struct urb_priv, node); - ptd = list_entry(purbp->td_list.prev, struct uhci_td, - list); - ltd = list_entry(urbp->td_list.prev, struct uhci_td, - list); - ptd->link = ltd->link; - } - - /* If the URB completed with an error, then the QH element certainly - * points to one of the URB's TDs. If it completed normally then - * the QH element has certainly moved on to the next URB. And if - * the URB is still in progress then it must have been dequeued. - * The QH element either hasn't reached it yet or is somewhere in - * the middle. If the URB wasn't first we can assume that it - * hasn't started yet (see above): Otherwise all the preceding URBs - * would have completed and been removed from the queue, so this one - * _would_ be first. - * - * If the QH element is inside this URB, clear it. It will be - * set properly when the QH is activated. - */ - if (status < 0) - urbp->qh->element = UHCI_PTR_TERM; -} - static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, struct usb_device *udev, struct usb_host_endpoint *hep) { @@ -250,6 +203,90 @@ static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) dma_pool_free(uhci->qh_pool, qh, qh->dma_handle); } +/* + * When the currently executing URB is dequeued, save its current toggle value + */ +static void uhci_save_toggle(struct uhci_qh *qh, struct urb *urb) +{ + struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv; + struct uhci_td *td; + + /* If the QH element pointer is UHCI_PTR_TERM then then currently + * executing URB has already been unlinked, so this one isn't it. */ + if (qh_element(qh) == UHCI_PTR_TERM || + qh->queue.next != &urbp->node) + return; + qh->element = UHCI_PTR_TERM; + + /* Only bulk and interrupt pipes have to worry about toggles */ + if (!(usb_pipetype(urb->pipe) == PIPE_BULK || + usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) + return; + + /* Find the first active TD; that's the device's toggle state */ + list_for_each_entry(td, &urbp->td_list, list) { + if (td_status(td) & TD_CTRL_ACTIVE) { + qh->needs_fixup = 1; + qh->initial_toggle = uhci_toggle(td_token(td)); + return; + } + } + + WARN_ON(1); +} + +/* + * Fix up the data toggles for URBs in a queue, when one of them + * terminates early (short transfer, error, or dequeued). + */ +static void uhci_fixup_toggles(struct uhci_qh *qh, int skip_first) +{ + struct urb_priv *urbp = NULL; + struct uhci_td *td; + unsigned int toggle = qh->initial_toggle; + unsigned int pipe; + + /* Fixups for a short transfer start with the second URB in the + * queue (the short URB is the first). */ + if (skip_first) + urbp = list_entry(qh->queue.next, struct urb_priv, node); + + /* When starting with the first URB, if the QH element pointer is + * still valid then we know the URB's toggles are okay. */ + else if (qh_element(qh) != UHCI_PTR_TERM) + toggle = 2; + + /* Fix up the toggle for the URBs in the queue. Normally this + * loop won't run more than once: When an error or short transfer + * occurs, the queue usually gets emptied. */ + list_prepare_entry(urbp, &qh->queue, node); + list_for_each_entry_continue(urbp, &qh->queue, node) { + + /* If the first TD has the right toggle value, we don't + * need to change any toggles in this URB */ + td = list_entry(urbp->td_list.next, struct uhci_td, list); + if (toggle > 1 || uhci_toggle(td_token(td)) == toggle) { + td = list_entry(urbp->td_list.next, struct uhci_td, + list); + toggle = uhci_toggle(td_token(td)) ^ 1; + + /* Otherwise all the toggles in the URB have to be switched */ + } else { + list_for_each_entry(td, &urbp->td_list, list) { + td->token ^= __constant_cpu_to_le32( + TD_TOKEN_TOGGLE); + toggle ^= 1; + } + } + } + + wmb(); + pipe = list_entry(qh->queue.next, struct urb_priv, node)->urb->pipe; + usb_settoggle(qh->udev, usb_pipeendpoint(pipe), + usb_pipeout(pipe), toggle); + qh->needs_fixup = 0; +} + /* * Put a QH on the schedule in both hardware and software */ @@ -276,6 +313,9 @@ static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) /* Move the QH from its old list to the end of the appropriate * skeleton's list */ + if (qh == uhci->next_qh) + uhci->next_qh = list_entry(qh->node.next, struct uhci_qh, + node); list_move_tail(&qh->node, &qh->skel->node); /* Link it into the schedule */ @@ -310,6 +350,9 @@ static void uhci_unlink_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) uhci_set_next_interrupt(uhci); /* Move the QH from its old list to the end of the unlinking list */ + if (qh == uhci->next_qh) + uhci->next_qh = list_entry(qh->node.next, struct uhci_qh, + node); list_move_tail(&qh->node, &uhci->skel_unlink_qh->node); } @@ -323,6 +366,9 @@ static void uhci_make_qh_idle(struct uhci_hcd *uhci, struct uhci_qh *qh) { WARN_ON(qh->state == QH_STATE_ACTIVE); + if (qh == uhci->next_qh) + uhci->next_qh = list_entry(qh->node.next, struct uhci_qh, + node); list_move(&qh->node, &uhci->idle_qh_list); qh->state = QH_STATE_IDLE; @@ -344,11 +390,9 @@ static inline struct urb_priv *uhci_alloc_urb_priv(struct uhci_hcd *uhci, urbp->urb = urb; urb->hcpriv = urbp; - urbp->fsbrtime = jiffies; INIT_LIST_HEAD(&urbp->node); INIT_LIST_HEAD(&urbp->td_list); - INIT_LIST_HEAD(&urbp->urb_list); return urbp; } @@ -373,9 +417,6 @@ static void uhci_free_urb_priv(struct uhci_hcd *uhci, { struct uhci_td *td, *tmp; - if (!list_empty(&urbp->urb_list)) - dev_warn(uhci_dev(uhci), "urb %p still on uhci->urb_list!\n", - urbp->urb); if (!list_empty(&urbp->node)) dev_warn(uhci_dev(uhci), "urb %p still on QH's list!\n", urbp->urb); @@ -452,71 +493,6 @@ static int uhci_map_status(int status, int dir_out) return 0; } -/* - * Fix up the data toggles for URBs in a queue, when one of them - * terminates early (short transfer, error, or dequeued). - */ -static void uhci_fixup_toggles(struct urb *urb) -{ - struct list_head *head; - struct uhci_td *td; - struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv; - int prevactive = 0; - unsigned int toggle = 0; - struct urb_priv *turbp, *list_end; - - /* - * We need to find out what the last successful toggle was so - * we can update the data toggles for the following transfers. - * - * There are 2 ways the last successful completed TD is found: - * - * 1) The TD is NOT active and the actual length < expected length - * 2) The TD is NOT active and it's the last TD in the chain - * - * and a third way the first uncompleted TD is found: - * - * 3) The TD is active and the previous TD is NOT active - */ - head = &urbp->td_list; - list_for_each_entry(td, head, list) { - unsigned int ctrlstat = td_status(td); - - if (!(ctrlstat & TD_CTRL_ACTIVE) && - (uhci_actual_length(ctrlstat) < - uhci_expected_length(td_token(td)) || - td->list.next == head)) - toggle = uhci_toggle(td_token(td)) ^ 1; - else if ((ctrlstat & TD_CTRL_ACTIVE) && !prevactive) - toggle = uhci_toggle(td_token(td)); - - prevactive = ctrlstat & TD_CTRL_ACTIVE; - } - - /* - * Fix up the toggle for the following URBs in the queue. - * - * We can stop as soon as we find an URB with toggles set correctly, - * because then all the following URBs will be correct also. - */ - list_end = list_entry(&urbp->qh->queue, struct urb_priv, node); - turbp = urbp; - while ((turbp = list_entry(turbp->node.next, struct urb_priv, node)) - != list_end) { - td = list_entry(turbp->td_list.next, struct uhci_td, list); - if (uhci_toggle(td_token(td)) == toggle) - return; - - list_for_each_entry(td, &turbp->td_list, list) { - td->token ^= __constant_cpu_to_le32(TD_TOKEN_TOGGLE); - toggle ^= 1; - } - } - - usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe), toggle); -} - /* * Control transfers */ @@ -765,6 +741,9 @@ err: } } + /* Note that the queue has stopped */ + urbp->qh->element = UHCI_PTR_TERM; + urbp->qh->is_stopped = 1; return ret; } @@ -927,7 +906,10 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb) */ if (!urbp->short_transfer) { urbp->short_transfer = 1; - uhci_fixup_toggles(urb); + urbp->qh->initial_toggle = + uhci_toggle(td_token(td)) ^ 1; + uhci_fixup_toggles(urbp->qh, 1); + td = list_entry(urbp->td_list.prev, struct uhci_td, list); urbp->qh->element = td->link; @@ -962,6 +944,13 @@ err: } } #endif + + /* Note that the queue has stopped and save the next toggle value */ + urbp->qh->element = UHCI_PTR_TERM; + urbp->qh->is_stopped = 1; + urbp->qh->needs_fixup = 1; + urbp->qh->initial_toggle = uhci_toggle(td_token(td)) ^ + (ret == -EREMOTEIO); return ret; } @@ -995,76 +984,39 @@ static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb, /* * Isochronous transfers */ -static int isochronous_find_limits(struct uhci_hcd *uhci, struct urb *urb, unsigned int *start, unsigned int *end) -{ - struct urb *last_urb = NULL; - struct urb_priv *up; - int ret = 0; - - list_for_each_entry(up, &uhci->urb_list, urb_list) { - struct urb *u = up->urb; - - /* look for pending URBs with identical pipe handle */ - if ((urb->pipe == u->pipe) && (urb->dev == u->dev) && - (u->status == -EINPROGRESS) && (u != urb)) { - if (!last_urb) - *start = u->start_frame; - last_urb = u; - } - } - - if (last_urb) { - *end = (last_urb->start_frame + last_urb->number_of_packets * - last_urb->interval) & (UHCI_NUMFRAMES-1); - ret = 0; - } else - ret = -1; /* no previous urb found */ - - return ret; -} - -static int isochronous_find_start(struct uhci_hcd *uhci, struct urb *urb) +static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb, + struct uhci_qh *qh) { - int limits; - unsigned int start = 0, end = 0; + struct uhci_td *td = NULL; /* Since urb->number_of_packets > 0 */ + int i, frame; + unsigned long destination, status; + struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv; if (urb->number_of_packets > 900) /* 900? Why? */ return -EFBIG; - limits = isochronous_find_limits(uhci, urb, &start, &end); + status = TD_CTRL_ACTIVE | TD_CTRL_IOS; + destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); + /* Figure out the starting frame number */ if (urb->transfer_flags & URB_ISO_ASAP) { - if (limits) { + if (list_empty(&qh->queue)) { uhci_get_current_frame_number(uhci); - urb->start_frame = (uhci->frame_number + 10) - & (UHCI_NUMFRAMES - 1); - } else - urb->start_frame = end; + urb->start_frame = (uhci->frame_number + 10); + + } else { /* Go right after the last one */ + struct urb *last_urb; + + last_urb = list_entry(qh->queue.prev, + struct urb_priv, node)->urb; + urb->start_frame = (last_urb->start_frame + + last_urb->number_of_packets * + last_urb->interval); + } } else { - urb->start_frame &= (UHCI_NUMFRAMES - 1); /* FIXME: Sanity check */ } - - return 0; -} - -/* - * Isochronous transfers - */ -static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb, - struct uhci_qh *qh) -{ - struct uhci_td *td = NULL; /* Since urb->number_of_packets > 0 */ - int i, ret, frame; - unsigned long destination, status; - struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv; - - status = TD_CTRL_ACTIVE | TD_CTRL_IOS; - destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); - - ret = isochronous_find_start(uhci, urb); - if (ret) - return ret; + urb->start_frame &= (UHCI_NUMFRAMES - 1); for (i = 0; i < urb->number_of_packets; i++) { td = uhci_alloc_td(uhci); @@ -1203,7 +1155,6 @@ static int uhci_urb_enqueue(struct usb_hcd *hcd, /* Add this URB to the QH */ urbp->qh = qh; list_add_tail(&urbp->node, &qh->queue); - list_add_tail(&urbp->urb_list, &uhci->urb_list); /* If the new URB is the first and only one on this QH then either * the QH is new and idle or else it's unlinked and waiting to @@ -1224,49 +1175,66 @@ done: return ret; } +static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) +{ + struct uhci_hcd *uhci = hcd_to_uhci(hcd); + unsigned long flags; + struct urb_priv *urbp; + + spin_lock_irqsave(&uhci->lock, flags); + urbp = urb->hcpriv; + if (!urbp) /* URB was never linked! */ + goto done; + + /* Remove Isochronous TDs from the frame list ASAP */ + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) + uhci_unlink_isochronous_tds(uhci, urb); + uhci_unlink_qh(uhci, urbp->qh); + +done: + spin_unlock_irqrestore(&uhci->lock, flags); + return 0; +} + /* - * Return the result of a transfer + * Finish unlinking an URB and give it back */ -static void uhci_transfer_result(struct uhci_hcd *uhci, struct urb *urb) +static void uhci_giveback_urb(struct uhci_hcd *uhci, struct uhci_qh *qh, + struct urb *urb, struct pt_regs *regs) +__releases(uhci->lock) +__acquires(uhci->lock) { - int status; - int okay_to_giveback = 0; struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv; - switch (usb_pipetype(urb->pipe)) { - case PIPE_CONTROL: - status = uhci_result_control(uhci, urb); - break; - case PIPE_ISOCHRONOUS: - status = uhci_result_isochronous(uhci, urb); - break; - default: /* PIPE_BULK or PIPE_INTERRUPT */ - status = uhci_result_common(uhci, urb); - break; - } + /* Isochronous TDs get unlinked directly from the frame list */ + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) + uhci_unlink_isochronous_tds(uhci, urb); - spin_lock(&urb->lock); - if (urb->status == -EINPROGRESS) { /* Not yet dequeued */ - if (status != -EINPROGRESS) { /* URB has completed */ - urb->status = status; + /* If the URB isn't first on its queue, adjust the link pointer + * of the last TD in the previous URB. */ + else if (qh->queue.next != &urbp->node) { + struct urb_priv *purbp; + struct uhci_td *ptd, *ltd; - /* If the URB got a real error (as opposed to - * simply being dequeued), we don't have to - * unlink the QH. Fix this later... */ - if (status < 0) - uhci_unlink_qh(uhci, urbp->qh); - else - okay_to_giveback = 1; - } - } else { /* Already dequeued */ - if (urbp->qh->state == QH_STATE_UNLINKING && - uhci->frame_number + uhci->is_stopped != - urbp->qh->unlink_frame) - okay_to_giveback = 1; + purbp = list_entry(urbp->node.prev, struct urb_priv, node); + ptd = list_entry(purbp->td_list.prev, struct uhci_td, + list); + ltd = list_entry(urbp->td_list.prev, struct uhci_td, + list); + ptd->link = ltd->link; } - spin_unlock(&urb->lock); - if (!okay_to_giveback) - return; + + /* Take the URB off the QH's queue. If the queue is now empty, + * this is a perfect time for a toggle fixup. */ + list_del_init(&urbp->node); + if (list_empty(&qh->queue) && qh->needs_fixup) { + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe), qh->initial_toggle); + qh->needs_fixup = 0; + } + + uhci_dec_fsbr(uhci, urb); /* Safe since it checks */ + uhci_free_urb_priv(uhci, urbp); switch (usb_pipetype(urb->pipe)) { case PIPE_ISOCHRONOUS: @@ -1277,122 +1245,107 @@ static void uhci_transfer_result(struct uhci_hcd *uhci, struct urb *urb) case PIPE_INTERRUPT: /* Release bandwidth for Interrupt or Isoc. transfers */ /* Make sure we don't release if we have a queued URB */ - if (list_empty(&urbp->qh->queue) && urb->bandwidth) + if (list_empty(&qh->queue) && urb->bandwidth) usb_release_bandwidth(urb->dev, urb, 0); else /* bandwidth was passed on to queued URB, */ /* so don't let usb_unlink_urb() release it */ urb->bandwidth = 0; - /* Falls through */ - case PIPE_BULK: - if (status < 0) - uhci_fixup_toggles(urb); - break; - default: /* PIPE_CONTROL */ break; } - /* Take the URB's TDs off the hardware schedule */ - uhci_remove_tds_from_schedule(uhci, urb, status); - - /* Take the URB off the QH's queue and see if the QH is now unused */ - list_del_init(&urbp->node); - if (list_empty(&urbp->qh->queue)) - uhci_unlink_qh(uhci, urbp->qh); + spin_unlock(&uhci->lock); + usb_hcd_giveback_urb(uhci_to_hcd(uhci), urb, regs); + spin_lock(&uhci->lock); - uhci_dec_fsbr(uhci, urb); /* Safe since it checks */ + /* If the queue is now empty, we can unlink the QH and give up its + * reserved bandwidth. */ + if (list_empty(&qh->queue)) { + uhci_unlink_qh(uhci, qh); - /* Queue it for giving back */ - list_move_tail(&urbp->urb_list, &uhci->complete_list); + /* Bandwidth stuff not yet implemented */ + } } /* - * Check out the QHs waiting to be fully unlinked + * Scan the URBs in a QH's queue */ -static void uhci_scan_unlinking_qhs(struct uhci_hcd *uhci) -{ - struct uhci_qh *qh, *tmp; +#define QH_FINISHED_UNLINKING(qh) \ + (qh->state == QH_STATE_UNLINKING && \ + uhci->frame_number + uhci->is_stopped != qh->unlink_frame) - list_for_each_entry_safe(qh, tmp, &uhci->skel_unlink_qh->node, node) { - - /* If the queue is empty and the QH is fully unlinked then - * it can become IDLE. */ - if (list_empty(&qh->queue)) { - if (uhci->frame_number + uhci->is_stopped != - qh->unlink_frame) - uhci_make_qh_idle(uhci, qh); - - /* If none of the QH's URBs have been dequeued then the QH - * should be re-activated. */ - } else { - struct urb_priv *urbp; - int any_dequeued = 0; - - list_for_each_entry(urbp, &qh->queue, node) { - if (urbp->urb->status != -EINPROGRESS) { - any_dequeued = 1; - break; - } - } - if (!any_dequeued) - uhci_activate_qh(uhci, qh); - } - } -} - -static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) +static void uhci_scan_qh(struct uhci_hcd *uhci, struct uhci_qh *qh, + struct pt_regs *regs) { - struct uhci_hcd *uhci = hcd_to_uhci(hcd); - unsigned long flags; struct urb_priv *urbp; + struct urb *urb; + int status; - spin_lock_irqsave(&uhci->lock, flags); - urbp = urb->hcpriv; - if (!urbp) /* URB was never linked! */ - goto done; + while (!list_empty(&qh->queue)) { + urbp = list_entry(qh->queue.next, struct urb_priv, node); + urb = urbp->urb; - /* Remove Isochronous TDs from the frame list ASAP */ - if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) - uhci_unlink_isochronous_tds(uhci, urb); - uhci_unlink_qh(uhci, urbp->qh); + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + status = uhci_result_control(uhci, urb); + break; + case PIPE_ISOCHRONOUS: + status = uhci_result_isochronous(uhci, urb); + break; + default: /* PIPE_BULK or PIPE_INTERRUPT */ + status = uhci_result_common(uhci, urb); + break; + } + if (status == -EINPROGRESS) + break; -done: - spin_unlock_irqrestore(&uhci->lock, flags); - return 0; -} + spin_lock(&urb->lock); + if (urb->status == -EINPROGRESS) /* Not dequeued */ + urb->status = status; + else + status = -ECONNRESET; + spin_unlock(&urb->lock); -static int uhci_fsbr_timeout(struct uhci_hcd *uhci, struct urb *urb) -{ - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct list_head *head; - struct uhci_td *td; - int count = 0; + /* Dequeued but completed URBs can't be given back unless + * the QH is stopped or has finished unlinking. */ + if (status == -ECONNRESET && + !(qh->is_stopped || QH_FINISHED_UNLINKING(qh))) + return; - uhci_dec_fsbr(uhci, urb); + uhci_giveback_urb(uhci, qh, urb, regs); + if (qh->is_stopped) + break; + } - urbp->fsbr_timeout = 1; + /* If the QH is neither stopped nor finished unlinking (normal case), + * our work here is done. */ + restart: + if (!(qh->is_stopped || QH_FINISHED_UNLINKING(qh))) + return; - /* - * Ideally we would want to fix qh->element as well, but it's - * read/write by the HC, so that can introduce a race. It's not - * really worth the hassle - */ + /* Otherwise give back each of the dequeued URBs */ + list_for_each_entry(urbp, &qh->queue, node) { + urb = urbp->urb; + if (urb->status != -EINPROGRESS) { + uhci_save_toggle(qh, urb); + uhci_giveback_urb(uhci, qh, urb, regs); + goto restart; + } + } + qh->is_stopped = 0; - head = &urbp->td_list; - list_for_each_entry(td, head, list) { - /* - * Make sure we don't do the last one (since it'll have the - * TERM bit set) as well as we skip every so many TDs to - * make sure it doesn't hog the bandwidth - */ - if (td->list.next != head && (count % DEPTH_INTERVAL) == - (DEPTH_INTERVAL - 1)) - td->link |= UHCI_PTR_DEPTH; - - count++; + /* There are no more dequeued URBs. If there are still URBs on the + * queue, the QH can now be re-activated. */ + if (!list_empty(&qh->queue)) { + if (qh->needs_fixup) + uhci_fixup_toggles(qh, 0); + uhci_activate_qh(uhci, qh); } - return 0; + /* The queue is empty. The QH can become idle if it is fully + * unlinked. */ + else if (QH_FINISHED_UNLINKING(qh)) + uhci_make_qh_idle(uhci, qh); } static void uhci_free_pending_tds(struct uhci_hcd *uhci) @@ -1406,36 +1359,13 @@ static void uhci_free_pending_tds(struct uhci_hcd *uhci) } } -static void -uhci_finish_urb(struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs) -__releases(uhci->lock) -__acquires(uhci->lock) -{ - struct uhci_hcd *uhci = hcd_to_uhci(hcd); - - uhci_free_urb_priv(uhci, (struct urb_priv *) (urb->hcpriv)); - - spin_unlock(&uhci->lock); - usb_hcd_giveback_urb(hcd, urb, regs); - spin_lock(&uhci->lock); -} - -static void uhci_finish_completion(struct uhci_hcd *uhci, struct pt_regs *regs) -{ - struct urb_priv *urbp, *tmp; - - list_for_each_entry_safe(urbp, tmp, &uhci->complete_list, urb_list) { - struct urb *urb = urbp->urb; - - list_del_init(&urbp->urb_list); - uhci_finish_urb(uhci_to_hcd(uhci), urb, regs); - } -} - -/* Process events in the schedule, but only in one thread at a time */ +/* + * Process events in the schedule, but only in one thread at a time + */ static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs) { - struct urb_priv *urbp, *tmp; + int i; + struct uhci_qh *qh; /* Don't allow re-entrant calls */ if (uhci->scan_in_progress) { @@ -1452,26 +1382,24 @@ static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs) if (uhci->frame_number + uhci->is_stopped != uhci->td_remove_age) uhci_free_pending_tds(uhci); - /* Walk the list of pending URBs to see which ones completed - * (must be _safe because uhci_transfer_result() dequeues URBs) */ - list_for_each_entry_safe(urbp, tmp, &uhci->urb_list, urb_list) { - struct urb *urb = urbp->urb; - - /* Checks the status and does all of the magic necessary */ - uhci_transfer_result(uhci, urb); + /* Go through all the QH queues and process the URBs in each one */ + for (i = 0; i < UHCI_NUM_SKELQH - 1; ++i) { + uhci->next_qh = list_entry(uhci->skelqh[i]->node.next, + struct uhci_qh, node); + while ((qh = uhci->next_qh) != uhci->skelqh[i]) { + uhci->next_qh = list_entry(qh->node.next, + struct uhci_qh, node); + uhci_scan_qh(uhci, qh, regs); + } } - uhci_finish_completion(uhci, regs); - - /* If the controller is stopped, we can finish these off right now */ - if (uhci->is_stopped) - uhci_free_pending_tds(uhci); if (uhci->need_rescan) goto rescan; uhci->scan_in_progress = 0; - /* Check out the QHs waiting for unlinking */ - uhci_scan_unlinking_qhs(uhci); + /* If the controller is stopped, we can finish these off right now */ + if (uhci->is_stopped) + uhci_free_pending_tds(uhci); if (list_empty(&uhci->td_remove_list) && list_empty(&uhci->skel_unlink_qh->node)) @@ -1482,19 +1410,8 @@ static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs) static void check_fsbr(struct uhci_hcd *uhci) { - struct urb_priv *up; - - list_for_each_entry(up, &uhci->urb_list, urb_list) { - struct urb *u = up->urb; - - spin_lock(&u->lock); - - /* Check if the FSBR timed out */ - if (up->fsbr && !up->fsbr_timeout && time_after_eq(jiffies, up->fsbrtime + IDLE_TIMEOUT)) - uhci_fsbr_timeout(uhci, u); - - spin_unlock(&u->lock); - } + /* For now, don't scan URBs for FSBR timeouts. + * Add it back in later... */ /* Really disable FSBR */ if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) { -- cgit v1.2.3-59-g8ed1b From 8d402e1ae03656c1ad215514f8885ef4793f0948 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sat, 17 Dec 2005 18:03:37 -0500 Subject: [PATCH] UHCI: improve debugging code This patch (as626) makes some improvements to the debugging code in uhci-hcd. The main change is that now the code won't get compiled if CONFIG_USB_DEBUG isn't set. But there are other changes too, like adding a missing .owner field and printing a debugging dump if the controller dies. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-debug.c | 36 +++++++++++++++++++++----- drivers/usb/host/uhci-hcd.c | 60 ++++++++++++++++++++++++++++--------------- drivers/usb/host/uhci-hcd.h | 1 + drivers/usb/host/uhci-q.c | 12 +-------- 4 files changed, 70 insertions(+), 39 deletions(-) diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c index f2f5f8ce1715..e1239319655c 100644 --- a/drivers/usb/host/uhci-debug.c +++ b/drivers/usb/host/uhci-debug.c @@ -17,10 +17,13 @@ #include "uhci-hcd.h" -static struct dentry *uhci_debugfs_root = NULL; +#define uhci_debug_operations (* (struct file_operations *) NULL) +static struct dentry *uhci_debugfs_root; + +#ifdef DEBUG /* Handle REALLY large printks so we don't overflow buffers */ -static inline void lprintk(char *buf) +static void lprintk(char *buf) { char *p; @@ -196,7 +199,6 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space) return out - buf; } -#ifdef CONFIG_PROC_FS static const char * const qh_names[] = { "skel_unlink_qh", "skel_iso_qh", "skel_int128_qh", "skel_int64_qh", @@ -393,12 +395,13 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) return out - buf; } +#ifdef CONFIG_DEBUG_FS + #define MAX_OUTPUT (64 * 1024) struct uhci_debug { int size; char *data; - struct uhci_hcd *uhci; }; static int uhci_debug_open(struct inode *inode, struct file *file) @@ -419,8 +422,10 @@ static int uhci_debug_open(struct inode *inode, struct file *file) goto out; } + up->size = 0; spin_lock_irqsave(&uhci->lock, flags); - up->size = uhci_sprint_schedule(uhci, up->data, MAX_OUTPUT); + if (uhci->is_initialized) + up->size = uhci_sprint_schedule(uhci, up->data, MAX_OUTPUT); spin_unlock_irqrestore(&uhci->lock, flags); file->private_data = up; @@ -472,15 +477,32 @@ static int uhci_debug_release(struct inode *inode, struct file *file) return 0; } +#undef uhci_debug_operations static struct file_operations uhci_debug_operations = { + .owner = THIS_MODULE, .open = uhci_debug_open, .llseek = uhci_debug_lseek, .read = uhci_debug_read, .release = uhci_debug_release, }; -#else /* CONFIG_DEBUG_FS */ +#endif /* CONFIG_DEBUG_FS */ -#define uhci_debug_operations (* (struct file_operations *) NULL) +#else /* DEBUG */ + +static inline void lprintk(char *buf) +{} + +static inline int uhci_show_qh(struct uhci_qh *qh, char *buf, + int len, int space) +{ + return 0; +} + +static inline int uhci_sprint_schedule(struct uhci_hcd *uhci, + char *buf, int len) +{ + return 0; +} #endif diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 9865f303d3fc..4edb8330c440 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -68,12 +68,16 @@ Alan Stern" * debug = 3, show all TDs in URBs when dumping */ #ifdef DEBUG +#define DEBUG_CONFIGURED 1 static int debug = 1; -#else -static int debug = 0; -#endif module_param(debug, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "Debug level"); + +#else +#define DEBUG_CONFIGURED 0 +#define debug 0 +#endif + static char *errbuf; #define ERRBUF_LEN (32 * 1024) @@ -338,6 +342,12 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs) dev_err(uhci_dev(uhci), "host controller halted, " "very bad!\n"); + if (debug > 1 && errbuf) { + /* Print the schedule for debugging */ + uhci_sprint_schedule(uhci, + errbuf, ERRBUF_LEN); + lprintk(errbuf); + } hc_died(uhci); /* Force a callback in case there are @@ -376,6 +386,14 @@ static void release_uhci(struct uhci_hcd *uhci) { int i; + if (DEBUG_CONFIGURED) { + spin_lock_irq(&uhci->lock); + uhci->is_initialized = 0; + spin_unlock_irq(&uhci->lock); + + debugfs_remove(uhci->dentry); + } + for (i = 0; i < UHCI_NUM_SKELQH; i++) uhci_free_qh(uhci, uhci->skelqh[i]); @@ -390,8 +408,6 @@ static void release_uhci(struct uhci_hcd *uhci) dma_free_coherent(uhci_dev(uhci), UHCI_NUMFRAMES * sizeof(*uhci->frame), uhci->frame, uhci->frame_dma_handle); - - debugfs_remove(uhci->dentry); } static int uhci_reset(struct usb_hcd *hcd) @@ -474,17 +490,6 @@ static int uhci_start(struct usb_hcd *hcd) hcd->uses_new_polling = 1; - dentry = debugfs_create_file(hcd->self.bus_name, - S_IFREG|S_IRUGO|S_IWUSR, uhci_debugfs_root, uhci, - &uhci_debug_operations); - if (!dentry) { - dev_err(uhci_dev(uhci), - "couldn't create uhci debugfs entry\n"); - retval = -ENOMEM; - goto err_create_debug_entry; - } - uhci->dentry = dentry; - uhci->fsbr = 0; uhci->fsbrtimeout = 0; @@ -495,6 +500,19 @@ static int uhci_start(struct usb_hcd *hcd) init_waitqueue_head(&uhci->waitqh); + if (DEBUG_CONFIGURED) { + dentry = debugfs_create_file(hcd->self.bus_name, + S_IFREG|S_IRUGO|S_IWUSR, uhci_debugfs_root, + uhci, &uhci_debug_operations); + if (!dentry) { + dev_err(uhci_dev(uhci), "couldn't create uhci " + "debugfs entry\n"); + retval = -ENOMEM; + goto err_create_debug_entry; + } + uhci->dentry = dentry; + } + uhci->frame = dma_alloc_coherent(uhci_dev(uhci), UHCI_NUMFRAMES * sizeof(*uhci->frame), &uhci->frame_dma_handle, 0); @@ -609,6 +627,7 @@ static int uhci_start(struct usb_hcd *hcd) mb(); configure_hc(uhci); + uhci->is_initialized = 1; start_rh(uhci); return 0; @@ -872,16 +891,15 @@ static int __init uhci_hcd_init(void) if (usb_disabled()) return -ENODEV; - if (debug) { + if (DEBUG_CONFIGURED) { errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL); if (!errbuf) goto errbuf_failed; + uhci_debugfs_root = debugfs_create_dir("uhci", NULL); + if (!uhci_debugfs_root) + goto debug_failed; } - uhci_debugfs_root = debugfs_create_dir("uhci", NULL); - if (!uhci_debugfs_root) - goto debug_failed; - uhci_up_cachep = kmem_cache_create("uhci_urb_priv", sizeof(struct urb_priv), 0, 0, NULL, NULL); if (!uhci_up_cachep) diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index 7e96bef2e88f..4a69c7eb09bd 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -411,6 +411,7 @@ struct uhci_hcd { unsigned int hc_inaccessible:1; /* HC is suspended or dead */ unsigned int working_RD:1; /* Suspended root hub doesn't need to be polled */ + unsigned int is_initialized:1; /* Data structure is usable */ /* Support for port suspend/resume/reset */ unsigned long port_c_suspend; /* Bit-arrays of ports */ diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 44bba9a6d196..5d6c4f75d0d8 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -736,7 +736,6 @@ err: if (errbuf) { /* Print the chain for debugging purposes */ uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0); - lprintk(errbuf); } } @@ -924,26 +923,17 @@ td_error: ret = uhci_map_status(status, uhci_packetout(td_token(td))); err: - /* - * Enable this chunk of code if you want to see some more debugging. - * But be careful, it has the tendancy to starve out khubd and prevent - * disconnects from happening successfully if you have a slow debug - * log interface (like a serial console. - */ -#if 0 if ((debug == 1 && ret != -EPIPE) || debug > 1) { /* Some debugging code */ dev_dbg(uhci_dev(uhci), "%s: failed with status %x\n", __FUNCTION__, status); - if (errbuf) { + if (debug > 1 && errbuf) { /* Print the chain for debugging purposes */ uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0); - lprintk(errbuf); } } -#endif /* Note that the queue has stopped and save the next toggle value */ urbp->qh->element = UHCI_PTR_TERM; -- cgit v1.2.3-59-g8ed1b From dbf4fcad62467ce1bd6966292b6850fc7a210e0b Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 20 Dec 2005 09:58:08 -0500 Subject: [PATCH] UHCI: Don't log short transfers Even when the URB_SHORT_NOT_OK flag is set, a short transfer shouldn't generate a debugging log message. Especially not one with the confusing claim that the transfer "failed with status 0". This patch (as627) fixes that behavior in uhci-hcd. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-q.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 5d6c4f75d0d8..0c1b12091c48 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -922,7 +922,6 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb) td_error: ret = uhci_map_status(status, uhci_packetout(td_token(td))); -err: if ((debug == 1 && ret != -EPIPE) || debug > 1) { /* Some debugging code */ dev_dbg(uhci_dev(uhci), "%s: failed with status %x\n", @@ -934,6 +933,7 @@ err: lprintk(errbuf); } } +err: /* Note that the queue has stopped and save the next toggle value */ urbp->qh->element = UHCI_PTR_TERM; -- cgit v1.2.3-59-g8ed1b From 1393adb2ebb00a2cd54b293cd7ee71e3376f4e9f Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 31 Jan 2006 10:02:55 -0500 Subject: [PATCH] uhci-hcd: fix mistaken usage of list_prepare_entry A recent update to the uhci-hcd driver invoked the list_prepare_entry macro incorrectly. This patch (as646) corrects it. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-q.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 0c1b12091c48..5d2f9575ab4f 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -259,7 +259,7 @@ static void uhci_fixup_toggles(struct uhci_qh *qh, int skip_first) /* Fix up the toggle for the URBs in the queue. Normally this * loop won't run more than once: When an error or short transfer * occurs, the queue usually gets emptied. */ - list_prepare_entry(urbp, &qh->queue, node); + urbp = list_prepare_entry(urbp, &qh->queue, node); list_for_each_entry_continue(urbp, &qh->queue, node) { /* If the first TD has the right toggle value, we don't -- cgit v1.2.3-59-g8ed1b From 6a8e87b23ff4a979bde5451a242466a4b3f9fe7d Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 19 Jan 2006 10:46:27 -0500 Subject: [PATCH] USB core and HCDs: don't put_device while atomic This patch (as640) removes several put_device and the corresponding get_device calls from the USB core and HCDs. Some of the puts were done in atomic contexts, and none of them are needed since the core now guarantees that every endpoint will be disabled and every URB completed before a USB device is released. Signed-off-by: Alan Stern Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 2 -- drivers/usb/host/ehci-mem.c | 1 - drivers/usb/host/ehci-q.c | 2 +- drivers/usb/host/ehci-sched.c | 6 ++---- drivers/usb/host/isp116x-hcd.c | 3 +-- drivers/usb/host/sl811-hcd.c | 3 +-- drivers/usb/host/uhci-q.c | 2 -- 7 files changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index a98d978d76e2..fbd938d4ea58 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1105,7 +1105,6 @@ static void urb_unlink (struct urb *urb) spin_lock_irqsave (&hcd_data_lock, flags); list_del_init (&urb->urb_list); spin_unlock_irqrestore (&hcd_data_lock, flags); - usb_put_dev (urb->dev); } @@ -1145,7 +1144,6 @@ static int hcd_submit_urb (struct urb *urb, gfp_t mem_flags) case HC_STATE_RUNNING: case HC_STATE_RESUMING: doit: - usb_get_dev (urb->dev); list_add_tail (&urb->urb_list, &ep->urb_list); status = 0; break; diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c index 91c2ab43cbcc..ec7eb3f4f867 100644 --- a/drivers/usb/host/ehci-mem.c +++ b/drivers/usb/host/ehci-mem.c @@ -75,7 +75,6 @@ static void qh_destroy (struct kref *kref) } if (qh->dummy) ehci_qtd_free (ehci, qh->dummy); - usb_put_dev (qh->dev); dma_pool_free (ehci->qh_pool, qh, qh->qh_dma); } diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 5ada7cd5698b..e469221e7ec3 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -702,7 +702,7 @@ qh_make ( } /* support for tt scheduling, and access to toggles */ - qh->dev = usb_get_dev (urb->dev); + qh->dev = urb->dev; /* using TT? */ switch (urb->dev->speed) { diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 88419c6823a8..90d6900a37f4 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1399,7 +1399,7 @@ itd_complete ( */ /* give urb back to the driver ... can be out-of-order */ - dev = usb_get_dev (urb->dev); + dev = urb->dev; ehci_urb_done (ehci, urb, regs); urb = NULL; @@ -1418,7 +1418,6 @@ itd_complete ( (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); } iso_stream_put (ehci, stream); - usb_put_dev (dev); return 1; } @@ -1765,7 +1764,7 @@ sitd_complete ( */ /* give urb back to the driver */ - dev = usb_get_dev (urb->dev); + dev = urb->dev; ehci_urb_done (ehci, urb, regs); urb = NULL; @@ -1784,7 +1783,6 @@ sitd_complete ( (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); } iso_stream_put (ehci, stream); - usb_put_dev (dev); return 1; } diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index 972ce04889f8..fea5dcbec7c4 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -724,7 +724,7 @@ static int isp116x_urb_enqueue(struct usb_hcd *hcd, ep = hep->hcpriv; else { INIT_LIST_HEAD(&ep->schedule); - ep->udev = usb_get_dev(udev); + ep->udev = udev; ep->epnum = epnum; ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out); usb_settoggle(udev, epnum, is_out, 0); @@ -891,7 +891,6 @@ static void isp116x_endpoint_disable(struct usb_hcd *hcd, if (!list_empty(&hep->urb_list)) WARN("ep %p not empty?\n", ep); - usb_put_dev(ep->udev); kfree(ep); hep->hcpriv = NULL; } diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 517360b77d8e..a92343052751 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -853,7 +853,7 @@ static int sl811h_urb_enqueue( } else { INIT_LIST_HEAD(&ep->schedule); - ep->udev = usb_get_dev(udev); + ep->udev = udev; ep->epnum = epnum; ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out); ep->defctrl = SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENABLE; @@ -1052,7 +1052,6 @@ sl811h_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) if (!list_empty(&hep->urb_list)) WARN("ep %p not empty?\n", ep); - usb_put_dev(ep->udev); kfree(ep); hep->hcpriv = NULL; } diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 5d2f9575ab4f..a06d84c19e13 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -179,7 +179,6 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, qh->hep = hep; qh->udev = udev; hep->hcpriv = qh; - usb_get_dev(udev); } else { /* Skeleton QH */ qh->state = QH_STATE_ACTIVE; @@ -197,7 +196,6 @@ static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) list_del(&qh->node); if (qh->udev) { qh->hep->hcpriv = NULL; - usb_put_dev(qh->udev); uhci_free_td(uhci, qh->dummy_td); } dma_pool_free(uhci->qh_pool, qh, qh->dma_handle); -- cgit v1.2.3-59-g8ed1b From 43c5d5aaafef56618a6efbcab7f91615da1a8659 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 1 Feb 2006 10:47:11 -0500 Subject: [PATCH] usbcore: fix compile error with CONFIG_USB_SUSPEND=n This patch (as647) fixes a small error introduced by a recent change to the USB core suspend/resume code. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index f1d64d4bbf5f..7dd28f8e1cea 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1892,8 +1892,8 @@ int usb_resume_device(struct usb_device *udev) status = hub_port_resume(hdev_to_hub(udev->parent), udev->portnum, udev); } else - status = 0; #endif + status = 0; } else status = finish_device_resume(udev); if (status < 0) -- cgit v1.2.3-59-g8ed1b From 329af28b141ab4ae847aff1362864c4cc332641f Mon Sep 17 00:00:00 2001 From: David Brownell Date: Sat, 18 Feb 2006 12:31:05 -0800 Subject: [PATCH] USB: gadget driver section fixups This adds __init section annotations to gadget driver bind() routines to remove calls from .text into .init sections (for endpoint autoconfig). Likewise it adds __exit section annotations to their unbind() routines. The specification of the gadget driver register/unregister functions is updated to explicitly allow use of those sections. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/ether.c | 4 ++-- drivers/usb/gadget/file_storage.c | 4 ++-- drivers/usb/gadget/serial.c | 6 +++--- drivers/usb/gadget/zero.c | 6 +++--- include/linux/usb_gadget.h | 7 +++++-- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 3d2603e31808..0d9d9bbb73f0 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -2125,7 +2125,7 @@ eth_req_free (struct usb_ep *ep, struct usb_request *req) } -static void +static void __exit eth_unbind (struct usb_gadget *gadget) { struct eth_dev *dev = get_gadget_data (gadget); @@ -2532,7 +2532,7 @@ static struct usb_gadget_driver eth_driver = { .function = (char *) driver_desc, .bind = eth_bind, - .unbind = eth_unbind, + .unbind = __exit_p(eth_unbind), .setup = eth_setup, .disconnect = eth_disconnect, diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index de59c58896d6..cf3be299e353 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -3678,7 +3678,7 @@ static void lun_release(struct device *dev) kref_put(&fsg->ref, fsg_release); } -static void fsg_unbind(struct usb_gadget *gadget) +static void __exit fsg_unbind(struct usb_gadget *gadget) { struct fsg_dev *fsg = get_gadget_data(gadget); int i; @@ -4064,7 +4064,7 @@ static struct usb_gadget_driver fsg_driver = { #endif .function = (char *) longname, .bind = fsg_bind, - .unbind = fsg_unbind, + .unbind = __exit_p(fsg_unbind), .disconnect = fsg_disconnect, .setup = fsg_setup, .suspend = fsg_suspend, diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index ba9acd531024..548feaac4553 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -369,7 +369,7 @@ static struct usb_gadget_driver gs_gadget_driver = { #endif /* CONFIG_USB_GADGET_DUALSPEED */ .function = GS_LONG_NAME, .bind = gs_bind, - .unbind = gs_unbind, + .unbind = __exit_p(gs_unbind), .setup = gs_setup, .disconnect = gs_disconnect, .driver = { @@ -1413,7 +1413,7 @@ requeue: * Called on module load. Allocates and initializes the device * structure and a control request. */ -static int gs_bind(struct usb_gadget *gadget) +static int __init gs_bind(struct usb_gadget *gadget) { int ret; struct usb_ep *ep; @@ -1538,7 +1538,7 @@ autoconf_fail: * Called on module unload. Frees the control request and device * structure. */ -static void gs_unbind(struct usb_gadget *gadget) +static void __exit gs_unbind(struct usb_gadget *gadget) { struct gs_dev *dev = get_gadget_data(gadget); diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index 5e9fe8a70543..44d8e5e77da7 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -1119,7 +1119,7 @@ zero_autoresume (unsigned long _dev) /*-------------------------------------------------------------------------*/ -static void +static void __exit zero_unbind (struct usb_gadget *gadget) { struct zero_dev *dev = get_gadget_data (gadget); @@ -1136,7 +1136,7 @@ zero_unbind (struct usb_gadget *gadget) set_gadget_data (gadget, NULL); } -static int +static int __init zero_bind (struct usb_gadget *gadget) { struct zero_dev *dev; @@ -1288,7 +1288,7 @@ static struct usb_gadget_driver zero_driver = { #endif .function = (char *) longname, .bind = zero_bind, - .unbind = zero_unbind, + .unbind = __exit_p(zero_unbind), .setup = zero_setup, .disconnect = zero_disconnect, diff --git a/include/linux/usb_gadget.h b/include/linux/usb_gadget.h index ff81117eb733..1d78870ed8af 100644 --- a/include/linux/usb_gadget.h +++ b/include/linux/usb_gadget.h @@ -801,7 +801,9 @@ struct usb_gadget_driver { * Call this in your gadget driver's module initialization function, * to tell the underlying usb controller driver about your driver. * The driver's bind() function will be called to bind it to a - * gadget. This function must be called in a context that can sleep. + * gadget before this registration call returns. It's expected that + * the bind() functions will be in init sections. + * This function must be called in a context that can sleep. */ int usb_gadget_register_driver (struct usb_gadget_driver *driver); @@ -814,7 +816,8 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver); * going away. If the controller is connected to a USB host, * it will first disconnect(). The driver is also requested * to unbind() and clean up any device state, before this procedure - * finally returns. + * finally returns. It's expected that the unbind() functions + * will in in exit sections, so may not be linked in some kernels. * This function must be called in a context that can sleep. */ int usb_gadget_unregister_driver (struct usb_gadget_driver *driver); -- cgit v1.2.3-59-g8ed1b From 1afc64a3d68174fe524f11d92e045a30eacc927e Mon Sep 17 00:00:00 2001 From: Aras Vaichas Date: Sat, 18 Feb 2006 12:31:23 -0800 Subject: [PATCH] USB: ethernet gadget driver section fixups This patch allows you to set the iSerialNumber field in the usb_device_descriptor structure for your USB ethernet gadget. It also changes the parameters shown through sysfs so they're no longer declared as __initdata, preventing potential oopses. That's most useful for the Ethernet addresses, which may in some cases be random "locally administered" addresses. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/ether.c | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 0d9d9bbb73f0..c3d8e5c5bf28 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -182,33 +182,37 @@ struct eth_dev { * parameters are in UTF-8 (superset of ASCII's 7 bit characters). */ -static ushort __initdata idVendor; +static ushort idVendor; module_param(idVendor, ushort, S_IRUGO); MODULE_PARM_DESC(idVendor, "USB Vendor ID"); -static ushort __initdata idProduct; +static ushort idProduct; module_param(idProduct, ushort, S_IRUGO); MODULE_PARM_DESC(idProduct, "USB Product ID"); -static ushort __initdata bcdDevice; +static ushort bcdDevice; module_param(bcdDevice, ushort, S_IRUGO); MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)"); -static char *__initdata iManufacturer; +static char *iManufacturer; module_param(iManufacturer, charp, S_IRUGO); MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string"); -static char *__initdata iProduct; +static char *iProduct; module_param(iProduct, charp, S_IRUGO); MODULE_PARM_DESC(iProduct, "USB Product string"); +static char *iSerialNumber; +module_param(iSerialNumber, charp, S_IRUGO); +MODULE_PARM_DESC(iSerialNumber, "SerialNumber"); + /* initial value, changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx" */ -static char *__initdata dev_addr; +static char *dev_addr; module_param(dev_addr, charp, S_IRUGO); MODULE_PARM_DESC(dev_addr, "Device Ethernet Address"); /* this address is invisible to ifconfig */ -static char *__initdata host_addr; +static char *host_addr; module_param(host_addr, charp, S_IRUGO); MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); @@ -403,6 +407,7 @@ static inline int BITRATE(struct usb_gadget *g) #define STRING_CDC 7 #define STRING_SUBSET 8 #define STRING_RNDIS 9 +#define STRING_SERIALNUMBER 10 /* holds our biggest descriptor (or RNDIS response) */ #define USB_BUFSIZ 256 @@ -870,6 +875,7 @@ static inline void __init hs_subset_descriptors(void) static char manufacturer [50]; static char product_desc [40] = DRIVER_DESC; +static char serial_number [20]; #ifdef DEV_CONFIG_CDC /* address that the host will use ... usually assigned at random */ @@ -880,6 +886,7 @@ static char ethaddr [2 * ETH_ALEN + 1]; static struct usb_string strings [] = { { STRING_MANUFACTURER, manufacturer, }, { STRING_PRODUCT, product_desc, }, + { STRING_SERIALNUMBER, serial_number, }, { STRING_DATA, "Ethernet Data", }, #ifdef DEV_CONFIG_CDC { STRING_CDC, "CDC Ethernet", }, @@ -2162,7 +2169,7 @@ static u8 __init nibble (unsigned char c) return 0; } -static void __init get_ether_addr (const char *str, u8 *dev_addr) +static int __init get_ether_addr(const char *str, u8 *dev_addr) { if (str) { unsigned i; @@ -2177,9 +2184,10 @@ static void __init get_ether_addr (const char *str, u8 *dev_addr) dev_addr [i] = num; } if (is_valid_ether_addr (dev_addr)) - return; + return 0; } random_ether_addr(dev_addr); + return 1; } static int __init @@ -2277,6 +2285,10 @@ eth_bind (struct usb_gadget *gadget) strlcpy (manufacturer, iManufacturer, sizeof manufacturer); if (iProduct) strlcpy (product_desc, iProduct, sizeof product_desc); + if (iSerialNumber) { + device_desc.iSerialNumber = STRING_SERIALNUMBER, + strlcpy(serial_number, iSerialNumber, sizeof serial_number); + } /* all we really need is bulk IN/OUT */ usb_ep_autoconfig_reset (gadget); @@ -2386,9 +2398,13 @@ autoconf_fail: * The host side address is used with CDC and RNDIS, and commonly * ends up in a persistent config database. */ - get_ether_addr(dev_addr, net->dev_addr); + if (get_ether_addr(dev_addr, net->dev_addr)) + dev_warn(&gadget->dev, + "using random %s ethernet address\n", "self"); if (cdc || rndis) { - get_ether_addr(host_addr, dev->host_mac); + if (get_ether_addr(host_addr, dev->host_mac)) + dev_warn(&gadget->dev, + "using random %s ethernet address\n", "host"); #ifdef DEV_CONFIG_CDC snprintf (ethaddr, sizeof ethaddr, "%02X%02X%02X%02X%02X%02X", dev->host_mac [0], dev->host_mac [1], -- cgit v1.2.3-59-g8ed1b From addf36fec058691f7ba4f95b5487d140f4a86f5a Mon Sep 17 00:00:00 2001 From: Matthew Martin Date: Wed, 15 Feb 2006 15:41:25 -0600 Subject: [PATCH] USB: Fix warning in drivers/usb/media/ov511.c Gcc 4.0.2 had the warning: drivers/usb/media/ov511.c: In function 'show_exposure': drivers/usb/media/ov511.c:5642: warning: 'exp' may be used uninitialized in this function Here is the patch to fix that warning. Signed-off-by: Matthew Martin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/media/ov511.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/media/ov511.c b/drivers/usb/media/ov511.c index cfec8c61574e..1d887ec06080 100644 --- a/drivers/usb/media/ov511.c +++ b/drivers/usb/media/ov511.c @@ -5639,7 +5639,7 @@ static CLASS_DEVICE_ATTR(hue, S_IRUGO, show_hue, NULL); static ssize_t show_exposure(struct class_device *cd, char *buf) { struct usb_ov511 *ov = cd_to_ov(cd); - unsigned char exp; + unsigned char exp = 0; if (!ov->dev) return -ENODEV; -- cgit v1.2.3-59-g8ed1b From 9e47a52bf36fa4a24f56f878f4ca57eb885c78dd Mon Sep 17 00:00:00 2001 From: Luca Risolia Date: Wed, 8 Feb 2006 00:05:27 +0000 Subject: [PATCH] USB: ZC0301 driver updates "Cosmetic" driver updates for the ZC0301 driver: - Fix stream_interrupt() (and work around a possible kernel bug); - Fix vidioc_enum_input() and split vidioc_gs_input() in two parts; - Use wait_event_interruptible_timeout() instead of wait_event_interruptible() when waiting for video frames; - replace erroneous wake_up_interruptible(&wait_stream) with wake_up(&wait_stream); - Cosmetic cleanups in the documentation. Signed-off-by: Luca Risolia Signed-off-by: Greg Kroah-Hartman --- Documentation/usb/zc0301.txt | 19 ++++++---------- drivers/usb/media/zc0301.h | 2 +- drivers/usb/media/zc0301_core.c | 43 +++++++++++++++++++++++------------- drivers/usb/media/zc0301_pas202bcb.c | 2 +- 4 files changed, 37 insertions(+), 29 deletions(-) diff --git a/Documentation/usb/zc0301.txt b/Documentation/usb/zc0301.txt index 10590bf625e4..0889ae6ac7d8 100644 --- a/Documentation/usb/zc0301.txt +++ b/Documentation/usb/zc0301.txt @@ -67,8 +67,7 @@ Some of the features of the driver are: - available mmap or read/poll methods for video streaming through isochronous data transfers; - automatic detection of image sensor; -- video formats is standard JPEG in various compression qualities - (see also "Notes for V4L2 application developers" paragraph); +- video format is standard JPEG; - full support for the capabilities of every possible image sensors that can be connected to the ZC0301 bridges, including, for istance, red, green, blue and global gain adjustments and exposure control (see "Supported @@ -226,10 +225,6 @@ The same number of buffers as before will be allocated again to match the size of the new video frames, so you have to map the buffers again before any I/O attempts on them. -This driver supports the standard JPEG video format. The current compression -quality may vary from 0 to 3 and can be selected or queried thanks to the -VIDIOC_S_JPEGCOMP and VIDIOC_G_JPEGCOMP V4L2 ioctl's. - 10. Contact information ======================= @@ -242,9 +237,9 @@ the fingerprint is: '88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4'. 11. Credits =========== -- Informations about the chip internals to enable the I2C protocol have been - taken from the documentation of the ZC030x Video4Linux1 driver written by - Andrew Birkett ; -- Initialization values of the ZC0301 connected to the PAS202BCB image sensor - have been taken from the SPCA5XX driver maintained by - Michel Xhaard +- Informations about the chip internals needed to enable the I2C protocol have + been taken from the documentation of the ZC030x Video4Linux1 driver written + by Andrew Birkett ; +- Initialization values of the ZC0301 controller connected to the PAS202BCB + image sensor have been taken from the SPCA5XX driver maintained by + Michel Xhaard . diff --git a/drivers/usb/media/zc0301.h b/drivers/usb/media/zc0301.h index 6fa5075f1e3d..cb1a0823bc63 100644 --- a/drivers/usb/media/zc0301.h +++ b/drivers/usb/media/zc0301.h @@ -45,7 +45,7 @@ #define ZC0301_MAX_DEVICES 64 #define ZC0301_FORCE_MUNMAP 0 #define ZC0301_MAX_FRAMES 32 -#define ZC0301_COMPRESSION_QUALITY 2 +#define ZC0301_COMPRESSION_QUALITY 0 #define ZC0301_URBS 2 #define ZC0301_ISO_PACKETS 7 #define ZC0301_ALTERNATE_SETTING 7 diff --git a/drivers/usb/media/zc0301_core.c b/drivers/usb/media/zc0301_core.c index 238f60f7f472..7e8b1676d144 100644 --- a/drivers/usb/media/zc0301_core.c +++ b/drivers/usb/media/zc0301_core.c @@ -3,9 +3,9 @@ * * * Copyright (C) 2006 by Luca Risolia * * * - * Informations about the chip internals to enable the I2C protocol have * - * been taken from the documentation of the ZC030x Video4Linux1 driver * - * written by Andrew Birkett * + * Informations about the chip internals needed to enable the I2C protocol * + * have been taken from the documentation of the ZC030x Video4Linux1 * + * driver written by Andrew Birkett * * * * 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 * @@ -54,8 +54,8 @@ #define ZC0301_MODULE_AUTHOR "(C) 2006 Luca Risolia" #define ZC0301_AUTHOR_EMAIL "" #define ZC0301_MODULE_LICENSE "GPL" -#define ZC0301_MODULE_VERSION "1:1.00" -#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 0) +#define ZC0301_MODULE_VERSION "1:1.01" +#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 1) /*****************************************************************************/ @@ -314,7 +314,7 @@ static void zc0301_urb_complete(struct urb *urb, struct pt_regs* regs) if ((*f)) (*f)->state = F_QUEUED; DBG(3, "Stream interrupted"); - wake_up_interruptible(&cam->wait_stream); + wake_up(&cam->wait_stream); } if (cam->state & DEV_DISCONNECTED) @@ -526,7 +526,7 @@ static int zc0301_stream_interrupt(struct zc0301_device* cam) ZC0301_URB_TIMEOUT); if (cam->state & DEV_DISCONNECTED) return -ENODEV; - else if (!timeout) { + else if (cam->stream != STREAM_OFF) { cam->state |= DEV_MISCONFIGURED; DBG(1, "URB timeout reached. The camera is misconfigured. To " "use it, close and open /dev/video%d again.", @@ -547,8 +547,7 @@ zc0301_set_compression(struct zc0301_device* cam, if ((r = zc0301_read_reg(cam, 0x0008)) < 0) err += r; - err += zc0301_write_reg(cam, 0x0008, - r | 0x11 | (compression->quality >> 1)); + err += zc0301_write_reg(cam, 0x0008, r | 0x11 | compression->quality); return err ? -EIO : 0; } @@ -737,8 +736,7 @@ static int zc0301_release(struct inode* inode, struct file* filp) static ssize_t -zc0301_read(struct file* filp, char __user * buf, - size_t count, loff_t* f_pos) +zc0301_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) { struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); struct zc0301_frame_t* f, * i; @@ -1019,6 +1017,7 @@ zc0301_vidioc_enuminput(struct zc0301_device* cam, void __user * arg) memset(&i, 0, sizeof(i)); strcpy(i.name, "Camera"); + i.type = V4L2_INPUT_TYPE_CAMERA; if (copy_to_user(arg, &i, sizeof(i))) return -EFAULT; @@ -1028,7 +1027,19 @@ zc0301_vidioc_enuminput(struct zc0301_device* cam, void __user * arg) static int -zc0301_vidioc_gs_input(struct zc0301_device* cam, void __user * arg) +zc0301_vidioc_g_input(struct zc0301_device* cam, void __user * arg) +{ + int index = 0; + + if (copy_to_user(arg, &index, sizeof(index))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_s_input(struct zc0301_device* cam, void __user * arg) { int index; @@ -1446,7 +1457,7 @@ zc0301_vidioc_s_jpegcomp(struct zc0301_device* cam, void __user * arg) if (copy_from_user(&jc, arg, sizeof(jc))) return -EFAULT; - if (jc.quality < 0 || jc.quality > 3) + if (jc.quality != 0) return -EINVAL; if (cam->stream == STREAM_ON) @@ -1738,8 +1749,10 @@ static int zc0301_ioctl_v4l2(struct inode* inode, struct file* filp, return zc0301_vidioc_enuminput(cam, arg); case VIDIOC_G_INPUT: + return zc0301_vidioc_g_input(cam, arg); + case VIDIOC_S_INPUT: - return zc0301_vidioc_gs_input(cam, arg); + return zc0301_vidioc_s_input(cam, arg); case VIDIOC_QUERYCTRL: return zc0301_vidioc_query_ctrl(cam, arg); @@ -1980,7 +1993,7 @@ static void zc0301_usb_disconnect(struct usb_interface* intf) zc0301_stop_transfer(cam); cam->state |= DEV_DISCONNECTED; wake_up_interruptible(&cam->wait_frame); - wake_up_interruptible(&cam->wait_stream); + wake_up(&cam->wait_stream); } else { cam->state |= DEV_DISCONNECTED; zc0301_release_resources(cam); diff --git a/drivers/usb/media/zc0301_pas202bcb.c b/drivers/usb/media/zc0301_pas202bcb.c index 83b45f1c0f4e..32b9b9329cbf 100644 --- a/drivers/usb/media/zc0301_pas202bcb.c +++ b/drivers/usb/media/zc0301_pas202bcb.c @@ -318,7 +318,7 @@ static struct zc0301_sensor pas202bcb = { .width = 640, .height = 480, .pixelformat = V4L2_PIX_FMT_JPEG, - .priv = 16, + .priv = 8, }, }; -- cgit v1.2.3-59-g8ed1b From d54a5cb6484705f7808b337917cc7598f2f971c3 Mon Sep 17 00:00:00 2001 From: Luca Risolia Date: Wed, 8 Feb 2006 00:50:59 +0000 Subject: [PATCH] USB: CREDITS: Add credits about the ZC0301 and ET61X[12]51 USB drivers This patch adds credits about the ZC0301 and ET61X[12]51 USB drivers which have been included in the mainline kernel recently. Signed-off-by: Luca Risolia Signed-off-by: Greg Kroah-Hartman --- CREDITS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CREDITS b/CREDITS index 1f171f1033bd..af70678a0afd 100644 --- a/CREDITS +++ b/CREDITS @@ -2813,6 +2813,8 @@ E: luca.risolia@studio.unibo.it P: 1024D/FCE635A4 88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4 D: V4L driver for W996[87]CF JPEG USB Dual Mode Camera Chips D: V4L2 driver for SN9C10x PC Camera Controllers +D: V4L2 driver for ET61X151 and ET61X251 PC Camera Controllers +D: V4L2 driver for ZC0301 Image Processor and Control Chip S: Via Liberta' 41/A S: Osio Sotto, 24046, Bergamo S: Italy -- cgit v1.2.3-59-g8ed1b From 80b6ca48321974a6566a1c9048ba34f60420bca6 Mon Sep 17 00:00:00 2001 From: Eric Sesterhenn Date: Mon, 27 Feb 2006 21:29:43 +0100 Subject: [PATCH] USB: kzalloc() conversion for rest of drivers/usb Signed-off-by: Eric Sesterhenn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-mem.c | 10 +++------- drivers/usb/host/ehci-sched.c | 3 +-- drivers/usb/host/hc_crisv10.c | 12 ++++-------- drivers/usb/media/ov511.c | 4 +--- drivers/usb/media/pwc/pwc-if.c | 3 +-- drivers/usb/media/se401.c | 4 +--- drivers/usb/media/stv680.c | 7 ++----- drivers/usb/misc/auerswald.c | 6 ++---- drivers/usb/misc/usblcd.c | 3 +-- drivers/usb/misc/usbtest.c | 9 +++------ drivers/usb/mon/mon_main.c | 3 +-- drivers/usb/mon/mon_text.c | 3 +-- drivers/usb/net/zd1201.c | 9 +++------ drivers/usb/serial/cp2101.c | 4 +--- drivers/usb/serial/cypress_m8.c | 3 +-- drivers/usb/serial/ftdi_sio.c | 3 +-- drivers/usb/serial/garmin_gps.c | 3 +-- drivers/usb/serial/io_edgeport.c | 3 +-- drivers/usb/serial/io_ti.c | 6 ++---- drivers/usb/serial/ir-usb.c | 3 +-- drivers/usb/serial/keyspan.c | 6 ++---- drivers/usb/serial/kobil_sct.c | 16 +++++----------- drivers/usb/serial/mct_u232.c | 3 +-- drivers/usb/serial/option.c | 3 +-- drivers/usb/serial/pl2303.c | 6 ++---- drivers/usb/serial/ti_usb_3410_5052.c | 3 +-- drivers/usb/serial/usb-serial.c | 6 ++---- drivers/usb/serial/visor.c | 3 +-- drivers/usb/storage/isd200.c | 2 +- 29 files changed, 48 insertions(+), 101 deletions(-) diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c index ec7eb3f4f867..766061e0260a 100644 --- a/drivers/usb/host/ehci-mem.c +++ b/drivers/usb/host/ehci-mem.c @@ -220,13 +220,9 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags) ehci->periodic [i] = EHCI_LIST_END; /* software shadow of hardware table */ - ehci->pshadow = kmalloc (ehci->periodic_size * sizeof (void *), flags); - if (ehci->pshadow == NULL) { - goto fail; - } - memset (ehci->pshadow, 0, ehci->periodic_size * sizeof (void *)); - - return 0; + ehci->pshadow = kcalloc(ehci->periodic_size, sizeof(void *), flags); + if (ehci->pshadow != NULL) + return 0; fail: ehci_dbg (ehci, "couldn't init memory\n"); diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 90d6900a37f4..5871944e6145 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -864,9 +864,8 @@ iso_sched_alloc (unsigned packets, gfp_t mem_flags) int size = sizeof *iso_sched; size += packets * sizeof (struct ehci_iso_packet); - iso_sched = kmalloc (size, mem_flags); + iso_sched = kzalloc(size, mem_flags); if (likely (iso_sched != NULL)) { - memset(iso_sched, 0, size); INIT_LIST_HEAD (&iso_sched->td_list); } return iso_sched; diff --git a/drivers/usb/host/hc_crisv10.c b/drivers/usb/host/hc_crisv10.c index 641268d7e6f3..2fe7fd19437b 100644 --- a/drivers/usb/host/hc_crisv10.c +++ b/drivers/usb/host/hc_crisv10.c @@ -2137,10 +2137,9 @@ static int etrax_usb_submit_bulk_urb(struct urb *urb) urb->status = -EINPROGRESS; /* Setup the hcpriv data. */ - urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG); + urb_priv = kzalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG); assert(urb_priv != NULL); /* This sets rx_offset to 0. */ - memset(urb_priv, 0, sizeof(etrax_urb_priv_t)); urb_priv->urb_state = NOT_STARTED; urb->hcpriv = urb_priv; @@ -2475,10 +2474,9 @@ static int etrax_usb_submit_ctrl_urb(struct urb *urb) urb->status = -EINPROGRESS; /* Setup the hcpriv data. */ - urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG); + urb_priv = kzalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG); assert(urb_priv != NULL); /* This sets rx_offset to 0. */ - memset(urb_priv, 0, sizeof(etrax_urb_priv_t)); urb_priv->urb_state = NOT_STARTED; urb->hcpriv = urb_priv; @@ -2767,9 +2765,8 @@ static void etrax_usb_add_to_intr_sb_list(struct urb *urb, int epid) maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); interval = urb->interval; - urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG); + urb_priv = kzalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG); assert(urb_priv != NULL); - memset(urb_priv, 0, sizeof(etrax_urb_priv_t)); urb->hcpriv = urb_priv; first_ep = &TxIntrEPList[0]; @@ -2997,9 +2994,8 @@ static void etrax_usb_add_to_isoc_sb_list(struct urb *urb, int epid) prev_sb_desc = next_sb_desc = temp_sb_desc = NULL; - urb_priv = kmalloc(sizeof(etrax_urb_priv_t), GFP_ATOMIC); + urb_priv = kzalloc(sizeof(etrax_urb_priv_t), GFP_ATOMIC); assert(urb_priv != NULL); - memset(urb_priv, 0, sizeof(etrax_urb_priv_t)); urb->hcpriv = urb_priv; urb_priv->epid = epid; diff --git a/drivers/usb/media/ov511.c b/drivers/usb/media/ov511.c index 1d887ec06080..da44579d6f29 100644 --- a/drivers/usb/media/ov511.c +++ b/drivers/usb/media/ov511.c @@ -5686,13 +5686,11 @@ ov51x_probe(struct usb_interface *intf, const struct usb_device_id *id) if (idesc->bInterfaceSubClass != 0x00) return -ENODEV; - if ((ov = kmalloc(sizeof(*ov), GFP_KERNEL)) == NULL) { + if ((ov = kzalloc(sizeof(*ov), GFP_KERNEL)) == NULL) { err("couldn't kmalloc ov struct"); goto error_out; } - memset(ov, 0, sizeof(*ov)); - ov->dev = dev; ov->iface = idesc->bInterfaceNumber; ov->led_policy = led; diff --git a/drivers/usb/media/pwc/pwc-if.c b/drivers/usb/media/pwc/pwc-if.c index db6753dc5ba5..90eb26042817 100644 --- a/drivers/usb/media/pwc/pwc-if.c +++ b/drivers/usb/media/pwc/pwc-if.c @@ -1867,12 +1867,11 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id Info("Warning: more than 1 configuration available.\n"); /* Allocate structure, initialize pointers, mutexes, etc. and link it to the usb_device */ - pdev = kmalloc(sizeof(struct pwc_device), GFP_KERNEL); + pdev = kzalloc(sizeof(struct pwc_device), GFP_KERNEL); if (pdev == NULL) { Err("Oops, could not allocate memory for pwc_device.\n"); return -ENOMEM; } - memset(pdev, 0, sizeof(struct pwc_device)); pdev->type = type_id; pdev->vsize = default_size; pdev->vframes = default_fps; diff --git a/drivers/usb/media/se401.c b/drivers/usb/media/se401.c index 8c3b1ad8a8fd..f03ea7f89596 100644 --- a/drivers/usb/media/se401.c +++ b/drivers/usb/media/se401.c @@ -1345,13 +1345,11 @@ static int se401_probe(struct usb_interface *intf, /* We found one */ info("SE401 camera found: %s", camera_name); - if ((se401 = kmalloc(sizeof(*se401), GFP_KERNEL)) == NULL) { + if ((se401 = kzalloc(sizeof(*se401), GFP_KERNEL)) == NULL) { err("couldn't kmalloc se401 struct"); return -ENOMEM; } - memset(se401, 0, sizeof(*se401)); - se401->dev = dev; se401->iface = interface->bInterfaceNumber; se401->camera_name = camera_name; diff --git a/drivers/usb/media/stv680.c b/drivers/usb/media/stv680.c index b1a6be2958ed..9636da20748d 100644 --- a/drivers/usb/media/stv680.c +++ b/drivers/usb/media/stv680.c @@ -318,12 +318,11 @@ static int stv_init (struct usb_stv *stv680) unsigned char *buffer; unsigned long int bufsize; - buffer = kmalloc (40, GFP_KERNEL); + buffer = kzalloc (40, GFP_KERNEL); if (buffer == NULL) { PDEBUG (0, "STV(e): Out of (small buf) memory"); return -1; } - memset (buffer, 0, 40); udelay (100); /* set config 1, interface 0, alternate 0 */ @@ -1388,14 +1387,12 @@ static int stv680_probe (struct usb_interface *intf, const struct usb_device_id goto error; } /* We found one */ - if ((stv680 = kmalloc (sizeof (*stv680), GFP_KERNEL)) == NULL) { + if ((stv680 = kzalloc (sizeof (*stv680), GFP_KERNEL)) == NULL) { PDEBUG (0, "STV(e): couldn't kmalloc stv680 struct."); retval = -ENOMEM; goto error; } - memset (stv680, 0, sizeof (*stv680)); - stv680->udev = dev; stv680->camera_name = camera_name; diff --git a/drivers/usb/misc/auerswald.c b/drivers/usb/misc/auerswald.c index ad2f4cccd388..1fef36e71c57 100644 --- a/drivers/usb/misc/auerswald.c +++ b/drivers/usb/misc/auerswald.c @@ -570,10 +570,9 @@ static int auerchain_setup (pauerchain_t acp, unsigned int numElements) /* fill the list of free elements */ for (;numElements; numElements--) { - acep = (pauerchainelement_t) kmalloc (sizeof (auerchainelement_t), GFP_KERNEL); + acep = kzalloc(sizeof(auerchainelement_t), GFP_KERNEL); if (!acep) goto ac_fail; - memset (acep, 0, sizeof (auerchainelement_t)); INIT_LIST_HEAD (&acep->list); list_add_tail (&acep->list, &acp->free_list); } @@ -761,10 +760,9 @@ static int auerbuf_setup (pauerbufctl_t bcp, unsigned int numElements, unsigned /* fill the list of free elements */ for (;numElements; numElements--) { - bep = (pauerbuf_t) kmalloc (sizeof (auerbuf_t), GFP_KERNEL); + bep = kzalloc(sizeof(auerbuf_t), GFP_KERNEL); if (!bep) goto bl_fail; - memset (bep, 0, sizeof (auerbuf_t)); bep->list = bcp; INIT_LIST_HEAD (&bep->buff_list); bep->bufp = kmalloc (bufsize, GFP_KERNEL); diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index cc3dae3f34e0..c82c402285a0 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c @@ -270,12 +270,11 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id int retval = -ENOMEM; /* allocate memory for our device state and initialize it */ - dev = kmalloc(sizeof(*dev), GFP_KERNEL); + dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (dev == NULL) { err("Out of memory"); goto error; } - memset(dev, 0x00, sizeof(*dev)); kref_init(&dev->kref); dev->udev = usb_get_dev(interface_to_usbdev(interface)); diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 84fa1728f052..9d59b901841c 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -382,12 +382,11 @@ alloc_sglist (int nents, int max, int vary) for (i = 0; i < nents; i++) { char *buf; - buf = kmalloc (size, SLAB_KERNEL); + buf = kzalloc (size, SLAB_KERNEL); if (!buf) { free_sglist (sg, i); return NULL; } - memset (buf, 0, size); /* kmalloc pages are always physically contiguous! */ sg_init_one(&sg[i], buf, size); @@ -842,10 +841,9 @@ test_ctrl_queue (struct usbtest_dev *dev, struct usbtest_param *param) * as with bulk/intr sglists, sglen is the queue depth; it also * controls which subtests run (more tests than sglen) or rerun. */ - urb = kmalloc (param->sglen * sizeof (struct urb *), SLAB_KERNEL); + urb = kcalloc(param->sglen, sizeof(struct urb *), SLAB_KERNEL); if (!urb) return -ENOMEM; - memset (urb, 0, param->sglen * sizeof (struct urb *)); for (i = 0; i < param->sglen; i++) { int pipe = usb_rcvctrlpipe (udev, 0); unsigned len; @@ -1865,10 +1863,9 @@ usbtest_probe (struct usb_interface *intf, const struct usb_device_id *id) } #endif - dev = kmalloc (sizeof *dev, SLAB_KERNEL); + dev = kzalloc(sizeof(*dev), SLAB_KERNEL); if (!dev) return -ENOMEM; - memset (dev, 0, sizeof *dev); info = (struct usbtest_info *) id->driver_info; dev->info = info; init_MUTEX (&dev->sem); diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c index b03e346e33f1..6ecc27302211 100644 --- a/drivers/usb/mon/mon_main.c +++ b/drivers/usb/mon/mon_main.c @@ -277,9 +277,8 @@ static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus) char name[NAMESZ]; int rc; - if ((mbus = kmalloc(sizeof(struct mon_bus), GFP_KERNEL)) == NULL) + if ((mbus = kzalloc(sizeof(struct mon_bus), GFP_KERNEL)) == NULL) goto err_alloc; - memset(mbus, 0, sizeof(struct mon_bus)); kref_init(&mbus->ref); spin_lock_init(&mbus->lock); INIT_LIST_HEAD(&mbus->r_list); diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c index 59089e8b7e5e..ac043ec2b8dc 100644 --- a/drivers/usb/mon/mon_text.c +++ b/drivers/usb/mon/mon_text.c @@ -213,12 +213,11 @@ static int mon_text_open(struct inode *inode, struct file *file) mbus = inode->u.generic_ip; ubus = mbus->u_bus; - rp = kmalloc(sizeof(struct mon_reader_text), GFP_KERNEL); + rp = kzalloc(sizeof(struct mon_reader_text), GFP_KERNEL); if (rp == NULL) { rc = -ENOMEM; goto err_alloc; } - memset(rp, 0, sizeof(struct mon_reader_text)); INIT_LIST_HEAD(&rp->e_list); init_waitqueue_head(&rp->wait); mutex_init(&rp->printf_lock); diff --git a/drivers/usb/net/zd1201.c b/drivers/usb/net/zd1201.c index f3a8e2807c3b..fe9b60cd8d95 100644 --- a/drivers/usb/net/zd1201.c +++ b/drivers/usb/net/zd1201.c @@ -621,10 +621,9 @@ static int zd1201_drvr_start(struct zd1201 *zd) __le16 zdmax; unsigned char *buffer; - buffer = kmalloc(ZD1201_RXSIZE, GFP_KERNEL); + buffer = kzalloc(ZD1201_RXSIZE, GFP_KERNEL); if (!buffer) return -ENOMEM; - memset(buffer, 0, ZD1201_RXSIZE); usb_fill_bulk_urb(zd->rx_urb, zd->usb, usb_rcvbulkpipe(zd->usb, zd->endp_in), buffer, ZD1201_RXSIZE, @@ -1750,11 +1749,9 @@ static int zd1201_probe(struct usb_interface *interface, usb = interface_to_usbdev(interface); - zd = kmalloc(sizeof(struct zd1201), GFP_KERNEL); - if (!zd) { + zd = kzalloc(sizeof(struct zd1201), GFP_KERNEL); + if (!zd) return -ENOMEM; - } - memset(zd, 0, sizeof(struct zd1201)); zd->ap = ap; zd->usb = usb; zd->removed = 0; diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c index dc7a069503e0..9ea7b4a4a22b 100644 --- a/drivers/usb/serial/cp2101.c +++ b/drivers/usb/serial/cp2101.c @@ -169,9 +169,7 @@ static int cp2101_get_config(struct usb_serial_port* port, u8 request, /* Number of integers required to contain the array */ length = (((size - 1) | 3) + 1)/4; - buf = kmalloc (length * sizeof(u32), GFP_KERNEL); - memset(buf, 0, length * sizeof(u32)); - + buf = kcalloc(length, sizeof(u32), GFP_KERNEL); if (!buf) { dev_err(&port->dev, "%s - out of memory.\n", __FUNCTION__); return -ENOMEM; diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 68067fe117a4..71af3bf5adb7 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -435,11 +435,10 @@ static int generic_startup (struct usb_serial *serial) dbg("%s - port %d", __FUNCTION__, serial->port[0]->number); - priv = kmalloc(sizeof (struct cypress_private), GFP_KERNEL); + priv = kzalloc(sizeof (struct cypress_private), GFP_KERNEL); if (!priv) return -ENOMEM; - memset(priv, 0x00, sizeof (struct cypress_private)); spin_lock_init(&priv->lock); priv->buf = cypress_buf_alloc(CYPRESS_BUF_SIZE); if (priv->buf == NULL) { diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index c145e1ed8429..ece52a6328b5 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1141,12 +1141,11 @@ static int ftdi_sio_attach (struct usb_serial *serial) dbg("%s",__FUNCTION__); - priv = kmalloc(sizeof(struct ftdi_private), GFP_KERNEL); + priv = kzalloc(sizeof(struct ftdi_private), GFP_KERNEL); if (!priv){ err("%s- kmalloc(%Zd) failed.", __FUNCTION__, sizeof(struct ftdi_private)); return -ENOMEM; } - memset(priv, 0, sizeof(*priv)); spin_lock_init(&priv->rx_lock); init_waitqueue_head(&priv->delta_msr_wait); diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index d6f55e9dccae..5ec9bf5bac8d 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -1422,12 +1422,11 @@ static int garmin_attach (struct usb_serial *serial) dbg("%s", __FUNCTION__); - garmin_data_p = kmalloc (sizeof(struct garmin_data), GFP_KERNEL); + garmin_data_p = kzalloc(sizeof(struct garmin_data), GFP_KERNEL); if (garmin_data_p == NULL) { dev_err(&port->dev, "%s - Out of memory\n", __FUNCTION__); return -ENOMEM; } - memset (garmin_data_p, 0, sizeof(struct garmin_data)); init_timer(&garmin_data_p->timer); spin_lock_init(&garmin_data_p->lock); INIT_LIST_HEAD(&garmin_data_p->pktlist); diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 3f29e6b0fd19..b606c5968102 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -2725,12 +2725,11 @@ static int edge_startup (struct usb_serial *serial) dev = serial->dev; /* create our private serial structure */ - edge_serial = kmalloc (sizeof(struct edgeport_serial), GFP_KERNEL); + edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL); if (edge_serial == NULL) { dev_err(&serial->dev->dev, "%s - Out of memory\n", __FUNCTION__); return -ENOMEM; } - memset (edge_serial, 0, sizeof(struct edgeport_serial)); spin_lock_init(&edge_serial->es_lock); edge_serial->serial = serial; usb_set_serial_data(serial, edge_serial); diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index afc0f34b3a46..8e1e2253748b 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -2727,12 +2727,11 @@ static int edge_startup (struct usb_serial *serial) dev = serial->dev; /* create our private serial structure */ - edge_serial = kmalloc (sizeof(struct edgeport_serial), GFP_KERNEL); + edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL); if (edge_serial == NULL) { dev_err(&serial->dev->dev, "%s - Out of memory\n", __FUNCTION__); return -ENOMEM; } - memset (edge_serial, 0, sizeof(struct edgeport_serial)); sema_init(&edge_serial->es_sem, 1); edge_serial->serial = serial; usb_set_serial_data(serial, edge_serial); @@ -2745,12 +2744,11 @@ static int edge_startup (struct usb_serial *serial) /* set up our port private structures */ for (i = 0; i < serial->num_ports; ++i) { - edge_port = kmalloc (sizeof(struct edgeport_port), GFP_KERNEL); + edge_port = kzalloc(sizeof(struct edgeport_port), GFP_KERNEL); if (edge_port == NULL) { dev_err(&serial->dev->dev, "%s - Out of memory\n", __FUNCTION__); goto cleanup; } - memset (edge_port, 0, sizeof(struct edgeport_port)); spin_lock_init(&edge_port->ep_lock); edge_port->ep_out_buf = edge_buf_alloc(EDGE_OUT_BUF_SIZE); if (edge_port->ep_out_buf == NULL) { diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index a59010421444..426182ddc42a 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -184,10 +184,9 @@ static struct irda_class_desc *irda_usb_find_class_desc(struct usb_device *dev, struct irda_class_desc *desc; int ret; - desc = kmalloc(sizeof (struct irda_class_desc), GFP_KERNEL); + desc = kzalloc(sizeof (struct irda_class_desc), GFP_KERNEL); if (desc == NULL) return NULL; - memset(desc, 0, sizeof(struct irda_class_desc)); ret = usb_control_msg(dev, usb_rcvctrlpipe(dev,0), IU_REQ_GET_CLASS_DESC, diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 3b958e60f5e8..052b735c4fbd 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -2250,12 +2250,11 @@ static int keyspan_startup (struct usb_serial *serial) } /* Setup private data for serial driver */ - s_priv = kmalloc(sizeof(struct keyspan_serial_private), GFP_KERNEL); + s_priv = kzalloc(sizeof(struct keyspan_serial_private), GFP_KERNEL); if (!s_priv) { dbg("%s - kmalloc for keyspan_serial_private failed.", __FUNCTION__); return -ENOMEM; } - memset(s_priv, 0, sizeof(struct keyspan_serial_private)); s_priv->device_details = d_details; usb_set_serial_data(serial, s_priv); @@ -2263,12 +2262,11 @@ static int keyspan_startup (struct usb_serial *serial) /* Now setup per port private data */ for (i = 0; i < serial->num_ports; i++) { port = serial->port[i]; - p_priv = kmalloc(sizeof(struct keyspan_port_private), GFP_KERNEL); + p_priv = kzalloc(sizeof(struct keyspan_port_private), GFP_KERNEL); if (!p_priv) { dbg("%s - kmalloc for keyspan_port_private (%d) failed!.", __FUNCTION__, i); return (1); } - memset(p_priv, 0, sizeof(struct keyspan_port_private)); p_priv->device_details = d_details; usb_set_serial_port_data(port, p_priv); } diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index b8b213185d0f..87dfcd89ffab 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -255,11 +255,9 @@ static int kobil_open (struct usb_serial_port *port, struct file *filp) } // allocate memory for transfer buffer - transfer_buffer = (unsigned char *) kmalloc(transfer_buffer_length, GFP_KERNEL); + transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL); if (! transfer_buffer) { return -ENOMEM; - } else { - memset(transfer_buffer, 0, transfer_buffer_length); } // allocate write_urb @@ -383,11 +381,10 @@ static void kobil_read_int_callback( struct urb *purb, struct pt_regs *regs) // BEGIN DEBUG /* - dbg_data = (unsigned char *) kmalloc((3 * purb->actual_length + 10) * sizeof(char), GFP_KERNEL); + dbg_data = kzalloc((3 * purb->actual_length + 10) * sizeof(char), GFP_KERNEL); if (! dbg_data) { return; } - memset(dbg_data, 0, (3 * purb->actual_length + 10)); for (i = 0; i < purb->actual_length; i++) { sprintf(dbg_data +3*i, "%02X ", data[i]); } @@ -518,11 +515,10 @@ static int kobil_tiocmget(struct usb_serial_port *port, struct file *file) } // allocate memory for transfer buffer - transfer_buffer = (unsigned char *) kmalloc(transfer_buffer_length, GFP_KERNEL); + transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL); if (!transfer_buffer) { return -ENOMEM; } - memset(transfer_buffer, 0, transfer_buffer_length); result = usb_control_msg( port->serial->dev, usb_rcvctrlpipe(port->serial->dev, 0 ), @@ -564,11 +560,10 @@ static int kobil_tiocmset(struct usb_serial_port *port, struct file *file, } // allocate memory for transfer buffer - transfer_buffer = (unsigned char *) kmalloc(transfer_buffer_length, GFP_KERNEL); + transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL); if (! transfer_buffer) { return -ENOMEM; } - memset(transfer_buffer, 0, transfer_buffer_length); if (set & TIOCM_RTS) rts = 1; @@ -655,11 +650,10 @@ static int kobil_ioctl(struct usb_serial_port *port, struct file *file, (struct termios __user *)arg)) return -EFAULT; - settings = (unsigned char *) kmalloc(50, GFP_KERNEL); + settings = kzalloc(50, GFP_KERNEL); if (! settings) { return -ENOBUFS; } - memset(settings, 0, 50); switch (priv->internal_termios.c_cflag & CBAUD) { case B1200: diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index b6d6cab9c859..35bd29b6c408 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -348,10 +348,9 @@ static int mct_u232_startup (struct usb_serial *serial) struct mct_u232_private *priv; struct usb_serial_port *port, *rport; - priv = kmalloc(sizeof(struct mct_u232_private), GFP_KERNEL); + priv = kzalloc(sizeof(struct mct_u232_private), GFP_KERNEL); if (!priv) return -ENOMEM; - memset(priv, 0, sizeof(struct mct_u232_private)); spin_lock_init(&priv->lock); usb_set_serial_port_data(serial->port[0], priv); diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 52bdf6fe46f2..a8455c9e79dd 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -631,13 +631,12 @@ static int option_startup(struct usb_serial *serial) /* Now setup per port private data */ for (i = 0; i < serial->num_ports; i++) { port = serial->port[i]; - portdata = kmalloc(sizeof(*portdata), GFP_KERNEL); + portdata = kzalloc(sizeof(*portdata), GFP_KERNEL); if (!portdata) { dbg("%s: kmalloc for option_port_private (%d) failed!.", __FUNCTION__, i); return (1); } - memset(portdata, 0, sizeof(struct option_port_private)); usb_set_serial_port_data(port, portdata); diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 37c81c08faad..cf2213bae0e0 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -218,10 +218,9 @@ static int pl2303_startup (struct usb_serial *serial) dbg("device type: %d", type); for (i = 0; i < serial->num_ports; ++i) { - priv = kmalloc (sizeof (struct pl2303_private), GFP_KERNEL); + priv = kzalloc(sizeof(struct pl2303_private), GFP_KERNEL); if (!priv) goto cleanup; - memset (priv, 0x00, sizeof (struct pl2303_private)); spin_lock_init(&priv->lock); priv->buf = pl2303_buf_alloc(PL2303_BUF_SIZE); if (priv->buf == NULL) { @@ -383,12 +382,11 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol } } - buf = kmalloc (7, GFP_KERNEL); + buf = kzalloc (7, GFP_KERNEL); if (!buf) { dev_err(&port->dev, "%s - out of memory.\n", __FUNCTION__); return; } - memset (buf, 0x00, 0x07); i = usb_control_msg (serial->dev, usb_rcvctrlpipe (serial->dev, 0), GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index c18db3257073..c3a2071b802d 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -416,12 +416,11 @@ static int ti_startup(struct usb_serial *serial) dev->actconfig->desc.bConfigurationValue); /* create device structure */ - tdev = kmalloc(sizeof(struct ti_device), GFP_KERNEL); + tdev = kzalloc(sizeof(struct ti_device), GFP_KERNEL); if (tdev == NULL) { dev_err(&dev->dev, "%s - out of memory\n", __FUNCTION__); return -ENOMEM; } - memset(tdev, 0, sizeof(struct ti_device)); sema_init(&tdev->td_open_close_sem, 1); tdev->td_serial = serial; usb_set_serial_data(serial, tdev); diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index b5c96e74a903..097f4e8488fe 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -564,12 +564,11 @@ static struct usb_serial * create_serial (struct usb_device *dev, { struct usb_serial *serial; - serial = kmalloc (sizeof (*serial), GFP_KERNEL); + serial = kzalloc(sizeof(*serial), GFP_KERNEL); if (!serial) { dev_err(&dev->dev, "%s - out of memory\n", __FUNCTION__); return NULL; } - memset (serial, 0, sizeof(*serial)); serial->dev = usb_get_dev(dev); serial->type = driver; serial->interface = interface; @@ -778,10 +777,9 @@ int usb_serial_probe(struct usb_interface *interface, serial->num_port_pointers = max_endpoints; dbg("%s - setting up %d port structures for this device", __FUNCTION__, max_endpoints); for (i = 0; i < max_endpoints; ++i) { - port = kmalloc(sizeof(struct usb_serial_port), GFP_KERNEL); + port = kzalloc(sizeof(struct usb_serial_port), GFP_KERNEL); if (!port) goto probe_error; - memset(port, 0x00, sizeof(struct usb_serial_port)); port->number = i + serial->minor; port->serial = serial; spin_lock_init(&port->lock); diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 11a48d874752..f5c3841d4843 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -763,10 +763,9 @@ static int generic_startup(struct usb_serial *serial) int i; for (i = 0; i < serial->num_ports; ++i) { - priv = kmalloc (sizeof(*priv), GFP_KERNEL); + priv = kzalloc (sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - memset (priv, 0x00, sizeof(*priv)); spin_lock_init(&priv->lock); usb_set_serial_port_data(serial->port[i], priv); } diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c index 7308e8cbe8f9..6831dca93c1b 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -1382,7 +1382,7 @@ static int isd200_init_info(struct us_data *us) } else US_DEBUGP("ERROR - kmalloc failure\n"); - return(retStatus); + return retStatus; } /************************************************************************** -- cgit v1.2.3-59-g8ed1b From 7039f4224d4e40b06308d5c1a97427af1a142459 Mon Sep 17 00:00:00 2001 From: Eric Sesterhenn Date: Mon, 27 Feb 2006 13:34:10 -0800 Subject: [PATCH] USB: kzalloc() conversion in drivers/usb/gadget this patch converts drivers/usb to kzalloc usage. Compile tested with allyes config. I think there was a bug in drivers/usb/gadget/inode.c because it used sizeof(*data) for the kmalloc() and sizeof(data) for the memset(), since sizeof(data) just returns the size for a pointer. Signed-off-by: Eric Sesterhenn Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/dummy_hcd.c | 3 +-- drivers/usb/gadget/goku_udc.c | 3 +-- drivers/usb/gadget/inode.c | 6 ++---- drivers/usb/gadget/lh7a40x_udc.c | 3 +-- drivers/usb/gadget/net2280.c | 3 +-- drivers/usb/gadget/omap_udc.c | 6 ++---- drivers/usb/gadget/pxa2xx_udc.c | 3 +-- drivers/usb/gadget/serial.c | 3 +-- drivers/usb/gadget/zero.c | 3 +-- 9 files changed, 11 insertions(+), 22 deletions(-) diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 9734cb76dd6c..42ce41d71b7f 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -478,10 +478,9 @@ dummy_alloc_request (struct usb_ep *_ep, gfp_t mem_flags) return NULL; ep = usb_ep_to_dummy_ep (_ep); - req = kmalloc (sizeof *req, mem_flags); + req = kzalloc(sizeof(*req), mem_flags); if (!req) return NULL; - memset (req, 0, sizeof *req); INIT_LIST_HEAD (&req->queue); return &req->req; } diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index b0f3cd63e3b9..66b81bbf6bee 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -275,11 +275,10 @@ goku_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) if (!_ep) return NULL; - req = kmalloc(sizeof *req, gfp_flags); + req = kzalloc(sizeof *req, gfp_flags); if (!req) return NULL; - memset(req, 0, sizeof *req); req->req.dma = DMA_ADDR_INVALID; INIT_LIST_HEAD(&req->queue); return &req->req; diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index 0aab7d24c768..b44cfda76b61 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -170,10 +170,9 @@ static struct dev_data *dev_new (void) { struct dev_data *dev; - dev = kmalloc (sizeof *dev, GFP_KERNEL); + dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return NULL; - memset (dev, 0, sizeof *dev); dev->state = STATE_DEV_DISABLED; atomic_set (&dev->count, 1); spin_lock_init (&dev->lock); @@ -1592,10 +1591,9 @@ static int activate_ep_files (struct dev_data *dev) gadget_for_each_ep (ep, dev->gadget) { struct ep_data *data; - data = kmalloc (sizeof *data, GFP_KERNEL); + data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) goto enomem; - memset (data, 0, sizeof data); data->state = STATE_EP_DISABLED; init_MUTEX (&data->lock); init_waitqueue_head (&data->wait); diff --git a/drivers/usb/gadget/lh7a40x_udc.c b/drivers/usb/gadget/lh7a40x_udc.c index 1a362c5e7f3d..0d3424eda038 100644 --- a/drivers/usb/gadget/lh7a40x_udc.c +++ b/drivers/usb/gadget/lh7a40x_udc.c @@ -1114,11 +1114,10 @@ static struct usb_request *lh7a40x_alloc_request(struct usb_ep *ep, DEBUG("%s, %p\n", __FUNCTION__, ep); - req = kmalloc(sizeof *req, gfp_flags); + req = kzalloc(sizeof(*req), gfp_flags); if (!req) return 0; - memset(req, 0, sizeof *req); INIT_LIST_HEAD(&req->queue); return &req->req; diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index 67b13ab2f3f5..fb73dc100535 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -386,11 +386,10 @@ net2280_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags) return NULL; ep = container_of (_ep, struct net2280_ep, ep); - req = kmalloc (sizeof *req, gfp_flags); + req = kzalloc(sizeof(*req), gfp_flags); if (!req) return NULL; - memset (req, 0, sizeof *req); req->req.dma = DMA_ADDR_INVALID; INIT_LIST_HEAD (&req->queue); diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index a8972d7c97be..fbea51448909 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -273,9 +273,8 @@ omap_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) { struct omap_req *req; - req = kmalloc(sizeof *req, gfp_flags); + req = kzalloc(sizeof(*req), gfp_flags); if (req) { - memset (req, 0, sizeof *req); req->req.dma = DMA_ADDR_INVALID; INIT_LIST_HEAD (&req->queue); } @@ -2586,11 +2585,10 @@ omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv) /* UDC_PULLUP_EN gates the chip clock */ // OTG_SYSCON_1_REG |= DEV_IDLE_EN; - udc = kmalloc (sizeof *udc, SLAB_KERNEL); + udc = kzalloc(sizeof(*udc), SLAB_KERNEL); if (!udc) return -ENOMEM; - memset(udc, 0, sizeof *udc); spin_lock_init (&udc->lock); udc->gadget.ops = &omap_gadget_ops; diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c index bb028c5b8952..680f7fc5b171 100644 --- a/drivers/usb/gadget/pxa2xx_udc.c +++ b/drivers/usb/gadget/pxa2xx_udc.c @@ -335,11 +335,10 @@ pxa2xx_ep_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags) { struct pxa2xx_request *req; - req = kmalloc (sizeof *req, gfp_flags); + req = kzalloc(sizeof(*req), gfp_flags); if (!req) return NULL; - memset (req, 0, sizeof *req); INIT_LIST_HEAD (&req->queue); return &req->req; } diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index 548feaac4553..b992546c394d 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -2178,10 +2178,9 @@ static int gs_alloc_ports(struct gs_dev *dev, gfp_t kmalloc_flags) return -EIO; for (i=0; iport_dev = dev; port->port_num = i; port->port_line_coding.dwDTERate = cpu_to_le32(GS_DEFAULT_DTE_RATE); diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index 44d8e5e77da7..51424f66a765 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -1188,10 +1188,9 @@ autoconf_fail: /* ok, we made sense of the hardware ... */ - dev = kmalloc (sizeof *dev, SLAB_KERNEL); + dev = kzalloc(sizeof(*dev), SLAB_KERNEL); if (!dev) return -ENOMEM; - memset (dev, 0, sizeof *dev); spin_lock_init (&dev->lock); dev->gadget = gadget; set_gadget_data (gadget, dev); -- cgit v1.2.3-59-g8ed1b From 2ffab02fea5880da284dc5511479b25a796a8dee Mon Sep 17 00:00:00 2001 From: Luca Risolia Date: Sat, 25 Feb 2006 06:50:47 +0000 Subject: [PATCH] USB: SN9C10x driver updates SN9C10x driver updates. Changes: + new, - removed, * cleanup, @ bugfix @ Fix stream_interrupt() @ Fix vidioc_enum_input() and split vidioc_gs_input() @ Need usb_get|put_dev() when disconnecting, if the device is open * Use wait_event_interruptible_timeout() instead of wait_event_interruptible() when waiting for video frames * replace wake_up_interruptible(&wait_stream) with wake_up(&wait_stream) * Cleanups and updates in the documentation + Use per-device sensor structures + Add support for PAS202BCA image sensors + Add frame_timeout module parameter Signed-off-by: Luca Risolia Signed-off-by: Greg Kroah-Hartman --- Documentation/usb/sn9c102.txt | 11 ++ drivers/usb/media/Makefile | 5 +- drivers/usb/media/sn9c102.h | 23 +++- drivers/usb/media/sn9c102_core.c | 170 +++++++++++++---------- drivers/usb/media/sn9c102_ov7630.c | 33 +++-- drivers/usb/media/sn9c102_pas202bca.c | 238 +++++++++++++++++++++++++++++++++ drivers/usb/media/sn9c102_pas202bcb.c | 2 +- drivers/usb/media/sn9c102_sensor.h | 15 ++- drivers/usb/media/sn9c102_tas5110c1b.c | 14 +- drivers/usb/media/sn9c102_tas5130d1b.c | 12 +- 10 files changed, 416 insertions(+), 107 deletions(-) create mode 100644 drivers/usb/media/sn9c102_pas202bca.c diff --git a/Documentation/usb/sn9c102.txt b/Documentation/usb/sn9c102.txt index c6b76414172c..b957beae5607 100644 --- a/Documentation/usb/sn9c102.txt +++ b/Documentation/usb/sn9c102.txt @@ -196,6 +196,14 @@ Description: Force the application to unmap previously mapped buffer memory 1 = force memory unmapping (save memory) Default: 0 ------------------------------------------------------------------------------- +Name: frame_timeout +Type: uint array (min = 0, max = 64) +Syntax: +Description: Timeout for a video frame in seconds. This parameter is + specific for each detected camera. This parameter can be + changed at runtime thanks to the /sys filesystem interface. +Default: 2 +------------------------------------------------------------------------------- Name: debug Type: ushort Syntax: @@ -321,6 +329,7 @@ Vendor ID Product ID --------- ---------- 0x0c45 0x6001 0x0c45 0x6005 +0x0c45 0x6007 0x0c45 0x6009 0x0c45 0x600d 0x0c45 0x6024 @@ -370,6 +379,7 @@ HV7131D Hynix Semiconductor, Inc. MI-0343 Micron Technology, Inc. OV7630 OmniVision Technologies, Inc. PAS106B PixArt Imaging, Inc. +PAS202BCA PixArt Imaging, Inc. PAS202BCB PixArt Imaging, Inc. TAS5110C1B Taiwan Advanced Sensor Corporation TAS5130D1B Taiwan Advanced Sensor Corporation @@ -493,6 +503,7 @@ Many thanks to following persons for their contribute (listed in alphabetical order): - Luca Capello for the donation of a webcam; +- Philippe Coval for having helped testing the PAS202BCA image sensor; - Joao Rodrigo Fuzaro, Joao Limirio, Claudio Filho and Caio Begotti for the donation of a webcam; - Jon Hollstrom for the donation of a webcam; diff --git a/drivers/usb/media/Makefile b/drivers/usb/media/Makefile index 333bf09ca2d6..50e89a33b85e 100644 --- a/drivers/usb/media/Makefile +++ b/drivers/usb/media/Makefile @@ -2,7 +2,10 @@ # Makefile for USB Media drivers # -sn9c102-objs := sn9c102_core.o sn9c102_hv7131d.o sn9c102_mi0343.o sn9c102_ov7630.o sn9c102_pas106b.o sn9c102_pas202bcb.o sn9c102_tas5110c1b.o sn9c102_tas5130d1b.o +sn9c102-objs := sn9c102_core.o sn9c102_hv7131d.o sn9c102_mi0343.o \ + sn9c102_ov7630.o sn9c102_pas106b.o sn9c102_pas202bca.o \ + sn9c102_pas202bcb.o sn9c102_tas5110c1b.o \ + sn9c102_tas5130d1b.o et61x251-objs := et61x251_core.o et61x251_tas5130d1b.o zc0301-objs := zc0301_core.o zc0301_pas202bcb.o diff --git a/drivers/usb/media/sn9c102.h b/drivers/usb/media/sn9c102.h index 59e44be27cb4..1d70a62b9f23 100644 --- a/drivers/usb/media/sn9c102.h +++ b/drivers/usb/media/sn9c102.h @@ -34,7 +34,8 @@ #include #include #include -#include +#include +#include #include "sn9c102_sensor.h" @@ -51,6 +52,7 @@ #define SN9C102_ALTERNATE_SETTING 8 #define SN9C102_URB_TIMEOUT msecs_to_jiffies(2 * SN9C102_ISO_PACKETS) #define SN9C102_CTRL_TIMEOUT 300 +#define SN9C102_FRAME_TIMEOUT 2 /*****************************************************************************/ @@ -108,6 +110,7 @@ struct sn9c102_sysfs_attr { struct sn9c102_module_param { u8 force_munmap; + u16 frame_timeout; }; static DEFINE_MUTEX(sn9c102_sysfs_lock); @@ -117,7 +120,7 @@ struct sn9c102_device { struct video_device* v4ldev; enum sn9c102_bridge bridge; - struct sn9c102_sensor* sensor; + struct sn9c102_sensor sensor; struct usb_device* usbdev; struct urb* urb[SN9C102_URBS]; @@ -149,12 +152,21 @@ struct sn9c102_device { /*****************************************************************************/ +struct sn9c102_device* +sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id) +{ + if (usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id)) + return cam; + + return NULL; +} + + void sn9c102_attach_sensor(struct sn9c102_device* cam, struct sn9c102_sensor* sensor) { - cam->sensor = sensor; - cam->sensor->usbdev = cam->usbdev; + memcpy(&cam->sensor, sensor, sizeof(struct sn9c102_sensor)); } /*****************************************************************************/ @@ -197,7 +209,8 @@ do { \ #undef PDBG #define PDBG(fmt, args...) \ -dev_info(&cam->dev, "[%s:%d] " fmt "\n", __FUNCTION__, __LINE__ , ## args) +dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args) #undef PDBGG #define PDBGG(fmt, args...) do {;} while(0) /* placeholder */ diff --git a/drivers/usb/media/sn9c102_core.c b/drivers/usb/media/sn9c102_core.c index 30119080871b..4c6cc6395723 100644 --- a/drivers/usb/media/sn9c102_core.c +++ b/drivers/usb/media/sn9c102_core.c @@ -25,11 +25,9 @@ #include #include #include -#include #include #include #include -#include #include #include #include @@ -49,8 +47,8 @@ #define SN9C102_MODULE_AUTHOR "(C) 2004-2006 Luca Risolia" #define SN9C102_AUTHOR_EMAIL "" #define SN9C102_MODULE_LICENSE "GPL" -#define SN9C102_MODULE_VERSION "1:1.26" -#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 26) +#define SN9C102_MODULE_VERSION "1:1.27" +#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 27) /*****************************************************************************/ @@ -89,6 +87,15 @@ MODULE_PARM_DESC(force_munmap, "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"." "\n"); +static unsigned int frame_timeout[] = {[0 ... SN9C102_MAX_DEVICES-1] = + SN9C102_FRAME_TIMEOUT}; +module_param_array(frame_timeout, uint, NULL, 0644); +MODULE_PARM_DESC(frame_timeout, + "\n Timeout for a video frame in seconds." + "\nThis parameter is specific for each detected camera." + "\nDefault value is "__MODULE_STRING(SN9C102_FRAME_TIMEOUT)"." + "\n"); + #ifdef SN9C102_DEBUG static unsigned short debug = SN9C102_DEBUG_LEVEL; module_param(debug, ushort, 0644); @@ -128,8 +135,8 @@ static u32 sn9c102_request_buffers(struct sn9c102_device* cam, u32 count, enum sn9c102_io_method io) { - struct v4l2_pix_format* p = &(cam->sensor->pix_format); - struct v4l2_rect* r = &(cam->sensor->cropcap.bounds); + struct v4l2_pix_format* p = &(cam->sensor.pix_format); + struct v4l2_rect* r = &(cam->sensor.cropcap.bounds); const size_t imagesize = cam->module_param.force_munmap || io == IO_READ ? (p->width * p->height * p->priv) / 8 : @@ -449,19 +456,13 @@ sn9c102_i2c_try_write(struct sn9c102_device* cam, int sn9c102_i2c_read(struct sn9c102_device* cam, u8 address) { - if (!cam->sensor) - return -1; - - return sn9c102_i2c_try_read(cam, cam->sensor, address); + return sn9c102_i2c_try_read(cam, &cam->sensor, address); } int sn9c102_i2c_write(struct sn9c102_device* cam, u8 address, u8 value) { - if (!cam->sensor) - return -1; - - return sn9c102_i2c_try_write(cam, cam->sensor, address, value); + return sn9c102_i2c_try_write(cam, &cam->sensor, address, value); } /*****************************************************************************/ @@ -505,7 +506,7 @@ sn9c102_find_eof_header(struct sn9c102_device* cam, void* mem, size_t len) size_t eoflen = sizeof(sn9c102_eof_header_t), i; unsigned j, n = sizeof(sn9c102_eof_header) / eoflen; - if (cam->sensor->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X) + if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X) return NULL; /* EOF header does not exist in compressed data */ for (i = 0; (len >= eoflen) && (i <= len - eoflen); i++) @@ -535,7 +536,7 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs) if ((*f)) (*f)->state = F_QUEUED; DBG(3, "Stream interrupted"); - wake_up_interruptible(&cam->wait_stream); + wake_up(&cam->wait_stream); } if (cam->state & DEV_DISCONNECTED) @@ -553,9 +554,9 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs) (*f) = list_entry(cam->inqueue.next, struct sn9c102_frame_t, frame); - imagesize = (cam->sensor->pix_format.width * - cam->sensor->pix_format.height * - cam->sensor->pix_format.priv) / 8; + imagesize = (cam->sensor.pix_format.width * + cam->sensor.pix_format.height * + cam->sensor.pix_format.priv) / 8; soflen = (cam->bridge) == BRIDGE_SN9C103 ? sizeof(sn9c103_sof_header_t) : @@ -579,7 +580,7 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs) redo: sof = sn9c102_find_sof_header(cam, pos, len); - if (!sof) { + if (likely(!sof)) { eof = sn9c102_find_eof_header(cam, pos, len); if ((*f)->state == F_GRABBING) { end_of_frame: @@ -589,8 +590,9 @@ end_of_frame: img = (eof > pos) ? eof - pos - 1 : 0; if ((*f)->buf.bytesused+img > imagesize) { - u32 b = (*f)->buf.bytesused + img - - imagesize; + u32 b; + b = (*f)->buf.bytesused + img - + imagesize; img = imagesize - (*f)->buf.bytesused; DBG(3, "Expected EOF not found: " "video frame cut"); @@ -608,9 +610,10 @@ end_of_frame: (*f)->buf.bytesused += img; if ((*f)->buf.bytesused == imagesize || - (cam->sensor->pix_format.pixelformat == + (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X && eof)) { - u32 b = (*f)->buf.bytesused; + u32 b; + b = (*f)->buf.bytesused; (*f)->state = F_DONE; (*f)->buf.sequence= ++cam->frame_count; spin_lock(&cam->queue_lock); @@ -667,7 +670,7 @@ start_of_frame: if (eof && eof < sof) goto end_of_frame; /* (1) */ else { - if (cam->sensor->pix_format.pixelformat == + if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X) { eof = sof - soflen; goto end_of_frame; @@ -808,20 +811,21 @@ static int sn9c102_stop_transfer(struct sn9c102_device* cam) static int sn9c102_stream_interrupt(struct sn9c102_device* cam) { - int err = 0; + long timeout; cam->stream = STREAM_INTERRUPT; - err = wait_event_timeout(cam->wait_stream, - (cam->stream == STREAM_OFF) || - (cam->state & DEV_DISCONNECTED), - SN9C102_URB_TIMEOUT); + timeout = wait_event_timeout(cam->wait_stream, + (cam->stream == STREAM_OFF) || + (cam->state & DEV_DISCONNECTED), + SN9C102_URB_TIMEOUT); if (cam->state & DEV_DISCONNECTED) return -ENODEV; - else if (err) { + else if (cam->stream != STREAM_OFF) { cam->state |= DEV_MISCONFIGURED; - DBG(1, "The camera is misconfigured. To use it, close and " - "open /dev/video%d again.", cam->v4ldev->minor); - return err; + DBG(1, "URB timeout reached. The camera is misconfigured. " + "To use it, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -EIO; } return 0; @@ -1057,7 +1061,7 @@ static ssize_t sn9c102_show_i2c_val(struct class_device* cd, char* buf) return -ENODEV; } - if (!(cam->sensor->sysfs_ops & SN9C102_I2C_READ)) { + if (!(cam->sensor.sysfs_ops & SN9C102_I2C_READ)) { mutex_unlock(&sn9c102_sysfs_lock); return -ENOSYS; } @@ -1094,7 +1098,7 @@ sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len) return -ENODEV; } - if (!(cam->sensor->sysfs_ops & SN9C102_I2C_WRITE)) { + if (!(cam->sensor.sysfs_ops & SN9C102_I2C_WRITE)) { mutex_unlock(&sn9c102_sysfs_lock); return -ENOSYS; } @@ -1249,7 +1253,7 @@ static void sn9c102_create_sysfs(struct sn9c102_device* cam) video_device_create_file(v4ldev, &class_device_attr_blue); video_device_create_file(v4ldev, &class_device_attr_red); } - if (cam->sensor && cam->sensor->sysfs_ops) { + if (cam->sensor.sysfs_ops) { video_device_create_file(v4ldev, &class_device_attr_i2c_reg); video_device_create_file(v4ldev, &class_device_attr_i2c_val); } @@ -1312,7 +1316,7 @@ static int sn9c102_set_scale(struct sn9c102_device* cam, u8 scale) static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect) { - struct sn9c102_sensor* s = cam->sensor; + struct sn9c102_sensor* s = &cam->sensor; u8 h_start = (u8)(rect->left - s->cropcap.bounds.left), v_start = (u8)(rect->top - s->cropcap.bounds.top), h_size = (u8)(rect->width / 16), @@ -1335,7 +1339,7 @@ static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect) static int sn9c102_init(struct sn9c102_device* cam) { - struct sn9c102_sensor* s = cam->sensor; + struct sn9c102_sensor* s = &cam->sensor; struct v4l2_control ctrl; struct v4l2_queryctrl *qctrl; struct v4l2_rect* rect; @@ -1428,6 +1432,8 @@ static void sn9c102_release_resources(struct sn9c102_device* cam) video_set_drvdata(cam->v4ldev, NULL); video_unregister_device(cam->v4ldev); + usb_put_dev(cam->usbdev); + mutex_unlock(&sn9c102_sysfs_lock); kfree(cam->control_buffer); @@ -1541,6 +1547,7 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); struct sn9c102_frame_t* f, * i; unsigned long lock_flags; + long timeout; int err = 0; if (mutex_lock_interruptible(&cam->fileop_mutex)) @@ -1592,20 +1599,22 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) mutex_unlock(&cam->fileop_mutex); return -EAGAIN; } - err = wait_event_interruptible - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED) ); - if (err) { + timeout = wait_event_interruptible_timeout + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); + if (timeout < 0) { mutex_unlock(&cam->fileop_mutex); - return err; + return timeout; } if (cam->state & DEV_DISCONNECTED) { mutex_unlock(&cam->fileop_mutex); return -ENODEV; } - if (cam->state & DEV_MISCONFIGURED) { + if (!timeout || (cam->state & DEV_MISCONFIGURED)) { mutex_unlock(&cam->fileop_mutex); return -EIO; } @@ -1816,6 +1825,7 @@ sn9c102_vidioc_enuminput(struct sn9c102_device* cam, void __user * arg) memset(&i, 0, sizeof(i)); strcpy(i.name, "Camera"); + i.type = V4L2_INPUT_TYPE_CAMERA; if (copy_to_user(arg, &i, sizeof(i))) return -EFAULT; @@ -1825,7 +1835,19 @@ sn9c102_vidioc_enuminput(struct sn9c102_device* cam, void __user * arg) static int -sn9c102_vidioc_gs_input(struct sn9c102_device* cam, void __user * arg) +sn9c102_vidioc_g_input(struct sn9c102_device* cam, void __user * arg) +{ + int index = 0; + + if (copy_to_user(arg, &index, sizeof(index))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_s_input(struct sn9c102_device* cam, void __user * arg) { int index; @@ -1842,7 +1864,7 @@ sn9c102_vidioc_gs_input(struct sn9c102_device* cam, void __user * arg) static int sn9c102_vidioc_query_ctrl(struct sn9c102_device* cam, void __user * arg) { - struct sn9c102_sensor* s = cam->sensor; + struct sn9c102_sensor* s = &cam->sensor; struct v4l2_queryctrl qc; u8 i; @@ -1864,7 +1886,7 @@ sn9c102_vidioc_query_ctrl(struct sn9c102_device* cam, void __user * arg) static int sn9c102_vidioc_g_ctrl(struct sn9c102_device* cam, void __user * arg) { - struct sn9c102_sensor* s = cam->sensor; + struct sn9c102_sensor* s = &cam->sensor; struct v4l2_control ctrl; int err = 0; u8 i; @@ -1896,7 +1918,7 @@ exit: static int sn9c102_vidioc_s_ctrl(struct sn9c102_device* cam, void __user * arg) { - struct sn9c102_sensor* s = cam->sensor; + struct sn9c102_sensor* s = &cam->sensor; struct v4l2_control ctrl; u8 i; int err = 0; @@ -1909,6 +1931,8 @@ sn9c102_vidioc_s_ctrl(struct sn9c102_device* cam, void __user * arg) for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) if (ctrl.id == s->qctrl[i].id) { + if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED) + return -EINVAL; if (ctrl.value < s->qctrl[i].minimum || ctrl.value > s->qctrl[i].maximum) return -ERANGE; @@ -1931,7 +1955,7 @@ sn9c102_vidioc_s_ctrl(struct sn9c102_device* cam, void __user * arg) static int sn9c102_vidioc_cropcap(struct sn9c102_device* cam, void __user * arg) { - struct v4l2_cropcap* cc = &(cam->sensor->cropcap); + struct v4l2_cropcap* cc = &(cam->sensor.cropcap); cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; cc->pixelaspect.numerator = 1; @@ -1947,7 +1971,7 @@ sn9c102_vidioc_cropcap(struct sn9c102_device* cam, void __user * arg) static int sn9c102_vidioc_g_crop(struct sn9c102_device* cam, void __user * arg) { - struct sn9c102_sensor* s = cam->sensor; + struct sn9c102_sensor* s = &cam->sensor; struct v4l2_crop crop = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, }; @@ -1964,7 +1988,7 @@ sn9c102_vidioc_g_crop(struct sn9c102_device* cam, void __user * arg) static int sn9c102_vidioc_s_crop(struct sn9c102_device* cam, void __user * arg) { - struct sn9c102_sensor* s = cam->sensor; + struct sn9c102_sensor* s = &cam->sensor; struct v4l2_crop crop; struct v4l2_rect* rect; struct v4l2_rect* bounds = &(s->cropcap.bounds); @@ -2105,7 +2129,7 @@ static int sn9c102_vidioc_g_fmt(struct sn9c102_device* cam, void __user * arg) { struct v4l2_format format; - struct v4l2_pix_format* pfmt = &(cam->sensor->pix_format); + struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format); if (copy_from_user(&format, arg, sizeof(format))) return -EFAULT; @@ -2130,7 +2154,7 @@ static int sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd, void __user * arg) { - struct sn9c102_sensor* s = cam->sensor; + struct sn9c102_sensor* s = &cam->sensor; struct v4l2_format format; struct v4l2_pix_format* pix; struct v4l2_pix_format* pfmt = &(s->pix_format); @@ -2417,7 +2441,7 @@ sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp, struct v4l2_buffer b; struct sn9c102_frame_t *f; unsigned long lock_flags; - int err = 0; + long timeout; if (copy_from_user(&b, arg, sizeof(b))) return -EFAULT; @@ -2430,16 +2454,18 @@ sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp, return -EINVAL; if (filp->f_flags & O_NONBLOCK) return -EAGAIN; - err = wait_event_interruptible - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED) ); - if (err) - return err; + timeout = wait_event_interruptible_timeout + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); + if (timeout < 0) + return timeout; if (cam->state & DEV_DISCONNECTED) return -ENODEV; - if (cam->state & DEV_MISCONFIGURED) + if (!timeout || (cam->state & DEV_MISCONFIGURED)) return -EIO; } @@ -2571,8 +2597,10 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, return sn9c102_vidioc_enuminput(cam, arg); case VIDIOC_G_INPUT: + return sn9c102_vidioc_g_input(cam, arg); + case VIDIOC_S_INPUT: - return sn9c102_vidioc_gs_input(cam, arg); + return sn9c102_vidioc_s_input(cam, arg); case VIDIOC_QUERYCTRL: return sn9c102_vidioc_query_ctrl(cam, arg); @@ -2752,10 +2780,10 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) break; } - if (!err && cam->sensor) { - DBG(2, "%s image sensor detected", cam->sensor->name); + if (!err) { + DBG(2, "%s image sensor detected", cam->sensor.name); DBG(3, "Support for %s maintained by %s", - cam->sensor->name, cam->sensor->maintainer); + cam->sensor.name, cam->sensor.maintainer); } else { DBG(1, "No supported image sensor detected"); err = -ENODEV; @@ -2793,6 +2821,7 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor); cam->module_param.force_munmap = force_munmap[dev_nr]; + cam->module_param.frame_timeout = frame_timeout[dev_nr]; dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0; @@ -2841,7 +2870,8 @@ static void sn9c102_usb_disconnect(struct usb_interface* intf) sn9c102_stop_transfer(cam); cam->state |= DEV_DISCONNECTED; wake_up_interruptible(&cam->wait_frame); - wake_up_interruptible(&cam->wait_stream); + wake_up(&cam->wait_stream); + usb_get_dev(cam->usbdev); } else { cam->state |= DEV_DISCONNECTED; sn9c102_release_resources(cam); diff --git a/drivers/usb/media/sn9c102_ov7630.c b/drivers/usb/media/sn9c102_ov7630.c index 4a36519b5af4..42852b7cb042 100644 --- a/drivers/usb/media/sn9c102_ov7630.c +++ b/drivers/usb/media/sn9c102_ov7630.c @@ -34,8 +34,8 @@ static int ov7630_init(struct sn9c102_device* cam) err += sn9c102_write_reg(cam, 0x0f, 0x18); err += sn9c102_write_reg(cam, 0x50, 0x19); - err += sn9c102_i2c_write(cam, 0x12, 0x8d); - err += sn9c102_i2c_write(cam, 0x11, 0x00); + err += sn9c102_i2c_write(cam, 0x12, 0x80); + err += sn9c102_i2c_write(cam, 0x11, 0x01); err += sn9c102_i2c_write(cam, 0x15, 0x34); err += sn9c102_i2c_write(cam, 0x16, 0x03); err += sn9c102_i2c_write(cam, 0x17, 0x1c); @@ -43,12 +43,14 @@ static int ov7630_init(struct sn9c102_device* cam) err += sn9c102_i2c_write(cam, 0x19, 0x06); err += sn9c102_i2c_write(cam, 0x1a, 0xf6); err += sn9c102_i2c_write(cam, 0x1b, 0x04); - err += sn9c102_i2c_write(cam, 0x20, 0x44); + err += sn9c102_i2c_write(cam, 0x20, 0xf6); err += sn9c102_i2c_write(cam, 0x23, 0xee); err += sn9c102_i2c_write(cam, 0x26, 0xa0); err += sn9c102_i2c_write(cam, 0x27, 0x9a); - err += sn9c102_i2c_write(cam, 0x28, 0x20); + err += sn9c102_i2c_write(cam, 0x28, 0xa0); err += sn9c102_i2c_write(cam, 0x29, 0x30); + err += sn9c102_i2c_write(cam, 0x2a, 0xa0); + err += sn9c102_i2c_write(cam, 0x2b, 0x1f); err += sn9c102_i2c_write(cam, 0x2f, 0x3d); err += sn9c102_i2c_write(cam, 0x30, 0x24); err += sn9c102_i2c_write(cam, 0x32, 0x86); @@ -80,7 +82,7 @@ static int ov7630_set_ctrl(struct sn9c102_device* cam, err += sn9c102_i2c_write(cam, 0x02, ctrl->value); break; case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_write(cam, 0x03, ctrl->value); + err += sn9c102_i2c_write(cam, 0x01, ctrl->value); break; case V4L2_CID_GAIN: err += sn9c102_i2c_write(cam, 0x00, ctrl->value); @@ -108,7 +110,7 @@ static int ov7630_set_ctrl(struct sn9c102_device* cam, err += sn9c102_i2c_write(cam, 0x0d, ctrl->value); break; case V4L2_CID_AUTO_WHITE_BALANCE: - err += sn9c102_i2c_write(cam, 0x12, (ctrl->value << 2) | 0x09); + err += sn9c102_i2c_write(cam, 0x12, (ctrl->value << 2) | 0x78); break; case V4L2_CID_AUTOGAIN: err += sn9c102_i2c_write(cam, 0x13, ctrl->value); @@ -371,26 +373,29 @@ static struct sn9c102_sensor ov7630 = { int sn9c102_probe_ov7630(struct sn9c102_device* cam) { + const struct usb_device_id ov7630_id_table[] = { + { USB_DEVICE(0x0c45, 0x602c), }, + { USB_DEVICE(0x0c45, 0x602d), }, + { USB_DEVICE(0x0c45, 0x608f), }, + { USB_DEVICE(0x0c45, 0x60b0), }, + { } + }; int err = 0; - sn9c102_attach_sensor(cam, &ov7630); - - if (le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x602c && - le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x602d && - le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x608f && - le16_to_cpu(ov7630.usbdev->descriptor.idProduct) != 0x60b0) + if (!sn9c102_match_id(cam, ov7630_id_table)) return -ENODEV; err += sn9c102_write_reg(cam, 0x01, 0x01); err += sn9c102_write_reg(cam, 0x00, 0x01); err += sn9c102_write_reg(cam, 0x28, 0x17); - if (err) return -EIO; - err += sn9c102_i2c_write(cam, 0x0b, 0); + err += sn9c102_i2c_try_write(cam, &ov7630, 0x0b, 0); if (err) return -ENODEV; + sn9c102_attach_sensor(cam, &ov7630); + return 0; } diff --git a/drivers/usb/media/sn9c102_pas202bca.c b/drivers/usb/media/sn9c102_pas202bca.c new file mode 100644 index 000000000000..3453237055bb --- /dev/null +++ b/drivers/usb/media/sn9c102_pas202bca.c @@ -0,0 +1,238 @@ +/*************************************************************************** + * Plug-in for PAS202BCA image sensor connected to the SN9C10x PC Camera * + * Controllers * + * * + * Copyright (C) 2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include +#include "sn9c102_sensor.h" + + +static struct sn9c102_sensor pas202bca; + + +static int pas202bca_init(struct sn9c102_device* cam) +{ + int err = 0; + + err += sn9c102_write_reg(cam, 0x00, 0x10); + err += sn9c102_write_reg(cam, 0x00, 0x11); + err += sn9c102_write_reg(cam, 0x00, 0x14); + err += sn9c102_write_reg(cam, 0x20, 0x17); + err += sn9c102_write_reg(cam, 0x30, 0x19); + err += sn9c102_write_reg(cam, 0x09, 0x18); + + err += sn9c102_i2c_write(cam, 0x02, 0x14); + err += sn9c102_i2c_write(cam, 0x03, 0x40); + err += sn9c102_i2c_write(cam, 0x0d, 0x2c); + err += sn9c102_i2c_write(cam, 0x0e, 0x01); + err += sn9c102_i2c_write(cam, 0x0f, 0xa9); + err += sn9c102_i2c_write(cam, 0x10, 0x08); + err += sn9c102_i2c_write(cam, 0x13, 0x63); + err += sn9c102_i2c_write(cam, 0x15, 0x70); + err += sn9c102_i2c_write(cam, 0x11, 0x01); + + msleep(400); + + return err; +} + + +static int pas202bca_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x24, 0x17); + else + err += sn9c102_write_reg(cam, 0x20, 0x17); + + return err; +} + + +static int pas202bca_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x04, ctrl->value >> 6); + err += sn9c102_i2c_write(cam, 0x05, ctrl->value & 0x3f); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_write(cam, 0x09, ctrl->value); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_write(cam, 0x07, ctrl->value); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x10, ctrl->value); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_write(cam, 0x08, ctrl->value); + break; + case SN9C102_V4L2_CID_DAC_MAGNITUDE: + err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); + break; + default: + return -EINVAL; + } + err += sn9c102_i2c_write(cam, 0x11, 0x01); + + return err ? -EIO : 0; +} + + +static int pas202bca_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = &pas202bca; + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 3, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static struct sn9c102_sensor pas202bca = { + .name = "PAS202BCA", + .maintainer = "Luca Risolia ", + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x40, + .init = &pas202bca_init, + .qctrl = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x01e5, + .maximum = 0x3fff, + .step = 0x0001, + .default_value = 0x01e5, + .flags = 0, + }, + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x0c, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x01, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x05, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_DAC_MAGNITUDE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "DAC magnitude", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x04, + .flags = 0, + }, + }, + .set_ctrl = &pas202bca_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &pas202bca_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &pas202bca_set_pix_format +}; + + +int sn9c102_probe_pas202bca(struct sn9c102_device* cam) +{ + const struct usb_device_id pas202bca_id_table[] = { + { USB_DEVICE(0x0c45, 0x60af), }, + { } + }; + int err = 0; + + if (!sn9c102_match_id(cam,pas202bca_id_table)) + return -ENODEV; + + err += sn9c102_write_reg(cam, 0x01, 0x01); + err += sn9c102_write_reg(cam, 0x40, 0x01); + err += sn9c102_write_reg(cam, 0x28, 0x17); + if (err) + return -EIO; + + if (sn9c102_i2c_try_write(cam, &pas202bca, 0x10, 0)) /* try to write */ + return -ENODEV; + + sn9c102_attach_sensor(cam, &pas202bca); + + return 0; +} diff --git a/drivers/usb/media/sn9c102_pas202bcb.c b/drivers/usb/media/sn9c102_pas202bcb.c index 5ca54c7daaf2..d068616ab337 100644 --- a/drivers/usb/media/sn9c102_pas202bcb.c +++ b/drivers/usb/media/sn9c102_pas202bcb.c @@ -263,7 +263,7 @@ static struct sn9c102_sensor pas202bcb = { int sn9c102_probe_pas202bcb(struct sn9c102_device* cam) -{ +{ int r0 = 0, r1 = 0, err = 0; unsigned int pid = 0; diff --git a/drivers/usb/media/sn9c102_sensor.h b/drivers/usb/media/sn9c102_sensor.h index 7d953b24f2f2..2afd9e9d09bb 100644 --- a/drivers/usb/media/sn9c102_sensor.h +++ b/drivers/usb/media/sn9c102_sensor.h @@ -66,6 +66,7 @@ extern int sn9c102_probe_hv7131d(struct sn9c102_device* cam); extern int sn9c102_probe_mi0343(struct sn9c102_device* cam); extern int sn9c102_probe_ov7630(struct sn9c102_device* cam); extern int sn9c102_probe_pas106b(struct sn9c102_device* cam); +extern int sn9c102_probe_pas202bca(struct sn9c102_device* cam); extern int sn9c102_probe_pas202bcb(struct sn9c102_device* cam); extern int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam); extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam); @@ -81,12 +82,17 @@ static int (*sn9c102_sensor_table[])(struct sn9c102_device*) = { \ &sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */ \ &sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */ \ &sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */ \ + &sn9c102_probe_pas202bca, /* detection mostly based on USB pid/vid */ \ &sn9c102_probe_ov7630, /* detection mostly based on USB pid/vid */ \ &sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */ \ &sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */ \ NULL, \ }; +/* Device identification */ +extern struct sn9c102_device* +sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id); + /* Attach a probed sensor to the camera. */ extern void sn9c102_attach_sensor(struct sn9c102_device* cam, @@ -108,6 +114,7 @@ sn9c102_attach_sensor(struct sn9c102_device* cam, static const struct usb_device_id sn9c102_id_table[] = { \ { USB_DEVICE(0x0c45, 0x6001), }, /* TAS5110C1B */ \ { USB_DEVICE(0x0c45, 0x6005), }, /* TAS5110C1B */ \ + { USB_DEVICE(0x0c45, 0x6007), }, \ { USB_DEVICE(0x0c45, 0x6009), }, /* PAS106B */ \ { USB_DEVICE(0x0c45, 0x600d), }, /* PAS106B */ \ { USB_DEVICE(0x0c45, 0x6024), }, \ @@ -126,7 +133,7 @@ static const struct usb_device_id sn9c102_id_table[] = { \ { SN9C102_USB_DEVICE(0x0c45, 0x6088, 0xff), }, \ { SN9C102_USB_DEVICE(0x0c45, 0x608a, 0xff), }, \ { SN9C102_USB_DEVICE(0x0c45, 0x608b, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x608c, 0xff), }, /* HV7131x */ \ + { SN9C102_USB_DEVICE(0x0c45, 0x608c, 0xff), }, /* HV7131/R */ \ { SN9C102_USB_DEVICE(0x0c45, 0x608e, 0xff), }, /* CIS-VF10 */ \ { SN9C102_USB_DEVICE(0x0c45, 0x608f, 0xff), }, /* OV7630 */ \ { SN9C102_USB_DEVICE(0x0c45, 0x60a0, 0xff), }, \ @@ -359,12 +366,6 @@ struct sn9c102_sensor { error code without rolling back. */ - const struct usb_device* usbdev; - /* - Points to the usb_device struct after the sensor is attached. - Do not touch unless you know what you are doing. - */ - /* Do NOT write to the data below, it's READ ONLY. It is used by the core module to store successfully updated values of the above diff --git a/drivers/usb/media/sn9c102_tas5110c1b.c b/drivers/usb/media/sn9c102_tas5110c1b.c index 32ddf236cafe..2e08c552f40a 100644 --- a/drivers/usb/media/sn9c102_tas5110c1b.c +++ b/drivers/usb/media/sn9c102_tas5110c1b.c @@ -142,14 +142,18 @@ static struct sn9c102_sensor tas5110c1b = { int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam) { - /* This sensor has no identifiers, so let's attach it anyway */ - sn9c102_attach_sensor(cam, &tas5110c1b); + const struct usb_device_id tas5110c1b_id_table[] = { + { USB_DEVICE(0x0c45, 0x6001), }, + { USB_DEVICE(0x0c45, 0x6005), }, + { USB_DEVICE(0x0c45, 0x60ab), }, + { } + }; /* Sensor detection is based on USB pid/vid */ - if (le16_to_cpu(tas5110c1b.usbdev->descriptor.idProduct) != 0x6001 && - le16_to_cpu(tas5110c1b.usbdev->descriptor.idProduct) != 0x6005 && - le16_to_cpu(tas5110c1b.usbdev->descriptor.idProduct) != 0x60ab) + if (!sn9c102_match_id(cam, tas5110c1b_id_table)) return -ENODEV; + sn9c102_attach_sensor(cam, &tas5110c1b); + return 0; } diff --git a/drivers/usb/media/sn9c102_tas5130d1b.c b/drivers/usb/media/sn9c102_tas5130d1b.c index a0728f0ae00c..c7b339740bbf 100644 --- a/drivers/usb/media/sn9c102_tas5130d1b.c +++ b/drivers/usb/media/sn9c102_tas5130d1b.c @@ -153,13 +153,17 @@ static struct sn9c102_sensor tas5130d1b = { int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam) { - /* This sensor has no identifiers, so let's attach it anyway */ - sn9c102_attach_sensor(cam, &tas5130d1b); + const struct usb_device_id tas5130d1b_id_table[] = { + { USB_DEVICE(0x0c45, 0x6025), }, + { USB_DEVICE(0x0c45, 0x60aa), }, + { } + }; /* Sensor detection is based on USB pid/vid */ - if (le16_to_cpu(tas5130d1b.usbdev->descriptor.idProduct) != 0x6025 && - le16_to_cpu(tas5130d1b.usbdev->descriptor.idProduct) != 0x60aa) + if (!sn9c102_match_id(cam, tas5130d1b_id_table)) return -ENODEV; + sn9c102_attach_sensor(cam, &tas5130d1b); + return 0; } -- cgit v1.2.3-59-g8ed1b From ccad7789d5e557644d1c866b018394872af0ec5b Mon Sep 17 00:00:00 2001 From: Luca Risolia Date: Sat, 25 Feb 2006 06:54:18 +0000 Subject: [PATCH] USB: ET61X[12]51 driver updates USB: ET61X[12]51 driver updates Changes: + new, - removed, * cleanup, @ bugfix @ Fix stream_interrupt() @ Fix vidioc_enum_input() and split vidioc_gs_input() @ Need usb_get|put_dev() when disconnecting, if the device is open * Use wait_event_interruptible_timeout() instead of wait_event_interruptible() when waiting for video frames * replace wake_up_interruptible(&wait_stream) with wake_up(&wait_stream) * Cleanups and updates in the documentation * Use mutexes instead of semaphores + Use per-device sensor structures + Add support for PAS202BCA image sensors + Add frame_timeout module parameter Signed-off-by: Luca Risolia Signed-off-by: Greg Kroah-Hartman --- Documentation/usb/et61x251.txt | 10 +- drivers/usb/media/et61x251.h | 28 ++- drivers/usb/media/et61x251_core.c | 321 +++++++++++++++++--------------- drivers/usb/media/et61x251_sensor.h | 5 +- drivers/usb/media/et61x251_tas5130d1b.c | 10 +- 5 files changed, 213 insertions(+), 161 deletions(-) diff --git a/Documentation/usb/et61x251.txt b/Documentation/usb/et61x251.txt index b44dda407ce2..29340282ab5f 100644 --- a/Documentation/usb/et61x251.txt +++ b/Documentation/usb/et61x251.txt @@ -176,6 +176,14 @@ Description: Force the application to unmap previously mapped buffer memory 1 = force memory unmapping (save memory) Default: 0 ------------------------------------------------------------------------------- +Name: frame_timeout +Type: uint array (min = 0, max = 64) +Syntax: +Description: Timeout for a video frame in seconds. This parameter is + specific for each detected camera. This parameter can be + changed at runtime thanks to the /sys filesystem interface. +Default: 2 +------------------------------------------------------------------------------- Name: debug Type: ushort Syntax: @@ -266,7 +274,7 @@ the V4L2 interface. 10. Notes for V4L2 application developers -======================================== +========================================= This driver follows the V4L2 API specifications. In particular, it enforces two rules: diff --git a/drivers/usb/media/et61x251.h b/drivers/usb/media/et61x251.h index 652238f329f3..eee8afc9be72 100644 --- a/drivers/usb/media/et61x251.h +++ b/drivers/usb/media/et61x251.h @@ -33,7 +33,9 @@ #include #include #include -#include +#include +#include +#include #include "et61x251_sensor.h" @@ -51,6 +53,7 @@ #define ET61X251_ALTERNATE_SETTING 13 #define ET61X251_URB_TIMEOUT msecs_to_jiffies(2 * ET61X251_ISO_PACKETS) #define ET61X251_CTRL_TIMEOUT 100 +#define ET61X251_FRAME_TIMEOUT 2 /*****************************************************************************/ @@ -127,15 +130,16 @@ struct et61x251_sysfs_attr { struct et61x251_module_param { u8 force_munmap; + u16 frame_timeout; }; -static DECLARE_MUTEX(et61x251_sysfs_lock); +static DEFINE_MUTEX(et61x251_sysfs_lock); static DECLARE_RWSEM(et61x251_disconnect); struct et61x251_device { struct video_device* v4ldev; - struct et61x251_sensor* sensor; + struct et61x251_sensor sensor; struct usb_device* usbdev; struct urb* urb[ET61X251_URBS]; @@ -157,19 +161,28 @@ struct et61x251_device { enum et61x251_dev_state state; u8 users; - struct semaphore dev_sem, fileop_sem; + struct mutex dev_mutex, fileop_mutex; spinlock_t queue_lock; wait_queue_head_t open, wait_frame, wait_stream; }; /*****************************************************************************/ +struct et61x251_device* +et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id) +{ + if (usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id)) + return cam; + + return NULL; +} + + void et61x251_attach_sensor(struct et61x251_device* cam, struct et61x251_sensor* sensor) { - cam->sensor = sensor; - cam->sensor->usbdev = cam->usbdev; + memcpy(&cam->sensor, sensor, sizeof(struct et61x251_sensor)); } /*****************************************************************************/ @@ -212,7 +225,8 @@ do { \ #undef PDBG #define PDBG(fmt, args...) \ -dev_info(&cam->dev, "[%s:%d] " fmt "\n", __FUNCTION__, __LINE__ , ## args) +dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args) #undef PDBGG #define PDBGG(fmt, args...) do {;} while(0) /* placeholder */ diff --git a/drivers/usb/media/et61x251_core.c b/drivers/usb/media/et61x251_core.c index 2c0171a5ad62..7cc01b828b3d 100644 --- a/drivers/usb/media/et61x251_core.c +++ b/drivers/usb/media/et61x251_core.c @@ -25,11 +25,9 @@ #include #include #include -#include #include #include #include -#include #include #include #include @@ -50,8 +48,8 @@ #define ET61X251_MODULE_AUTHOR "(C) 2006 Luca Risolia" #define ET61X251_AUTHOR_EMAIL "" #define ET61X251_MODULE_LICENSE "GPL" -#define ET61X251_MODULE_VERSION "1:1.01" -#define ET61X251_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 1) +#define ET61X251_MODULE_VERSION "1:1.02" +#define ET61X251_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 2) /*****************************************************************************/ @@ -90,6 +88,16 @@ MODULE_PARM_DESC(force_munmap, "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"." "\n"); +static unsigned int frame_timeout[] = {[0 ... ET61X251_MAX_DEVICES-1] = + ET61X251_FRAME_TIMEOUT}; +module_param_array(frame_timeout, uint, NULL, 0644); +MODULE_PARM_DESC(frame_timeout, + "\n Timeout for a video frame in seconds." + "\nThis parameter is specific for each detected camera." + "\nDefault value is " + __MODULE_STRING(ET61X251_FRAME_TIMEOUT)"." + "\n"); + #ifdef ET61X251_DEBUG static unsigned short debug = ET61X251_DEBUG_LEVEL; module_param(debug, ushort, 0644); @@ -111,8 +119,8 @@ static u32 et61x251_request_buffers(struct et61x251_device* cam, u32 count, enum et61x251_io_method io) { - struct v4l2_pix_format* p = &(cam->sensor->pix_format); - struct v4l2_rect* r = &(cam->sensor->cropcap.bounds); + struct v4l2_pix_format* p = &(cam->sensor.pix_format); + struct v4l2_rect* r = &(cam->sensor.cropcap.bounds); const size_t imagesize = cam->module_param.force_munmap || io == IO_READ ? (p->width * p->height * p->priv) / 8 : @@ -268,8 +276,8 @@ et61x251_i2c_try_read(struct et61x251_device* cam, int err = 0, res; data[0] = address; - data[1] = cam->sensor->i2c_slave_id; - data[2] = cam->sensor->rsta | 0x10; + data[1] = cam->sensor.i2c_slave_id; + data[2] = cam->sensor.rsta | 0x10; data[3] = !(et61x251_read_reg(cam, 0x8b) & 0x02); res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, 0, 0x88, data, 4, ET61X251_CTRL_TIMEOUT); @@ -301,8 +309,8 @@ et61x251_i2c_try_write(struct et61x251_device* cam, int err = 0, res; data[0] = address; - data[1] = cam->sensor->i2c_slave_id; - data[2] = cam->sensor->rsta | 0x12; + data[1] = cam->sensor.i2c_slave_id; + data[2] = cam->sensor.rsta | 0x12; res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, 0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT); if (res < 0) @@ -334,9 +342,6 @@ et61x251_i2c_raw_write(struct et61x251_device* cam, u8 n, u8 data1, u8 data2, u8* data = cam->control_buffer; int err = 0, res; - if (!cam->sensor) - return -1; - data[0] = data2; data[1] = data3; data[2] = data4; @@ -350,8 +355,8 @@ et61x251_i2c_raw_write(struct et61x251_device* cam, u8 n, u8 data1, u8 data2, err += res; data[0] = address; - data[1] = cam->sensor->i2c_slave_id; - data[2] = cam->sensor->rsta | 0x02 | (n << 4); + data[1] = cam->sensor.i2c_slave_id; + data[2] = cam->sensor.rsta | 0x02 | (n << 4); res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, 0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT); if (res < 0) @@ -364,11 +369,11 @@ et61x251_i2c_raw_write(struct et61x251_device* cam, u8 n, u8 data1, u8 data2, if (res < 0) err += res; - err += et61x251_i2c_wait(cam, cam->sensor); + err += et61x251_i2c_wait(cam, &cam->sensor); if (err) DBG(3, "I2C raw write failed for %s image sensor", - cam->sensor->name); + cam->sensor.name); PDBGG("I2C raw write: %u bytes, address = 0x%02X, data1 = 0x%02X, " "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X," @@ -382,19 +387,13 @@ et61x251_i2c_raw_write(struct et61x251_device* cam, u8 n, u8 data1, u8 data2, int et61x251_i2c_read(struct et61x251_device* cam, u8 address) { - if (!cam->sensor) - return -1; - - return et61x251_i2c_try_read(cam, cam->sensor, address); + return et61x251_i2c_try_read(cam, &cam->sensor, address); } int et61x251_i2c_write(struct et61x251_device* cam, u8 address, u8 value) { - if (!cam->sensor) - return -1; - - return et61x251_i2c_try_write(cam, cam->sensor, address, value); + return et61x251_i2c_try_write(cam, &cam->sensor, address, value); } /*****************************************************************************/ @@ -417,7 +416,7 @@ static void et61x251_urb_complete(struct urb *urb, struct pt_regs* regs) if ((*f)) (*f)->state = F_QUEUED; DBG(3, "Stream interrupted"); - wake_up_interruptible(&cam->wait_stream); + wake_up(&cam->wait_stream); } if (cam->state & DEV_DISCONNECTED) @@ -435,9 +434,9 @@ static void et61x251_urb_complete(struct urb *urb, struct pt_regs* regs) (*f) = list_entry(cam->inqueue.next, struct et61x251_frame_t, frame); - imagesize = (cam->sensor->pix_format.width * - cam->sensor->pix_format.height * - cam->sensor->pix_format.priv) / 8; + imagesize = (cam->sensor.pix_format.width * + cam->sensor.pix_format.height * + cam->sensor.pix_format.priv) / 8; for (i = 0; i < urb->number_of_packets; i++) { unsigned int len, status; @@ -476,7 +475,7 @@ start_of_frame: if ((*f)->state == F_GRABBING) { if (sof && (*f)->buf.bytesused) { - if (cam->sensor->pix_format.pixelformat == + if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_ET61X251) goto end_of_frame; else { @@ -521,7 +520,7 @@ end_of_frame: goto resubmit_urb; if (sof && - cam->sensor->pix_format.pixelformat == + cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_ET61X251) goto start_of_frame; } @@ -650,21 +649,21 @@ static int et61x251_stop_transfer(struct et61x251_device* cam) static int et61x251_stream_interrupt(struct et61x251_device* cam) { - int err = 0; + long timeout; cam->stream = STREAM_INTERRUPT; - err = wait_event_timeout(cam->wait_stream, - (cam->stream == STREAM_OFF) || - (cam->state & DEV_DISCONNECTED), - ET61X251_URB_TIMEOUT); + timeout = wait_event_timeout(cam->wait_stream, + (cam->stream == STREAM_OFF) || + (cam->state & DEV_DISCONNECTED), + ET61X251_URB_TIMEOUT); if (cam->state & DEV_DISCONNECTED) return -ENODEV; - else if (err) { + else if (cam->stream != STREAM_OFF) { cam->state |= DEV_MISCONFIGURED; DBG(1, "URB timeout reached. The camera is misconfigured. To " "use it, close and open /dev/video%d again.", cam->v4ldev->minor); - return err; + return -EIO; } return 0; @@ -709,18 +708,18 @@ static ssize_t et61x251_show_reg(struct class_device* cd, char* buf) struct et61x251_device* cam; ssize_t count; - if (down_interruptible(&et61x251_sysfs_lock)) + if (mutex_lock_interruptible(&et61x251_sysfs_lock)) return -ERESTARTSYS; cam = video_get_drvdata(to_video_device(cd)); if (!cam) { - up(&et61x251_sysfs_lock); + mutex_unlock(&et61x251_sysfs_lock); return -ENODEV; } count = sprintf(buf, "%u\n", cam->sysfs.reg); - up(&et61x251_sysfs_lock); + mutex_unlock(&et61x251_sysfs_lock); return count; } @@ -733,18 +732,18 @@ et61x251_store_reg(struct class_device* cd, const char* buf, size_t len) u8 index; ssize_t count; - if (down_interruptible(&et61x251_sysfs_lock)) + if (mutex_lock_interruptible(&et61x251_sysfs_lock)) return -ERESTARTSYS; cam = video_get_drvdata(to_video_device(cd)); if (!cam) { - up(&et61x251_sysfs_lock); + mutex_unlock(&et61x251_sysfs_lock); return -ENODEV; } index = et61x251_strtou8(buf, len, &count); if (index > 0x8e || !count) { - up(&et61x251_sysfs_lock); + mutex_unlock(&et61x251_sysfs_lock); return -EINVAL; } @@ -753,7 +752,7 @@ et61x251_store_reg(struct class_device* cd, const char* buf, size_t len) DBG(2, "Moved ET61X[12]51 register index to 0x%02X", cam->sysfs.reg); DBG(3, "Written bytes: %zd", count); - up(&et61x251_sysfs_lock); + mutex_unlock(&et61x251_sysfs_lock); return count; } @@ -765,17 +764,17 @@ static ssize_t et61x251_show_val(struct class_device* cd, char* buf) ssize_t count; int val; - if (down_interruptible(&et61x251_sysfs_lock)) + if (mutex_lock_interruptible(&et61x251_sysfs_lock)) return -ERESTARTSYS; cam = video_get_drvdata(to_video_device(cd)); if (!cam) { - up(&et61x251_sysfs_lock); + mutex_unlock(&et61x251_sysfs_lock); return -ENODEV; } if ((val = et61x251_read_reg(cam, cam->sysfs.reg)) < 0) { - up(&et61x251_sysfs_lock); + mutex_unlock(&et61x251_sysfs_lock); return -EIO; } @@ -783,7 +782,7 @@ static ssize_t et61x251_show_val(struct class_device* cd, char* buf) DBG(3, "Read bytes: %zd", count); - up(&et61x251_sysfs_lock); + mutex_unlock(&et61x251_sysfs_lock); return count; } @@ -797,24 +796,24 @@ et61x251_store_val(struct class_device* cd, const char* buf, size_t len) ssize_t count; int err; - if (down_interruptible(&et61x251_sysfs_lock)) + if (mutex_lock_interruptible(&et61x251_sysfs_lock)) return -ERESTARTSYS; cam = video_get_drvdata(to_video_device(cd)); if (!cam) { - up(&et61x251_sysfs_lock); + mutex_unlock(&et61x251_sysfs_lock); return -ENODEV; } value = et61x251_strtou8(buf, len, &count); if (!count) { - up(&et61x251_sysfs_lock); + mutex_unlock(&et61x251_sysfs_lock); return -EINVAL; } err = et61x251_write_reg(cam, value, cam->sysfs.reg); if (err) { - up(&et61x251_sysfs_lock); + mutex_unlock(&et61x251_sysfs_lock); return -EIO; } @@ -822,7 +821,7 @@ et61x251_store_val(struct class_device* cd, const char* buf, size_t len) cam->sysfs.reg, value); DBG(3, "Written bytes: %zd", count); - up(&et61x251_sysfs_lock); + mutex_unlock(&et61x251_sysfs_lock); return count; } @@ -833,12 +832,12 @@ static ssize_t et61x251_show_i2c_reg(struct class_device* cd, char* buf) struct et61x251_device* cam; ssize_t count; - if (down_interruptible(&et61x251_sysfs_lock)) + if (mutex_lock_interruptible(&et61x251_sysfs_lock)) return -ERESTARTSYS; cam = video_get_drvdata(to_video_device(cd)); if (!cam) { - up(&et61x251_sysfs_lock); + mutex_unlock(&et61x251_sysfs_lock); return -ENODEV; } @@ -846,7 +845,7 @@ static ssize_t et61x251_show_i2c_reg(struct class_device* cd, char* buf) DBG(3, "Read bytes: %zd", count); - up(&et61x251_sysfs_lock); + mutex_unlock(&et61x251_sysfs_lock); return count; } @@ -859,18 +858,18 @@ et61x251_store_i2c_reg(struct class_device* cd, const char* buf, size_t len) u8 index; ssize_t count; - if (down_interruptible(&et61x251_sysfs_lock)) + if (mutex_lock_interruptible(&et61x251_sysfs_lock)) return -ERESTARTSYS; cam = video_get_drvdata(to_video_device(cd)); if (!cam) { - up(&et61x251_sysfs_lock); + mutex_unlock(&et61x251_sysfs_lock); return -ENODEV; } index = et61x251_strtou8(buf, len, &count); if (!count) { - up(&et61x251_sysfs_lock); + mutex_unlock(&et61x251_sysfs_lock); return -EINVAL; } @@ -879,7 +878,7 @@ et61x251_store_i2c_reg(struct class_device* cd, const char* buf, size_t len) DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg); DBG(3, "Written bytes: %zd", count); - up(&et61x251_sysfs_lock); + mutex_unlock(&et61x251_sysfs_lock); return count; } @@ -891,22 +890,22 @@ static ssize_t et61x251_show_i2c_val(struct class_device* cd, char* buf) ssize_t count; int val; - if (down_interruptible(&et61x251_sysfs_lock)) + if (mutex_lock_interruptible(&et61x251_sysfs_lock)) return -ERESTARTSYS; cam = video_get_drvdata(to_video_device(cd)); if (!cam) { - up(&et61x251_sysfs_lock); + mutex_unlock(&et61x251_sysfs_lock); return -ENODEV; } - if (!(cam->sensor->sysfs_ops & ET61X251_I2C_READ)) { - up(&et61x251_sysfs_lock); + if (!(cam->sensor.sysfs_ops & ET61X251_I2C_READ)) { + mutex_unlock(&et61x251_sysfs_lock); return -ENOSYS; } if ((val = et61x251_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) { - up(&et61x251_sysfs_lock); + mutex_unlock(&et61x251_sysfs_lock); return -EIO; } @@ -914,7 +913,7 @@ static ssize_t et61x251_show_i2c_val(struct class_device* cd, char* buf) DBG(3, "Read bytes: %zd", count); - up(&et61x251_sysfs_lock); + mutex_unlock(&et61x251_sysfs_lock); return count; } @@ -928,29 +927,29 @@ et61x251_store_i2c_val(struct class_device* cd, const char* buf, size_t len) ssize_t count; int err; - if (down_interruptible(&et61x251_sysfs_lock)) + if (mutex_lock_interruptible(&et61x251_sysfs_lock)) return -ERESTARTSYS; cam = video_get_drvdata(to_video_device(cd)); if (!cam) { - up(&et61x251_sysfs_lock); + mutex_unlock(&et61x251_sysfs_lock); return -ENODEV; } - if (!(cam->sensor->sysfs_ops & ET61X251_I2C_READ)) { - up(&et61x251_sysfs_lock); + if (!(cam->sensor.sysfs_ops & ET61X251_I2C_READ)) { + mutex_unlock(&et61x251_sysfs_lock); return -ENOSYS; } value = et61x251_strtou8(buf, len, &count); if (!count) { - up(&et61x251_sysfs_lock); + mutex_unlock(&et61x251_sysfs_lock); return -EINVAL; } err = et61x251_i2c_write(cam, cam->sysfs.i2c_reg, value); if (err) { - up(&et61x251_sysfs_lock); + mutex_unlock(&et61x251_sysfs_lock); return -EIO; } @@ -958,7 +957,7 @@ et61x251_store_i2c_val(struct class_device* cd, const char* buf, size_t len) cam->sysfs.i2c_reg, value); DBG(3, "Written bytes: %zd", count); - up(&et61x251_sysfs_lock); + mutex_unlock(&et61x251_sysfs_lock); return count; } @@ -980,7 +979,7 @@ static void et61x251_create_sysfs(struct et61x251_device* cam) video_device_create_file(v4ldev, &class_device_attr_reg); video_device_create_file(v4ldev, &class_device_attr_val); - if (cam->sensor && cam->sensor->sysfs_ops) { + if (cam->sensor.sysfs_ops) { video_device_create_file(v4ldev, &class_device_attr_i2c_reg); video_device_create_file(v4ldev, &class_device_attr_i2c_val); } @@ -1048,7 +1047,7 @@ static int et61x251_set_scale(struct et61x251_device* cam, u8 scale) static int et61x251_set_crop(struct et61x251_device* cam, struct v4l2_rect* rect) { - struct et61x251_sensor* s = cam->sensor; + struct et61x251_sensor* s = &cam->sensor; u16 fmw_sx = (u16)(rect->left - s->cropcap.bounds.left + s->active_pixel.left), fmw_sy = (u16)(rect->top - s->cropcap.bounds.top + @@ -1076,7 +1075,7 @@ et61x251_set_crop(struct et61x251_device* cam, struct v4l2_rect* rect) static int et61x251_init(struct et61x251_device* cam) { - struct et61x251_sensor* s = cam->sensor; + struct et61x251_sensor* s = &cam->sensor; struct v4l2_control ctrl; struct v4l2_queryctrl *qctrl; struct v4l2_rect* rect; @@ -1143,7 +1142,7 @@ static int et61x251_init(struct et61x251_device* cam) } if (!(cam->state & DEV_INITIALIZED)) { - init_MUTEX(&cam->fileop_sem); + mutex_init(&cam->fileop_mutex); spin_lock_init(&cam->queue_lock); init_waitqueue_head(&cam->wait_frame); init_waitqueue_head(&cam->wait_stream); @@ -1161,13 +1160,15 @@ static int et61x251_init(struct et61x251_device* cam) static void et61x251_release_resources(struct et61x251_device* cam) { - down(&et61x251_sysfs_lock); + mutex_lock(&et61x251_sysfs_lock); DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor); video_set_drvdata(cam->v4ldev, NULL); video_unregister_device(cam->v4ldev); - up(&et61x251_sysfs_lock); + usb_put_dev(cam->usbdev); + + mutex_unlock(&et61x251_sysfs_lock); kfree(cam->control_buffer); } @@ -1188,7 +1189,7 @@ static int et61x251_open(struct inode* inode, struct file* filp) cam = video_get_drvdata(video_devdata(filp)); - if (down_interruptible(&cam->dev_sem)) { + if (mutex_lock_interruptible(&cam->dev_mutex)) { up_read(&et61x251_disconnect); return -ERESTARTSYS; } @@ -1200,7 +1201,7 @@ static int et61x251_open(struct inode* inode, struct file* filp) err = -EWOULDBLOCK; goto out; } - up(&cam->dev_sem); + mutex_unlock(&cam->dev_mutex); err = wait_event_interruptible_exclusive(cam->open, cam->state & DEV_DISCONNECTED || !cam->users); @@ -1212,7 +1213,7 @@ static int et61x251_open(struct inode* inode, struct file* filp) up_read(&et61x251_disconnect); return -ENODEV; } - down(&cam->dev_sem); + mutex_lock(&cam->dev_mutex); } @@ -1240,7 +1241,7 @@ static int et61x251_open(struct inode* inode, struct file* filp) DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor); out: - up(&cam->dev_sem); + mutex_unlock(&cam->dev_mutex); up_read(&et61x251_disconnect); return err; } @@ -1250,7 +1251,7 @@ static int et61x251_release(struct inode* inode, struct file* filp) { struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); - down(&cam->dev_sem); /* prevent disconnect() to be called */ + mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */ et61x251_stop_transfer(cam); @@ -1258,7 +1259,7 @@ static int et61x251_release(struct inode* inode, struct file* filp) if (cam->state & DEV_DISCONNECTED) { et61x251_release_resources(cam); - up(&cam->dev_sem); + mutex_unlock(&cam->dev_mutex); kfree(cam); return 0; } @@ -1268,7 +1269,7 @@ static int et61x251_release(struct inode* inode, struct file* filp) DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor); - up(&cam->dev_sem); + mutex_unlock(&cam->dev_mutex); return 0; } @@ -1281,28 +1282,29 @@ et61x251_read(struct file* filp, char __user * buf, struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); struct et61x251_frame_t* f, * i; unsigned long lock_flags; + long timeout; int err = 0; - if (down_interruptible(&cam->fileop_sem)) + if (mutex_lock_interruptible(&cam->fileop_mutex)) return -ERESTARTSYS; if (cam->state & DEV_DISCONNECTED) { DBG(1, "Device not present"); - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -ENODEV; } if (cam->state & DEV_MISCONFIGURED) { DBG(1, "The camera is misconfigured. Close and open it " "again."); - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -EIO; } if (cam->io == IO_MMAP) { DBG(3, "Close and open the device again to choose the read " "method"); - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -EINVAL; } @@ -1310,7 +1312,7 @@ et61x251_read(struct file* filp, char __user * buf, if (!et61x251_request_buffers(cam, cam->nreadbuffers, IO_READ)) { DBG(1, "read() failed, not enough memory"); - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -ENOMEM; } cam->io = IO_READ; @@ -1324,30 +1326,32 @@ et61x251_read(struct file* filp, char __user * buf, } if (!count) { - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return 0; } if (list_empty(&cam->outqueue)) { if (filp->f_flags & O_NONBLOCK) { - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -EAGAIN; } - err = wait_event_interruptible - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED) ); - if (err) { - up(&cam->fileop_sem); - return err; + timeout = wait_event_interruptible_timeout + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); + if (timeout < 0) { + mutex_unlock(&cam->fileop_mutex); + return timeout; } if (cam->state & DEV_DISCONNECTED) { - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -ENODEV; } - if (cam->state & DEV_MISCONFIGURED) { - up(&cam->fileop_sem); + if (!timeout || (cam->state & DEV_MISCONFIGURED)) { + mutex_unlock(&cam->fileop_mutex); return -EIO; } } @@ -1375,7 +1379,7 @@ exit: PDBGG("Frame #%lu, bytes read: %zu", (unsigned long)f->buf.index, count); - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return err ? err : count; } @@ -1388,7 +1392,7 @@ static unsigned int et61x251_poll(struct file *filp, poll_table *wait) unsigned long lock_flags; unsigned int mask = 0; - if (down_interruptible(&cam->fileop_sem)) + if (mutex_lock_interruptible(&cam->fileop_mutex)) return POLLERR; if (cam->state & DEV_DISCONNECTED) { @@ -1426,12 +1430,12 @@ static unsigned int et61x251_poll(struct file *filp, poll_table *wait) if (!list_empty(&cam->outqueue)) mask |= POLLIN | POLLRDNORM; - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return mask; error: - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return POLLERR; } @@ -1465,25 +1469,25 @@ static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma) void *pos; u32 i; - if (down_interruptible(&cam->fileop_sem)) + if (mutex_lock_interruptible(&cam->fileop_mutex)) return -ERESTARTSYS; if (cam->state & DEV_DISCONNECTED) { DBG(1, "Device not present"); - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -ENODEV; } if (cam->state & DEV_MISCONFIGURED) { DBG(1, "The camera is misconfigured. Close and open it " "again."); - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -EIO; } if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) || size != PAGE_ALIGN(cam->frame[0].buf.length)) { - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -EINVAL; } @@ -1492,7 +1496,7 @@ static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma) break; } if (i == cam->nbuffers) { - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -EINVAL; } @@ -1502,7 +1506,7 @@ static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma) pos = cam->frame[i].bufmem; while (size > 0) { /* size is page-aligned */ if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -EAGAIN; } start += PAGE_SIZE; @@ -1515,7 +1519,7 @@ static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma) et61x251_vm_open(vma); - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return 0; } @@ -1557,6 +1561,7 @@ et61x251_vidioc_enuminput(struct et61x251_device* cam, void __user * arg) memset(&i, 0, sizeof(i)); strcpy(i.name, "Camera"); + i.type = V4L2_INPUT_TYPE_CAMERA; if (copy_to_user(arg, &i, sizeof(i))) return -EFAULT; @@ -1566,7 +1571,19 @@ et61x251_vidioc_enuminput(struct et61x251_device* cam, void __user * arg) static int -et61x251_vidioc_gs_input(struct et61x251_device* cam, void __user * arg) +et61x251_vidioc_g_input(struct et61x251_device* cam, void __user * arg) +{ + int index = 0; + + if (copy_to_user(arg, &index, sizeof(index))) + return -EFAULT; + + return 0; +} + + +static int +et61x251_vidioc_s_input(struct et61x251_device* cam, void __user * arg) { int index; @@ -1583,7 +1600,7 @@ et61x251_vidioc_gs_input(struct et61x251_device* cam, void __user * arg) static int et61x251_vidioc_query_ctrl(struct et61x251_device* cam, void __user * arg) { - struct et61x251_sensor* s = cam->sensor; + struct et61x251_sensor* s = &cam->sensor; struct v4l2_queryctrl qc; u8 i; @@ -1605,7 +1622,7 @@ et61x251_vidioc_query_ctrl(struct et61x251_device* cam, void __user * arg) static int et61x251_vidioc_g_ctrl(struct et61x251_device* cam, void __user * arg) { - struct et61x251_sensor* s = cam->sensor; + struct et61x251_sensor* s = &cam->sensor; struct v4l2_control ctrl; int err = 0; u8 i; @@ -1637,7 +1654,7 @@ exit: static int et61x251_vidioc_s_ctrl(struct et61x251_device* cam, void __user * arg) { - struct et61x251_sensor* s = cam->sensor; + struct et61x251_sensor* s = &cam->sensor; struct v4l2_control ctrl; u8 i; int err = 0; @@ -1650,6 +1667,8 @@ et61x251_vidioc_s_ctrl(struct et61x251_device* cam, void __user * arg) for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) if (ctrl.id == s->qctrl[i].id) { + if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED) + return -EINVAL; if (ctrl.value < s->qctrl[i].minimum || ctrl.value > s->qctrl[i].maximum) return -ERANGE; @@ -1669,7 +1688,7 @@ et61x251_vidioc_s_ctrl(struct et61x251_device* cam, void __user * arg) static int et61x251_vidioc_cropcap(struct et61x251_device* cam, void __user * arg) { - struct v4l2_cropcap* cc = &(cam->sensor->cropcap); + struct v4l2_cropcap* cc = &(cam->sensor.cropcap); cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; cc->pixelaspect.numerator = 1; @@ -1685,7 +1704,7 @@ et61x251_vidioc_cropcap(struct et61x251_device* cam, void __user * arg) static int et61x251_vidioc_g_crop(struct et61x251_device* cam, void __user * arg) { - struct et61x251_sensor* s = cam->sensor; + struct et61x251_sensor* s = &cam->sensor; struct v4l2_crop crop = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, }; @@ -1702,7 +1721,7 @@ et61x251_vidioc_g_crop(struct et61x251_device* cam, void __user * arg) static int et61x251_vidioc_s_crop(struct et61x251_device* cam, void __user * arg) { - struct et61x251_sensor* s = cam->sensor; + struct et61x251_sensor* s = &cam->sensor; struct v4l2_crop crop; struct v4l2_rect* rect; struct v4l2_rect* bounds = &(s->cropcap.bounds); @@ -1843,7 +1862,7 @@ static int et61x251_vidioc_g_fmt(struct et61x251_device* cam, void __user * arg) { struct v4l2_format format; - struct v4l2_pix_format* pfmt = &(cam->sensor->pix_format); + struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format); if (copy_from_user(&format, arg, sizeof(format))) return -EFAULT; @@ -1868,7 +1887,7 @@ static int et61x251_vidioc_try_s_fmt(struct et61x251_device* cam, unsigned int cmd, void __user * arg) { - struct et61x251_sensor* s = cam->sensor; + struct et61x251_sensor* s = &cam->sensor; struct v4l2_format format; struct v4l2_pix_format* pix; struct v4l2_pix_format* pfmt = &(s->pix_format); @@ -2155,7 +2174,7 @@ et61x251_vidioc_dqbuf(struct et61x251_device* cam, struct file* filp, struct v4l2_buffer b; struct et61x251_frame_t *f; unsigned long lock_flags; - int err = 0; + long timeout; if (copy_from_user(&b, arg, sizeof(b))) return -EFAULT; @@ -2168,16 +2187,18 @@ et61x251_vidioc_dqbuf(struct et61x251_device* cam, struct file* filp, return -EINVAL; if (filp->f_flags & O_NONBLOCK) return -EAGAIN; - err = wait_event_interruptible - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED) ); - if (err) - return err; + timeout = wait_event_interruptible_timeout + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); + if (timeout < 0) + return timeout; if (cam->state & DEV_DISCONNECTED) return -ENODEV; - if (cam->state & DEV_MISCONFIGURED) + if (!timeout || (cam->state & DEV_MISCONFIGURED)) return -EIO; } @@ -2309,8 +2330,10 @@ static int et61x251_ioctl_v4l2(struct inode* inode, struct file* filp, return et61x251_vidioc_enuminput(cam, arg); case VIDIOC_G_INPUT: + return et61x251_vidioc_g_input(cam, arg); + case VIDIOC_S_INPUT: - return et61x251_vidioc_gs_input(cam, arg); + return et61x251_vidioc_s_input(cam, arg); case VIDIOC_QUERYCTRL: return et61x251_vidioc_query_ctrl(cam, arg); @@ -2393,19 +2416,19 @@ static int et61x251_ioctl(struct inode* inode, struct file* filp, struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); int err = 0; - if (down_interruptible(&cam->fileop_sem)) + if (mutex_lock_interruptible(&cam->fileop_mutex)) return -ERESTARTSYS; if (cam->state & DEV_DISCONNECTED) { DBG(1, "Device not present"); - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -ENODEV; } if (cam->state & DEV_MISCONFIGURED) { DBG(1, "The camera is misconfigured. Close and open it " "again."); - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return -EIO; } @@ -2413,7 +2436,7 @@ static int et61x251_ioctl(struct inode* inode, struct file* filp, err = et61x251_ioctl_v4l2(inode, filp, cmd, (void __user *)arg); - up(&cam->fileop_sem); + mutex_unlock(&cam->fileop_mutex); return err; } @@ -2459,7 +2482,7 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) goto fail; } - init_MUTEX(&cam->dev_sem); + mutex_init(&cam->dev_mutex); DBG(2, "ET61X[12]51 PC Camera Controller detected " "(vid/pid 0x%04X/0x%04X)",id->idVendor, id->idProduct); @@ -2470,8 +2493,8 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) break; } - if (!err && cam->sensor) - DBG(2, "%s image sensor detected", cam->sensor->name); + if (!err) + DBG(2, "%s image sensor detected", cam->sensor.name); else { DBG(1, "No supported image sensor detected"); err = -ENODEV; @@ -2492,7 +2515,7 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) cam->v4ldev->release = video_device_release; video_set_drvdata(cam->v4ldev, cam); - down(&cam->dev_sem); + mutex_lock(&cam->dev_mutex); err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, video_nr[dev_nr]); @@ -2502,13 +2525,14 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) DBG(1, "Free /dev/videoX node not found"); video_nr[dev_nr] = -1; dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0; - up(&cam->dev_sem); + mutex_unlock(&cam->dev_mutex); goto fail; } DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor); cam->module_param.force_munmap = force_munmap[dev_nr]; + cam->module_param.frame_timeout = frame_timeout[dev_nr]; dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0; @@ -2519,7 +2543,7 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) usb_set_intfdata(intf, cam); - up(&cam->dev_sem); + mutex_unlock(&cam->dev_mutex); return 0; @@ -2543,7 +2567,7 @@ static void et61x251_usb_disconnect(struct usb_interface* intf) down_write(&et61x251_disconnect); - down(&cam->dev_sem); + mutex_lock(&cam->dev_mutex); DBG(2, "Disconnecting %s...", cam->v4ldev->name); @@ -2557,13 +2581,14 @@ static void et61x251_usb_disconnect(struct usb_interface* intf) et61x251_stop_transfer(cam); cam->state |= DEV_DISCONNECTED; wake_up_interruptible(&cam->wait_frame); - wake_up_interruptible(&cam->wait_stream); + wake_up(&cam->wait_stream); + usb_get_dev(cam->usbdev); } else { cam->state |= DEV_DISCONNECTED; et61x251_release_resources(cam); } - up(&cam->dev_sem); + mutex_unlock(&cam->dev_mutex); if (!cam->users) kfree(cam); diff --git a/drivers/usb/media/et61x251_sensor.h b/drivers/usb/media/et61x251_sensor.h index b9df91062fc0..56841ae8a207 100644 --- a/drivers/usb/media/et61x251_sensor.h +++ b/drivers/usb/media/et61x251_sensor.h @@ -42,6 +42,9 @@ static int (*et61x251_sensor_table[])(struct et61x251_device*) = { \ NULL, \ }; +extern struct et61x251_device* +et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id); + extern void et61x251_attach_sensor(struct et61x251_device* cam, struct et61x251_sensor* sensor); @@ -105,8 +108,6 @@ struct et61x251_sensor { int (*set_pix_format)(struct et61x251_device* cam, const struct v4l2_pix_format* pix); - const struct usb_device* usbdev; - /* Private */ struct v4l2_queryctrl _qctrl[ET61X251_MAX_CTRLS]; struct v4l2_rect _rect; diff --git a/drivers/usb/media/et61x251_tas5130d1b.c b/drivers/usb/media/et61x251_tas5130d1b.c index 65f1ae9cf2b3..3998d76a307a 100644 --- a/drivers/usb/media/et61x251_tas5130d1b.c +++ b/drivers/usb/media/et61x251_tas5130d1b.c @@ -126,12 +126,16 @@ static struct et61x251_sensor tas5130d1b = { int et61x251_probe_tas5130d1b(struct et61x251_device* cam) { - /* This sensor has no identifiers, so let's attach it anyway */ - et61x251_attach_sensor(cam, &tas5130d1b); + const struct usb_device_id tas5130d1b_id_table[] = { + { USB_DEVICE(0x102c, 0x6251), }, + { } + }; /* Sensor detection is based on USB pid/vid */ - if (le16_to_cpu(tas5130d1b.usbdev->descriptor.idProduct) != 0x6251) + if (!et61x251_match_id(cam, tas5130d1b_id_table)) return -ENODEV; + et61x251_attach_sensor(cam, &tas5130d1b); + return 0; } -- cgit v1.2.3-59-g8ed1b From a847423905c6a8ccd6671d05f5877d893d10cd9f Mon Sep 17 00:00:00 2001 From: Luca Risolia Date: Sat, 25 Feb 2006 06:57:49 +0000 Subject: [PATCH] USB: ZC0301 driver updates ZC0301 driver updates. Changes: + new, - removed, * cleanup, @ bugfix @ Need usb_get|put_dev() when disconnecting, if the device is open * Cleanups and updates in the documentation + Use per-device sensor structures + Add frame_timeout module parameter Signed-off-by: Luca Risolia Signed-off-by: Greg Kroah-Hartman --- Documentation/usb/zc0301.txt | 29 +++++++++++------- drivers/usb/media/zc0301.h | 20 +++++++++---- drivers/usb/media/zc0301_core.c | 58 ++++++++++++++++++++++-------------- drivers/usb/media/zc0301_pas202bcb.c | 38 +++++++++++++---------- drivers/usb/media/zc0301_sensor.h | 25 +++++++++------- 5 files changed, 108 insertions(+), 62 deletions(-) diff --git a/Documentation/usb/zc0301.txt b/Documentation/usb/zc0301.txt index 0889ae6ac7d8..095838420e82 100644 --- a/Documentation/usb/zc0301.txt +++ b/Documentation/usb/zc0301.txt @@ -68,11 +68,6 @@ Some of the features of the driver are: data transfers; - automatic detection of image sensor; - video format is standard JPEG; -- full support for the capabilities of every possible image sensors that can - be connected to the ZC0301 bridges, including, for istance, red, green, - blue and global gain adjustments and exposure control (see "Supported - devices" paragraph for details); -- use of default color settings for sunlight conditions; - dynamic driver control thanks to various module parameters (see "Module parameters" paragraph); - up to 64 cameras can be handled at the same time; they can be connected and @@ -171,6 +166,14 @@ Description: Force the application to unmap previously mapped buffer memory 1 = force memory unmapping (save memory) Default: 0 ------------------------------------------------------------------------------- +Name: frame_timeout +Type: uint array (min = 0, max = 64) +Syntax: +Description: Timeout for a video frame in seconds. This parameter is + specific for each detected camera. This parameter can be + changed at runtime thanks to the /sys filesystem interface. +Default: 2 +------------------------------------------------------------------------------- Name: debug Type: ushort Syntax: @@ -198,17 +201,23 @@ devices mounting the ZC0301 Image Processor and Control Chips: Vendor ID Product ID --------- ---------- +0x10fd 0x8050 +0x041e 0x0417 +0x041e 0x041e +0x041e 0x081c +0x041e 0x0834 +0x041e 0x0835 0x046d 0x08ae +0x0ac8 0x0301 -The following image sensors are supported: +The list above does not imply that all those devices work with this driver: up +until now only the ones that mount the following image sensors are supported; +kernel messages will always tell you whether this is the case: Model Manufacturer ----- ------------ PAS202BCB PixArt Imaging, Inc. -All the available control settings of each image sensor are supported through -the V4L2 interface. - 9. Notes for V4L2 application developers ======================================== @@ -240,6 +249,6 @@ the fingerprint is: '88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4'. - Informations about the chip internals needed to enable the I2C protocol have been taken from the documentation of the ZC030x Video4Linux1 driver written by Andrew Birkett ; -- Initialization values of the ZC0301 controller connected to the PAS202BCB +- The initialization values of the ZC0301 controller connected to the PAS202BCB image sensor have been taken from the SPCA5XX driver maintained by Michel Xhaard . diff --git a/drivers/usb/media/zc0301.h b/drivers/usb/media/zc0301.h index cb1a0823bc63..9ba9135e824b 100644 --- a/drivers/usb/media/zc0301.h +++ b/drivers/usb/media/zc0301.h @@ -34,7 +34,8 @@ #include #include #include -#include +#include +#include #include "zc0301_sensor.h" @@ -51,7 +52,7 @@ #define ZC0301_ALTERNATE_SETTING 7 #define ZC0301_URB_TIMEOUT msecs_to_jiffies(2 * ZC0301_ISO_PACKETS) #define ZC0301_CTRL_TIMEOUT 100 -#define ZC0301_FRAME_TIMEOUT 2 * 1000 * msecs_to_jiffies(1) +#define ZC0301_FRAME_TIMEOUT 2 /*****************************************************************************/ @@ -94,6 +95,7 @@ enum zc0301_stream_state { struct zc0301_module_param { u8 force_munmap; + u16 frame_timeout; }; static DECLARE_RWSEM(zc0301_disconnect); @@ -101,7 +103,7 @@ static DECLARE_RWSEM(zc0301_disconnect); struct zc0301_device { struct video_device* v4ldev; - struct zc0301_sensor* sensor; + struct zc0301_sensor sensor; struct usb_device* usbdev; struct urb* urb[ZC0301_URBS]; @@ -129,11 +131,19 @@ struct zc0301_device { /*****************************************************************************/ +struct zc0301_device* +zc0301_match_id(struct zc0301_device* cam, const struct usb_device_id *id) +{ + if (usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id)) + return cam; + + return NULL; +} + void zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor) { - cam->sensor = sensor; - cam->sensor->usbdev = cam->usbdev; + memcpy(&cam->sensor, sensor, sizeof(struct zc0301_sensor)); } /*****************************************************************************/ diff --git a/drivers/usb/media/zc0301_core.c b/drivers/usb/media/zc0301_core.c index 7e8b1676d144..5773688d3dae 100644 --- a/drivers/usb/media/zc0301_core.c +++ b/drivers/usb/media/zc0301_core.c @@ -29,11 +29,9 @@ #include #include #include -#include #include #include #include -#include #include #include #include @@ -54,8 +52,8 @@ #define ZC0301_MODULE_AUTHOR "(C) 2006 Luca Risolia" #define ZC0301_AUTHOR_EMAIL "" #define ZC0301_MODULE_LICENSE "GPL" -#define ZC0301_MODULE_VERSION "1:1.01" -#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 1) +#define ZC0301_MODULE_VERSION "1:1.02" +#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 2) /*****************************************************************************/ @@ -94,6 +92,15 @@ MODULE_PARM_DESC(force_munmap, "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"." "\n"); +static unsigned int frame_timeout[] = {[0 ... ZC0301_MAX_DEVICES-1] = + ZC0301_FRAME_TIMEOUT}; +module_param_array(frame_timeout, uint, NULL, 0644); +MODULE_PARM_DESC(frame_timeout, + "\n Timeout for a video frame in seconds." + "\nThis parameter is specific for each detected camera." + "\nDefault value is "__MODULE_STRING(ZC0301_FRAME_TIMEOUT)"." + "\n"); + #ifdef ZC0301_DEBUG static unsigned short debug = ZC0301_DEBUG_LEVEL; module_param(debug, ushort, 0644); @@ -115,8 +122,8 @@ static u32 zc0301_request_buffers(struct zc0301_device* cam, u32 count, enum zc0301_io_method io) { - struct v4l2_pix_format* p = &(cam->sensor->pix_format); - struct v4l2_rect* r = &(cam->sensor->cropcap.bounds); + struct v4l2_pix_format* p = &(cam->sensor.pix_format); + struct v4l2_rect* r = &(cam->sensor.cropcap.bounds); const size_t imagesize = cam->module_param.force_munmap || io == IO_READ ? (p->width * p->height * p->priv) / 8 : @@ -332,9 +339,9 @@ static void zc0301_urb_complete(struct urb *urb, struct pt_regs* regs) (*f) = list_entry(cam->inqueue.next, struct zc0301_frame_t, frame); - imagesize = (cam->sensor->pix_format.width * - cam->sensor->pix_format.height * - cam->sensor->pix_format.priv) / 8; + imagesize = (cam->sensor.pix_format.width * + cam->sensor.pix_format.height * + cam->sensor.pix_format.priv) / 8; for (i = 0; i < urb->number_of_packets; i++) { unsigned int len, status; @@ -555,7 +562,7 @@ zc0301_set_compression(struct zc0301_device* cam, static int zc0301_init(struct zc0301_device* cam) { - struct zc0301_sensor* s = cam->sensor; + struct zc0301_sensor* s = &cam->sensor; struct v4l2_control ctrl; struct v4l2_queryctrl *qctrl; struct v4l2_rect* rect; @@ -630,6 +637,7 @@ static void zc0301_release_resources(struct zc0301_device* cam) DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor); video_set_drvdata(cam->v4ldev, NULL); video_unregister_device(cam->v4ldev); + usb_put_dev(cam->usbdev); kfree(cam->control_buffer); } @@ -798,7 +806,8 @@ zc0301_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) (!list_empty(&cam->outqueue)) || (cam->state & DEV_DISCONNECTED) || (cam->state & DEV_MISCONFIGURED), - ZC0301_FRAME_TIMEOUT ); + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); if (timeout < 0) { mutex_unlock(&cam->fileop_mutex); return timeout; @@ -1056,7 +1065,7 @@ zc0301_vidioc_s_input(struct zc0301_device* cam, void __user * arg) static int zc0301_vidioc_query_ctrl(struct zc0301_device* cam, void __user * arg) { - struct zc0301_sensor* s = cam->sensor; + struct zc0301_sensor* s = &cam->sensor; struct v4l2_queryctrl qc; u8 i; @@ -1078,7 +1087,7 @@ zc0301_vidioc_query_ctrl(struct zc0301_device* cam, void __user * arg) static int zc0301_vidioc_g_ctrl(struct zc0301_device* cam, void __user * arg) { - struct zc0301_sensor* s = cam->sensor; + struct zc0301_sensor* s = &cam->sensor; struct v4l2_control ctrl; int err = 0; u8 i; @@ -1110,7 +1119,7 @@ exit: static int zc0301_vidioc_s_ctrl(struct zc0301_device* cam, void __user * arg) { - struct zc0301_sensor* s = cam->sensor; + struct zc0301_sensor* s = &cam->sensor; struct v4l2_control ctrl; u8 i; int err = 0; @@ -1123,6 +1132,8 @@ zc0301_vidioc_s_ctrl(struct zc0301_device* cam, void __user * arg) for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) if (ctrl.id == s->qctrl[i].id) { + if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED) + return -EINVAL; if (ctrl.value < s->qctrl[i].minimum || ctrl.value > s->qctrl[i].maximum) return -ERANGE; @@ -1142,7 +1153,7 @@ zc0301_vidioc_s_ctrl(struct zc0301_device* cam, void __user * arg) static int zc0301_vidioc_cropcap(struct zc0301_device* cam, void __user * arg) { - struct v4l2_cropcap* cc = &(cam->sensor->cropcap); + struct v4l2_cropcap* cc = &(cam->sensor.cropcap); cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; cc->pixelaspect.numerator = 1; @@ -1158,7 +1169,7 @@ zc0301_vidioc_cropcap(struct zc0301_device* cam, void __user * arg) static int zc0301_vidioc_g_crop(struct zc0301_device* cam, void __user * arg) { - struct zc0301_sensor* s = cam->sensor; + struct zc0301_sensor* s = &cam->sensor; struct v4l2_crop crop = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, }; @@ -1175,7 +1186,7 @@ zc0301_vidioc_g_crop(struct zc0301_device* cam, void __user * arg) static int zc0301_vidioc_s_crop(struct zc0301_device* cam, void __user * arg) { - struct zc0301_sensor* s = cam->sensor; + struct zc0301_sensor* s = &cam->sensor; struct v4l2_crop crop; struct v4l2_rect* rect; struct v4l2_rect* bounds = &(s->cropcap.bounds); @@ -1304,7 +1315,7 @@ static int zc0301_vidioc_g_fmt(struct zc0301_device* cam, void __user * arg) { struct v4l2_format format; - struct v4l2_pix_format* pfmt = &(cam->sensor->pix_format); + struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format); if (copy_from_user(&format, arg, sizeof(format))) return -EFAULT; @@ -1328,7 +1339,7 @@ static int zc0301_vidioc_try_s_fmt(struct zc0301_device* cam, unsigned int cmd, void __user * arg) { - struct zc0301_sensor* s = cam->sensor; + struct zc0301_sensor* s = &cam->sensor; struct v4l2_format format; struct v4l2_pix_format* pix; struct v4l2_pix_format* pfmt = &(s->pix_format); @@ -1612,7 +1623,8 @@ zc0301_vidioc_dqbuf(struct zc0301_device* cam, struct file* filp, (!list_empty(&cam->outqueue)) || (cam->state & DEV_DISCONNECTED) || (cam->state & DEV_MISCONFIGURED), - ZC0301_FRAME_TIMEOUT ); + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); if (timeout < 0) return timeout; if (cam->state & DEV_DISCONNECTED) @@ -1911,8 +1923,8 @@ zc0301_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) break; } - if (!err && cam->sensor) - DBG(2, "%s image sensor detected", cam->sensor->name); + if (!err) + DBG(2, "%s image sensor detected", cam->sensor.name); else { DBG(1, "No supported image sensor detected"); err = -ENODEV; @@ -1950,6 +1962,7 @@ zc0301_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor); cam->module_param.force_munmap = force_munmap[dev_nr]; + cam->module_param.frame_timeout = frame_timeout[dev_nr]; dev_nr = (dev_nr < ZC0301_MAX_DEVICES-1) ? dev_nr+1 : 0; @@ -1994,6 +2007,7 @@ static void zc0301_usb_disconnect(struct usb_interface* intf) cam->state |= DEV_DISCONNECTED; wake_up_interruptible(&cam->wait_frame); wake_up(&cam->wait_stream); + usb_get_dev(cam->usbdev); } else { cam->state |= DEV_DISCONNECTED; zc0301_release_resources(cam); diff --git a/drivers/usb/media/zc0301_pas202bcb.c b/drivers/usb/media/zc0301_pas202bcb.c index 32b9b9329cbf..9d282a22c15f 100644 --- a/drivers/usb/media/zc0301_pas202bcb.c +++ b/drivers/usb/media/zc0301_pas202bcb.c @@ -22,6 +22,14 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ +/* + NOTE: Sensor controls are disabled for now, becouse changing them while + streaming sometimes results in out-of-sync video frames. We'll use + the default initialization, until we know how to stop and start video + in the chip. However, the image quality still looks good under various + light conditions. +*/ + #include #include "zc0301_sensor.h" @@ -245,7 +253,7 @@ static struct zc0301_sensor pas202bcb = { .maximum = 0x3fff, .step = 0x0001, .default_value = 0x01e5, - .flags = 0, + .flags = V4L2_CTRL_FLAG_DISABLED, }, { .id = V4L2_CID_GAIN, @@ -255,7 +263,17 @@ static struct zc0301_sensor pas202bcb = { .maximum = 0x1f, .step = 0x01, .default_value = 0x0c, - .flags = 0, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = ZC0301_V4L2_CID_DAC_MAGNITUDE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "DAC magnitude", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x00, + .flags = V4L2_CTRL_FLAG_DISABLED, }, { .id = V4L2_CID_RED_BALANCE, @@ -265,7 +283,7 @@ static struct zc0301_sensor pas202bcb = { .maximum = 0x0f, .step = 0x01, .default_value = 0x01, - .flags = 0, + .flags = V4L2_CTRL_FLAG_DISABLED, }, { .id = V4L2_CID_BLUE_BALANCE, @@ -275,7 +293,7 @@ static struct zc0301_sensor pas202bcb = { .maximum = 0x0f, .step = 0x01, .default_value = 0x05, - .flags = 0, + .flags = V4L2_CTRL_FLAG_DISABLED, }, { .id = ZC0301_V4L2_CID_GREEN_BALANCE, @@ -285,17 +303,7 @@ static struct zc0301_sensor pas202bcb = { .maximum = 0x0f, .step = 0x01, .default_value = 0x00, - .flags = 0, - }, - { - .id = ZC0301_V4L2_CID_DAC_MAGNITUDE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "DAC magnitude", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = 0x04, - .flags = 0, + .flags = V4L2_CTRL_FLAG_DISABLED, }, }, .get_ctrl = &pas202bcb_get_ctrl, diff --git a/drivers/usb/media/zc0301_sensor.h b/drivers/usb/media/zc0301_sensor.h index 8890e32405d4..e3cb6cc920ca 100644 --- a/drivers/usb/media/zc0301_sensor.h +++ b/drivers/usb/media/zc0301_sensor.h @@ -43,9 +43,11 @@ static int (*zc0301_sensor_table[])(struct zc0301_device*) = { \ NULL, \ }; +extern struct zc0301_device* +zc0301_match_id(struct zc0301_device* cam, const struct usb_device_id *id); + extern void -zc0301_attach_sensor(struct zc0301_device* cam, - struct zc0301_sensor* sensor); +zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor); #define ZC0301_USB_DEVICE(vend, prod, intclass) \ .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ @@ -56,7 +58,14 @@ zc0301_attach_sensor(struct zc0301_device* cam, #define ZC0301_ID_TABLE \ static const struct usb_device_id zc0301_id_table[] = { \ + { ZC0301_USB_DEVICE(0x10fd, 0x8050, 0xff), }, /* TAS5130D */ \ + { ZC0301_USB_DEVICE(0x041e, 0x0417, 0xff), }, \ + { ZC0301_USB_DEVICE(0x041e, 0x041e, 0xff), }, /* HV7131B */ \ + { ZC0301_USB_DEVICE(0x041e, 0x081c, 0xff), }, /* PAS106 */ \ + { ZC0301_USB_DEVICE(0x041e, 0x0834, 0xff), }, /* PAS106 */ \ + { ZC0301_USB_DEVICE(0x041e, 0x0835, 0xff), }, /* PAS106 */ \ { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202BCB */ \ + { ZC0301_USB_DEVICE(0x0ac8, 0x0301, 0xff), }, \ { } \ }; @@ -80,15 +89,11 @@ struct zc0301_sensor { struct v4l2_cropcap cropcap; struct v4l2_pix_format pix_format; - int (*init)(struct zc0301_device* cam); - int (*get_ctrl)(struct zc0301_device* cam, - struct v4l2_control* ctrl); - int (*set_ctrl)(struct zc0301_device* cam, + int (*init)(struct zc0301_device*); + int (*get_ctrl)(struct zc0301_device*, struct v4l2_control* ctrl); + int (*set_ctrl)(struct zc0301_device*, const struct v4l2_control* ctrl); - int (*set_crop)(struct zc0301_device* cam, - const struct v4l2_rect* rect); - - const struct usb_device* usbdev; + int (*set_crop)(struct zc0301_device*, const struct v4l2_rect* rect); /* Private */ struct v4l2_queryctrl _qctrl[ZC0301_MAX_CTRLS]; -- cgit v1.2.3-59-g8ed1b From a5c44e29e5637b5e6fe59d225eb4f438688b3849 Mon Sep 17 00:00:00 2001 From: Lonnie Mendez Date: Wed, 1 Mar 2006 10:45:24 -0600 Subject: [PATCH] USB: cypress_m8: add support for the Nokia ca42-version 2 cable This patch adds support for the Nokia ca42 version 2 cable to the cypress_m8 driver. The device was tested by others with this patch and found to be compatible with the cypress_m8 driver. A special note should be taken that this cable seems to vary in the type of chipset used. This patch supports the cable with product id 0x4101. Signed-off-by: Lonnie Mendez Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cypress_m8.c | 70 +++++++++++++++++++++++++++++++++++++++++ drivers/usb/serial/cypress_m8.h | 5 +++ 2 files changed, 75 insertions(+) diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 71af3bf5adb7..7212fbe3b6f2 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -98,10 +98,16 @@ static struct usb_device_id id_table_cyphidcomrs232 [] = { { } /* Terminating entry */ }; +static struct usb_device_id id_table_nokiaca42v2 [] = { + { USB_DEVICE(VENDOR_ID_DAZZLE, PRODUCT_ID_CA42) }, + { } /* Terminating entry */ +}; + static struct usb_device_id id_table_combined [] = { { USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB) }, { USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB_LT20) }, { USB_DEVICE(VENDOR_ID_CYPRESS, PRODUCT_ID_CYPHIDCOM) }, + { USB_DEVICE(VENDOR_ID_DAZZLE, PRODUCT_ID_CA42) }, { } /* Terminating entry */ }; @@ -149,6 +155,7 @@ struct cypress_buf { /* function prototypes for the Cypress USB to serial device */ static int cypress_earthmate_startup (struct usb_serial *serial); static int cypress_hidcom_startup (struct usb_serial *serial); +static int cypress_ca42v2_startup (struct usb_serial *serial); static void cypress_shutdown (struct usb_serial *serial); static int cypress_open (struct usb_serial_port *port, struct file *filp); static void cypress_close (struct usb_serial_port *port, struct file *filp); @@ -235,6 +242,34 @@ static struct usb_serial_driver cypress_hidcom_device = { .write_int_callback = cypress_write_int_callback, }; +static struct usb_serial_driver cypress_ca42v2_device = { + .driver = { + .owner = THIS_MODULE, + .name = "nokiaca42v2", + }, + .description = "Nokia CA-42 V2 Adapter", + .id_table = id_table_nokiaca42v2, + .num_interrupt_in = 1, + .num_interrupt_out = 1, + .num_bulk_in = NUM_DONT_CARE, + .num_bulk_out = NUM_DONT_CARE, + .num_ports = 1, + .attach = cypress_ca42v2_startup, + .shutdown = cypress_shutdown, + .open = cypress_open, + .close = cypress_close, + .write = cypress_write, + .write_room = cypress_write_room, + .ioctl = cypress_ioctl, + .set_termios = cypress_set_termios, + .tiocmget = cypress_tiocmget, + .tiocmset = cypress_tiocmset, + .chars_in_buffer = cypress_chars_in_buffer, + .throttle = cypress_throttle, + .unthrottle = cypress_unthrottle, + .read_int_callback = cypress_read_int_callback, + .write_int_callback = cypress_write_int_callback, +}; /***************************************************************************** * Cypress serial helper functions @@ -286,6 +321,12 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m __FUNCTION__); new_baudrate = priv->baud_rate; } + } else if (priv->chiptype == CT_CA42V2) { + if ( (new_baudrate = mask_to_rate(baud_mask)) == -1) { + err("%s - failed setting baud rate, unsupported speed", + __FUNCTION__); + new_baudrate = priv->baud_rate; + } } else if (priv->chiptype == CT_GENERIC) { if ( (new_baudrate = mask_to_rate(baud_mask)) == -1) { err("%s - failed setting baud rate, unsupported speed", @@ -499,6 +540,25 @@ static int cypress_hidcom_startup (struct usb_serial *serial) } /* cypress_hidcom_startup */ +static int cypress_ca42v2_startup (struct usb_serial *serial) +{ + struct cypress_private *priv; + + dbg("%s", __FUNCTION__); + + if (generic_startup(serial)) { + dbg("%s - Failed setting up port %d", __FUNCTION__, + serial->port[0]->number); + return 1; + } + + priv = usb_get_serial_port_data(serial->port[0]); + priv->chiptype = CT_CA42V2; + + return 0; +} /* cypress_ca42v2_startup */ + + static void cypress_shutdown (struct usb_serial *serial) { struct cypress_private *priv; @@ -943,6 +1003,10 @@ static void cypress_set_termios (struct usb_serial_port *port, *(tty->termios) = tty_std_termios; tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + } else if (priv->chiptype == CT_CA42V2) { + *(tty->termios) = tty_std_termios; + tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | + CLOCAL; } priv->termios_initialized = 1; } @@ -1541,6 +1605,9 @@ static int __init cypress_init(void) retval = usb_serial_register(&cypress_hidcom_device); if (retval) goto failed_hidcom_register; + retval = usb_serial_register(&cypress_ca42v2_device); + if (retval) + goto failed_ca42v2_register; retval = usb_register(&cypress_driver); if (retval) goto failed_usb_register; @@ -1549,6 +1616,8 @@ static int __init cypress_init(void) return 0; failed_usb_register: usb_deregister(&cypress_driver); +failed_ca42v2_register: + usb_serial_deregister(&cypress_ca42v2_device); failed_hidcom_register: usb_serial_deregister(&cypress_hidcom_device); failed_em_register: @@ -1565,6 +1634,7 @@ static void __exit cypress_exit (void) usb_deregister (&cypress_driver); usb_serial_deregister (&cypress_earthmate_device); usb_serial_deregister (&cypress_hidcom_device); + usb_serial_deregister (&cypress_ca42v2_device); } diff --git a/drivers/usb/serial/cypress_m8.h b/drivers/usb/serial/cypress_m8.h index 1fa119efe41a..e1c7c27e18b7 100644 --- a/drivers/usb/serial/cypress_m8.h +++ b/drivers/usb/serial/cypress_m8.h @@ -18,6 +18,10 @@ /* Cypress HID->COM RS232 Adapter */ #define VENDOR_ID_CYPRESS 0x04b4 #define PRODUCT_ID_CYPHIDCOM 0x5500 + +/* Nokia CA-42 USB to serial cable */ +#define VENDOR_ID_DAZZLE 0x07d0 +#define PRODUCT_ID_CA42 0x4101 /* End of device listing */ /* Used for setting / requesting serial line settings */ @@ -34,6 +38,7 @@ #define CT_EARTHMATE 0x01 #define CT_CYPHIDCOM 0x02 +#define CT_CA42V2 0x03 #define CT_GENERIC 0x0F /* End of chiptype definitions */ -- cgit v1.2.3-59-g8ed1b From 96050b11f690878c19c1cd39970ab7325d91e18b Mon Sep 17 00:00:00 2001 From: Julian Bradfield Date: Wed, 1 Mar 2006 10:19:44 +0000 Subject: [PATCH] USB: PL2303 and TIOCMIWAIT A while ago, I posted about TIOCMIWAIT not working with the PL2303 USB-serial adapter. After a brief exchange with Greg, I tracked this to a missing wake-up in the USB interrupt procedures. I got our systems staff to install the enclosed very simple patch to our 2.6.12 kernels, and it all works fine as expected. I guess this should also apply to the latest version and go into the mainstream. Apologies for the long delay in posting the result. The routine being patched is pl2303_update_line_status Signed-off-by: Julian Bradfield Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index cf2213bae0e0..fb3b6b7b9065 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -826,6 +826,7 @@ static void pl2303_update_line_status(struct usb_serial_port *port, spin_lock_irqsave(&priv->lock, flags); priv->line_status = data[status_idx]; spin_unlock_irqrestore(&priv->lock, flags); + wake_up_interruptible (&priv->delta_msr_wait); exit: return; -- cgit v1.2.3-59-g8ed1b From 491b04ce1c9adfa0cd73f095086f3c37da81b667 Mon Sep 17 00:00:00 2001 From: Dick Streefland Date: Wed, 1 Mar 2006 00:53:33 -0800 Subject: [PATCH] USB: support for USB-to-serial cable from Speed Dragon Multimedia The USB data cable for my Samsung GSM phone contains the USB-to-serial converter chip MS3303H from Speed Dragon Multimedia, Inc. that appears to be compatible with the PL2303 chip. The following patch adds support for this chip to the pl2303 driver. Signed-off-by: Dick Streefland Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 1 + drivers/usb/serial/pl2303.h | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index fb3b6b7b9065..b3014fda645c 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -77,6 +77,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(CA_42_CA42_VENDOR_ID, CA_42_CA42_PRODUCT_ID) }, { USB_DEVICE(SAGEM_VENDOR_ID, SAGEM_PRODUCT_ID) }, { USB_DEVICE(LEADTEK_VENDOR_ID, LEADTEK_9531_PRODUCT_ID) }, + { USB_DEVICE(SPEEDDRAGON_VENDOR_ID, SPEEDDRAGON_PRODUCT_ID) }, { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index 9bc4755162ad..77901d143979 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -75,3 +75,7 @@ /* Leadtek GPS 9531 (ID 0413:2101) */ #define LEADTEK_VENDOR_ID 0x0413 #define LEADTEK_9531_PRODUCT_ID 0x2101 + +/* USB GSM cable from Speed Dragon Multimedia, Ltd */ +#define SPEEDDRAGON_VENDOR_ID 0x0e55 +#define SPEEDDRAGON_PRODUCT_ID 0x110b -- cgit v1.2.3-59-g8ed1b From ae55717584431761b70215d3d574c13fe97093f2 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 28 Feb 2006 10:16:12 -0500 Subject: [PATCH] USB: UHCI: Increase port-reset completion delay for HP controllers This patch (as657) increases the port-reset completion delay in uhci-hcd for HP's embedded controllers. Unlike other UHCI controllers, the HP chips can take as long as 250 us to carry out the processing associated with finishing a port reset. This fixes Novell bug #148761. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hub.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c index a71e48a66805..152971d16769 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c @@ -99,6 +99,21 @@ static void uhci_finish_suspend(struct uhci_hcd *uhci, int port, } } +/* Wait for the UHCI controller in HP's iLO2 server management chip. + * It can take up to 250 us to finish a reset and set the CSC bit. + */ +static void wait_for_HP(unsigned long port_addr) +{ + int i; + + for (i = 10; i < 250; i += 10) { + if (inw(port_addr) & USBPORTSC_CSC) + return; + udelay(10); + } + /* Log a warning? */ +} + static void uhci_check_ports(struct uhci_hcd *uhci) { unsigned int port; @@ -113,6 +128,12 @@ static void uhci_check_ports(struct uhci_hcd *uhci) CLR_RH_PORTSTAT(USBPORTSC_PR); udelay(10); + /* HP's server management chip requires + * a longer delay. */ + if (to_pci_dev(uhci_dev(uhci))->vendor == + PCI_VENDOR_ID_HP) + wait_for_HP(port_addr); + /* If the port was enabled before, turning * reset on caused a port enable change. * Turning reset off causes a port connect -- cgit v1.2.3-59-g8ed1b From 4d69581929b8f8836f806bcc320b19ed886e9517 Mon Sep 17 00:00:00 2001 From: Pete Zaitcev Date: Thu, 2 Mar 2006 16:36:09 -0800 Subject: [PATCH] USB: ub 01 remove first_open The first_open was long overdue for removal, but I wanted to keep this separate for other changes in case of regressions. Signed-off-by: Pete Zaitcev Signed-off-by: Greg Kroah-Hartman --- drivers/block/ub.c | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/drivers/block/ub.c b/drivers/block/ub.c index 3e7689494c66..26aa96eba439 100644 --- a/drivers/block/ub.c +++ b/drivers/block/ub.c @@ -8,7 +8,6 @@ * and is not licensed separately. See file COPYING for details. * * TODO (sorted by decreasing priority) - * -- Kill first_open (Al Viro fixed the block layer now) * -- set readonly flag for CDs, set removable flag for CF readers * -- do inquiry and verify we got a disk and not a tape (for LUN mismatch) * -- special case some senses, e.g. 3a/0 -> no media present, reduce retries @@ -334,7 +333,6 @@ struct ub_lun { int changed; /* Media was changed */ int removable; int readonly; - int first_open; /* Kludge. See ub_bd_open. */ struct ub_request urq; @@ -1849,26 +1847,6 @@ static int ub_bd_open(struct inode *inode, struct file *filp) sc->openc++; spin_unlock_irqrestore(&ub_lock, flags); - /* - * This is a workaround for a specific problem in our block layer. - * In 2.6.9, register_disk duplicates the code from rescan_partitions. - * However, if we do add_disk with a device which persistently reports - * a changed media, add_disk calls register_disk, which does do_open, - * which will call rescan_paritions for changed media. After that, - * register_disk attempts to do it all again and causes double kobject - * registration and a eventually an oops on module removal. - * - * The bottom line is, Al Viro says that we should not allow - * bdev->bd_invalidated to be set when doing add_disk no matter what. - */ - if (lun->first_open) { - lun->first_open = 0; - if (lun->changed) { - rc = -ENOMEDIUM; - goto err_open; - } - } - if (lun->removable || lun->readonly) check_disk_change(inode->i_bdev); @@ -2537,7 +2515,6 @@ static int ub_probe_lun(struct ub_dev *sc, int lnum) lun->removable = 1; /* XXX Query this from the device */ lun->changed = 1; /* ub_revalidate clears only */ - lun->first_open = 1; ub_revalidate(sc, lun); rc = -ENOMEM; -- cgit v1.2.3-59-g8ed1b From 952ba222962bf3fb1336f139f1049030153cae55 Mon Sep 17 00:00:00 2001 From: Pete Zaitcev Date: Thu, 2 Mar 2006 16:42:59 -0800 Subject: [PATCH] USB: ub 02 remove diag Remove the "diag" file from the sysfs. The usbmon is good enough these days so I do not need this feature anymore. Also, sysfs is a pain. Al Viro caught a race in this, which I thought too bothersome to fix. Signed-off-by: Pete Zaitcev Signed-off-by: Greg Kroah-Hartman --- drivers/block/ub.c | 197 ++--------------------------------------------------- 1 file changed, 4 insertions(+), 193 deletions(-) diff --git a/drivers/block/ub.c b/drivers/block/ub.c index 26aa96eba439..21d0e075c928 100644 --- a/drivers/block/ub.c +++ b/drivers/block/ub.c @@ -180,6 +180,7 @@ struct ub_dev; #define UB_DIR_ILLEGAL2 2 #define UB_DIR_WRITE 3 +/* P3 */ #define UB_DIR_CHAR(c) (((c)==UB_DIR_WRITE)? 'w': \ (((c)==UB_DIR_READ)? 'r': 'n')) @@ -195,24 +196,11 @@ enum ub_scsi_cmd_state { UB_CMDST_DONE /* Final state */ }; -static char *ub_scsi_cmd_stname[] = { - ". ", - "Cmd", - "dat", - "c2s", - "sts", - "clr", - "crs", - "Sen", - "fin" -}; - struct ub_scsi_cmd { unsigned char cdb[UB_MAX_CDB_SIZE]; unsigned char cdb_len; unsigned char dir; /* 0 - none, 1 - read, 3 - write. */ - unsigned char trace_index; enum ub_scsi_cmd_state state; unsigned int tag; struct ub_scsi_cmd *next; @@ -248,28 +236,6 @@ struct ub_capacity { unsigned int bshift; /* Shift between 512 and hard sects */ }; -/* - * The SCSI command tracing structure. - */ - -#define SCMD_ST_HIST_SZ 8 -#define SCMD_TRACE_SZ 63 /* Less than 4KB of 61-byte lines */ - -struct ub_scsi_cmd_trace { - int hcur; - unsigned int tag; - unsigned int req_size, act_size; - unsigned char op; - unsigned char dir; - unsigned char key, asc, ascq; - char st_hst[SCMD_ST_HIST_SZ]; -}; - -struct ub_scsi_trace { - int cur; - struct ub_scsi_cmd_trace vec[SCMD_TRACE_SZ]; -}; - /* * This is a direct take-off from linux/include/completion.h * The difference is that I do not wait on this thing, just poll. @@ -388,7 +354,6 @@ struct ub_dev { wait_queue_head_t reset_wait; int sg_stat[6]; - struct ub_scsi_trace tr; }; /* @@ -457,137 +422,6 @@ static int ub_qlock_next = 0; static DEFINE_SPINLOCK(ub_lock); /* Locks globals and ->openc */ -/* - * The SCSI command tracing procedures. - */ - -static void ub_cmdtr_new(struct ub_dev *sc, struct ub_scsi_cmd *cmd) -{ - int n; - struct ub_scsi_cmd_trace *t; - - if ((n = sc->tr.cur + 1) == SCMD_TRACE_SZ) n = 0; - t = &sc->tr.vec[n]; - - memset(t, 0, sizeof(struct ub_scsi_cmd_trace)); - t->tag = cmd->tag; - t->op = cmd->cdb[0]; - t->dir = cmd->dir; - t->req_size = cmd->len; - t->st_hst[0] = cmd->state; - - sc->tr.cur = n; - cmd->trace_index = n; -} - -static void ub_cmdtr_state(struct ub_dev *sc, struct ub_scsi_cmd *cmd) -{ - int n; - struct ub_scsi_cmd_trace *t; - - t = &sc->tr.vec[cmd->trace_index]; - if (t->tag == cmd->tag) { - if ((n = t->hcur + 1) == SCMD_ST_HIST_SZ) n = 0; - t->st_hst[n] = cmd->state; - t->hcur = n; - } -} - -static void ub_cmdtr_act_len(struct ub_dev *sc, struct ub_scsi_cmd *cmd) -{ - struct ub_scsi_cmd_trace *t; - - t = &sc->tr.vec[cmd->trace_index]; - if (t->tag == cmd->tag) - t->act_size = cmd->act_len; -} - -static void ub_cmdtr_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd, - unsigned char *sense) -{ - struct ub_scsi_cmd_trace *t; - - t = &sc->tr.vec[cmd->trace_index]; - if (t->tag == cmd->tag) { - t->key = sense[2] & 0x0F; - t->asc = sense[12]; - t->ascq = sense[13]; - } -} - -static ssize_t ub_diag_show(struct device *dev, struct device_attribute *attr, - char *page) -{ - struct usb_interface *intf; - struct ub_dev *sc; - struct list_head *p; - struct ub_lun *lun; - int cnt; - unsigned long flags; - int nc, nh; - int i, j; - struct ub_scsi_cmd_trace *t; - - intf = to_usb_interface(dev); - sc = usb_get_intfdata(intf); - if (sc == NULL) - return 0; - - cnt = 0; - spin_lock_irqsave(sc->lock, flags); - - cnt += sprintf(page + cnt, - "poison %d reset %d\n", - atomic_read(&sc->poison), sc->reset); - cnt += sprintf(page + cnt, - "qlen %d qmax %d\n", - sc->cmd_queue.qlen, sc->cmd_queue.qmax); - cnt += sprintf(page + cnt, - "sg %d %d %d %d %d .. %d\n", - sc->sg_stat[0], - sc->sg_stat[1], - sc->sg_stat[2], - sc->sg_stat[3], - sc->sg_stat[4], - sc->sg_stat[5]); - - list_for_each (p, &sc->luns) { - lun = list_entry(p, struct ub_lun, link); - cnt += sprintf(page + cnt, - "lun %u changed %d removable %d readonly %d\n", - lun->num, lun->changed, lun->removable, lun->readonly); - } - - if ((nc = sc->tr.cur + 1) == SCMD_TRACE_SZ) nc = 0; - for (j = 0; j < SCMD_TRACE_SZ; j++) { - t = &sc->tr.vec[nc]; - - cnt += sprintf(page + cnt, "%08x %02x", t->tag, t->op); - if (t->op == REQUEST_SENSE) { - cnt += sprintf(page + cnt, " [sense %x %02x %02x]", - t->key, t->asc, t->ascq); - } else { - cnt += sprintf(page + cnt, " %c", UB_DIR_CHAR(t->dir)); - cnt += sprintf(page + cnt, " [%5d %5d]", - t->req_size, t->act_size); - } - if ((nh = t->hcur + 1) == SCMD_ST_HIST_SZ) nh = 0; - for (i = 0; i < SCMD_ST_HIST_SZ; i++) { - cnt += sprintf(page + cnt, " %s", - ub_scsi_cmd_stname[(int)t->st_hst[nh]]); - if (++nh == SCMD_ST_HIST_SZ) nh = 0; - } - cnt += sprintf(page + cnt, "\n"); - - if (++nc == SCMD_TRACE_SZ) nc = 0; - } - - spin_unlock_irqrestore(sc->lock, flags); - return cnt; -} - -static DEVICE_ATTR(diag, S_IRUGO, ub_diag_show, NULL); /* N.B. World readable */ - /* * The id allocator. * @@ -1090,7 +924,6 @@ static int ub_scsi_cmd_start(struct ub_dev *sc, struct ub_scsi_cmd *cmd) add_timer(&sc->work_timer); cmd->state = UB_CMDST_CMD; - ub_cmdtr_state(sc, cmd); return 0; } @@ -1143,12 +976,10 @@ static void ub_scsi_dispatch(struct ub_dev *sc) ub_cmdq_pop(sc); (*cmd->done)(sc, cmd); } else if (cmd->state == UB_CMDST_INIT) { - ub_cmdtr_new(sc, cmd); if ((rc = ub_scsi_cmd_start(sc, cmd)) == 0) break; cmd->error = rc; cmd->state = UB_CMDST_DONE; - ub_cmdtr_state(sc, cmd); } else { if (!ub_is_completed(&sc->work_done)) break; @@ -1245,7 +1076,6 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) return; } cmd->state = UB_CMDST_CLEAR; - ub_cmdtr_state(sc, cmd); return; case -ESHUTDOWN: /* unplug */ case -EILSEQ: /* unplug timeout on uhci */ @@ -1277,7 +1107,6 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) return; } cmd->state = UB_CMDST_CLR2STS; - ub_cmdtr_state(sc, cmd); return; } if (urb->status == -EOVERFLOW) { @@ -1302,7 +1131,6 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) if (urb->status != 0 || len != cmd->sgv[cmd->current_sg].length) { cmd->act_len += len; - ub_cmdtr_act_len(sc, cmd); cmd->error = -EIO; ub_state_stat(sc, cmd); @@ -1329,7 +1157,6 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) } cmd->act_len += urb->actual_length; - ub_cmdtr_act_len(sc, cmd); if (++cmd->current_sg < cmd->nsg) { ub_data_start(sc, cmd); @@ -1355,7 +1182,6 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) cmd->error = -EIO; /* A cheap trick... */ cmd->state = UB_CMDST_CLRRS; - ub_cmdtr_state(sc, cmd); return; } @@ -1439,7 +1265,6 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) return; } cmd->state = UB_CMDST_DONE; - ub_cmdtr_state(sc, cmd); ub_cmdq_pop(sc); (*cmd->done)(sc, cmd); @@ -1494,7 +1319,6 @@ static void ub_data_start(struct ub_dev *sc, struct ub_scsi_cmd *cmd) add_timer(&sc->work_timer); cmd->state = UB_CMDST_DATA; - ub_cmdtr_state(sc, cmd); } /* @@ -1506,7 +1330,6 @@ static void ub_state_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd, int rc) cmd->error = rc; cmd->state = UB_CMDST_DONE; - ub_cmdtr_state(sc, cmd); ub_cmdq_pop(sc); (*cmd->done)(sc, cmd); } @@ -1552,7 +1375,6 @@ static void ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd) cmd->stat_count = 0; cmd->state = UB_CMDST_STAT; - ub_cmdtr_state(sc, cmd); } /* @@ -1571,7 +1393,6 @@ static void ub_state_stat_counted(struct ub_dev *sc, struct ub_scsi_cmd *cmd) return; cmd->state = UB_CMDST_STAT; - ub_cmdtr_state(sc, cmd); } /* @@ -1609,7 +1430,6 @@ static void ub_state_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd) scmd->tag = sc->tagcnt++; cmd->state = UB_CMDST_SENSE; - ub_cmdtr_state(sc, cmd); ub_cmdq_insert(sc, scmd); return; @@ -1665,11 +1485,6 @@ static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd) unsigned char *sense = sc->top_sense; struct ub_scsi_cmd *cmd; - /* - * Ignoring scmd->act_len, because the buffer was pre-zeroed. - */ - ub_cmdtr_sense(sc, scmd, sense); - /* * Find the command which triggered the unit attention or a check, * save the sense into it, and advance its state machine. @@ -1691,6 +1506,9 @@ static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd) return; } + /* + * Ignoring scmd->act_len, because the buffer was pre-zeroed. + */ cmd->key = sense[2] & 0x0F; cmd->asc = sense[12]; cmd->ascq = sense[13]; @@ -2413,9 +2231,6 @@ static int ub_probe(struct usb_interface *intf, if (ub_get_pipes(sc, sc->dev, intf) != 0) goto err_dev_desc; - if (device_create_file(&sc->intf->dev, &dev_attr_diag) != 0) - goto err_diag; - /* * At this point, all USB initialization is done, do upper layer. * We really hate halfway initialized structures, so from the @@ -2480,8 +2295,6 @@ static int ub_probe(struct usb_interface *intf, } return 0; - /* device_remove_file(&sc->intf->dev, &dev_attr_diag); */ -err_diag: err_dev_desc: usb_set_intfdata(intf, NULL); // usb_put_intf(sc->intf); @@ -2609,7 +2422,6 @@ static void ub_disconnect(struct usb_interface *intf) while ((cmd = ub_cmdq_peek(sc)) != NULL) { cmd->error = -ENOTCONN; cmd->state = UB_CMDST_DONE; - ub_cmdtr_state(sc, cmd); ub_cmdq_pop(sc); (*cmd->done)(sc, cmd); cnt++; @@ -2660,7 +2472,6 @@ static void ub_disconnect(struct usb_interface *intf) * and no URBs left in transit. */ - device_remove_file(&sc->intf->dev, &dev_attr_diag); usb_set_intfdata(intf, NULL); // usb_put_intf(sc->intf); sc->intf = NULL; -- cgit v1.2.3-59-g8ed1b From 11a223ae3b86b94391774512e5671600367a305c Mon Sep 17 00:00:00 2001 From: Pete Zaitcev Date: Thu, 2 Mar 2006 16:53:00 -0800 Subject: [PATCH] USB: ub 03 drop stall clearing Matt mentioned that a very old ZIP-100 actually does need this, but I am yet to see anyone who actually has one still working and uses ub with it. He/she must be a retrocomputing geek, who can easily bias it to usb-storage with libusual, if needed. Meanwhile, common folks have trouble with poorly designed USB keys and some el-cheapo European music players. I think we better drop this for now. Signed-off-by: Pete Zaitcev Signed-off-by: Greg Kroah-Hartman --- drivers/block/ub.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/block/ub.c b/drivers/block/ub.c index 21d0e075c928..f73446f580df 100644 --- a/drivers/block/ub.c +++ b/drivers/block/ub.c @@ -2270,19 +2270,8 @@ static int ub_probe(struct usb_interface *intf, nluns = 1; for (i = 0; i < 3; i++) { - if ((rc = ub_sync_getmaxlun(sc)) < 0) { - /* - * This segment is taken from usb-storage. They say - * that ZIP-100 needs this, but my own ZIP-100 works - * fine without this. - * Still, it does not seem to hurt anything. - */ - if (rc == -EPIPE) { - ub_probe_clear_stall(sc, sc->recv_bulk_pipe); - ub_probe_clear_stall(sc, sc->send_bulk_pipe); - } + if ((rc = ub_sync_getmaxlun(sc)) < 0) break; - } if (rc != 0) { nluns = rc; break; -- cgit v1.2.3-59-g8ed1b From 6aa35675bbc370e5f11baae7e01a9ab255d8030c Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 8 Mar 2006 15:14:09 -0500 Subject: [PATCH] USB: usbcore: Don't assume a USB configuration includes any interfaces In a couple of places, usbcore assumes that a USB device configuration will have a nonzero number of interfaces. Having no interfaces may or may not be allowed by the USB spec; in any event we shouldn't die if we encounter such a thing. This patch (as662) removes the assumptions. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 7dd28f8e1cea..8e65f7a237e4 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1179,8 +1179,11 @@ static int choose_configuration(struct usb_device *udev) c = udev->config; num_configs = udev->descriptor.bNumConfigurations; for (i = 0; i < num_configs; (i++, c++)) { - struct usb_interface_descriptor *desc = - &c->intf_cache[0]->altsetting->desc; + struct usb_interface_descriptor *desc = NULL; + + /* It's possible that a config has no interfaces! */ + if (c->desc.bNumInterfaces > 0) + desc = &c->intf_cache[0]->altsetting->desc; /* * HP's USB bus-powered keyboard has only one configuration @@ -1215,7 +1218,8 @@ static int choose_configuration(struct usb_device *udev) /* If the first config's first interface is COMM/2/0xff * (MSFT RNDIS), rule it out unless Linux has host-side * RNDIS support. */ - if (i == 0 && desc->bInterfaceClass == USB_CLASS_COMM + if (i == 0 && desc + && desc->bInterfaceClass == USB_CLASS_COMM && desc->bInterfaceSubClass == 2 && desc->bInterfaceProtocol == 0xff) { #ifndef CONFIG_USB_NET_RNDIS @@ -1231,8 +1235,8 @@ static int choose_configuration(struct usb_device *udev) * than a vendor-specific driver. */ else if (udev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC && - desc->bInterfaceClass != - USB_CLASS_VENDOR_SPEC) { + (!desc || desc->bInterfaceClass != + USB_CLASS_VENDOR_SPEC)) { best = c; break; } @@ -3024,7 +3028,7 @@ int usb_reset_device(struct usb_device *udev) parent_hub = hdev_to_hub(parent_hdev); /* If we're resetting an active hub, take some special actions */ - if (udev->actconfig && + if (udev->actconfig && udev->actconfig->desc.bNumInterfaces > 0 && udev->actconfig->interface[0]->dev.driver == &hub_driver.driver && (hub = hdev_to_hub(udev)) != NULL) { -- cgit v1.2.3-59-g8ed1b From f48219db93eaee644e9fd9f22fb6421f38059cc5 Mon Sep 17 00:00:00 2001 From: Horst Schirmeier Date: Thu, 9 Mar 2006 14:10:49 +0100 Subject: [PATCH] USB: usbcore: usb_set_configuration oops (NULL ptr dereference) When trying to deconfigure a device via usb_set_configuration(dev, 0), 2.6.16-rc kernels after 55c527187c9d78f840b284d596a0b298bc1493af oops with "Unable to handle NULL pointer dereference at...". This is due to an unchecked dereference of cp in the power budget part. Signed-off-by: Horst Schirmeier Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/message.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 2f6009b0cffc..08fb20f06f3e 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1388,11 +1388,13 @@ free_interfaces: if (dev->state != USB_STATE_ADDRESS) usb_disable_device (dev, 1); // Skip ep0 - i = dev->bus_mA - cp->desc.bMaxPower * 2; - if (i < 0) - dev_warn(&dev->dev, "new config #%d exceeds power " - "limit by %dmA\n", - configuration, -i); + if (cp) { + i = dev->bus_mA - cp->desc.bMaxPower * 2; + if (i < 0) + dev_warn(&dev->dev, "new config #%d exceeds power " + "limit by %dmA\n", + configuration, -i); + } if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION, 0, configuration, 0, -- cgit v1.2.3-59-g8ed1b From b712548c5d88f2e4672f8482c3904e8c5728dbf1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 17 Mar 2006 17:40:08 -0800 Subject: [PATCH] USB: fix initdata issue in isp116x-hcd As found by Sam's scripts. Cc: Sam Ravnborg Cc: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp116x-hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index fea5dcbec7c4..e99210b7909b 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -1552,7 +1552,7 @@ static struct hc_driver isp116x_hc_driver = { /*----------------------------------------------------------------*/ -static int __init_or_module isp116x_remove(struct platform_device *pdev) +static int isp116x_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); struct isp116x *isp116x; -- cgit v1.2.3-59-g8ed1b From 1e7a5a84e407dfa20813a7f55413be8193ee5380 Mon Sep 17 00:00:00 2001 From: Phil Dibowitz Date: Sun, 5 Mar 2006 21:36:51 -0800 Subject: [PATCH] USB: storage: sandisk unusual_devices entry The following adds an unusual_devs entry for the SanDisk ImageMate CompactFlash USB drive, for the US_FL_FIX_CAPACITY flag. Additionally, it removes trailing whitespace from the previous entry. It's based on the patch sent by Roman Hodek . Signed-off-by: Phil Dibowitz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 31ca92056c27..a0d355831204 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -760,12 +760,19 @@ UNUSUAL_DEV( 0x069b, 0x3004, 0x0001, 0x0001, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_CAPACITY ), -UNUSUAL_DEV( 0x0781, 0x0001, 0x0200, 0x0200, +/* Submitted by Roman Hodek */ +UNUSUAL_DEV( 0x0781, 0x0001, 0x0200, 0x0200, "Sandisk", "ImageMate SDDR-05a", US_SC_SCSI, US_PR_CB, NULL, US_FL_SINGLE_LUN ), +UNUSUAL_DEV( 0x0781, 0x0002, 0x0009, 0x0009, + "SanDisk Corporation", + "ImageMate CompactFlash USB", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY ), + #ifdef CONFIG_USB_STORAGE_USBAT UNUSUAL_DEV( 0x0781, 0x0005, 0x0005, 0x0005, "Sandisk", -- cgit v1.2.3-59-g8ed1b From c713c973fb4bfdb22cce488cca4f9f8006ed58ce Mon Sep 17 00:00:00 2001 From: Pete Zaitcev Date: Sun, 5 Mar 2006 21:43:40 -0800 Subject: [PATCH] USB: storage: another unusual_devs.h entry Signed-off-by: Phil Dibowitz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index a0d355831204..5d78fb42cb04 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1080,6 +1080,16 @@ UNUSUAL_DEV( 0x0c0b, 0xa109, 0x0000, 0xffff, 0), #endif +/* + * Pete Zaitcev , bz#164688. + * The device blatantly ignores LUN and returns 1 in GetMaxLUN. + */ +UNUSUAL_DEV( 0x0c45, 0x1060, 0x0100, 0x0100, + "Unknown", + "Unknown", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_SINGLE_LUN ), + /* Submitted by Joris Struyve */ UNUSUAL_DEV( 0x0d96, 0x410a, 0x0001, 0xffff, "Medion", -- cgit v1.2.3-59-g8ed1b From 23b7885d50c5f5fe6dca93d4b0913f6e7b878dad Mon Sep 17 00:00:00 2001 From: Pete Zaitcev Date: Sun, 5 Mar 2006 21:45:44 -0800 Subject: [PATCH] USB: storage: unusual_devs.h entry 0420:0001 Ref https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=176584 Signed-off-by: Phil Dibowitz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 5d78fb42cb04..8310e86d745d 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -120,6 +120,12 @@ UNUSUAL_DEV( 0x0419, 0xaaf6, 0x0100, 0x0100, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_IGNORE_RESIDUE ), +/* Reported by Pete Zaitcev , bz#176584 */ +UNUSUAL_DEV( 0x0420, 0x0001, 0x0100, 0x0100, + "GENERIC", "MP3 PLAYER", /* MyMusix PD-205 on the outside. */ + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + /* Reported by Olaf Hering from novell bug #105878 */ UNUSUAL_DEV( 0x0424, 0x0fdc, 0x0210, 0x0210, "SMSC", -- cgit v1.2.3-59-g8ed1b From 9d5847bc1a3977fbfb4057aad41458df89792309 Mon Sep 17 00:00:00 2001 From: Rodolfo Quesada Date: Mon, 6 Mar 2006 10:45:42 -0500 Subject: [PATCH] USB: storage: new unusual_devs.h entry: Mitsumi 7in1 Card Reader This patch (as661) adds an unusual_devs entry for the Mitsumi 7in1 Card Reader. From: Rodolfo Quesada Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 8310e86d745d..c4a9dcff5f2b 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -62,6 +62,13 @@ UNUSUAL_DEV( 0x03ee, 0x6901, 0x0000, 0x0100, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_SINGLE_LUN ), +/* Reported by Rodolfo Quesada */ +UNUSUAL_DEV( 0x03ee, 0x6906, 0x0003, 0x0003, + "VIA Technologies Inc.", + "Mitsumi multi cardreader", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + UNUSUAL_DEV( 0x03f0, 0x0107, 0x0200, 0x0200, "HP", "CD-Writer+", -- cgit v1.2.3-59-g8ed1b From 2e56222ed52cec40427fa89f23b228232e3e327e Mon Sep 17 00:00:00 2001 From: Wolfgang Rohdewald Date: Wed, 8 Mar 2006 16:59:44 +0100 Subject: [PATCH] USB: add support for Creativelabs Silvercrest USB keyboard Signed-off-by: Wolfgang Rohdewald Signed-off-by: Greg Kroah-Hartman --- drivers/usb/input/hid-core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c index 6b8a51cad375..58b59f6e9881 100644 --- a/drivers/usb/input/hid-core.c +++ b/drivers/usb/input/hid-core.c @@ -1552,6 +1552,9 @@ void hid_init_reports(struct hid_device *hid) #define USB_VENDOR_ID_HP 0x03f0 #define USB_DEVICE_ID_HP_USBHUB_KB 0x020c +#define USB_VENDOR_ID_CREATIVELABS 0x062a +#define USB_DEVICE_ID_CREATIVELABS_SILVERCREST 0x0201 + /* * Alphabetically sorted blacklist by quirk type. */ @@ -1668,6 +1671,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET }, { USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_KEYBOARD, HID_QUIRK_NOGET}, { USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_USBHUB_KB, HID_QUIRK_NOGET}, + { USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVELABS_SILVERCREST, HID_QUIRK_NOGET }, { USB_VENDOR_ID_HP, USB_DEVICE_ID_HP_USBHUB_KB, HID_QUIRK_NOGET }, { USB_VENDOR_ID_TANGTOP, USB_DEVICE_ID_TANGTOP_USBPS2, HID_QUIRK_NOGET }, { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, -- cgit v1.2.3-59-g8ed1b From 6e0755a4b2a41a8cd5839db69532d07262294b41 Mon Sep 17 00:00:00 2001 From: Luca Risolia Date: Fri, 3 Mar 2006 09:58:39 +0000 Subject: [PATCH] USB: ZC0301 driver bugfix ZC0301 driver bugfix. Use correct PID/VID USB entries. Signed-off-by: Luca Risolia Signed-off-by: Greg Kroah-Hartman --- Documentation/usb/zc0301.txt | 12 ++++++------ drivers/usb/media/zc0301.h | 5 +---- drivers/usb/media/zc0301_core.c | 6 +++--- drivers/usb/media/zc0301_sensor.h | 12 ++++++------ 4 files changed, 16 insertions(+), 19 deletions(-) diff --git a/Documentation/usb/zc0301.txt b/Documentation/usb/zc0301.txt index 095838420e82..f55262c6733b 100644 --- a/Documentation/usb/zc0301.txt +++ b/Documentation/usb/zc0301.txt @@ -201,14 +201,14 @@ devices mounting the ZC0301 Image Processor and Control Chips: Vendor ID Product ID --------- ---------- -0x10fd 0x8050 -0x041e 0x0417 -0x041e 0x041e -0x041e 0x081c -0x041e 0x0834 -0x041e 0x0835 +0x041e 0x4017 +0x041e 0x401c +0x041e 0x401e +0x041e 0x4034 +0x041e 0x4035 0x046d 0x08ae 0x0ac8 0x0301 +0x10fd 0x8050 The list above does not imply that all those devices work with this driver: up until now only the ones that mount the following image sensors are supported; diff --git a/drivers/usb/media/zc0301.h b/drivers/usb/media/zc0301.h index 9ba9135e824b..8e0655140e60 100644 --- a/drivers/usb/media/zc0301.h +++ b/drivers/usb/media/zc0301.h @@ -134,10 +134,7 @@ struct zc0301_device { struct zc0301_device* zc0301_match_id(struct zc0301_device* cam, const struct usb_device_id *id) { - if (usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id)) - return cam; - - return NULL; + return usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id) ? cam : NULL; } void diff --git a/drivers/usb/media/zc0301_core.c b/drivers/usb/media/zc0301_core.c index 5773688d3dae..4036c6268bff 100644 --- a/drivers/usb/media/zc0301_core.c +++ b/drivers/usb/media/zc0301_core.c @@ -52,8 +52,8 @@ #define ZC0301_MODULE_AUTHOR "(C) 2006 Luca Risolia" #define ZC0301_AUTHOR_EMAIL "" #define ZC0301_MODULE_LICENSE "GPL" -#define ZC0301_MODULE_VERSION "1:1.02" -#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 2) +#define ZC0301_MODULE_VERSION "1:1.03" +#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 3) /*****************************************************************************/ @@ -637,7 +637,6 @@ static void zc0301_release_resources(struct zc0301_device* cam) DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor); video_set_drvdata(cam->v4ldev, NULL); video_unregister_device(cam->v4ldev); - usb_put_dev(cam->usbdev); kfree(cam->control_buffer); } @@ -727,6 +726,7 @@ static int zc0301_release(struct inode* inode, struct file* filp) if (cam->state & DEV_DISCONNECTED) { zc0301_release_resources(cam); + usb_put_dev(cam->usbdev); mutex_unlock(&cam->dev_mutex); kfree(cam); return 0; diff --git a/drivers/usb/media/zc0301_sensor.h b/drivers/usb/media/zc0301_sensor.h index e3cb6cc920ca..cf0965a81d01 100644 --- a/drivers/usb/media/zc0301_sensor.h +++ b/drivers/usb/media/zc0301_sensor.h @@ -58,14 +58,14 @@ zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor); #define ZC0301_ID_TABLE \ static const struct usb_device_id zc0301_id_table[] = { \ - { ZC0301_USB_DEVICE(0x10fd, 0x8050, 0xff), }, /* TAS5130D */ \ - { ZC0301_USB_DEVICE(0x041e, 0x0417, 0xff), }, \ - { ZC0301_USB_DEVICE(0x041e, 0x041e, 0xff), }, /* HV7131B */ \ - { ZC0301_USB_DEVICE(0x041e, 0x081c, 0xff), }, /* PAS106 */ \ - { ZC0301_USB_DEVICE(0x041e, 0x0834, 0xff), }, /* PAS106 */ \ - { ZC0301_USB_DEVICE(0x041e, 0x0835, 0xff), }, /* PAS106 */ \ + { ZC0301_USB_DEVICE(0x041e, 0x4017, 0xff), }, \ + { ZC0301_USB_DEVICE(0x041e, 0x401c, 0xff), }, /* PAS106 */ \ + { ZC0301_USB_DEVICE(0x041e, 0x401e, 0xff), }, /* HV7131B */ \ + { ZC0301_USB_DEVICE(0x041e, 0x4034, 0xff), }, /* PAS106 */ \ + { ZC0301_USB_DEVICE(0x041e, 0x4035, 0xff), }, /* PAS106 */ \ { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202BCB */ \ { ZC0301_USB_DEVICE(0x0ac8, 0x0301, 0xff), }, \ + { ZC0301_USB_DEVICE(0x10fd, 0x8050, 0xff), }, /* TAS5130D */ \ { } \ }; -- cgit v1.2.3-59-g8ed1b From f88f8295d8a9229fc8b647baf4f15959384d7bb7 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 10 Mar 2006 23:25:06 +0100 Subject: [PATCH] USB: vicam.c: fix a NULL pointer dereference This patch fixes a NULL pointer dereference spotted by the Coverity checker. Signed-off-by: Adrian Bunk Signed-off-by: Greg Kroah-Hartman --- drivers/usb/media/vicam.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/media/vicam.c b/drivers/usb/media/vicam.c index 8cfc47b831fb..1d06e53ec7c5 100644 --- a/drivers/usb/media/vicam.c +++ b/drivers/usb/media/vicam.c @@ -764,6 +764,7 @@ vicam_open(struct inode *inode, struct file *file) if (!cam) { printk(KERN_ERR "vicam video_device improperly initialized"); + return -EINVAL; } /* the videodev_lock held above us protects us from -- cgit v1.2.3-59-g8ed1b From 24f8b116c45e46779dec553f934c3d74f79c06fb Mon Sep 17 00:00:00 2001 From: Horst Schirmeier Date: Sat, 11 Mar 2006 00:16:55 -0800 Subject: [PATCH] USB: fix check_ctrlrecip to allow control transfers in state ADDRESS check_ctrlrecip() disallows any control transfers if the device is deconfigured (in configuration 0, ie. state ADDRESS). This for example makes it impossible to read the device descriptors without configuring the device, although most standard device requests are allowed in this state by the spec. This patch allows control transfers for the ADDRESS state, too. Signed-off-by: Horst Schirmeier Cc: Alan Stern Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index de6a7c07cf97..545da37afca7 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -493,7 +493,8 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, unsig { int ret = 0; - if (ps->dev->state != USB_STATE_CONFIGURED) + if (ps->dev->state != USB_STATE_ADDRESS + && ps->dev->state != USB_STATE_CONFIGURED) return -EHOSTUNREACH; if (USB_TYPE_VENDOR == (USB_TYPE_MASK & requesttype)) return 0; -- cgit v1.2.3-59-g8ed1b From 59224f5352542b968c41200954e56c26c4f0a075 Mon Sep 17 00:00:00 2001 From: Craig Shelley Date: Sat, 11 Mar 2006 11:29:02 +0000 Subject: [PATCH] USB: cp2101: add new device IDs This patch adds a new device ID to the cp2101 driver Signed-off-by: Craig Shelley Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cp2101.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c index 9ea7b4a4a22b..e0c2acdb3f06 100644 --- a/drivers/usb/serial/cp2101.c +++ b/drivers/usb/serial/cp2101.c @@ -32,7 +32,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v0.06" +#define DRIVER_VERSION "v0.07" #define DRIVER_DESC "Silicon Labs CP2101/CP2102 RS232 serial adaptor driver" /* @@ -58,6 +58,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x10A6, 0xAA26) }, /* Knock-off DCU-11 cable */ { USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */ { USB_DEVICE(0x10B5, 0xAC70) }, /* Nokia CA-42 USB */ + { USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */ { USB_DEVICE(0x10C4, 0x807A) }, /* Crumb128 board */ { USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */ { USB_DEVICE(0x10C4, 0x80F6) }, /* Suunto sports instrument */ -- cgit v1.2.3-59-g8ed1b From bf58fbd5e86a43466e638407ff8a4eb7766a3b68 Mon Sep 17 00:00:00 2001 From: "A. Maitland Bottoms" Date: Tue, 14 Mar 2006 18:44:23 -0500 Subject: [PATCH] USB: ftdi_sio: add Icom ID1 USB product and vendor ids The Icom ID-1 1.2 GHz band digital transceiver is a new radio that has a USB interface. With this patch, the ftdi_sio driver will report "Detected FT8U232AM" and provide a serial device interface. Signed-off-by: "A. Maitland Bottoms" Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 1 + drivers/usb/serial/ftdi_sio.h | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index ece52a6328b5..f3af81b4dd29 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -492,6 +492,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_WESTREX_MODEL_777_PID) }, { USB_DEVICE(FTDI_VID, FTDI_WESTREX_MODEL_8900F_PID) }, { USB_DEVICE(FTDI_VID, FTDI_PCDJ_DAC2_PID) }, + { USB_DEVICE(ICOM_ID1_VID, ICOM_ID1_PID) }, { }, /* Optional parameter entry */ { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index bdef3b8c731f..8da773c2744d 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -145,6 +145,13 @@ #define KOBIL_CONV_B1_PID 0x2020 /* KOBIL Konverter for B1 */ #define KOBIL_CONV_KAAN_PID 0x2021 /* KOBIL_Konverter for KAAN */ +/* + * Icom ID-1 digital transceiver + */ + +#define ICOM_ID1_VID 0x0C26 +#define ICOM_ID1_PID 0x0004 + /* * DSS-20 Sync Station for Sony Ericsson P800 */ -- cgit v1.2.3-59-g8ed1b From 09abfa8048de8e68eaa09eb07ac18f2d549dfe58 Mon Sep 17 00:00:00 2001 From: Petko Manolov Date: Wed, 15 Mar 2006 16:29:38 +0200 Subject: [PATCH] USB: rtl8150 small fix This one is about announcing the device registration after the last check has been made. From: Petko Manolov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/net/rtl8150.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/net/rtl8150.c b/drivers/usb/net/rtl8150.c index 8ca52be23976..1bbbae283c0b 100644 --- a/drivers/usb/net/rtl8150.c +++ b/drivers/usb/net/rtl8150.c @@ -880,7 +880,6 @@ static int rtl8150_probe(struct usb_interface *intf, } fill_skb_pool(dev); set_ethernet_addr(dev); - info("%s: rtl8150 is detected", netdev->name); usb_set_intfdata(intf, dev); SET_NETDEV_DEV(netdev, &intf->dev); @@ -888,6 +887,9 @@ static int rtl8150_probe(struct usb_interface *intf, err("couldn't register the device"); goto out2; } + + info("%s: rtl8150 is detected", netdev->name); + return 0; out2: -- cgit v1.2.3-59-g8ed1b From da81817fbd744ce70983f1d3c61841265003c7f4 Mon Sep 17 00:00:00 2001 From: Eugene Teo Date: Wed, 15 Mar 2006 14:57:19 -0800 Subject: [PATCH] USB: Fix irda-usb use after use Don't read from free'd memory after calling netif_rx(). docopy is used as a boolean (0 and 1) so unsigned int is sufficient. Coverity bug #928 Signed-off-by: Eugene Teo Cc: "David S. Miller" Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/net/irda/irda-usb.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c index 8936058a3cce..6e2ec56cde0b 100644 --- a/drivers/net/irda/irda-usb.c +++ b/drivers/net/irda/irda-usb.c @@ -740,7 +740,7 @@ static void irda_usb_receive(struct urb *urb, struct pt_regs *regs) struct sk_buff *newskb; struct sk_buff *dataskb; struct urb *next_urb; - int docopy; + unsigned int len, docopy; IRDA_DEBUG(2, "%s(), len=%d\n", __FUNCTION__, urb->actual_length); @@ -851,10 +851,11 @@ static void irda_usb_receive(struct urb *urb, struct pt_regs *regs) dataskb->dev = self->netdev; dataskb->mac.raw = dataskb->data; dataskb->protocol = htons(ETH_P_IRDA); + len = dataskb->len; netif_rx(dataskb); /* Keep stats up to date */ - self->stats.rx_bytes += dataskb->len; + self->stats.rx_bytes += len; self->stats.rx_packets++; self->netdev->last_rx = jiffies; -- cgit v1.2.3-59-g8ed1b From e9a66c64bb7033cb0180d419b2008acf7a141adc Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 17 Mar 2006 17:40:08 -0800 Subject: [PATCH] USB serial: add navman driver Thanks to Warren Lewis for the information needed to write the driver and for testing it out. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/Kconfig | 7 ++ drivers/usb/serial/Makefile | 1 + drivers/usb/serial/navman.c | 157 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 drivers/usb/serial/navman.c diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index be5dc80836c3..5a8a2c91c2b2 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -403,6 +403,13 @@ config USB_SERIAL_MCT_U232 To compile this driver as a module, choose M here: the module will be called mct_u232. +config USB_SERIAL_NAVMAN + tristate "USB Navman GPS device" + depends on USB_SERIAL + help + To compile this driver as a module, choose M here: the + module will be called navman. + config USB_SERIAL_PL2303 tristate "USB Prolific 2303 Single Port Serial Driver" depends on USB_SERIAL diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index f0b04420cea1..f7fe4172efed 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_USB_SERIAL_KEYSPAN_PDA) += keyspan_pda.o obj-$(CONFIG_USB_SERIAL_KLSI) += kl5kusb105.o obj-$(CONFIG_USB_SERIAL_KOBIL_SCT) += kobil_sct.o obj-$(CONFIG_USB_SERIAL_MCT_U232) += mct_u232.o +obj-$(CONFIG_USB_SERIAL_NAVMAN) += navman.o obj-$(CONFIG_USB_SERIAL_OMNINET) += omninet.o obj-$(CONFIG_USB_SERIAL_OPTION) += option.o obj-$(CONFIG_USB_SERIAL_PL2303) += pl2303.o diff --git a/drivers/usb/serial/navman.c b/drivers/usb/serial/navman.c new file mode 100644 index 000000000000..7f544081032e --- /dev/null +++ b/drivers/usb/serial/navman.c @@ -0,0 +1,157 @@ +/* + * Navman Serial USB driver + * + * Copyright (C) 2006 Greg Kroah-Hartman + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include "usb-serial.h" + +static int debug; + +static struct usb_device_id id_table [] = { + { USB_DEVICE(0x0a99, 0x0001) }, /* Talon Technology device */ + { }, +}; +MODULE_DEVICE_TABLE(usb, id_table); + +static struct usb_driver navman_driver = { + .name = "navman", + .probe = usb_serial_probe, + .disconnect = usb_serial_disconnect, + .id_table = id_table, + .no_dynamic_id = 1, +}; + +static void navman_read_int_callback(struct urb *urb, struct pt_regs *regs) +{ + struct usb_serial_port *port = urb->context; + unsigned char *data = urb->transfer_buffer; + struct tty_struct *tty; + int result; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", + __FUNCTION__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", + __FUNCTION__, urb->status); + goto exit; + } + + usb_serial_debug_data(debug, &port->dev, __FUNCTION__, + urb->actual_length, data); + + tty = port->tty; + if (tty && urb->actual_length) { + tty_buffer_request_room(tty, urb->actual_length); + tty_insert_flip_string(tty, data, urb->actual_length); + tty_flip_buffer_push(tty); + } + +exit: + result = usb_submit_urb(urb, GFP_ATOMIC); + if (result) + dev_err(&urb->dev->dev, + "%s - Error %d submitting interrupt urb\n", + __FUNCTION__, result); +} + +static int navman_open(struct usb_serial_port *port, struct file *filp) +{ + int result = 0; + + dbg("%s - port %d", __FUNCTION__, port->number); + + if (port->interrupt_in_urb) { + dbg("%s - adding interrupt input for treo", __FUNCTION__); + result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); + if (result) + dev_err(&port->dev, + "%s - failed submitting interrupt urb, error %d\n", + __FUNCTION__, result); + } + return result; +} + +static void navman_close(struct usb_serial_port *port, struct file *filp) +{ + dbg("%s - port %d", __FUNCTION__, port->number); + + if (port->interrupt_in_urb) + usb_kill_urb(port->interrupt_in_urb); +} + +static int navman_write(struct usb_serial_port *port, + const unsigned char *buf, int count) +{ + dbg("%s - port %d", __FUNCTION__, port->number); + + /* + * This device can't write any data, only read from the device + * so we just silently eat all data sent to us and say it was + * successfully sent. + * Evil, I know, but do you have a better idea? + */ + + return count; +} + +static struct usb_serial_driver navman_device = { + .driver = { + .owner = THIS_MODULE, + .name = "navman", + }, + .id_table = id_table, + .num_interrupt_in = NUM_DONT_CARE, + .num_bulk_in = NUM_DONT_CARE, + .num_bulk_out = NUM_DONT_CARE, + .num_ports = 1, + .open = navman_open, + .close = navman_close, + .write = navman_write, + .read_int_callback = navman_read_int_callback, +}; + +static int __init navman_init(void) +{ + int retval; + + retval = usb_serial_register(&navman_device); + if (retval) + return retval; + retval = usb_register(&navman_driver); + if (retval) + usb_serial_deregister(&navman_device); + return retval; +} + +static void __exit navman_exit(void) +{ + usb_deregister(&navman_driver); + usb_serial_deregister(&navman_device); +} + +module_init(navman_init); +module_exit(navman_exit); +MODULE_LICENSE("GPL"); + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug enabled or not"); -- cgit v1.2.3-59-g8ed1b From 71a8924bee63d891f6256d560e32416a458440b3 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 20 Mar 2006 17:28:39 -0500 Subject: [PATCH] USB: omninet: fix up debugging comments Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/omninet.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 762d8ff9a1e4..4d40704dea2c 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -204,7 +204,7 @@ static void omninet_read_bulk_callback (struct urb *urb, struct pt_regs *regs) int i; int result; -// dbg("omninet_read_bulk_callback"); + dbg("%s - port %d", __FUNCTION__, port->number); if (urb->status) { dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status); @@ -250,7 +250,7 @@ static int omninet_write (struct usb_serial_port *port, const unsigned char *buf int result; -// dbg("omninet_write port %d", port->number); + dbg("%s - port %d", __FUNCTION__, port->number); if (count == 0) { dbg("%s - write request of 0 bytes", __FUNCTION__); @@ -302,7 +302,7 @@ static int omninet_write_room (struct usb_serial_port *port) if (wport->write_urb_busy) room = wport->bulk_out_size - OMNINET_HEADERLEN; -// dbg("omninet_write_room returns %d", room); + dbg("%s - returns %d", __FUNCTION__, room); return (room); } @@ -312,7 +312,7 @@ static void omninet_write_bulk_callback (struct urb *urb, struct pt_regs *regs) /* struct omninet_header *header = (struct omninet_header *) urb->transfer_buffer; */ struct usb_serial_port *port = (struct usb_serial_port *) urb->context; -// dbg("omninet_write_bulk_callback, port %0x\n", port); + dbg("%s - port %0x\n", __FUNCTION__, port->number); port->write_urb_busy = 0; if (urb->status) { @@ -321,8 +321,6 @@ static void omninet_write_bulk_callback (struct urb *urb, struct pt_regs *regs) } schedule_work(&port->work); - -// dbg("omninet_write_bulk_callback, tty %0x\n", tty); } -- cgit v1.2.3-59-g8ed1b