diff options
author | 2023-02-21 18:24:12 -0800 | |
---|---|---|
committer | 2023-02-21 18:24:12 -0800 | |
commit | 5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 (patch) | |
tree | cc5c2d0a898769fd59549594fedb3ee6f84e59a0 /drivers/usb/image/mdc800.c | |
download | linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.tar.gz linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.zip |
Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-nextgrafted
Pull networking updates from Jakub Kicinski:
"Core:
- Add dedicated kmem_cache for typical/small skb->head, avoid having
to access struct page at kfree time, and improve memory use.
- Introduce sysctl to set default RPS configuration for new netdevs.
- Define Netlink protocol specification format which can be used to
describe messages used by each family and auto-generate parsers.
Add tools for generating kernel data structures and uAPI headers.
- Expose all net/core sysctls inside netns.
- Remove 4s sleep in netpoll if carrier is instantly detected on
boot.
- Add configurable limit of MDB entries per port, and port-vlan.
- Continue populating drop reasons throughout the stack.
- Retire a handful of legacy Qdiscs and classifiers.
Protocols:
- Support IPv4 big TCP (TSO frames larger than 64kB).
- Add IP_LOCAL_PORT_RANGE socket option, to control local port range
on socket by socket basis.
- Track and report in procfs number of MPTCP sockets used.
- Support mixing IPv4 and IPv6 flows in the in-kernel MPTCP path
manager.
- IPv6: don't check net.ipv6.route.max_size and rely on garbage
collection to free memory (similarly to IPv4).
- Support Penultimate Segment Pop (PSP) flavor in SRv6 (RFC8986).
- ICMP: add per-rate limit counters.
- Add support for user scanning requests in ieee802154.
- Remove static WEP support.
- Support minimal Wi-Fi 7 Extremely High Throughput (EHT) rate
reporting.
- WiFi 7 EHT channel puncturing support (client & AP).
BPF:
- Add a rbtree data structure following the "next-gen data structure"
precedent set by recently added linked list, that is, by using
kfunc + kptr instead of adding a new BPF map type.
- Expose XDP hints via kfuncs with initial support for RX hash and
timestamp metadata.
- Add BPF_F_NO_TUNNEL_KEY extension to bpf_skb_set_tunnel_key to
better support decap on GRE tunnel devices not operating in collect
metadata.
- Improve x86 JIT's codegen for PROBE_MEM runtime error checks.
- Remove the need for trace_printk_lock for bpf_trace_printk and
bpf_trace_vprintk helpers.
- Extend libbpf's bpf_tracing.h support for tracing arguments of
kprobes/uprobes and syscall as a special case.
- Significantly reduce the search time for module symbols by
livepatch and BPF.
- Enable cpumasks to be used as kptrs, which is useful for tracing
programs tracking which tasks end up running on which CPUs in
different time intervals.
- Add support for BPF trampoline on s390x and riscv64.
- Add capability to export the XDP features supported by the NIC.
- Add __bpf_kfunc tag for marking kernel functions as kfuncs.
- Add cgroup.memory=nobpf kernel parameter option to disable BPF
memory accounting for container environments.
Netfilter:
- Remove the CLUSTERIP target. It has been marked as obsolete for
years, and we still have WARN splats wrt races of the out-of-band
/proc interface installed by this target.
- Add 'destroy' commands to nf_tables. They are identical to the
existing 'delete' commands, but do not return an error if the
referenced object (set, chain, rule...) did not exist.
Driver API:
- Improve cpumask_local_spread() locality to help NICs set the right
IRQ affinity on AMD platforms.
- Separate C22 and C45 MDIO bus transactions more clearly.
- Introduce new DCB table to control DSCP rewrite on egress.
- Support configuration of Physical Layer Collision Avoidance (PLCA)
Reconciliation Sublayer (RS) (802.3cg-2019). Modern version of
shared medium Ethernet.
- Support for MAC Merge layer (IEEE 802.3-2018 clause 99). Allowing
preemption of low priority frames by high priority frames.
- Add support for controlling MACSec offload using netlink SET.
- Rework devlink instance refcounts to allow registration and
de-registration under the instance lock. Split the code into
multiple files, drop some of the unnecessarily granular locks and
factor out common parts of netlink operation handling.
- Add TX frame aggregation parameters (for USB drivers).
- Add a new attr TCA_EXT_WARN_MSG to report TC (offload) warning
messages with notifications for debug.
- Allow offloading of UDP NEW connections via act_ct.
- Add support for per action HW stats in TC.
- Support hardware miss to TC action (continue processing in SW from
a specific point in the action chain).
- Warn if old Wireless Extension user space interface is used with
modern cfg80211/mac80211 drivers. Do not support Wireless
Extensions for Wi-Fi 7 devices at all. Everyone should switch to
using nl80211 interface instead.
- Improve the CAN bit timing configuration. Use extack to return
error messages directly to user space, update the SJW handling,
including the definition of a new default value that will benefit
CAN-FD controllers, by increasing their oscillator tolerance.
New hardware / drivers:
- Ethernet:
- nVidia BlueField-3 support (control traffic driver)
- Ethernet support for imx93 SoCs
- Motorcomm yt8531 gigabit Ethernet PHY
- onsemi NCN26000 10BASE-T1S PHY (with support for PLCA)
- Microchip LAN8841 PHY (incl. cable diagnostics and PTP)
- Amlogic gxl MDIO mux
- WiFi:
- RealTek RTL8188EU (rtl8xxxu)
- Qualcomm Wi-Fi 7 devices (ath12k)
- CAN:
- Renesas R-Car V4H
Drivers:
- Bluetooth:
- Set Per Platform Antenna Gain (PPAG) for Intel controllers.
- Ethernet NICs:
- Intel (1G, igc):
- support TSN / Qbv / packet scheduling features of i226 model
- Intel (100G, ice):
- use GNSS subsystem instead of TTY
- multi-buffer XDP support
- extend support for GPIO pins to E823 devices
- nVidia/Mellanox:
- update the shared buffer configuration on PFC commands
- implement PTP adjphase function for HW offset control
- TC support for Geneve and GRE with VF tunnel offload
- more efficient crypto key management method
- multi-port eswitch support
- Netronome/Corigine:
- add DCB IEEE support
- support IPsec offloading for NFP3800
- Freescale/NXP (enetc):
- support XDP_REDIRECT for XDP non-linear buffers
- improve reconfig, avoid link flap and waiting for idle
- support MAC Merge layer
- Other NICs:
- sfc/ef100: add basic devlink support for ef100
- ionic: rx_push mode operation (writing descriptors via MMIO)
- bnxt: use the auxiliary bus abstraction for RDMA
- r8169: disable ASPM and reset bus in case of tx timeout
- cpsw: support QSGMII mode for J721e CPSW9G
- cpts: support pulse-per-second output
- ngbe: add an mdio bus driver
- usbnet: optimize usbnet_bh() by avoiding unnecessary queuing
- r8152: handle devices with FW with NCM support
- amd-xgbe: support 10Mbps, 2.5GbE speeds and rx-adaptation
- virtio-net: support multi buffer XDP
- virtio/vsock: replace virtio_vsock_pkt with sk_buff
- tsnep: XDP support
- Ethernet high-speed switches:
- nVidia/Mellanox (mlxsw):
- add support for latency TLV (in FW control messages)
- Microchip (sparx5):
- separate explicit and implicit traffic forwarding rules, make
the implicit rules always active
- add support for egress DSCP rewrite
- IS0 VCAP support (Ingress Classification)
- IS2 VCAP filters (protos, L3 addrs, L4 ports, flags, ToS
etc.)
- ES2 VCAP support (Egress Access Control)
- support for Per-Stream Filtering and Policing (802.1Q,
8.6.5.1)
- Ethernet embedded switches:
- Marvell (mv88e6xxx):
- add MAB (port auth) offload support
- enable PTP receive for mv88e6390
- NXP (ocelot):
- support MAC Merge layer
- support for the the vsc7512 internal copper phys
- Microchip:
- lan9303: convert to PHYLINK
- lan966x: support TC flower filter statistics
- lan937x: PTP support for KSZ9563/KSZ8563 and LAN937x
- lan937x: support Credit Based Shaper configuration
- ksz9477: support Energy Efficient Ethernet
- other:
- qca8k: convert to regmap read/write API, use bulk operations
- rswitch: Improve TX timestamp accuracy
- Intel WiFi (iwlwifi):
- EHT (Wi-Fi 7) rate reporting
- STEP equalizer support: transfer some STEP (connection to radio
on platforms with integrated wifi) related parameters from the
BIOS to the firmware.
- Qualcomm 802.11ax WiFi (ath11k):
- IPQ5018 support
- Fine Timing Measurement (FTM) responder role support
- channel 177 support
- MediaTek WiFi (mt76):
- per-PHY LED support
- mt7996: EHT (Wi-Fi 7) support
- Wireless Ethernet Dispatch (WED) reset support
- switch to using page pool allocator
- RealTek WiFi (rtw89):
- support new version of Bluetooth co-existance
- Mobile:
- rmnet: support TX aggregation"
* tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1872 commits)
page_pool: add a comment explaining the fragment counter usage
net: ethtool: fix __ethtool_dev_mm_supported() implementation
ethtool: pse-pd: Fix double word in comments
xsk: add linux/vmalloc.h to xsk.c
sefltests: netdevsim: wait for devlink instance after netns removal
selftest: fib_tests: Always cleanup before exit
net/mlx5e: Align IPsec ASO result memory to be as required by hardware
net/mlx5e: TC, Set CT miss to the specific ct action instance
net/mlx5e: Rename CHAIN_TO_REG to MAPPED_OBJ_TO_REG
net/mlx5: Refactor tc miss handling to a single function
net/mlx5: Kconfig: Make tc offload depend on tc skb extension
net/sched: flower: Support hardware miss to tc action
net/sched: flower: Move filter handle initialization earlier
net/sched: cls_api: Support hardware miss to tc action
net/sched: Rename user cookie and act cookie
sfc: fix builds without CONFIG_RTC_LIB
sfc: clean up some inconsistent indentings
net/mlx4_en: Introduce flexible array to silence overflow warning
net: lan966x: Fix possible deadlock inside PTP
net/ulp: Remove redundant ->clone() test in inet_clone_ulp().
...
Diffstat (limited to 'drivers/usb/image/mdc800.c')
-rw-r--r-- | drivers/usb/image/mdc800.c | 1077 |
1 files changed, 1077 insertions, 0 deletions
diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c new file mode 100644 index 000000000..67f098579 --- /dev/null +++ b/drivers/usb/image/mdc800.c @@ -0,0 +1,1077 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * copyright (C) 1999/2000 by Henning Zabel <henning@uni-paderborn.de> + */ + + +/* + * USB-Kernel Driver for the Mustek MDC800 Digital Camera + * (c) 1999/2000 Henning Zabel <henning@uni-paderborn.de> + * + * + * The driver brings the USB functions of the MDC800 to Linux. + * To use the Camera you must support the USB Protocol of the camera + * to the Kernel Node. + * The Driver uses a misc device Node. Create it with : + * mknod /dev/mustek c 180 32 + * + * The driver supports only one camera. + * + * Fix: mdc800 used sleep_on and slept with io_lock held. + * Converted sleep_on to waitqueues with schedule_timeout and made io_lock + * a semaphore from a spinlock. + * by Oliver Neukum <oliver@neukum.name> + * (02/12/2001) + * + * Identify version on module load. + * (08/04/2001) gb + * + * version 0.7.5 + * Fixed potential SMP races with Spinlocks. + * Thanks to Oliver Neukum <oliver@neukum.name> who + * noticed the race conditions. + * (30/10/2000) + * + * Fixed: Setting urb->dev before submitting urb. + * by Greg KH <greg@kroah.com> + * (13/10/2000) + * + * version 0.7.3 + * bugfix : The mdc800->state field gets set to READY after the + * disconnect function sets it to NOT_CONNECTED. This makes the + * driver running like the camera is connected and causes some + * hang ups. + * + * version 0.7.1 + * MOD_INC and MOD_DEC are changed in usb_probe to prevent load/unload + * problems when compiled as Module. + * (04/04/2000) + * + * The mdc800 driver gets assigned the USB Minor 32-47. The Registration + * was updated to use these values. + * (26/03/2000) + * + * The Init und Exit Module Function are updated. + * (01/03/2000) + * + * version 0.7.0 + * Rewrite of the driver : The driver now uses URB's. The old stuff + * has been removed. + * + * version 0.6.0 + * Rewrite of this driver: The Emulation of the rs232 protocoll + * has been removed from the driver. A special executeCommand function + * for this driver is included to gphoto. + * The driver supports two kind of communication to bulk endpoints. + * Either with the dev->bus->ops->bulk... or with callback function. + * (09/11/1999) + * + * version 0.5.0: + * first Version that gets a version number. Most of the needed + * functions work. + * (20/10/1999) + */ + +#include <linux/sched/signal.h> +#include <linux/signal.h> +#include <linux/spinlock.h> +#include <linux/errno.h> +#include <linux/random.h> +#include <linux/poll.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/wait.h> +#include <linux/mutex.h> + +#include <linux/usb.h> +#include <linux/fs.h> + +/* + * Version Information + */ +#define DRIVER_VERSION "v0.7.5 (30/10/2000)" +#define DRIVER_AUTHOR "Henning Zabel <henning@uni-paderborn.de>" +#define DRIVER_DESC "USB Driver for Mustek MDC800 Digital Camera" + +/* Vendor and Product Information */ +#define MDC800_VENDOR_ID 0x055f +#define MDC800_PRODUCT_ID 0xa800 + +/* Timeouts (msec) */ +#define TO_DOWNLOAD_GET_READY 1500 +#define TO_DOWNLOAD_GET_BUSY 1500 +#define TO_WRITE_GET_READY 1000 +#define TO_DEFAULT_COMMAND 5000 +#define TO_READ_FROM_IRQ TO_DEFAULT_COMMAND +#define TO_GET_READY TO_DEFAULT_COMMAND + +/* Minor Number of the device (create with mknod /dev/mustek c 180 32) */ +#define MDC800_DEVICE_MINOR_BASE 32 + + +/************************************************************************** + Data and structs +***************************************************************************/ + + +typedef enum { + NOT_CONNECTED, READY, WORKING, DOWNLOAD +} mdc800_state; + + +/* Data for the driver */ +struct mdc800_data +{ + struct usb_device * dev; // Device Data + mdc800_state state; + + unsigned int endpoint [4]; + + struct urb * irq_urb; + wait_queue_head_t irq_wait; + int irq_woken; + char* irq_urb_buffer; + + int camera_busy; // is camera busy ? + int camera_request_ready; // Status to synchronize with irq + char camera_response [8]; // last Bytes send after busy + + struct urb * write_urb; + char* write_urb_buffer; + wait_queue_head_t write_wait; + int written; + + + struct urb * download_urb; + char* download_urb_buffer; + wait_queue_head_t download_wait; + int downloaded; + int download_left; // Bytes left to download ? + + + /* Device Data */ + char out [64]; // Answer Buffer + int out_ptr; // Index to the first not readen byte + int out_count; // Bytes in the buffer + + int open; // Camera device open ? + struct mutex io_lock; // IO -lock + + char in [8]; // Command Input Buffer + int in_count; + + int pic_index; // Cache for the Imagesize (-1 for nothing cached ) + int pic_len; + int minor; +}; + + +/* Specification of the Endpoints */ +static struct usb_endpoint_descriptor mdc800_ed [4] = +{ + { + .bLength = 0, + .bDescriptorType = 0, + .bEndpointAddress = 0x01, + .bmAttributes = 0x02, + .wMaxPacketSize = cpu_to_le16(8), + .bInterval = 0, + .bRefresh = 0, + .bSynchAddress = 0, + }, + { + .bLength = 0, + .bDescriptorType = 0, + .bEndpointAddress = 0x82, + .bmAttributes = 0x03, + .wMaxPacketSize = cpu_to_le16(8), + .bInterval = 0, + .bRefresh = 0, + .bSynchAddress = 0, + }, + { + .bLength = 0, + .bDescriptorType = 0, + .bEndpointAddress = 0x03, + .bmAttributes = 0x02, + .wMaxPacketSize = cpu_to_le16(64), + .bInterval = 0, + .bRefresh = 0, + .bSynchAddress = 0, + }, + { + .bLength = 0, + .bDescriptorType = 0, + .bEndpointAddress = 0x84, + .bmAttributes = 0x02, + .wMaxPacketSize = cpu_to_le16(64), + .bInterval = 0, + .bRefresh = 0, + .bSynchAddress = 0, + }, +}; + +/* The Variable used by the driver */ +static struct mdc800_data* mdc800; + + +/*************************************************************************** + The USB Part of the driver +****************************************************************************/ + +static int mdc800_endpoint_equals (struct usb_endpoint_descriptor *a,struct usb_endpoint_descriptor *b) +{ + return ( + ( a->bEndpointAddress == b->bEndpointAddress ) + && ( a->bmAttributes == b->bmAttributes ) + && ( a->wMaxPacketSize == b->wMaxPacketSize ) + ); +} + + +/* + * Checks whether the camera responds busy + */ +static int mdc800_isBusy (char* ch) +{ + int i=0; + while (i<8) + { + if (ch [i] != (char)0x99) + return 0; + i++; + } + return 1; +} + + +/* + * Checks whether the Camera is ready + */ +static int mdc800_isReady (char *ch) +{ + int i=0; + while (i<8) + { + if (ch [i] != (char)0xbb) + return 0; + i++; + } + return 1; +} + + + +/* + * USB IRQ Handler for InputLine + */ +static void mdc800_usb_irq (struct urb *urb) +{ + int data_received=0, wake_up; + unsigned char* b=urb->transfer_buffer; + struct mdc800_data* mdc800=urb->context; + struct device *dev = &mdc800->dev->dev; + int status = urb->status; + + if (status >= 0) { + if (mdc800_isBusy (b)) + { + if (!mdc800->camera_busy) + { + mdc800->camera_busy=1; + dev_dbg(dev, "gets busy\n"); + } + } + else + { + if (mdc800->camera_busy && mdc800_isReady (b)) + { + mdc800->camera_busy=0; + dev_dbg(dev, "gets ready\n"); + } + } + if (!(mdc800_isBusy (b) || mdc800_isReady (b))) + { + /* Store Data in camera_answer field */ + dev_dbg(dev, "%i %i %i %i %i %i %i %i \n",b[0],b[1],b[2],b[3],b[4],b[5],b[6],b[7]); + + memcpy (mdc800->camera_response,b,8); + data_received=1; + } + } + wake_up= ( mdc800->camera_request_ready > 0 ) + && + ( + ((mdc800->camera_request_ready == 1) && (!mdc800->camera_busy)) + || + ((mdc800->camera_request_ready == 2) && data_received) + || + ((mdc800->camera_request_ready == 3) && (mdc800->camera_busy)) + || + (status < 0) + ); + + if (wake_up) + { + mdc800->camera_request_ready=0; + mdc800->irq_woken=1; + wake_up (&mdc800->irq_wait); + } +} + + +/* + * Waits a while until the irq responds that camera is ready + * + * mode : 0: Wait for camera gets ready + * 1: Wait for receiving data + * 2: Wait for camera gets busy + * + * msec: Time to wait + */ +static int mdc800_usb_waitForIRQ (int mode, int msec) +{ + mdc800->camera_request_ready=1+mode; + + wait_event_timeout(mdc800->irq_wait, mdc800->irq_woken, + msecs_to_jiffies(msec)); + mdc800->irq_woken = 0; + + if (mdc800->camera_request_ready>0) + { + mdc800->camera_request_ready=0; + dev_err(&mdc800->dev->dev, "timeout waiting for camera.\n"); + return -1; + } + + if (mdc800->state == NOT_CONNECTED) + { + printk(KERN_WARNING "mdc800: Camera gets disconnected " + "during waiting for irq.\n"); + mdc800->camera_request_ready=0; + return -2; + } + + return 0; +} + + +/* + * The write_urb callback function + */ +static void mdc800_usb_write_notify (struct urb *urb) +{ + struct mdc800_data* mdc800=urb->context; + int status = urb->status; + + if (status != 0) + dev_err(&mdc800->dev->dev, + "writing command fails (status=%i)\n", status); + else + mdc800->state=READY; + mdc800->written = 1; + wake_up (&mdc800->write_wait); +} + + +/* + * The download_urb callback function + */ +static void mdc800_usb_download_notify (struct urb *urb) +{ + struct mdc800_data* mdc800=urb->context; + int status = urb->status; + + if (status == 0) { + /* Fill output buffer with these data */ + memcpy (mdc800->out, urb->transfer_buffer, 64); + mdc800->out_count=64; + mdc800->out_ptr=0; + mdc800->download_left-=64; + if (mdc800->download_left == 0) + { + mdc800->state=READY; + } + } else { + dev_err(&mdc800->dev->dev, + "request bytes fails (status:%i)\n", status); + } + mdc800->downloaded = 1; + wake_up (&mdc800->download_wait); +} + + +/*************************************************************************** + Probing for the Camera + ***************************************************************************/ + +static struct usb_driver mdc800_usb_driver; +static const struct file_operations mdc800_device_ops; +static struct usb_class_driver mdc800_class = { + .name = "mdc800%d", + .fops = &mdc800_device_ops, + .minor_base = MDC800_DEVICE_MINOR_BASE, +}; + + +/* + * Callback to search the Mustek MDC800 on the USB Bus + */ +static int mdc800_usb_probe (struct usb_interface *intf, + const struct usb_device_id *id) +{ + int i,j; + struct usb_host_interface *intf_desc; + struct usb_device *dev = interface_to_usbdev (intf); + int irq_interval=0; + int retval; + + dev_dbg(&intf->dev, "(%s) called.\n", __func__); + + + if (mdc800->dev != NULL) + { + dev_warn(&intf->dev, "only one Mustek MDC800 is supported.\n"); + return -ENODEV; + } + + if (dev->descriptor.bNumConfigurations != 1) + { + dev_err(&intf->dev, + "probe fails -> wrong Number of Configuration\n"); + return -ENODEV; + } + intf_desc = intf->cur_altsetting; + + if ( + ( intf_desc->desc.bInterfaceClass != 0xff ) + || ( intf_desc->desc.bInterfaceSubClass != 0 ) + || ( intf_desc->desc.bInterfaceProtocol != 0 ) + || ( intf_desc->desc.bNumEndpoints != 4) + ) + { + dev_err(&intf->dev, "probe fails -> wrong Interface\n"); + return -ENODEV; + } + + /* Check the Endpoints */ + for (i=0; i<4; i++) + { + mdc800->endpoint[i]=-1; + for (j=0; j<4; j++) + { + if (mdc800_endpoint_equals (&intf_desc->endpoint [j].desc,&mdc800_ed [i])) + { + mdc800->endpoint[i]=intf_desc->endpoint [j].desc.bEndpointAddress ; + if (i==1) + { + irq_interval=intf_desc->endpoint [j].desc.bInterval; + } + } + } + if (mdc800->endpoint[i] == -1) + { + dev_err(&intf->dev, "probe fails -> Wrong Endpoints.\n"); + return -ENODEV; + } + } + + + dev_info(&intf->dev, "Found Mustek MDC800 on USB.\n"); + + mutex_lock(&mdc800->io_lock); + + retval = usb_register_dev(intf, &mdc800_class); + if (retval) { + dev_err(&intf->dev, "Not able to get a minor for this device.\n"); + mutex_unlock(&mdc800->io_lock); + return -ENODEV; + } + + mdc800->dev=dev; + mdc800->open=0; + + /* Setup URB Structs */ + usb_fill_int_urb ( + mdc800->irq_urb, + mdc800->dev, + usb_rcvintpipe (mdc800->dev,mdc800->endpoint [1]), + mdc800->irq_urb_buffer, + 8, + mdc800_usb_irq, + mdc800, + irq_interval + ); + + usb_fill_bulk_urb ( + mdc800->write_urb, + mdc800->dev, + usb_sndbulkpipe (mdc800->dev, mdc800->endpoint[0]), + mdc800->write_urb_buffer, + 8, + mdc800_usb_write_notify, + mdc800 + ); + + usb_fill_bulk_urb ( + mdc800->download_urb, + mdc800->dev, + usb_rcvbulkpipe (mdc800->dev, mdc800->endpoint [3]), + mdc800->download_urb_buffer, + 64, + mdc800_usb_download_notify, + mdc800 + ); + + mdc800->state=READY; + + mutex_unlock(&mdc800->io_lock); + + usb_set_intfdata(intf, mdc800); + return 0; +} + + +/* + * Disconnect USB device (maybe the MDC800) + */ +static void mdc800_usb_disconnect (struct usb_interface *intf) +{ + struct mdc800_data* mdc800 = usb_get_intfdata(intf); + + dev_dbg(&intf->dev, "(%s) called\n", __func__); + + if (mdc800) { + if (mdc800->state == NOT_CONNECTED) + return; + + usb_deregister_dev(intf, &mdc800_class); + + /* must be under lock to make sure no URB + is submitted after usb_kill_urb() */ + mutex_lock(&mdc800->io_lock); + mdc800->state=NOT_CONNECTED; + + usb_kill_urb(mdc800->irq_urb); + usb_kill_urb(mdc800->write_urb); + usb_kill_urb(mdc800->download_urb); + mutex_unlock(&mdc800->io_lock); + + mdc800->dev = NULL; + usb_set_intfdata(intf, NULL); + } + dev_info(&intf->dev, "Mustek MDC800 disconnected from USB.\n"); +} + + +/*************************************************************************** + The Misc device Part (file_operations) +****************************************************************************/ + +/* + * This Function calc the Answersize for a command. + */ +static int mdc800_getAnswerSize (char command) +{ + switch ((unsigned char) command) + { + case 0x2a: + case 0x49: + case 0x51: + case 0x0d: + case 0x20: + case 0x07: + case 0x01: + case 0x25: + case 0x00: + return 8; + + case 0x05: + case 0x3e: + return mdc800->pic_len; + + case 0x09: + return 4096; + + default: + return 0; + } +} + + +/* + * Init the device: (1) alloc mem (2) Increase MOD Count .. + */ +static int mdc800_device_open (struct inode* inode, struct file *file) +{ + int retval=0; + int errn=0; + + mutex_lock(&mdc800->io_lock); + + if (mdc800->state == NOT_CONNECTED) + { + errn=-EBUSY; + goto error_out; + } + if (mdc800->open) + { + errn=-EBUSY; + goto error_out; + } + + mdc800->in_count=0; + mdc800->out_count=0; + mdc800->out_ptr=0; + mdc800->pic_index=0; + mdc800->pic_len=-1; + mdc800->download_left=0; + + mdc800->camera_busy=0; + mdc800->camera_request_ready=0; + + retval=0; + mdc800->irq_urb->dev = mdc800->dev; + retval = usb_submit_urb (mdc800->irq_urb, GFP_KERNEL); + if (retval) { + dev_err(&mdc800->dev->dev, + "request USB irq fails (submit_retval=%i).\n", retval); + errn = -EIO; + goto error_out; + } + + mdc800->open=1; + dev_dbg(&mdc800->dev->dev, "Mustek MDC800 device opened.\n"); + +error_out: + mutex_unlock(&mdc800->io_lock); + return errn; +} + + +/* + * Close the Camera and release Memory + */ +static int mdc800_device_release (struct inode* inode, struct file *file) +{ + int retval=0; + + mutex_lock(&mdc800->io_lock); + if (mdc800->open && (mdc800->state != NOT_CONNECTED)) + { + usb_kill_urb(mdc800->irq_urb); + usb_kill_urb(mdc800->write_urb); + usb_kill_urb(mdc800->download_urb); + mdc800->open=0; + } + else + { + retval=-EIO; + } + + mutex_unlock(&mdc800->io_lock); + return retval; +} + + +/* + * The Device read callback Function + */ +static ssize_t mdc800_device_read (struct file *file, char __user *buf, size_t len, loff_t *pos) +{ + size_t left=len, sts=len; /* single transfer size */ + char __user *ptr = buf; + int retval; + + mutex_lock(&mdc800->io_lock); + if (mdc800->state == NOT_CONNECTED) + { + mutex_unlock(&mdc800->io_lock); + return -EBUSY; + } + if (mdc800->state == WORKING) + { + printk(KERN_WARNING "mdc800: Illegal State \"working\"" + "reached during read ?!\n"); + mutex_unlock(&mdc800->io_lock); + return -EBUSY; + } + if (!mdc800->open) + { + mutex_unlock(&mdc800->io_lock); + return -EBUSY; + } + + while (left) + { + if (signal_pending (current)) + { + mutex_unlock(&mdc800->io_lock); + return -EINTR; + } + + sts=left > (mdc800->out_count-mdc800->out_ptr)?mdc800->out_count-mdc800->out_ptr:left; + + if (sts <= 0) + { + /* Too less Data in buffer */ + if (mdc800->state == DOWNLOAD) + { + mdc800->out_count=0; + mdc800->out_ptr=0; + + /* Download -> Request new bytes */ + mdc800->download_urb->dev = mdc800->dev; + retval = usb_submit_urb (mdc800->download_urb, GFP_KERNEL); + if (retval) { + dev_err(&mdc800->dev->dev, + "Can't submit download urb " + "(retval=%i)\n", retval); + mutex_unlock(&mdc800->io_lock); + return len-left; + } + wait_event_timeout(mdc800->download_wait, + mdc800->downloaded, + msecs_to_jiffies(TO_DOWNLOAD_GET_READY)); + mdc800->downloaded = 0; + if (mdc800->download_urb->status != 0) + { + dev_err(&mdc800->dev->dev, + "request download-bytes fails " + "(status=%i)\n", + mdc800->download_urb->status); + mutex_unlock(&mdc800->io_lock); + return len-left; + } + } + else + { + /* No more bytes -> that's an error*/ + mutex_unlock(&mdc800->io_lock); + return -EIO; + } + } + else + { + /* Copy Bytes */ + if (copy_to_user(ptr, &mdc800->out [mdc800->out_ptr], + sts)) { + mutex_unlock(&mdc800->io_lock); + return -EFAULT; + } + ptr+=sts; + left-=sts; + mdc800->out_ptr+=sts; + } + } + + mutex_unlock(&mdc800->io_lock); + return len-left; +} + + +/* + * The Device write callback Function + * If a 8Byte Command is received, it will be send to the camera. + * After this the driver initiates the request for the answer or + * just waits until the camera becomes ready. + */ +static ssize_t mdc800_device_write (struct file *file, const char __user *buf, size_t len, loff_t *pos) +{ + size_t i=0; + int retval; + + mutex_lock(&mdc800->io_lock); + if (mdc800->state != READY) + { + mutex_unlock(&mdc800->io_lock); + return -EBUSY; + } + if (!mdc800->open ) + { + mutex_unlock(&mdc800->io_lock); + return -EBUSY; + } + + while (i<len) + { + unsigned char c; + if (signal_pending (current)) + { + mutex_unlock(&mdc800->io_lock); + return -EINTR; + } + + if(get_user(c, buf+i)) + { + mutex_unlock(&mdc800->io_lock); + return -EFAULT; + } + + /* check for command start */ + if (c == 0x55) + { + mdc800->in_count=0; + mdc800->out_count=0; + mdc800->out_ptr=0; + mdc800->download_left=0; + } + + /* save command byte */ + if (mdc800->in_count < 8) + { + mdc800->in[mdc800->in_count] = c; + mdc800->in_count++; + } + else + { + mutex_unlock(&mdc800->io_lock); + return -EIO; + } + + /* Command Buffer full ? -> send it to camera */ + if (mdc800->in_count == 8) + { + int answersize; + + if (mdc800_usb_waitForIRQ (0,TO_GET_READY)) + { + dev_err(&mdc800->dev->dev, + "Camera didn't get ready.\n"); + mutex_unlock(&mdc800->io_lock); + return -EIO; + } + + answersize=mdc800_getAnswerSize (mdc800->in[1]); + + mdc800->state=WORKING; + memcpy (mdc800->write_urb->transfer_buffer, mdc800->in,8); + mdc800->write_urb->dev = mdc800->dev; + retval = usb_submit_urb (mdc800->write_urb, GFP_KERNEL); + if (retval) { + dev_err(&mdc800->dev->dev, + "submitting write urb fails " + "(retval=%i)\n", retval); + mutex_unlock(&mdc800->io_lock); + return -EIO; + } + wait_event_timeout(mdc800->write_wait, mdc800->written, + msecs_to_jiffies(TO_WRITE_GET_READY)); + mdc800->written = 0; + if (mdc800->state == WORKING) + { + usb_kill_urb(mdc800->write_urb); + mutex_unlock(&mdc800->io_lock); + return -EIO; + } + + switch ((unsigned char) mdc800->in[1]) + { + case 0x05: /* Download Image */ + case 0x3e: /* Take shot in Fine Mode (WCam Mode) */ + if (mdc800->pic_len < 0) + { + dev_err(&mdc800->dev->dev, + "call 0x07 before " + "0x05,0x3e\n"); + mdc800->state=READY; + mutex_unlock(&mdc800->io_lock); + return -EIO; + } + mdc800->pic_len=-1; + fallthrough; + + case 0x09: /* Download Thumbnail */ + mdc800->download_left=answersize+64; + mdc800->state=DOWNLOAD; + mdc800_usb_waitForIRQ (0,TO_DOWNLOAD_GET_BUSY); + break; + + + default: + if (answersize) + { + + if (mdc800_usb_waitForIRQ (1,TO_READ_FROM_IRQ)) + { + dev_err(&mdc800->dev->dev, "requesting answer from irq fails\n"); + mutex_unlock(&mdc800->io_lock); + return -EIO; + } + + /* Write dummy data, (this is ugly but part of the USB Protocol */ + /* if you use endpoint 1 as bulk and not as irq) */ + memcpy (mdc800->out, mdc800->camera_response,8); + + /* This is the interpreted answer */ + memcpy (&mdc800->out[8], mdc800->camera_response,8); + + mdc800->out_ptr=0; + mdc800->out_count=16; + + /* Cache the Imagesize, if command was getImageSize */ + if (mdc800->in [1] == (char) 0x07) + { + mdc800->pic_len=(int) 65536*(unsigned char) mdc800->camera_response[0]+256*(unsigned char) mdc800->camera_response[1]+(unsigned char) mdc800->camera_response[2]; + + dev_dbg(&mdc800->dev->dev, "cached imagesize = %i\n", mdc800->pic_len); + } + + } + else + { + if (mdc800_usb_waitForIRQ (0,TO_DEFAULT_COMMAND)) + { + dev_err(&mdc800->dev->dev, "Command Timeout.\n"); + mutex_unlock(&mdc800->io_lock); + return -EIO; + } + } + mdc800->state=READY; + break; + } + } + i++; + } + mutex_unlock(&mdc800->io_lock); + return i; +} + + +/*************************************************************************** + Init and Cleanup this driver (Structs and types) +****************************************************************************/ + +/* File Operations of this drivers */ +static const struct file_operations mdc800_device_ops = +{ + .owner = THIS_MODULE, + .read = mdc800_device_read, + .write = mdc800_device_write, + .open = mdc800_device_open, + .release = mdc800_device_release, + .llseek = noop_llseek, +}; + + + +static const struct usb_device_id mdc800_table[] = { + { USB_DEVICE(MDC800_VENDOR_ID, MDC800_PRODUCT_ID) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, mdc800_table); +/* + * USB Driver Struct for this device + */ +static struct usb_driver mdc800_usb_driver = +{ + .name = "mdc800", + .probe = mdc800_usb_probe, + .disconnect = mdc800_usb_disconnect, + .id_table = mdc800_table +}; + + + +/************************************************************************ + Init and Cleanup this driver (Main Functions) +*************************************************************************/ + +static int __init usb_mdc800_init (void) +{ + int retval = -ENODEV; + /* Allocate Memory */ + mdc800=kzalloc (sizeof (struct mdc800_data), GFP_KERNEL); + if (!mdc800) + goto cleanup_on_fail; + + mdc800->dev = NULL; + mdc800->state=NOT_CONNECTED; + mutex_init (&mdc800->io_lock); + + init_waitqueue_head (&mdc800->irq_wait); + init_waitqueue_head (&mdc800->write_wait); + init_waitqueue_head (&mdc800->download_wait); + + mdc800->irq_woken = 0; + mdc800->downloaded = 0; + mdc800->written = 0; + + mdc800->irq_urb_buffer=kmalloc (8, GFP_KERNEL); + if (!mdc800->irq_urb_buffer) + goto cleanup_on_fail; + mdc800->write_urb_buffer=kmalloc (8, GFP_KERNEL); + if (!mdc800->write_urb_buffer) + goto cleanup_on_fail; + mdc800->download_urb_buffer=kmalloc (64, GFP_KERNEL); + if (!mdc800->download_urb_buffer) + goto cleanup_on_fail; + + mdc800->irq_urb=usb_alloc_urb (0, GFP_KERNEL); + if (!mdc800->irq_urb) + goto cleanup_on_fail; + mdc800->download_urb=usb_alloc_urb (0, GFP_KERNEL); + if (!mdc800->download_urb) + goto cleanup_on_fail; + mdc800->write_urb=usb_alloc_urb (0, GFP_KERNEL); + if (!mdc800->write_urb) + goto cleanup_on_fail; + + /* Register the driver */ + retval = usb_register(&mdc800_usb_driver); + if (retval) + goto cleanup_on_fail; + + printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" + DRIVER_DESC "\n"); + + return 0; + + /* Clean driver up, when something fails */ + +cleanup_on_fail: + + if (mdc800 != NULL) + { + printk(KERN_ERR "mdc800: can't alloc memory!\n"); + + kfree(mdc800->download_urb_buffer); + kfree(mdc800->write_urb_buffer); + kfree(mdc800->irq_urb_buffer); + + usb_free_urb(mdc800->write_urb); + usb_free_urb(mdc800->download_urb); + usb_free_urb(mdc800->irq_urb); + + kfree (mdc800); + } + mdc800 = NULL; + return retval; +} + + +static void __exit usb_mdc800_cleanup (void) +{ + usb_deregister (&mdc800_usb_driver); + + usb_free_urb (mdc800->irq_urb); + usb_free_urb (mdc800->download_urb); + usb_free_urb (mdc800->write_urb); + + kfree (mdc800->irq_urb_buffer); + kfree (mdc800->write_urb_buffer); + kfree (mdc800->download_urb_buffer); + + kfree (mdc800); + mdc800 = NULL; +} + +module_init (usb_mdc800_init); +module_exit (usb_mdc800_cleanup); + +MODULE_AUTHOR( DRIVER_AUTHOR ); +MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_LICENSE("GPL"); + |