- DRA722
compatible = "ti,dra722", "ti,dra72", "ti,dra7"
+- AM5728
+ compatible = "ti,am5728", "ti,dra742", "ti,dra74", "ti,dra7"
+
+- AM5726
+ compatible = "ti,am5726", "ti,dra742", "ti,dra74", "ti,dra7"
+
+- AM5718
+ compatible = "ti,am5718", "ti,dra722", "ti,dra72", "ti,dra7"
+
+- AM5716
+ compatible = "ti,am5716", "ti,dra722", "ti,dra72", "ti,dra7"
+
- AM4372
compatible = "ti,am4372", "ti,am43"
--- /dev/null
+* Toshiba TC3589x multi-purpose expander
+
+The Toshiba TC3589x series are I2C-based MFD devices which may expose the
+following built-in devices: gpio, keypad, rotator (vibrator), PWM (for
+e.g. LEDs or vibrators) The included models are:
+
+- TC35890
+- TC35892
+- TC35893
+- TC35894
+- TC35895
+- TC35896
+
+Required properties:
+ - compatible : must be "toshiba,tc35890", "toshiba,tc35892", "toshiba,tc35893",
+ "toshiba,tc35894", "toshiba,tc35895" or "toshiba,tc35896"
+ - reg : I2C address of the device
+ - interrupt-parent : specifies which IRQ controller we're connected to
+ - interrupts : the interrupt on the parent the controller is connected to
+ - interrupt-controller : marks the device node as an interrupt controller
+ - #interrupt-cells : should be <1>, the first cell is the IRQ offset on this
+ TC3589x interrupt controller.
+
+Optional nodes:
+
+- GPIO
+ This GPIO module inside the TC3589x has 24 (TC35890, TC35892) or 20
+ (other models) GPIO lines.
+ - compatible : must be "toshiba,tc3589x-gpio"
+ - interrupts : interrupt on the parent, which must be the tc3589x MFD device
+ - interrupt-controller : marks the device node as an interrupt controller
+ - #interrupt-cells : should be <2>, the first cell is the IRQ offset on this
+ TC3589x GPIO interrupt controller, the second cell is the interrupt flags
+ in accordance with <dt-bindings/interrupt-controller/irq.h>. The following
+ flags are valid:
+ - IRQ_TYPE_LEVEL_LOW
+ - IRQ_TYPE_LEVEL_HIGH
+ - IRQ_TYPE_EDGE_RISING
+ - IRQ_TYPE_EDGE_FALLING
+ - IRQ_TYPE_EDGE_BOTH
+ - gpio-controller : marks the device node as a GPIO controller
+ - #gpio-cells : should be <2>, the first cell is the GPIO offset on this
+ GPIO controller, the second cell is the flags.
+
+- Keypad
+ This keypad is the same on all variants, supporting up to 96 different
+ keys. The linux-specific properties are modeled on those already existing
+ in other input drivers.
+ - compatible : must be "toshiba,tc3589x-keypad"
+ - debounce-delay-ms : debounce interval in milliseconds
+ - keypad,num-rows : number of rows in the matrix, see
+ bindings/input/matrix-keymap.txt
+ - keypad,num-columns : number of columns in the matrix, see
+ bindings/input/matrix-keymap.txt
+ - linux,keymap: the definition can be found in
+ bindings/input/matrix-keymap.txt
+ - linux,no-autorepeat: do no enable autorepeat feature.
+ - linux,wakeup: use any event on keypad as wakeup event.
+
+Example:
+
+tc35893@44 {
+ compatible = "toshiba,tc35893";
+ reg = <0x44>;
+ interrupt-parent = <&gpio6>;
+ interrupts = <26 IRQ_TYPE_EDGE_RISING>;
+
+ interrupt-controller;
+ #interrupt-cells = <1>;
+
+ tc3589x_gpio {
+ compatible = "toshiba,tc3589x-gpio";
+ interrupts = <0>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+ tc3589x_keypad {
+ compatible = "toshiba,tc3589x-keypad";
+ interrupts = <6>;
+ debounce-delay-ms = <4>;
+ keypad,num-columns = <8>;
+ keypad,num-rows = <8>;
+ linux,no-autorepeat;
+ linux,wakeup;
+ linux,keymap = <0x0301006b
+ 0x04010066
+ 0x06040072
+ 0x040200d7
+ 0x0303006a
+ 0x0205000e
+ 0x0607008b
+ 0x0500001c
+ 0x0403000b
+ 0x03040034
+ 0x05020067
+ 0x0305006c
+ 0x040500e7
+ 0x0005009e
+ 0x06020073
+ 0x01030039
+ 0x07060069
+ 0x050500d9>;
+ };
+};
width of 8 is assumed.
- ti,nand-ecc-opt: A string setting the ECC layout to use. One of:
- "sw" <deprecated> use "ham1" instead
+ "sw" 1-bit Hamming ecc code via software
"hw" <deprecated> use "ham1" instead
"hw-romcode" <deprecated> use "ham1" instead
"ham1" 1-bit Hamming ecc code
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
- interrupts = <0 32 0x4>;
+ interrupts = <0 16 0x4>;
pinctrl-names = "default";
pinctrl-0 = <&gsbi5_uart_default>;
size_t size, int flags,
const char *exp_name)
- If this succeeds, dma_buf_export allocates a dma_buf structure, and returns a
- pointer to the same. It also associates an anonymous file with this buffer,
- so it can be exported. On failure to allocate the dma_buf object, it returns
- NULL.
+ If this succeeds, dma_buf_export_named allocates a dma_buf structure, and
+ returns a pointer to the same. It also associates an anonymous file with this
+ buffer, so it can be exported. On failure to allocate the dma_buf object,
+ it returns NULL.
'exp_name' is the name of exporter - to facilitate information while
debugging.
drivers and/or processes.
Interface:
- int dma_buf_fd(struct dma_buf *dmabuf)
+ int dma_buf_fd(struct dma_buf *dmabuf, int flags)
This API installs an fd for the anonymous file associated with this buffer;
returns either 'fd', or error.
"dma_buf->ops->" indirection from the users of this interface.
In struct dma_buf_ops, unmap_dma_buf is defined as
- void (*unmap_dma_buf)(struct dma_buf_attachment *, struct sg_table *);
+ void (*unmap_dma_buf)(struct dma_buf_attachment *,
+ struct sg_table *,
+ enum dma_data_direction);
unmap_dma_buf signifies the end-of-DMA for the attachment provided. Like
map_dma_buf, this API also must be implemented by the exporter.
a remote system.
Kdump and kexec are currently supported on the x86, x86_64, ppc64, ia64,
-and s390x architectures.
+s390x and arm architectures.
When the system kernel boots, it reserves a small section of memory for
the dump-capture kernel. This ensures that ongoing Direct Memory Access
2) Or use the system kernel binary itself as dump-capture kernel and there is
no need to build a separate dump-capture kernel. This is possible
only with the architectures which support a relocatable kernel. As
- of today, i386, x86_64, ppc64 and ia64 architectures support relocatable
+ of today, i386, x86_64, ppc64, ia64 and arm architectures support relocatable
kernel.
Building a relocatable kernel is advantageous from the point of view that
kernel will be aligned to 64Mb, so if the start address is not then
any space below the alignment point will be wasted.
+Dump-capture kernel config options (Arch Dependent, arm)
+----------------------------------------------------------
+
+- To use a relocatable kernel,
+ Enable "AUTO_ZRELADDR" support under "Boot" options:
+
+ AUTO_ZRELADDR=y
Extended crashkernel syntax
===========================
crashkernel=<range1>:<size1>[,<range2>:<size2>,...][@offset]
range=start-[end]
+Please note, on arm, the offset is required.
+ crashkernel=<range1>:<size1>[,<range2>:<size2>,...]@offset
+ range=start-[end]
+
'start' is inclusive and 'end' is exclusive.
For example:
on the memory consumption of the kdump system. In general this is not
dependent on the memory size of the production system.
+ On arm, use "crashkernel=Y@X". Note that the start address of the kernel
+ will be aligned to 128MiB (0x08000000), so if the start address is not then
+ any space below the alignment point may be overwritten by the dump-capture kernel,
+ which means it is possible that the vmcore is not that precise as expected.
+
+
Load the Dump-capture Kernel
============================
- Use vmlinux or vmlinuz.gz
For s390x:
- Use image or bzImage
-
+For arm:
+ - Use zImage
If you are using a uncompressed vmlinux image then use following command
to load dump-capture kernel.
--initrd=<initrd-for-dump-capture-kernel> \
--append="root=<root-dev> <arch-specific-options>"
+If you are using a compressed zImage, then use following command
+to load dump-capture kernel.
+
+ kexec --type zImage -p <dump-capture-kernel-bzImage> \
+ --initrd=<initrd-for-dump-capture-kernel> \
+ --dtb=<dtb-for-dump-capture-kernel> \
+ --append="root=<root-dev> <arch-specific-options>"
+
+
Please note, that --args-linux does not need to be specified for ia64.
It is planned to make this a no-op on that architecture, but for now
it should be omitted
For s390x:
"1 maxcpus=1 cgroup_disable=memory"
+For arm:
+ "1 maxcpus=1 reset_devices"
+
Notes on loading the dump-capture kernel:
* By default, the ELF headers are stored in ELF64 format to support
-------------------
this_cpu operations are a way of optimizing access to per cpu
-variables associated with the *currently* executing processor through
-the use of segment registers (or a dedicated register where the cpu
-permanently stored the beginning of the per cpu area for a specific
-processor).
+variables associated with the *currently* executing processor. This is
+done through the use of segment registers (or a dedicated register where
+the cpu permanently stored the beginning of the per cpu area for a
+specific processor).
-The this_cpu operations add a per cpu variable offset to the processor
-specific percpu base and encode that operation in the instruction
+this_cpu operations add a per cpu variable offset to the processor
+specific per cpu base and encode that operation in the instruction
operating on the per cpu variable.
-This means there are no atomicity issues between the calculation of
+This means that there are no atomicity issues between the calculation of
the offset and the operation on the data. Therefore it is not
-necessary to disable preempt or interrupts to ensure that the
+necessary to disable preemption or interrupts to ensure that the
processor is not changed between the calculation of the address and
the operation on the data.
Read-modify-write operations are of particular interest. Frequently
processors have special lower latency instructions that can operate
-without the typical synchronization overhead but still provide some
-sort of relaxed atomicity guarantee. The x86 for example can execute
-RMV (Read Modify Write) instructions like inc/dec/cmpxchg without the
+without the typical synchronization overhead, but still provide some
+sort of relaxed atomicity guarantees. The x86, for example, can execute
+RMW (Read Modify Write) instructions like inc/dec/cmpxchg without the
lock prefix and the associated latency penalty.
Access to the variable without the lock prefix is not synchronized but
processor should be accessing that variable and therefore there are no
concurrency issues with other processors in the system.
+Please note that accesses by remote processors to a per cpu area are
+exceptional situations and may impact performance and/or correctness
+(remote write operations) of local RMW operations via this_cpu_*.
+
+The main use of the this_cpu operations has been to optimize counter
+operations.
+
+The following this_cpu() operations with implied preemption protection
+are defined. These operations can be used without worrying about
+preemption and interrupts.
+
+ this_cpu_add()
+ this_cpu_read(pcp)
+ this_cpu_write(pcp, val)
+ this_cpu_add(pcp, val)
+ this_cpu_and(pcp, val)
+ this_cpu_or(pcp, val)
+ this_cpu_add_return(pcp, val)
+ this_cpu_xchg(pcp, nval)
+ this_cpu_cmpxchg(pcp, oval, nval)
+ this_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2)
+ this_cpu_sub(pcp, val)
+ this_cpu_inc(pcp)
+ this_cpu_dec(pcp)
+ this_cpu_sub_return(pcp, val)
+ this_cpu_inc_return(pcp)
+ this_cpu_dec_return(pcp)
+
+
+Inner working of this_cpu operations
+------------------------------------
+
On x86 the fs: or the gs: segment registers contain the base of the
per cpu area. It is then possible to simply use the segment override
to relocate a per cpu relative address to the proper per cpu area for
mov ax, gs:[x]
instead of a sequence of calculation of the address and then a fetch
-from that address which occurs with the percpu operations. Before
+from that address which occurs with the per cpu operations. Before
this_cpu_ops such sequence also required preempt disable/enable to
prevent the kernel from moving the thread to a different processor
while the calculation is performed.
-The main use of the this_cpu operations has been to optimize counter
-operations.
+Consider the following this_cpu operation:
this_cpu_inc(x)
-results in the following single instruction (no lock prefix!)
+The above results in the following single instruction (no lock prefix!)
inc gs:[x]
instead of the following operations required if there is no segment
-register.
+register:
int *y;
int cpu;
(*y)++;
put_cpu();
-Note that these operations can only be used on percpu data that is
+Note that these operations can only be used on per cpu data that is
reserved for a specific processor. Without disabling preemption in the
surrounding code this_cpu_inc() will only guarantee that one of the
-percpu counters is correctly incremented. However, there is no
+per cpu counters is correctly incremented. However, there is no
guarantee that the OS will not move the process directly before or
after the this_cpu instruction is executed. In general this means that
the value of the individual counters for each processor are
Per cpu variables are used for performance reasons. Bouncing cache
lines can be avoided if multiple processors concurrently go through
the same code paths. Since each processor has its own per cpu
-variables no concurrent cacheline updates take place. The price that
+variables no concurrent cache line updates take place. The price that
has to be paid for this optimization is the need to add up the per cpu
-counters when the value of the counter is needed.
+counters when the value of a counter is needed.
Special operations:
of the per cpu variable that belongs to the currently executing
processor. this_cpu_ptr avoids multiple steps that the common
get_cpu/put_cpu sequence requires. No processor number is
-available. Instead the offset of the local per cpu area is simply
-added to the percpu offset.
+available. Instead, the offset of the local per cpu area is simply
+added to the per cpu offset.
+Note that this operation is usually used in a code segment when
+preemption has been disabled. The pointer is then used to
+access local per cpu data in a critical section. When preemption
+is re-enabled this pointer is usually no longer useful since it may
+no longer point to per cpu data of the current processor.
Per cpu variables and offsets
-----------------------------
-Per cpu variables have *offsets* to the beginning of the percpu
+Per cpu variables have *offsets* to the beginning of the per cpu
area. They do not have addresses although they look like that in the
code. Offsets cannot be directly dereferenced. The offset must be
-added to a base pointer of a percpu area of a processor in order to
+added to a base pointer of a per cpu area of a processor in order to
form a valid address.
Therefore the use of x or &x outside of the context of per cpu
operations is invalid and will generally be treated like a NULL
pointer dereference.
-In the context of per cpu operations
+ DEFINE_PER_CPU(int, x);
- x is a per cpu variable. Most this_cpu operations take a cpu
- variable.
+In the context of per cpu operations the above implies that x is a per
+cpu variable. Most this_cpu operations take a cpu variable.
- &x is the *offset* a per cpu variable. this_cpu_ptr() takes
- the offset of a per cpu variable which makes this look a bit
- strange.
+ int __percpu *p = &x;
+&x and hence p is the *offset* of a per cpu variable. this_cpu_ptr()
+takes the offset of a per cpu variable which makes this look a bit
+strange.
Operations on a field of a per cpu structure
struct s __percpu *ps = &p;
- z = this_cpu_dec(ps->m);
+ this_cpu_dec(ps->m);
z = this_cpu_inc_return(ps->n);
Variants of this_cpu ops
-------------------------
-this_cpu ops are interrupt safe. Some architecture do not support
+this_cpu ops are interrupt safe. Some architectures do not support
these per cpu local operations. In that case the operation must be
replaced by code that disables interrupts, then does the operations
-that are guaranteed to be atomic and then reenable interrupts. Doing
+that are guaranteed to be atomic and then re-enable interrupts. Doing
so is expensive. If there are other reasons why the scheduler cannot
change the processor we are executing on then there is no reason to
-disable interrupts. For that purpose the __this_cpu operations are
-provided. For example.
-
- __this_cpu_inc(x);
-
-Will increment x and will not fallback to code that disables
+disable interrupts. For that purpose the following __this_cpu operations
+are provided.
+
+These operations have no guarantee against concurrent interrupts or
+preemption. If a per cpu variable is not used in an interrupt context
+and the scheduler cannot preempt, then they are safe. If any interrupts
+still occur while an operation is in progress and if the interrupt too
+modifies the variable, then RMW actions can not be guaranteed to be
+safe.
+
+ __this_cpu_add()
+ __this_cpu_read(pcp)
+ __this_cpu_write(pcp, val)
+ __this_cpu_add(pcp, val)
+ __this_cpu_and(pcp, val)
+ __this_cpu_or(pcp, val)
+ __this_cpu_add_return(pcp, val)
+ __this_cpu_xchg(pcp, nval)
+ __this_cpu_cmpxchg(pcp, oval, nval)
+ __this_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2)
+ __this_cpu_sub(pcp, val)
+ __this_cpu_inc(pcp)
+ __this_cpu_dec(pcp)
+ __this_cpu_sub_return(pcp, val)
+ __this_cpu_inc_return(pcp)
+ __this_cpu_dec_return(pcp)
+
+
+Will increment x and will not fall-back to code that disables
interrupts on platforms that cannot accomplish atomicity through
address relocation and a Read-Modify-Write operation in the same
instruction.
-
&this_cpu_ptr(pp)->n vs this_cpu_ptr(&pp->n)
--------------------------------------------
The first operation takes the offset and forms an address and then
-adds the offset of the n field.
+adds the offset of the n field. This may result in two add
+instructions emitted by the compiler.
The second one first adds the two offsets and then does the
relocation. IMHO the second form looks cleaner and has an easier time
this_cpu_read() and friends are used.
-Christoph Lameter, April 3rd, 2013
+Remote access to per cpu data
+------------------------------
+
+Per cpu data structures are designed to be used by one cpu exclusively.
+If you use the variables as intended, this_cpu_ops() are guaranteed to
+be "atomic" as no other CPU has access to these data structures.
+
+There are special cases where you might need to access per cpu data
+structures remotely. It is usually safe to do a remote read access
+and that is frequently done to summarize counters. Remote write access
+something which could be problematic because this_cpu ops do not
+have lock semantics. A remote write may interfere with a this_cpu
+RMW operation.
+
+Remote write accesses to percpu data structures are highly discouraged
+unless absolutely necessary. Please consider using an IPI to wake up
+the remote CPU and perform the update to its per cpu area.
+
+To access per-cpu data structure remotely, typically the per_cpu_ptr()
+function is used:
+
+
+ DEFINE_PER_CPU(struct data, datap);
+
+ struct data *p = per_cpu_ptr(&datap, cpu);
+
+This makes it explicit that we are getting ready to access a percpu
+area remotely.
+
+You can also do the following to convert the datap offset to an address
+
+ struct data *p = this_cpu_ptr(&datap);
+
+but, passing of pointers calculated via this_cpu_ptr to other cpus is
+unusual and should be avoided.
+
+Remote access are typically only for reading the status of another cpus
+per cpu data. Write accesses can cause unique problems due to the
+relaxed synchronization requirements for this_cpu operations.
+
+One example that illustrates some concerns with write operations is
+the following scenario that occurs because two per cpu variables
+share a cache-line but the relaxed synchronization is applied to
+only one process updating the cache-line.
+
+Consider the following example
+
+
+ struct test {
+ atomic_t a;
+ int b;
+ };
+
+ DEFINE_PER_CPU(struct test, onecacheline);
+
+There is some concern about what would happen if the field 'a' is updated
+remotely from one processor and the local processor would use this_cpu ops
+to update field b. Care should be taken that such simultaneous accesses to
+data within the same cache line are avoided. Also costly synchronization
+may be necessary. IPIs are generally recommended in such scenarios instead
+of a remote write to the per cpu area of another processor.
+
+Even in cases where the remote writes are rare, please bear in
+mind that a remote write will evict the cache line from the processor
+that most likely will access it. If the processor wakes up and finds a
+missing local cache line of a per cpu area, its performance and hence
+the wake up times will be affected.
+
+Christoph Lameter, August 4th, 2014
+Pranith Kumar, Aug 2nd, 2014
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
L: linux-rockchip@lists.infradead.org
S: Maintained
+F: arch/arm/boot/dts/rk3*
F: arch/arm/mach-rockchip/
+F: drivers/clk/rockchip/
+F: drivers/i2c/busses/i2c-rk3x.c
F: drivers/*/*rockchip*
+F: drivers/*/*/*rockchip*
+F: sound/soc/rockchip/
ARM/SAMSUNG ARM ARCHITECTURES
M: Ben Dooks <ben-linux@fluff.org>
F: Documentation/usb/ohci.txt
F: drivers/usb/host/ohci*
+USB OVER IP DRIVER
+M: Valentina Manea <valentina.manea.m@gmail.com>
+M: Shuah Khan <shuah.kh@samsung.com>
+L: linux-usb@vger.kernel.org
+S: Maintained
+F: drivers/usb/usbip/
+F: tools/usb/usbip/
+
USB PEGASUS DRIVER
M: Petko Manolov <petkan@nucleusys.com>
L: linux-usb@vger.kernel.org
VERSION = 3
PATCHLEVEL = 17
SUBLEVEL = 0
-EXTRAVERSION = -rc2
+EXTRAVERSION = -rc3
NAME = Shuffling Zombie Juror
# *DOCUMENTATION*
#define outb_p outb
#define outw_p outw
#define outl_p outl
-#define readb_relaxed(addr) __raw_readb(addr)
-#define readw_relaxed(addr) __raw_readw(addr)
-#define readl_relaxed(addr) __raw_readl(addr)
-#define readq_relaxed(addr) __raw_readq(addr)
+#define readb_relaxed(addr) __raw_readb(addr)
+#define readw_relaxed(addr) __raw_readw(addr)
+#define readl_relaxed(addr) __raw_readl(addr)
+#define readq_relaxed(addr) __raw_readq(addr)
+#define writeb_relaxed(b, addr) __raw_writeb(b, addr)
+#define writew_relaxed(b, addr) __raw_writew(b, addr)
+#define writel_relaxed(b, addr) __raw_writel(b, addr)
+#define writeq_relaxed(b, addr) __raw_writeq(b, addr)
#define mmiowb()
#include <uapi/asm/unistd.h>
-#define NR_SYSCALLS 508
+#define NR_SYSCALLS 511
#define __ARCH_WANT_OLD_READDIR
#define __ARCH_WANT_STAT64
#define __NR_process_vm_writev 505
#define __NR_kcmp 506
#define __NR_finit_module 507
+#define __NR_sched_setattr 508
+#define __NR_sched_getattr 509
+#define __NR_renameat2 510
#endif /* _UAPI_ALPHA_UNISTD_H */
.quad sys_process_vm_writev /* 505 */
.quad sys_kcmp
.quad sys_finit_module
+ .quad sys_sched_setattr
+ .quad sys_sched_getattr
+ .quad sys_renameat2 /* 510 */
.size sys_call_table, . - sys_call_table
.type sys_call_table, @object
tot_sz -= sz;
}
}
+EXPORT_SYMBOL(flush_icache_range);
/*
* General purpose helper to make I and D cache lines consistent.
config KEXEC
bool "Kexec system call (EXPERIMENTAL)"
depends on (!SMP || PM_SLEEP_SMP)
- select CRYPTO
- select CRYPTO_SHA256
help
kexec is a system call that implements the ability to shutdown your
current kernel, and to start another kernel. It is like a reboot
omap3-devkit8000.dtb \
omap3-evm.dtb \
omap3-evm-37xx.dtb \
- omap3-gta04.dtb \
+ omap3-gta04a3.dtb \
+ omap3-gta04a4.dtb \
+ omap3-gta04a5.dtb \
+ omap3-ha.dtb \
+ omap3-ha-lcd.dtb \
omap3-igep0020.dtb \
omap3-igep0030.dtb \
omap3-ldp.dtb \
omap3-sbc-t3517.dtb \
omap3-sbc-t3530.dtb \
omap3-sbc-t3730.dtb \
+ omap3-thunder.dtb \
omap3-zoom3.dtb
dtb-$(CONFIG_SOC_AM33XX) += am335x-base0033.dtb \
am335x-bone.dtb \
&tps {
regulators {
dcdc1_reg: regulator@0 {
+ regulator-name = "vdds_dpr";
regulator-always-on;
};
};
ldo1_reg: regulator@3 {
+ regulator-name = "vio,vrtc,vdds";
regulator-always-on;
};
ldo2_reg: regulator@4 {
+ regulator-name = "vdd_3v3aux";
regulator-always-on;
};
ldo3_reg: regulator@5 {
+ regulator-name = "vdd_1v8";
regulator-always-on;
};
ldo4_reg: regulator@6 {
+ regulator-name = "vdd_3v3a";
regulator-always-on;
};
};
ti,hwmods = "mailbox";
ti,mbox-num-users = <4>;
ti,mbox-num-fifos = <8>;
+ mbox_wkupm3: wkup_m3 {
+ ti,mbox-tx = <0 0 0>;
+ ti,mbox-rx = <0 0 3>;
+ };
};
timer1: timer@44e31000 {
};
am43xx_pinmux: pinmux@44e10800 {
- compatible = "pinctrl-single";
+ compatible = "ti,am437-padconf", "pinctrl-single";
reg = <0x44e10800 0x31c>;
#address-cells = <1>;
#size-cells = <0>;
+ #interrupt-cells = <1>;
+ interrupt-controller;
pinctrl-single,register-width = <32>;
pinctrl-single,function-mask = <0xffffffff>;
};
ti,hwmods = "mailbox";
ti,mbox-num-users = <4>;
ti,mbox-num-fifos = <8>;
+ mbox_wkupm3: wkup_m3 {
+ ti,mbox-tx = <0 0 0>;
+ ti,mbox-rx = <0 0 3>;
+ };
};
timer1: timer@44e31000 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&uart1_pins>;
+ interrupts-extended = <&gic GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>,
+ <&dra7_pmx_core 0x3e0>;
};
&uart2 {
};
dra7_pmx_core: pinmux@4a003400 {
- compatible = "pinctrl-single";
+ compatible = "ti,dra7-padconf", "pinctrl-single";
reg = <0x4a003400 0x0464>;
#address-cells = <1>;
#size-cells = <0>;
+ #interrupt-cells = <1>;
+ interrupt-controller;
pinctrl-single,register-width = <32>;
pinctrl-single,function-mask = <0x3fffffff>;
};
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio2: gpio@48055000 {
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio3: gpio@48057000 {
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio4: gpio@48059000 {
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio5: gpio@4805b000 {
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio6: gpio@4805d000 {
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio7: gpio@48051000 {
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
gpio8: gpio@48053000 {
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
};
uart1: serial@4806a000 {
compatible = "ti,omap4-uart";
reg = <0x4806a000 0x100>;
- interrupts = <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts-extended = <&gic GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
ti,hwmods = "uart1";
clock-frequency = <48000000>;
status = "disabled";
uart2: serial@4806c000 {
compatible = "ti,omap4-uart";
reg = <0x4806c000 0x100>;
- interrupts = <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts-extended = <&gic GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>;
ti,hwmods = "uart2";
clock-frequency = <48000000>;
status = "disabled";
uart3: serial@48020000 {
compatible = "ti,omap4-uart";
reg = <0x48020000 0x100>;
- interrupts = <GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts-extended = <&gic GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>;
ti,hwmods = "uart3";
clock-frequency = <48000000>;
status = "disabled";
uart4: serial@4806e000 {
compatible = "ti,omap4-uart";
reg = <0x4806e000 0x100>;
- interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts-extended = <&gic GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>;
ti,hwmods = "uart4";
clock-frequency = <48000000>;
status = "disabled";
uart5: serial@48066000 {
compatible = "ti,omap4-uart";
reg = <0x48066000 0x100>;
- interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts-extended = <&gic GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
ti,hwmods = "uart5";
clock-frequency = <48000000>;
status = "disabled";
uart6: serial@48068000 {
compatible = "ti,omap4-uart";
reg = <0x48068000 0x100>;
- interrupts = <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts-extended = <&gic GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>;
ti,hwmods = "uart6";
clock-frequency = <48000000>;
status = "disabled";
uart7: serial@48420000 {
compatible = "ti,omap4-uart";
reg = <0x48420000 0x100>;
- interrupts = <GIC_SPI 218 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts-extended = <&gic GIC_SPI 218 IRQ_TYPE_LEVEL_HIGH>;
ti,hwmods = "uart7";
clock-frequency = <48000000>;
status = "disabled";
uart8: serial@48422000 {
compatible = "ti,omap4-uart";
reg = <0x48422000 0x100>;
- interrupts = <GIC_SPI 219 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts-extended = <&gic GIC_SPI 219 IRQ_TYPE_LEVEL_HIGH>;
ti,hwmods = "uart8";
clock-frequency = <48000000>;
status = "disabled";
uart9: serial@48424000 {
compatible = "ti,omap4-uart";
reg = <0x48424000 0x100>;
- interrupts = <GIC_SPI 220 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts-extended = <&gic GIC_SPI 220 IRQ_TYPE_LEVEL_HIGH>;
ti,hwmods = "uart9";
clock-frequency = <48000000>;
status = "disabled";
uart10: serial@4ae2b000 {
compatible = "ti,omap4-uart";
reg = <0x4ae2b000 0x100>;
- interrupts = <GIC_SPI 221 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts-extended = <&gic GIC_SPI 221 IRQ_TYPE_LEVEL_HIGH>;
ti,hwmods = "uart10";
clock-frequency = <48000000>;
status = "disabled";
};
};
+&dra7_pmx_core {
+ i2c1_pins: pinmux_i2c1_pins {
+ pinctrl-single,pins = <
+ 0x400 (PIN_INPUT | MUX_MODE0) /* i2c1_sda.i2c1_sda */
+ 0x404 (PIN_INPUT | MUX_MODE0) /* i2c1_scl.i2c1_scl */
+ >;
+ };
+};
+
+&i2c1 {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_pins>;
+ clock-frequency = <400000>;
+
+ tps65917: tps65917@58 {
+ compatible = "ti,tps65917";
+ reg = <0x58>;
+
+ interrupts = <GIC_SPI 2 IRQ_TYPE_NONE>; /* IRQ_SYS_1N */
+ interrupt-parent = <&gic>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+
+ ti,system-power-controller;
+
+ tps65917_pmic {
+ compatible = "ti,tps65917-pmic";
+
+ regulators {
+ smps1_reg: smps1 {
+ /* VDD_MPU */
+ regulator-name = "smps1";
+ regulator-min-microvolt = <850000>;
+ regulator-max-microvolt = <1250000>;
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ smps2_reg: smps2 {
+ /* VDD_CORE */
+ regulator-name = "smps2";
+ regulator-min-microvolt = <850000>;
+ regulator-max-microvolt = <1030000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ smps3_reg: smps3 {
+ /* VDD_GPU IVA DSPEVE */
+ regulator-name = "smps3";
+ regulator-min-microvolt = <850000>;
+ regulator-max-microvolt = <1250000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ smps4_reg: smps4 {
+ /* VDDS1V8 */
+ regulator-name = "smps4";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ smps5_reg: smps5 {
+ /* VDD_DDR */
+ regulator-name = "smps5";
+ regulator-min-microvolt = <1350000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ ldo1_reg: ldo1 {
+ /* LDO1_OUT --> SDIO */
+ regulator-name = "ldo1";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-boot-on;
+ };
+
+ ldo2_reg: ldo2 {
+ /* LDO2_OUT --> TP1017 (UNUSED) */
+ regulator-name = "ldo2";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ ldo3_reg: ldo3 {
+ /* VDDA_1V8_PHY */
+ regulator-name = "ldo3";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ ldo5_reg: ldo5 {
+ /* VDDA_1V8_PLL */
+ regulator-name = "ldo5";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ ldo4_reg: ldo4 {
+ /* VDDA_3V_USB: VDDA_USBHS33 */
+ regulator-name = "ldo4";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-boot-on;
+ };
+ };
+ };
+ };
+};
+
&uart1 {
status = "okay";
};
MX53_PAD_CSI0_DAT9__I2C1_SCL 0x400001ec
>;
};
+
+ pinctrl_pmic: pmicgrp {
+ fsl,pins = <
+ MX53_PAD_CSI0_DAT5__GPIO5_23 0x1e4 /* IRQ */
+ >;
+ };
};
};
pmic: mc34708@8 {
compatible = "fsl,mc34708";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_pmic>;
reg = <0x08>;
interrupt-parent = <&gpio5>;
interrupts = <23 0x8>;
sound-spdif {
compatible = "fsl,imx-audio-spdif";
- model = "imx-spdif";
+ model = "On-board SPDIF";
/* IMX6 doesn't implement this yet */
spdif-controller = <&spdif>;
spdif-out;
};
&usbh1 {
+ disable-over-current;
vbus-supply = <®_usbh1_vbus>;
status = "okay";
};
&usbotg {
+ disable-over-current;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hummingboard_usbotg_id>;
vbus-supply = <®_usbotg_vbus>;
sound-spdif {
compatible = "fsl,imx-audio-spdif";
- model = "imx-spdif";
+ model = "Integrated SPDIF";
/* IMX6 doesn't implement this yet */
spdif-controller = <&spdif>;
spdif-out;
fsl,pins = <MX6QDL_PAD_GPIO_17__SPDIF_OUT 0x13091>;
};
+ pinctrl_cubox_i_usbh1: cubox-i-usbh1 {
+ fsl,pins = <MX6QDL_PAD_GPIO_3__USB_H1_OC 0x1b0b0>;
+ };
+
pinctrl_cubox_i_usbh1_vbus: cubox-i-usbh1-vbus {
fsl,pins = <MX6QDL_PAD_GPIO_0__GPIO1_IO00 0x4001b0b0>;
};
- pinctrl_cubox_i_usbotg_id: cubox-i-usbotg-id {
+ pinctrl_cubox_i_usbotg: cubox-i-usbotg {
/*
- * The Cubox-i pulls this low, but as it's pointless
+ * The Cubox-i pulls ID low, but as it's pointless
* leaving it as a pull-up, even if it is just 10uA.
*/
- fsl,pins = <MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x13059>;
+ fsl,pins = <
+ MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x13059
+ MX6QDL_PAD_KEY_COL4__USB_OTG_OC 0x1b0b0
+ >;
};
pinctrl_cubox_i_usbotg_vbus: cubox-i-usbotg-vbus {
};
&usbh1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_cubox_i_usbh1>;
vbus-supply = <®_usbh1_vbus>;
status = "okay";
};
&usbotg {
pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_cubox_i_usbotg_id>;
+ pinctrl-0 = <&pinctrl_cubox_i_usbotg>;
vbus-supply = <®_usbotg_vbus>;
status = "okay";
};
enet {
pinctrl_microsom_enet_ar8035: microsom-enet-ar8035 {
fsl,pins = <
- MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0
+ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b8b0
MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0
/* AR8035 reset */
MX6QDL_PAD_KEY_ROW4__GPIO4_IO15 0x130b0
ti,hwmods = "mailbox";
ti,mbox-num-users = <4>;
ti,mbox-num-fifos = <6>;
+ mbox_dsp: dsp {
+ ti,mbox-tx = <0 0 0>;
+ ti,mbox-rx = <1 0 0>;
+ };
+ mbox_iva: iva {
+ ti,mbox-tx = <2 1 3>;
+ ti,mbox-rx = <3 1 3>;
+ };
};
timer1: timer@48028000 {
ti,hwmods = "mailbox";
ti,mbox-num-users = <4>;
ti,mbox-num-fifos = <6>;
+ mbox_dsp: dsp {
+ ti,mbox-tx = <0 0 0>;
+ ti,mbox-rx = <1 0 0>;
+ };
};
timer1: timer@49018000 {
&uart3 {
pinctrl-names = "default";
pinctrl-0 = <&uart3_pins>;
+ interrupts-extended = <&intc 74 &omap3_pmx_core OMAP3_UART3_RX>;
};
&gpio1 {
+++ /dev/null
-/*
- * Copyright (C) 2013 Marek Belisko <marek@goldelico.com>
- *
- * Based on omap3-beagle-xm.dts
- *
- * 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.
- */
-/dts-v1/;
-
-#include "omap36xx.dtsi"
-
-/ {
- model = "OMAP3 GTA04";
- compatible = "ti,omap3-gta04", "ti,omap36xx", "ti,omap3";
-
- cpus {
- cpu@0 {
- cpu0-supply = <&vcc>;
- };
- };
-
- memory {
- device_type = "memory";
- reg = <0x80000000 0x20000000>; /* 512 MB */
- };
-
- gpio-keys {
- compatible = "gpio-keys";
-
- aux-button {
- label = "aux";
- linux,code = <169>;
- gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>;
- gpio-key,wakeup;
- };
- };
-
- sound {
- compatible = "ti,omap-twl4030";
- ti,model = "gta04";
-
- ti,mcbsp = <&mcbsp2>;
- ti,codec = <&twl_audio>;
- };
-
- spi_lcd {
- compatible = "spi-gpio";
- #address-cells = <0x1>;
- #size-cells = <0x0>;
- pinctrl-names = "default";
- pinctrl-0 = <&spi_gpio_pins>;
-
- gpio-sck = <&gpio1 12 0>;
- gpio-miso = <&gpio1 18 0>;
- gpio-mosi = <&gpio1 20 0>;
- cs-gpios = <&gpio1 19 0>;
- num-chipselects = <1>;
-
- /* lcd panel */
- lcd: td028ttec1@0 {
- compatible = "toppoly,td028ttec1";
- reg = <0>;
- spi-max-frequency = <100000>;
- spi-cpol;
- spi-cpha;
-
- label = "lcd";
- port {
- lcd_in: endpoint {
- remote-endpoint = <&dpi_out>;
- };
- };
- };
- };
-};
-
-&omap3_pmx_core {
- uart1_pins: pinmux_uart1_pins {
- pinctrl-single,pins = <
- 0x152 (PIN_INPUT | MUX_MODE0) /* uart1_rx.uart1_rx */
- 0x14c (PIN_OUTPUT |MUX_MODE0) /* uart1_tx.uart1_tx */
- >;
- };
-
- uart2_pins: pinmux_uart2_pins {
- pinctrl-single,pins = <
- 0x14a (PIN_INPUT | MUX_MODE0) /* uart2_rx.uart2_rx */
- 0x148 (PIN_OUTPUT | MUX_MODE0) /* uart2_tx.uart2_tx */
- >;
- };
-
- uart3_pins: pinmux_uart3_pins {
- pinctrl-single,pins = <
- 0x16e (PIN_INPUT | MUX_MODE0) /* uart3_rx.uart3_rx */
- 0x170 (PIN_OUTPUT | MUX_MODE0) /* uart3_tx.uart3_tx */
- >;
- };
-
- mmc1_pins: pinmux_mmc1_pins {
- pinctrl-single,pins = <
- 0x114 (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_clk.sdmmc1_clk */
- 0x116 (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_cmd.sdmmc1_cmd */
- 0x118 (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat0.sdmmc1_dat0 */
- 0x11a (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat1.sdmmc1_dat1 */
- 0x11c (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat2.sdmmc1_dat2 */
- 0x11e (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat3.sdmmc1_dat3 */
- >;
- };
-
- dss_dpi_pins: pinmux_dss_dpi_pins {
- pinctrl-single,pins = <
- 0x0a4 (PIN_OUTPUT | MUX_MODE0) /* dss_pclk.dss_pclk */
- 0x0a6 (PIN_OUTPUT | MUX_MODE0) /* dss_hsync.dss_hsync */
- 0x0a8 (PIN_OUTPUT | MUX_MODE0) /* dss_vsync.dss_vsync */
- 0x0aa (PIN_OUTPUT | MUX_MODE0) /* dss_acbias.dss_acbias */
- 0x0ac (PIN_OUTPUT | MUX_MODE0) /* dss_data0.dss_data0 */
- 0x0ae (PIN_OUTPUT | MUX_MODE0) /* dss_data1.dss_data1 */
- 0x0b0 (PIN_OUTPUT | MUX_MODE0) /* dss_data2.dss_data2 */
- 0x0b2 (PIN_OUTPUT | MUX_MODE0) /* dss_data3.dss_data3 */
- 0x0b4 (PIN_OUTPUT | MUX_MODE0) /* dss_data4.dss_data4 */
- 0x0b6 (PIN_OUTPUT | MUX_MODE0) /* dss_data5.dss_data5 */
- 0x0b8 (PIN_OUTPUT | MUX_MODE0) /* dss_data6.dss_data6 */
- 0x0ba (PIN_OUTPUT | MUX_MODE0) /* dss_data7.dss_data7 */
- 0x0bc (PIN_OUTPUT | MUX_MODE0) /* dss_data8.dss_data8 */
- 0x0be (PIN_OUTPUT | MUX_MODE0) /* dss_data9.dss_data9 */
- 0x0c0 (PIN_OUTPUT | MUX_MODE0) /* dss_data10.dss_data10 */
- 0x0c2 (PIN_OUTPUT | MUX_MODE0) /* dss_data11.dss_data11 */
- 0x0c4 (PIN_OUTPUT | MUX_MODE0) /* dss_data12.dss_data12 */
- 0x0c6 (PIN_OUTPUT | MUX_MODE0) /* dss_data13.dss_data13 */
- 0x0c8 (PIN_OUTPUT | MUX_MODE0) /* dss_data14.dss_data14 */
- 0x0ca (PIN_OUTPUT | MUX_MODE0) /* dss_data15.dss_data15 */
- 0x0cc (PIN_OUTPUT | MUX_MODE0) /* dss_data16.dss_data16 */
- 0x0ce (PIN_OUTPUT | MUX_MODE0) /* dss_data17.dss_data17 */
- 0x0d0 (PIN_OUTPUT | MUX_MODE0) /* dss_data18.dss_data18 */
- 0x0d2 (PIN_OUTPUT | MUX_MODE0) /* dss_data19.dss_data19 */
- 0x0d4 (PIN_OUTPUT | MUX_MODE0) /* dss_data20.dss_data20 */
- 0x0d6 (PIN_OUTPUT | MUX_MODE0) /* dss_data21.dss_data21 */
- 0x0d8 (PIN_OUTPUT | MUX_MODE0) /* dss_data22.dss_data22 */
- 0x0da (PIN_OUTPUT | MUX_MODE0) /* dss_data23.dss_data23 */
- >;
- };
-
- spi_gpio_pins: spi_gpio_pinmux {
- pinctrl-single,pins = <0x5a8 (PIN_OUTPUT | MUX_MODE4) /* clk */
- 0x5b6 (PIN_OUTPUT | MUX_MODE4) /* cs */
- 0x5b8 (PIN_OUTPUT | MUX_MODE4) /* tx */
- 0x5b4 (PIN_INPUT | MUX_MODE4) /* rx */
- >;
- };
-};
-
-&i2c1 {
- clock-frequency = <2600000>;
-
- twl: twl@48 {
- reg = <0x48>;
- interrupts = <7>; /* SYS_NIRQ cascaded to intc */
- interrupt-parent = <&intc>;
- };
-
- twl_audio: audio {
- compatible = "ti,twl4030-audio";
- codec {
- };
- };
-};
-
-#include "twl4030.dtsi"
-#include "twl4030_omap3.dtsi"
-
-&i2c2 {
- clock-frequency = <400000>;
-
- /* pressure sensor */
- bmp085@77 {
- compatible = "bosch,bmp085";
- reg = <0x77>;
- interrupt-parent = <&gpio4>;
- interrupts = <17 IRQ_TYPE_EDGE_RISING>;
- };
-
- /* accelerometer */
- bma180@41 {
- compatible = "bosch,bma180";
- reg = <0x41>;
- interrupt-parent = <&gpio3>;
- interrupts = <19 IRQ_TYPE_LEVEL_HIGH>;
- };
-
- /* leds */
- tca6507@45 {
- compatible = "ti,tca6507";
- #address-cells = <1>;
- #size-cells = <0>;
- reg = <0x45>;
-
- gta04_led0: red_aux@0 {
- label = "gta04:red:aux";
- reg = <0x0>;
- };
-
- gta04_led1: green_aux@1 {
- label = "gta04:green:aux";
- reg = <0x1>;
- };
-
- gta04_led3: red_power@3 {
- label = "gta04:red:power";
- reg = <0x3>;
- linux,default-trigger = "default-on";
- };
-
- gta04_led4: green_power@4 {
- label = "gta04:green:power";
- reg = <0x4>;
- };
- };
-
- /* compass aka magnetometer */
- hmc5843@1e {
- compatible = "honeywell,hmc5843";
- reg = <0x1e>;
- };
-
- /* touchscreen */
- tsc2007@48 {
- compatible = "ti,tsc2007";
- reg = <0x48>;
- interrupt-parent = <&gpio6>;
- interrupts = <0 IRQ_TYPE_EDGE_FALLING>;
- gpios = <&gpio6 0 GPIO_ACTIVE_LOW>;
- ti,x-plate-ohms = <600>;
- };
-};
-
-&i2c3 {
- clock-frequency = <100000>;
-};
-
-&usb_otg_hs {
- interface-type = <0>;
- usb-phy = <&usb2_phy>;
- phys = <&usb2_phy>;
- phy-names = "usb2-phy";
- mode = <3>;
- power = <50>;
-};
-
-&mmc1 {
- pinctrl-names = "default";
- pinctrl-0 = <&mmc1_pins>;
- vmmc-supply = <&vmmc1>;
- bus-width = <4>;
- ti,non-removable;
-};
-
-&mmc2 {
- vmmc-supply = <&vaux4>;
- bus-width = <4>;
- ti,non-removable;
-};
-
-&mmc3 {
- status = "disabled";
-};
-
-&uart1 {
- pinctrl-names = "default";
- pinctrl-0 = <&uart1_pins>;
-};
-
-&uart2 {
- pinctrl-names = "default";
- pinctrl-0 = <&uart2_pins>;
-};
-
-&uart3 {
- pinctrl-names = "default";
- pinctrl-0 = <&uart3_pins>;
-};
-
-&charger {
- bb_uvolt = <3200000>;
- bb_uamp = <150>;
-};
-
-&vaux4 {
- regulator-min-microvolt = <2800000>;
- regulator-max-microvolt = <3150000>;
-};
-
-/* Needed to power the DPI pins */
-&vpll2 {
- regulator-always-on;
-};
-
-&dss {
- pinctrl-names = "default";
- pinctrl-0 = < &dss_dpi_pins >;
-
- status = "okay";
-
- port {
- dpi_out: endpoint {
- remote-endpoint = <&lcd_in>;
- data-lines = <24>;
- };
- };
-};
--- /dev/null
+/*
+ * Copyright (C) 2013 Marek Belisko <marek@goldelico.com>
+ *
+ * Based on omap3-beagle-xm.dts
+ *
+ * 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.
+ */
+/dts-v1/;
+
+#include "omap36xx.dtsi"
+
+/ {
+ model = "OMAP3 GTA04";
+ compatible = "ti,omap3-gta04", "ti,omap36xx", "ti,omap3";
+
+ cpus {
+ cpu@0 {
+ cpu0-supply = <&vcc>;
+ };
+ };
+
+ memory {
+ device_type = "memory";
+ reg = <0x80000000 0x20000000>; /* 512 MB */
+ };
+
+ aliases {
+ display0 = &lcd;
+ };
+
+ gpio-keys {
+ compatible = "gpio-keys";
+
+ aux-button {
+ label = "aux";
+ linux,code = <169>;
+ gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>;
+ gpio-key,wakeup;
+ };
+ };
+
+ sound {
+ compatible = "ti,omap-twl4030";
+ ti,model = "gta04";
+
+ ti,mcbsp = <&mcbsp2>;
+ ti,codec = <&twl_audio>;
+ };
+
+ spi_lcd {
+ compatible = "spi-gpio";
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi_gpio_pins>;
+
+ gpio-sck = <&gpio1 12 0>;
+ gpio-miso = <&gpio1 18 0>;
+ gpio-mosi = <&gpio1 20 0>;
+ cs-gpios = <&gpio1 19 0>;
+ num-chipselects = <1>;
+
+ /* lcd panel */
+ lcd: td028ttec1@0 {
+ compatible = "toppoly,td028ttec1";
+ reg = <0>;
+ spi-max-frequency = <100000>;
+ spi-cpol;
+ spi-cpha;
+
+ label = "lcd";
+ port {
+ lcd_in: endpoint {
+ remote-endpoint = <&dpi_out>;
+ };
+ };
+ };
+ };
+
+ hsusb2_phy: hsusb2_phy {
+ compatible = "usb-nop-xceiv";
+ reset-gpios = <&gpio6 14 GPIO_ACTIVE_LOW>;
+ };
+};
+
+&omap3_pmx_core {
+ pinctrl-names = "default";
+ pinctrl-0 = <
+ &hsusb2_pins
+ >;
+
+ hsusb2_pins: pinmux_hsusb2_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x21d4, PIN_INPUT_PULLDOWN | MUX_MODE3) /* mcspi1_cs3.hsusb2_data2 */
+ OMAP3_CORE1_IOPAD(0x21d6, PIN_INPUT_PULLDOWN | MUX_MODE3) /* mcspi2_clk.hsusb2_data7 */
+ OMAP3_CORE1_IOPAD(0x21d8, PIN_INPUT_PULLDOWN | MUX_MODE3) /* mcspi2_simo.hsusb2_data4 */
+ OMAP3_CORE1_IOPAD(0x21da, PIN_INPUT_PULLDOWN | MUX_MODE3) /* mcspi2_somi.hsusb2_data5 */
+ OMAP3_CORE1_IOPAD(0x21dc, PIN_INPUT_PULLDOWN | MUX_MODE3) /* mcspi2_cs0.hsusb2_data6 */
+ OMAP3_CORE1_IOPAD(0x21de, PIN_INPUT_PULLDOWN | MUX_MODE3) /* mcspi2_cs1.hsusb2_data3 */
+ >;
+ };
+
+ uart1_pins: pinmux_uart1_pins {
+ pinctrl-single,pins = <
+ 0x152 (PIN_INPUT | MUX_MODE0) /* uart1_rx.uart1_rx */
+ 0x14c (PIN_OUTPUT |MUX_MODE0) /* uart1_tx.uart1_tx */
+ >;
+ };
+
+ uart2_pins: pinmux_uart2_pins {
+ pinctrl-single,pins = <
+ 0x14a (PIN_INPUT | MUX_MODE0) /* uart2_rx.uart2_rx */
+ 0x148 (PIN_OUTPUT | MUX_MODE0) /* uart2_tx.uart2_tx */
+ >;
+ };
+
+ uart3_pins: pinmux_uart3_pins {
+ pinctrl-single,pins = <
+ 0x16e (PIN_INPUT | MUX_MODE0) /* uart3_rx.uart3_rx */
+ 0x170 (PIN_OUTPUT | MUX_MODE0) /* uart3_tx.uart3_tx */
+ >;
+ };
+
+ mmc1_pins: pinmux_mmc1_pins {
+ pinctrl-single,pins = <
+ 0x114 (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_clk.sdmmc1_clk */
+ 0x116 (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_cmd.sdmmc1_cmd */
+ 0x118 (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat0.sdmmc1_dat0 */
+ 0x11a (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat1.sdmmc1_dat1 */
+ 0x11c (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat2.sdmmc1_dat2 */
+ 0x11e (PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat3.sdmmc1_dat3 */
+ >;
+ };
+
+ dss_dpi_pins: pinmux_dss_dpi_pins {
+ pinctrl-single,pins = <
+ 0x0a4 (PIN_OUTPUT | MUX_MODE0) /* dss_pclk.dss_pclk */
+ 0x0a6 (PIN_OUTPUT | MUX_MODE0) /* dss_hsync.dss_hsync */
+ 0x0a8 (PIN_OUTPUT | MUX_MODE0) /* dss_vsync.dss_vsync */
+ 0x0aa (PIN_OUTPUT | MUX_MODE0) /* dss_acbias.dss_acbias */
+ 0x0ac (PIN_OUTPUT | MUX_MODE0) /* dss_data0.dss_data0 */
+ 0x0ae (PIN_OUTPUT | MUX_MODE0) /* dss_data1.dss_data1 */
+ 0x0b0 (PIN_OUTPUT | MUX_MODE0) /* dss_data2.dss_data2 */
+ 0x0b2 (PIN_OUTPUT | MUX_MODE0) /* dss_data3.dss_data3 */
+ 0x0b4 (PIN_OUTPUT | MUX_MODE0) /* dss_data4.dss_data4 */
+ 0x0b6 (PIN_OUTPUT | MUX_MODE0) /* dss_data5.dss_data5 */
+ 0x0b8 (PIN_OUTPUT | MUX_MODE0) /* dss_data6.dss_data6 */
+ 0x0ba (PIN_OUTPUT | MUX_MODE0) /* dss_data7.dss_data7 */
+ 0x0bc (PIN_OUTPUT | MUX_MODE0) /* dss_data8.dss_data8 */
+ 0x0be (PIN_OUTPUT | MUX_MODE0) /* dss_data9.dss_data9 */
+ 0x0c0 (PIN_OUTPUT | MUX_MODE0) /* dss_data10.dss_data10 */
+ 0x0c2 (PIN_OUTPUT | MUX_MODE0) /* dss_data11.dss_data11 */
+ 0x0c4 (PIN_OUTPUT | MUX_MODE0) /* dss_data12.dss_data12 */
+ 0x0c6 (PIN_OUTPUT | MUX_MODE0) /* dss_data13.dss_data13 */
+ 0x0c8 (PIN_OUTPUT | MUX_MODE0) /* dss_data14.dss_data14 */
+ 0x0ca (PIN_OUTPUT | MUX_MODE0) /* dss_data15.dss_data15 */
+ 0x0cc (PIN_OUTPUT | MUX_MODE0) /* dss_data16.dss_data16 */
+ 0x0ce (PIN_OUTPUT | MUX_MODE0) /* dss_data17.dss_data17 */
+ 0x0d0 (PIN_OUTPUT | MUX_MODE0) /* dss_data18.dss_data18 */
+ 0x0d2 (PIN_OUTPUT | MUX_MODE0) /* dss_data19.dss_data19 */
+ 0x0d4 (PIN_OUTPUT | MUX_MODE0) /* dss_data20.dss_data20 */
+ 0x0d6 (PIN_OUTPUT | MUX_MODE0) /* dss_data21.dss_data21 */
+ 0x0d8 (PIN_OUTPUT | MUX_MODE0) /* dss_data22.dss_data22 */
+ 0x0da (PIN_OUTPUT | MUX_MODE0) /* dss_data23.dss_data23 */
+ >;
+ };
+};
+
+&omap3_pmx_core2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <
+ &hsusb2_2_pins
+ >;
+
+ hsusb2_2_pins: pinmux_hsusb2_2_pins {
+ pinctrl-single,pins = <
+ OMAP3630_CORE2_IOPAD(0x25f0, PIN_OUTPUT | MUX_MODE3) /* etk_d10.hsusb2_clk */
+ OMAP3630_CORE2_IOPAD(0x25f2, PIN_OUTPUT | MUX_MODE3) /* etk_d11.hsusb2_stp */
+ OMAP3630_CORE2_IOPAD(0x25f4, PIN_INPUT_PULLDOWN | MUX_MODE3) /* etk_d12.hsusb2_dir */
+ OMAP3630_CORE2_IOPAD(0x25f6, PIN_INPUT_PULLDOWN | MUX_MODE3) /* etk_d13.hsusb2_nxt */
+ OMAP3630_CORE2_IOPAD(0x25f8, PIN_INPUT_PULLDOWN | MUX_MODE3) /* etk_d14.hsusb2_data0 */
+ OMAP3630_CORE2_IOPAD(0x25fa, PIN_INPUT_PULLDOWN | MUX_MODE3) /* etk_d15.hsusb2_data1 */
+ >;
+ };
+
+ spi_gpio_pins: spi_gpio_pinmux {
+ pinctrl-single,pins = <
+ OMAP3630_CORE2_IOPAD(0x25d8, PIN_OUTPUT | MUX_MODE4) /* clk */
+ OMAP3630_CORE2_IOPAD(0x25e6, PIN_OUTPUT | MUX_MODE4) /* cs */
+ OMAP3630_CORE2_IOPAD(0x25e8, PIN_OUTPUT | MUX_MODE4) /* tx */
+ OMAP3630_CORE2_IOPAD(0x25e4, PIN_INPUT | MUX_MODE4) /* rx */
+ >;
+ };
+};
+
+&i2c1 {
+ clock-frequency = <2600000>;
+
+ twl: twl@48 {
+ reg = <0x48>;
+ interrupts = <7>; /* SYS_NIRQ cascaded to intc */
+ interrupt-parent = <&intc>;
+ };
+
+ twl_audio: audio {
+ compatible = "ti,twl4030-audio";
+ codec {
+ };
+ };
+};
+
+#include "twl4030.dtsi"
+#include "twl4030_omap3.dtsi"
+
+&i2c2 {
+ clock-frequency = <400000>;
+
+ /* pressure sensor */
+ bmp085@77 {
+ compatible = "bosch,bmp085";
+ reg = <0x77>;
+ interrupt-parent = <&gpio4>;
+ interrupts = <17 IRQ_TYPE_EDGE_RISING>;
+ };
+
+ /* accelerometer */
+ bma180@41 {
+ compatible = "bosch,bma180";
+ reg = <0x41>;
+ interrupt-parent = <&gpio3>;
+ interrupts = <19 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ /* leds */
+ tca6507@45 {
+ compatible = "ti,tca6507";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x45>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ gta04_led0: red_aux@0 {
+ label = "gta04:red:aux";
+ reg = <0x0>;
+ };
+
+ gta04_led1: green_aux@1 {
+ label = "gta04:green:aux";
+ reg = <0x1>;
+ };
+
+ gta04_led3: red_power@3 {
+ label = "gta04:red:power";
+ reg = <0x3>;
+ linux,default-trigger = "default-on";
+ };
+
+ gta04_led4: green_power@4 {
+ label = "gta04:green:power";
+ reg = <0x4>;
+ };
+
+ wifi_reset: wifi_reset@6 {
+ reg = <0x6>;
+ compatible = "gpio";
+ };
+ };
+
+ /* compass aka magnetometer */
+ hmc5843@1e {
+ compatible = "honeywell,hmc5883l";
+ reg = <0x1e>;
+ };
+
+ /* touchscreen */
+ tsc2007@48 {
+ compatible = "ti,tsc2007";
+ reg = <0x48>;
+ interrupt-parent = <&gpio6>;
+ interrupts = <0 IRQ_TYPE_EDGE_FALLING>;
+ gpios = <&gpio6 0 GPIO_ACTIVE_LOW>;
+ ti,x-plate-ohms = <600>;
+ };
+};
+
+&i2c3 {
+ clock-frequency = <100000>;
+};
+
+&usb_otg_hs {
+ interface-type = <0>;
+ usb-phy = <&usb2_phy>;
+ phys = <&usb2_phy>;
+ phy-names = "usb2-phy";
+ mode = <3>;
+ power = <50>;
+};
+
+&usbhshost {
+ port2-mode = "ehci-phy";
+};
+
+&usbhsehci {
+ phys = <0 &hsusb2_phy>;
+};
+
+&mmc1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc1_pins>;
+ vmmc-supply = <&vmmc1>;
+ bus-width = <4>;
+ ti,non-removable;
+};
+
+&mmc2 {
+ vmmc-supply = <&vaux4>;
+ bus-width = <4>;
+ ti,non-removable;
+};
+
+&mmc3 {
+ status = "disabled";
+};
+
+&uart1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart1_pins>;
+};
+
+&uart2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart2_pins>;
+};
+
+&uart3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart3_pins>;
+};
+
+&charger {
+ bb_uvolt = <3200000>;
+ bb_uamp = <150>;
+};
+
+/* spare */
+&vaux1 {
+ regulator-min-microvolt = <2500000>;
+ regulator-max-microvolt = <3000000>;
+};
+
+/* sensors */
+&vaux2 {
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ regulator-always-on;
+};
+
+/* camera */
+&vaux3 {
+ regulator-min-microvolt = <2500000>;
+ regulator-max-microvolt = <2500000>;
+};
+
+/* WLAN/BT */
+&vaux4 {
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <3150000>;
+};
+
+/* GPS LNA */
+&vsim {
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <3150000>;
+};
+
+/* Needed to power the DPI pins */
+&vpll2 {
+ regulator-always-on;
+};
+
+&dss {
+ pinctrl-names = "default";
+ pinctrl-0 = < &dss_dpi_pins >;
+
+ status = "okay";
+
+ port {
+ dpi_out: endpoint {
+ remote-endpoint = <&lcd_in>;
+ data-lines = <24>;
+ };
+ };
+};
+
+&gpmc {
+ ranges = <0 0 0x30000000 0x04>; /* CS0: NAND */
+
+ nand@0,0 {
+ reg = <0 0 0>; /* CS0, offset 0 */
+ nand-bus-width = <16>;
+ ti,nand-ecc-opt = "bch8";
+
+ gpmc,sync-clk-ps = <0>;
+ gpmc,cs-on-ns = <0>;
+ gpmc,cs-rd-off-ns = <44>;
+ gpmc,cs-wr-off-ns = <44>;
+ gpmc,adv-on-ns = <6>;
+ gpmc,adv-rd-off-ns = <34>;
+ gpmc,adv-wr-off-ns = <44>;
+ gpmc,we-off-ns = <40>;
+ gpmc,oe-off-ns = <54>;
+ gpmc,access-ns = <64>;
+ gpmc,rd-cycle-ns = <82>;
+ gpmc,wr-cycle-ns = <82>;
+ gpmc,wr-access-ns = <40>;
+ gpmc,wr-data-mux-bus-ns = <0>;
+ gpmc,device-width = <2>;
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ x-loader@0 {
+ label = "X-Loader";
+ reg = <0 0x80000>;
+ };
+
+ bootloaders@80000 {
+ label = "U-Boot";
+ reg = <0x80000 0x1e0000>;
+ };
+
+ bootloaders_env@260000 {
+ label = "U-Boot Env";
+ reg = <0x260000 0x20000>;
+ };
+
+ kernel@280000 {
+ label = "Kernel";
+ reg = <0x280000 0x400000>;
+ };
+
+ filesystem@680000 {
+ label = "File System";
+ reg = <0x680000 0xf980000>;
+ };
+ };
+};
--- /dev/null
+/*
+ * Copyright (C) 2014 H. Nikolaus Schaller <hns@goldelico.com>
+ *
+ * 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 "omap3-gta04.dtsi"
+
+/ {
+ model = "Goldelico GTA04A3";
+};
+
+&i2c2 {
+
+ /* alternate accelerometer that might be installed on some GTA04A3 boards */
+ lis302@1d {
+ compatible = "st,lis331dlh", "st,lis3lv02d";
+ reg = <0x1d>;
+ interrupt-parent = <&gpio3>;
+ interrupts = <18 (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_EDGE_RISING)>;
+ Vdd-supply = <&vaux2>;
+ Vdd_IO-supply = <&vaux2>;
+
+ st,click-single-x;
+ st,click-single-y;
+ st,click-single-z;
+ st,click-thresh-x = <8>;
+ st,click-thresh-y = <8>;
+ st,click-thresh-z = <10>;
+ st,click-click-time-limit = <9>;
+ st,click-latency = <50>;
+ st,irq1-click;
+ st,wakeup-x-lo;
+ st,wakeup-x-hi;
+ st,wakeup-y-lo;
+ st,wakeup-y-hi;
+ st,wakeup-z-lo;
+ st,wakeup-z-hi;
+ st,min-limit-x = <32>;
+ st,min-limit-y = <3>;
+ st,min-limit-z = <3>;
+ st,max-limit-x = <3>;
+ st,max-limit-y = <32>;
+ st,max-limit-z = <32>;
+ };
+};
--- /dev/null
+/*
+ * Copyright (C) 2014 Marek Belisko <marek@goldelico.com>
+ *
+ * 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 "omap3-gta04.dtsi"
+
+/ {
+ model = "Goldelico GTA04A4";
+};
--- /dev/null
+/*
+ * Copyright (C) 2014 H. Nikolaus Schaller <hns@goldelico.com>
+ *
+ * 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 "omap3-gta04.dtsi"
+
+/ {
+ model = "Goldelico GTA04A5";
+
+ sound {
+ ti,jack-det-gpio = <&twl_gpio 2 0>; /* GTA04A5 only */
+ };
+};
--- /dev/null
+/*
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ * Copyright (C) 2014 Stefan Roese <sr@denx.de>
+ *
+ * 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 "omap3-tao3530.dtsi"
+
+/ {
+ gpio_poweroff {
+ pinctrl-names = "default";
+ pinctrl-0 = <&poweroff_pins>;
+
+ compatible = "gpio-poweroff";
+ gpios = <&gpio6 8 GPIO_ACTIVE_LOW>; /* GPIO 168 */
+ };
+};
+
+&omap3_pmx_core {
+ sound2_pins: pinmux_sound2_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x209e, PIN_OUTPUT | MUX_MODE4) /* gpmc_d8 gpio_44 */
+ >;
+ };
+
+ led_blue_pins: pinmux_led_blue_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x2110, PIN_OUTPUT | MUX_MODE4) /* cam_xclka gpio_96, LED blue */
+ >;
+ };
+
+ led_green_pins: pinmux_led_green_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x2126, PIN_OUTPUT | MUX_MODE4) /* cam_d8 gpio_107, LED green */
+ >;
+ };
+
+ led_red_pins: pinmux_led_red_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x212e, PIN_OUTPUT_PULLUP | MUX_MODE4) /* cam_xclkb gpio_111, LED red */
+ >;
+ };
+
+ poweroff_pins: pinmux_poweroff_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x21be, PIN_OUTPUT_PULLUP | MUX_MODE4) /* i2c2_scl gpio_168 */
+ >;
+ };
+
+ powerdown_input_pins: pinmux_powerdown_input_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x21c0, PIN_INPUT_PULLUP | MUX_MODE4) /* i2c2_sda gpio_183 */
+ >;
+ };
+
+ fpga_boot0_pins: fpga_boot0_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x211a, PIN_INPUT | MUX_MODE4) /* cam_d2 gpio_101 */
+ OMAP3_CORE1_IOPAD(0x211c, PIN_OUTPUT | MUX_MODE4) /* cam_d3 gpio_102 */
+ OMAP3_CORE1_IOPAD(0x211e, PIN_OUTPUT | MUX_MODE4) /* cam_d4 gpio_103 */
+ OMAP3_CORE1_IOPAD(0x2120, PIN_INPUT_PULLUP | MUX_MODE4) /* cam_d5 gpio_104 */
+ >;
+ };
+
+ fpga_boot1_pins: fpga_boot1_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x20a2, PIN_INPUT | MUX_MODE4) /* gpmc_d10 gpio_46 */
+ OMAP3_CORE1_IOPAD(0x20a4, PIN_OUTPUT | MUX_MODE4) /* gpmc_d11 gpio_47 */
+ OMAP3_CORE1_IOPAD(0x20a6, PIN_OUTPUT | MUX_MODE4) /* gpmc_d12 gpio_48 */
+ OMAP3_CORE1_IOPAD(0x20a8, PIN_INPUT_PULLUP | MUX_MODE4) /* gpmc_d13 gpio_49 */
+ >;
+ };
+};
+
+/* I2C2: mux'ed with GPIO168 which is connected to nKILL_POWER */
+&i2c2 {
+ status = "disabled";
+};
+
+&i2c3 {
+ clock-frequency = <100000>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c3_pins>;
+};
--- /dev/null
+/*
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ * Copyright (C) 2014 Stefan Roese <sr@denx.de>
+ *
+ * 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 "omap3-ha-common.dtsi"
+
+/ {
+ model = "TI OMAP3 HEAD acoustics LCD-baseboard with TAO3530 SOM";
+ compatible = "headacoustics,omap3-ha-lcd", "technexion,omap3-tao3530", "ti,omap34xx", "ti,omap3";
+};
+
+&omap3_pmx_core {
+ pinctrl-names = "default";
+ pinctrl-0 = <
+ &hsusbb2_pins
+ &powerdown_input_pins
+ &fpga_boot0_pins
+ &fpga_boot1_pins
+ &led_blue_pins
+ &led_green_pins
+ &led_red_pins
+ &touchscreen_wake_pins
+ >;
+
+ touchscreen_irq_pins: pinmux_touchscreen_irq_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x2164, PIN_INPUT_PULLUP | MUX_MODE4) /* gpio_136, Touchscreen IRQ */
+ >;
+ };
+
+ touchscreen_wake_pins: pinmux_touchscreen_wake_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x212c, PIN_OUTPUT_PULLUP | MUX_MODE4) /* gpio_110, Touchscreen Wake */
+ >;
+ };
+
+ dss_dpi_pins: pinmux_dss_dpi_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x20d4, PIN_OUTPUT | MUX_MODE0) /* dss_pclk.dss_pclk */
+ OMAP3_CORE1_IOPAD(0x20d6, PIN_OUTPUT | MUX_MODE0) /* dss_hsync.dss_hsync */
+ OMAP3_CORE1_IOPAD(0x20d8, PIN_OUTPUT | MUX_MODE0) /* dss_vsync.dss_vsync */
+ OMAP3_CORE1_IOPAD(0x20da, PIN_OUTPUT | MUX_MODE0) /* dss_acbias.dss_acbias */
+ OMAP3_CORE1_IOPAD(0x20dc, PIN_OUTPUT | MUX_MODE0) /* dss_data0.dss_data0 */
+ OMAP3_CORE1_IOPAD(0x20de, PIN_OUTPUT | MUX_MODE0) /* dss_data1.dss_data1 */
+ OMAP3_CORE1_IOPAD(0x20e0, PIN_OUTPUT | MUX_MODE0) /* dss_data2.dss_data2 */
+ OMAP3_CORE1_IOPAD(0x20e2, PIN_OUTPUT | MUX_MODE0) /* dss_data3.dss_data3 */
+ OMAP3_CORE1_IOPAD(0x20e4, PIN_OUTPUT | MUX_MODE0) /* dss_data4.dss_data4 */
+ OMAP3_CORE1_IOPAD(0x20e6, PIN_OUTPUT | MUX_MODE0) /* dss_data5.dss_data5 */
+ OMAP3_CORE1_IOPAD(0x20e8, PIN_OUTPUT | MUX_MODE0) /* dss_data6.dss_data6 */
+ OMAP3_CORE1_IOPAD(0x20ea, PIN_OUTPUT | MUX_MODE0) /* dss_data7.dss_data7 */
+ OMAP3_CORE1_IOPAD(0x20ec, PIN_OUTPUT | MUX_MODE0) /* dss_data8.dss_data8 */
+ OMAP3_CORE1_IOPAD(0x20ee, PIN_OUTPUT | MUX_MODE0) /* dss_data9.dss_data9 */
+ OMAP3_CORE1_IOPAD(0x20f0, PIN_OUTPUT | MUX_MODE0) /* dss_data10.dss_data10 */
+ OMAP3_CORE1_IOPAD(0x20f2, PIN_OUTPUT | MUX_MODE0) /* dss_data11.dss_data11 */
+ OMAP3_CORE1_IOPAD(0x20f4, PIN_OUTPUT | MUX_MODE0) /* dss_data12.dss_data12 */
+ OMAP3_CORE1_IOPAD(0x20f6, PIN_OUTPUT | MUX_MODE0) /* dss_data13.dss_data13 */
+ OMAP3_CORE1_IOPAD(0x20f8, PIN_OUTPUT | MUX_MODE0) /* dss_data14.dss_data14 */
+ OMAP3_CORE1_IOPAD(0x20fa, PIN_OUTPUT | MUX_MODE0) /* dss_data15.dss_data15 */
+ OMAP3_CORE1_IOPAD(0x20fc, PIN_OUTPUT | MUX_MODE0) /* dss_data16.dss_data16 */
+ OMAP3_CORE1_IOPAD(0x20fe, PIN_OUTPUT | MUX_MODE0) /* dss_data17.dss_data17 */
+ OMAP3_CORE1_IOPAD(0x2100, PIN_OUTPUT | MUX_MODE0) /* dss_data18.dss_data18 */
+ OMAP3_CORE1_IOPAD(0x2102, PIN_OUTPUT | MUX_MODE0) /* dss_data19.dss_data19 */
+ OMAP3_CORE1_IOPAD(0x2104, PIN_OUTPUT | MUX_MODE0) /* dss_data20.dss_data20 */
+ OMAP3_CORE1_IOPAD(0x2106, PIN_OUTPUT | MUX_MODE0) /* dss_data21.dss_data21 */
+ OMAP3_CORE1_IOPAD(0x2108, PIN_OUTPUT | MUX_MODE0) /* dss_data22.dss_data22 */
+ OMAP3_CORE1_IOPAD(0x210a, PIN_OUTPUT | MUX_MODE0) /* dss_data23.dss_data23 */
+ >;
+ };
+
+ lte430_pins: pinmux_lte430_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x2168, PIN_OUTPUT | MUX_MODE4) /* sdmmc2_dat6.gpio_138 */
+ >;
+ };
+
+ backlight_pins: pinmux_backlight_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x216a, PIN_OUTPUT | MUX_MODE4) /* sdmmc2_dat7.gpio_139 */
+ >;
+ };
+};
+
+/* I2C2: mux'ed with GPIO168 which is connected to nKILL_POWER */
+&i2c2 {
+ status = "disabled";
+};
+
+&i2c3 {
+ clock-frequency = <100000>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c3_pins>;
+};
+
+/* Needed to power the DPI pins */
+&vpll2 {
+ regulator-always-on;
+};
+
+&dss {
+ status = "ok";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&dss_dpi_pins>;
+
+ port {
+ dpi_out: endpoint {
+ remote-endpoint = <&lcd_in>;
+ data-lines = <24>;
+ };
+ };
+};
+
+/ {
+ aliases {
+ display0 = &lcd0;
+ };
+
+ lcd0: display@0 {
+ compatible = "panel-dpi";
+ label = "lcd";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <<e430_pins>;
+ enable-gpios = <&gpio5 10 GPIO_ACTIVE_LOW>; /* gpio_138 */
+
+ port {
+ lcd_in: endpoint {
+ remote-endpoint = <&dpi_out>;
+ };
+ };
+
+ panel-timing {
+ clock-frequency = <31250000>;
+ hactive = <800>;
+ vactive = <480>;
+ hfront-porch = <40>;
+ hback-porch = <86>;
+ hsync-len = <1>;
+ vback-porch = <30>;
+ vfront-porch = <13>;
+ vsync-len = <3>;
+
+ hsync-active = <0>;
+ vsync-active = <0>;
+ de-active = <1>;
+ pixelclk-active = <1>;
+ };
+ };
+
+ backlight {
+ compatible = "gpio-backlight";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&backlight_pins>;
+ gpios = <&gpio5 11 GPIO_ACTIVE_HIGH>; /* gpio_139 */
+
+ default-on;
+ };
+};
--- /dev/null
+/*
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ * Copyright (C) 2014 Stefan Roese <sr@denx.de>
+ *
+ * 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 "omap3-ha-common.dtsi"
+
+/ {
+ model = "TI OMAP3 HEAD acoustics baseboard with TAO3530 SOM";
+ compatible = "headacoustics,omap3-ha", "technexion,omap3-tao3530", "ti,omap34xx", "ti,omap3";
+};
+
+&omap3_pmx_core {
+ pinctrl-names = "default";
+ pinctrl-0 = <
+ &hsusbb2_pins
+ &powerdown_input_pins
+ &fpga_boot0_pins
+ &fpga_boot1_pins
+ &led_blue_pins
+ &led_green_pins
+ &led_red_pins
+ >;
+};
};
twl_power: power {
- compatible = "ti,twl4030-power-n900";
+ compatible = "ti,twl4030-power-n900", "ti,twl4030-power-idle-osc-off";
ti,use_poweroff;
};
};
--- /dev/null
+/*
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ * Copyright (C) 2014 Stefan Roese <sr@denx.de>
+ *
+ * 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.
+ */
+/dts-v1/;
+
+#include "omap34xx-hs.dtsi"
+
+/ {
+ cpus {
+ cpu@0 {
+ cpu0-supply = <&vcc>;
+ };
+ };
+
+ memory {
+ device_type = "memory";
+ reg = <0x80000000 0x10000000>; /* 256 MB */
+ };
+
+ /* HS USB Port 2 Power */
+ hsusb2_power: hsusb2_power_reg {
+ compatible = "regulator-fixed";
+ regulator-name = "hsusb2_vbus";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ gpio = <&twl_gpio 18 0>; /* GPIO LEDA */
+ startup-delay-us = <70000>;
+ };
+
+ /* HS USB Host PHY on PORT 2 */
+ hsusb2_phy: hsusb2_phy {
+ compatible = "usb-nop-xceiv";
+ reset-gpios = <&gpio6 2 GPIO_ACTIVE_LOW>; /* gpio_162 */
+ vcc-supply = <&hsusb2_power>;
+ };
+
+ sound {
+ compatible = "ti,omap-twl4030";
+ ti,model = "omap3beagle";
+
+ /* McBSP2 is used for onboard sound, same as on beagle */
+ ti,mcbsp = <&mcbsp2>;
+ ti,codec = <&twl_audio>;
+ };
+
+ /* Regulator to enable/switch the vcc of the Wifi module */
+ mmc2_sdio_poweron: regulator-mmc2-sdio-poweron {
+ compatible = "regulator-fixed";
+ regulator-name = "regulator-mmc2-sdio-poweron";
+ regulator-min-microvolt = <3150000>;
+ regulator-max-microvolt = <3150000>;
+ gpio = <&gpio5 29 GPIO_ACTIVE_LOW>; /* gpio_157 */
+ enable-active-low;
+ startup-delay-us = <10000>;
+ };
+};
+
+&omap3_pmx_core {
+ hsusbb2_pins: pinmux_hsusbb2_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x25f0, PIN_OUTPUT | MUX_MODE3) /* etk_d10.hsusb2_clk */
+ OMAP3_CORE1_IOPAD(0x25f2, PIN_OUTPUT | MUX_MODE3) /* etk_d11.hsusb2_stp */
+ OMAP3_CORE1_IOPAD(0x25f4, PIN_INPUT_PULLDOWN | MUX_MODE3) /* etk_d12.hsusb2_dir */
+ OMAP3_CORE1_IOPAD(0x25f6, PIN_INPUT_PULLDOWN | MUX_MODE3) /* etk_d13.hsusb2_nxt */
+ OMAP3_CORE1_IOPAD(0x25f8, PIN_INPUT_PULLDOWN | MUX_MODE3) /* etk_d14.hsusb2_data0 */
+ OMAP3_CORE1_IOPAD(0x25fa, PIN_INPUT_PULLDOWN | MUX_MODE3) /* etk_d15.hsusb2_data1 */
+ OMAP3_CORE1_IOPAD(0x21d4, PIN_INPUT_PULLDOWN | MUX_MODE3) /* mcspi1_cs3.hsusb2_data2 */
+ OMAP3_CORE1_IOPAD(0x21d6, PIN_INPUT_PULLDOWN | MUX_MODE3) /* mcspi2_clk.hsusb2_data7 */
+ OMAP3_CORE1_IOPAD(0x21d8, PIN_INPUT_PULLDOWN | MUX_MODE3) /* mcspi2_simo.hsusb2_data4 */
+ OMAP3_CORE1_IOPAD(0x21da, PIN_INPUT_PULLDOWN | MUX_MODE3) /* mcspi2_somi.hsusb2_data5 */
+ OMAP3_CORE1_IOPAD(0x21dc, PIN_INPUT_PULLDOWN | MUX_MODE3) /* mcspi2_cs0.hsusb2_data6 */
+ OMAP3_CORE1_IOPAD(0x21de, PIN_INPUT_PULLDOWN | MUX_MODE3) /* mcspi2_cs1.hsusb2_data3 */
+ >;
+ };
+
+ mmc1_pins: pinmux_mmc1_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x2144, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_clk.sdmmc1_clk */
+ OMAP3_CORE1_IOPAD(0x2146, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_cmd.sdmmc1_cmd */
+ OMAP3_CORE1_IOPAD(0x2148, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat0.sdmmc1_dat0 */
+ OMAP3_CORE1_IOPAD(0x214a, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat1.sdmmc1_dat1 */
+ OMAP3_CORE1_IOPAD(0x214c, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat2.sdmmc1_dat2 */
+ OMAP3_CORE1_IOPAD(0x214e, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat3.sdmmc1_dat3 */
+ OMAP3_CORE1_IOPAD(0x2150, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat4.sdmmc1_dat4 */
+ OMAP3_CORE1_IOPAD(0x2152, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat5.sdmmc1_dat5 */
+ OMAP3_CORE1_IOPAD(0x2154, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat6.sdmmc1_dat6 */
+ OMAP3_CORE1_IOPAD(0x2156, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc1_dat7.sdmmc1_dat7 */
+ >;
+ };
+
+ mmc2_pins: pinmux_mmc2_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x2158, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_clk.sdmmc2_clk */
+ OMAP3_CORE1_IOPAD(0x215a, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_cmd.sdmmc2_cmd */
+ OMAP3_CORE1_IOPAD(0x215c, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat0.sdmmc2_dat0 */
+ OMAP3_CORE1_IOPAD(0x215e, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat1.sdmmc2_dat1 */
+ OMAP3_CORE1_IOPAD(0x2160, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat2.sdmmc2_dat2 */
+ OMAP3_CORE1_IOPAD(0x2162, PIN_INPUT_PULLUP | MUX_MODE0) /* sdmmc2_dat3.sdmmc2_dat3 */
+ >;
+ };
+
+ /* wlan GPIO output for WLAN_EN */
+ wlan_gpio: pinmux_wlan_gpio {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x218e, PIN_OUTPUT | MUX_MODE4) /* mcbsp1_fsr gpio_157 */
+ >;
+ };
+
+ uart3_pins: pinmux_uart3_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x219e, PIN_INPUT | PIN_OFF_WAKEUPENABLE | MUX_MODE0) /* uart3_rx_irrx.uart3_rx_irrx */
+ OMAP3_CORE1_IOPAD(0x21a0, PIN_OUTPUT | MUX_MODE0) /* uart3_tx_irtx.uart3_tx_irtx */
+ >;
+ };
+
+ i2c3_pins: pinmux_i2c3_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x21c2, PIN_INPUT_PULLUP | MUX_MODE0) /* i2c3_scl.i2c3_scl */
+ OMAP3_CORE1_IOPAD(0x21c4, PIN_INPUT_PULLUP | MUX_MODE0) /* i2c3_sda.i2c3_sda */
+ >;
+ };
+
+ mcspi1_pins: pinmux_mcspi1_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x21c8, PIN_INPUT | MUX_MODE0) /* mcspi1_clk.mcspi1_clk */
+ OMAP3_CORE1_IOPAD(0x21ca, PIN_OUTPUT | MUX_MODE0) /* mcspi1_simo.mcspi1_simo */
+ OMAP3_CORE1_IOPAD(0x21cc, PIN_INPUT_PULLUP | MUX_MODE0) /* mcspi1_somi.mcspi1_somi */
+ OMAP3_CORE1_IOPAD(0x21ce, PIN_OUTPUT | MUX_MODE0) /* mcspi1_cs0.mcspi1_cs0 */
+ >;
+ };
+
+ mcspi3_pins: pinmux_mcspi3_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x25dc, PIN_OUTPUT | MUX_MODE1) /* etk_d0.mcspi3_simo gpio14 INPUT | MODE1 */
+ OMAP3_CORE1_IOPAD(0x25de, PIN_INPUT_PULLUP | MUX_MODE1) /* etk_d1.mcspi3_somi gpio15 INPUT | MODE1 */
+ OMAP3_CORE1_IOPAD(0x25e0, PIN_OUTPUT | MUX_MODE1) /* etk_d2.mcspi3_cs0 gpio16 INPUT | MODE1 */
+ OMAP3_CORE1_IOPAD(0x25e2, PIN_INPUT | MUX_MODE1) /* etk_d3.mcspi3_clk gpio17 INPUT | MODE1 */
+ >;
+ };
+
+ mcbsp3_pins: pinmux_mcbsp3_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x216c, PIN_OUTPUT | MUX_MODE0) /* mcbsp3_dx.uart2_cts */
+ OMAP3_CORE1_IOPAD(0x216e, PIN_INPUT | MUX_MODE0) /* mcbsp3_dr.uart2_rts */
+ OMAP3_CORE1_IOPAD(0x2170, PIN_INPUT | MUX_MODE0) /* mcbsp3_clk.uart2_tx */
+ OMAP3_CORE1_IOPAD(0x2172, PIN_INPUT | MUX_MODE0) /* mcbsp3_fsx.uart2_rx */
+ >;
+ };
+};
+
+/* McBSP1: mux'ed with GPIO158 as clock for HA-DSP */
+&mcbsp1 {
+ status = "disabled";
+};
+
+&mcbsp2 {
+ status = "okay";
+};
+
+&i2c1 {
+ clock-frequency = <2600000>;
+
+ twl: twl@48 {
+ reg = <0x48>;
+ interrupts = <7>; /* SYS_NIRQ cascaded to intc */
+ interrupt-parent = <&intc>;
+
+ twl_audio: audio {
+ compatible = "ti,twl4030-audio";
+ codec {
+ };
+ };
+ };
+};
+
+&i2c3 {
+ clock-frequency = <100000>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c3_pins>;
+};
+
+&mcspi1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mcspi1_pins>;
+
+ spidev@0 {
+ compatible = "spidev";
+ spi-max-frequency = <48000000>;
+ reg = <0>;
+ spi-cpha;
+ };
+};
+
+&mcspi3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mcspi3_pins>;
+
+ spidev@0 {
+ compatible = "spidev";
+ spi-max-frequency = <48000000>;
+ reg = <0>;
+ spi-cpha;
+ };
+};
+
+#include "twl4030.dtsi"
+#include "twl4030_omap3.dtsi"
+
+&mmc1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc1_pins>;
+ vmmc-supply = <&vmmc1>;
+ vmmc_aux-supply = <&vsim>;
+ cd-gpios = <&twl_gpio 0 0>;
+ bus-width = <8>;
+};
+
+// WiFi (Marvell 88W8686) on MMC2/SDIO
+&mmc2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc2_pins>;
+ vmmc-supply = <&mmc2_sdio_poweron>;
+ non-removable;
+ bus-width = <4>;
+ cap-power-off-card;
+};
+
+&mmc3 {
+ status = "disabled";
+};
+
+&usbhshost {
+ port2-mode = "ehci-phy";
+};
+
+&usbhsehci {
+ phys = <0 &hsusb2_phy>;
+};
+
+&twl_gpio {
+ ti,use-leds;
+ /* pullups: BIT(1) */
+ ti,pullups = <0x000002>;
+ /*
+ * pulldowns:
+ * BIT(2), BIT(6), BIT(7), BIT(8), BIT(13)
+ * BIT(15), BIT(16), BIT(17)
+ */
+ ti,pulldowns = <0x03a1c4>;
+};
+
+&uart3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart3_pins>;
+};
+
+&mcbsp3 {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&mcbsp3_pins>;
+};
+
+&gpmc {
+ ranges = <0 0 0x00000000 0x01000000>;
+
+ nand@0,0 {
+ reg = <0 0 0>; /* CS0, offset 0 */
+ nand-bus-width = <16>;
+ gpmc,device-width = <2>; /* GPMC_DEVWIDTH_16BIT */
+ ti,nand-ecc-opt = "sw";
+
+ gpmc,cs-on-ns = <0>;
+ gpmc,cs-rd-off-ns = <36>;
+ gpmc,cs-wr-off-ns = <36>;
+ gpmc,adv-on-ns = <6>;
+ gpmc,adv-rd-off-ns = <24>;
+ gpmc,adv-wr-off-ns = <36>;
+ gpmc,oe-on-ns = <6>;
+ gpmc,oe-off-ns = <48>;
+ gpmc,we-on-ns = <6>;
+ gpmc,we-off-ns = <30>;
+ gpmc,rd-cycle-ns = <72>;
+ gpmc,wr-cycle-ns = <72>;
+ gpmc,access-ns = <54>;
+ gpmc,wr-access-ns = <30>;
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ x-loader@0 {
+ label = "X-Loader";
+ reg = <0 0x80000>;
+ };
+
+ bootloaders@80000 {
+ label = "U-Boot";
+ reg = <0x80000 0x1e0000>;
+ };
+
+ bootloaders_env@260000 {
+ label = "U-Boot Env";
+ reg = <0x260000 0x20000>;
+ };
+
+ kernel@280000 {
+ label = "Kernel";
+ reg = <0x280000 0x400000>;
+ };
+
+ filesystem@680000 {
+ label = "File System";
+ reg = <0x680000 0xf980000>;
+ };
+ };
+};
+
+&usb_otg_hs {
+ interface-type = <0>;
+ usb-phy = <&usb2_phy>;
+ phys = <&usb2_phy>;
+ phy-names = "usb2-phy";
+ mode = <3>;
+ power = <50>;
+};
+
+&vaux2 {
+ regulator-name = "vdd_ehci";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+};
--- /dev/null
+/*
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ * Copyright (C) 2014 Stefan Roese <sr@denx.de>
+ *
+ * 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 "omap3-tao3530.dtsi"
+
+/ {
+ model = "TI OMAP3 Thunder baseboard with TAO3530 SOM";
+ compatible = "technexion,omap3-thunder", "technexion,omap3-tao3530", "ti,omap34xx", "ti,omap3";
+};
+
+&omap3_pmx_core {
+ dss_dpi_pins: pinmux_dss_dpi_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x20d4, PIN_OUTPUT | MUX_MODE0) /* dss_pclk.dss_pclk */
+ OMAP3_CORE1_IOPAD(0x20d6, PIN_OUTPUT | MUX_MODE0) /* dss_hsync.dss_hsync */
+ OMAP3_CORE1_IOPAD(0x20d8, PIN_OUTPUT | MUX_MODE0) /* dss_vsync.dss_vsync */
+ OMAP3_CORE1_IOPAD(0x20da, PIN_OUTPUT | MUX_MODE0) /* dss_acbias.dss_acbias */
+ OMAP3_CORE1_IOPAD(0x20dc, PIN_OUTPUT | MUX_MODE0) /* dss_data0.dss_data0 */
+ OMAP3_CORE1_IOPAD(0x20de, PIN_OUTPUT | MUX_MODE0) /* dss_data1.dss_data1 */
+ OMAP3_CORE1_IOPAD(0x20e0, PIN_OUTPUT | MUX_MODE0) /* dss_data2.dss_data2 */
+ OMAP3_CORE1_IOPAD(0x20e2, PIN_OUTPUT | MUX_MODE0) /* dss_data3.dss_data3 */
+ OMAP3_CORE1_IOPAD(0x20e4, PIN_OUTPUT | MUX_MODE0) /* dss_data4.dss_data4 */
+ OMAP3_CORE1_IOPAD(0x20e6, PIN_OUTPUT | MUX_MODE0) /* dss_data5.dss_data5 */
+ OMAP3_CORE1_IOPAD(0x20e8, PIN_OUTPUT | MUX_MODE0) /* dss_data6.dss_data6 */
+ OMAP3_CORE1_IOPAD(0x20ea, PIN_OUTPUT | MUX_MODE0) /* dss_data7.dss_data7 */
+ OMAP3_CORE1_IOPAD(0x20ec, PIN_OUTPUT | MUX_MODE0) /* dss_data8.dss_data8 */
+ OMAP3_CORE1_IOPAD(0x20ee, PIN_OUTPUT | MUX_MODE0) /* dss_data9.dss_data9 */
+ OMAP3_CORE1_IOPAD(0x20f0, PIN_OUTPUT | MUX_MODE0) /* dss_data10.dss_data10 */
+ OMAP3_CORE1_IOPAD(0x20f2, PIN_OUTPUT | MUX_MODE0) /* dss_data11.dss_data11 */
+ OMAP3_CORE1_IOPAD(0x20f4, PIN_OUTPUT | MUX_MODE0) /* dss_data12.dss_data12 */
+ OMAP3_CORE1_IOPAD(0x20f6, PIN_OUTPUT | MUX_MODE0) /* dss_data13.dss_data13 */
+ OMAP3_CORE1_IOPAD(0x20f8, PIN_OUTPUT | MUX_MODE0) /* dss_data14.dss_data14 */
+ OMAP3_CORE1_IOPAD(0x20fa, PIN_OUTPUT | MUX_MODE0) /* dss_data15.dss_data15 */
+ OMAP3_CORE1_IOPAD(0x20fc, PIN_OUTPUT | MUX_MODE0) /* dss_data16.dss_data16 */
+ OMAP3_CORE1_IOPAD(0x20fe, PIN_OUTPUT | MUX_MODE0) /* dss_data17.dss_data17 */
+ OMAP3_CORE1_IOPAD(0x2100, PIN_OUTPUT | MUX_MODE0) /* dss_data18.dss_data18 */
+ OMAP3_CORE1_IOPAD(0x2102, PIN_OUTPUT | MUX_MODE0) /* dss_data19.dss_data19 */
+ OMAP3_CORE1_IOPAD(0x2104, PIN_OUTPUT | MUX_MODE0) /* dss_data20.dss_data20 */
+ OMAP3_CORE1_IOPAD(0x2106, PIN_OUTPUT | MUX_MODE0) /* dss_data21.dss_data21 */
+ OMAP3_CORE1_IOPAD(0x2108, PIN_OUTPUT | MUX_MODE0) /* dss_data22.dss_data22 */
+ OMAP3_CORE1_IOPAD(0x210a, PIN_OUTPUT | MUX_MODE0) /* dss_data23.dss_data23 */
+ >;
+ };
+
+ lte430_pins: pinmux_lte430_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x2168, PIN_OUTPUT | MUX_MODE4) /* sdmmc2_dat6.gpio_138 */
+ >;
+ };
+
+ backlight_pins: pinmux_backlight_pins {
+ pinctrl-single,pins = <
+ OMAP3_CORE1_IOPAD(0x216a, PIN_OUTPUT | MUX_MODE4) /* sdmmc2_dat7.gpio_139 */
+ >;
+ };
+};
+
+/* Needed to power the DPI pins */
+&vpll2 {
+ regulator-always-on;
+};
+
+&dss {
+ status = "ok";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&dss_dpi_pins>;
+
+ port {
+ dpi_out: endpoint {
+ remote-endpoint = <&lcd_in>;
+ data-lines = <24>;
+ };
+ };
+};
+
+/ {
+ aliases {
+ display0 = &lcd0;
+ };
+
+ lcd0: display@0 {
+ compatible = "samsung,lte430wq-f0c", "panel-dpi";
+ label = "lcd";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <<e430_pins>;
+ enable-gpios = <&gpio5 10 GPIO_ACTIVE_LOW>; /* gpio_138 */
+
+ port {
+ lcd_in: endpoint {
+ remote-endpoint = <&dpi_out>;
+ };
+ };
+
+ panel-timing {
+ clock-frequency = <9000000>;
+ hactive = <480>;
+ vactive = <272>;
+ hfront-porch = <3>;
+ hback-porch = <2>;
+ hsync-len = <42>;
+ vback-porch = <2>;
+ vfront-porch = <3>;
+ vsync-len = <11>;
+
+ hsync-active = <0>;
+ vsync-active = <0>;
+ de-active = <1>;
+ pixelclk-active = <1>;
+ };
+ };
+
+ backlight {
+ compatible = "gpio-backlight";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&backlight_pins>;
+ gpios = <&gpio5 11 GPIO_ACTIVE_HIGH>; /* gpio_139 */
+
+ default-on;
+ };
+};
interrupts = <26>;
ti,mbox-num-users = <2>;
ti,mbox-num-fifos = <2>;
+ mbox_dsp: dsp {
+ ti,mbox-tx = <0 0 0>;
+ ti,mbox-rx = <1 0 0>;
+ };
};
mcspi1: spi@48098000 {
#address-cells = <1>;
#size-cells = <1>;
reg = <1 0 0x08000000>;
- ti,nand-ecc-opt = "ham1";
+ ti,nand-ecc-opt = "sw";
nand-bus-width = <8>;
gpmc,cs-on-ns = <0>;
gpmc,cs-rd-off-ns = <36>;
ti,hwmods = "mailbox";
ti,mbox-num-users = <3>;
ti,mbox-num-fifos = <8>;
+ mbox_ipu: mbox_ipu {
+ ti,mbox-tx = <0 0 0>;
+ ti,mbox-rx = <1 0 0>;
+ };
+ mbox_dsp: mbox_dsp {
+ ti,mbox-tx = <3 0 0>;
+ ti,mbox-rx = <2 0 0>;
+ };
};
timer1: timer@4a318000 {
/*
- * Suppport for CompuLab SBC-T54 with CM-T54
+ * Suppport for CompuLab CM-T54 on SB-T54 baseboard
*/
#include "omap5-cm-t54.dts"
/ {
- model = "CompuLab SBC-T54 with CM-T54";
+ model = "CompuLab CM-T54 on SB-T54";
compatible = "compulab,omap5-sbc-t54", "compulab,omap5-cm-t54", "ti,omap5";
};
};
omap5_pmx_core: pinmux@4a002840 {
- compatible = "ti,omap4-padconf", "pinctrl-single";
+ compatible = "ti,omap5-padconf", "pinctrl-single";
reg = <0x4a002840 0x01b6>;
#address-cells = <1>;
#size-cells = <0>;
+ #interrupt-cells = <1>;
+ interrupt-controller;
pinctrl-single,register-width = <16>;
pinctrl-single,function-mask = <0x7fff>;
};
omap5_pmx_wkup: pinmux@4ae0c840 {
- compatible = "ti,omap4-padconf", "pinctrl-single";
+ compatible = "ti,omap5-padconf", "pinctrl-single";
reg = <0x4ae0c840 0x0038>;
#address-cells = <1>;
#size-cells = <0>;
+ #interrupt-cells = <1>;
+ interrupt-controller;
pinctrl-single,register-width = <16>;
pinctrl-single,function-mask = <0x7fff>;
};
uart1: serial@4806a000 {
compatible = "ti,omap4-uart";
reg = <0x4806a000 0x100>;
- interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts-extended = <&gic GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
ti,hwmods = "uart1";
clock-frequency = <48000000>;
};
uart2: serial@4806c000 {
compatible = "ti,omap4-uart";
reg = <0x4806c000 0x100>;
- interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts-extended = <&gic GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
ti,hwmods = "uart2";
clock-frequency = <48000000>;
};
uart3: serial@48020000 {
compatible = "ti,omap4-uart";
reg = <0x48020000 0x100>;
- interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts-extended = <&gic GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>;
ti,hwmods = "uart3";
clock-frequency = <48000000>;
};
uart4: serial@4806e000 {
compatible = "ti,omap4-uart";
reg = <0x4806e000 0x100>;
- interrupts = <GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
ti,hwmods = "uart4";
clock-frequency = <48000000>;
};
uart5: serial@48066000 {
compatible = "ti,omap4-uart";
reg = <0x48066000 0x100>;
- interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts-extended = <&gic GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
ti,hwmods = "uart5";
clock-frequency = <48000000>;
};
uart6: serial@48068000 {
compatible = "ti,omap4-uart";
reg = <0x48068000 0x100>;
- interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts-extended = <&gic GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>;
ti,hwmods = "uart6";
clock-frequency = <48000000>;
};
ti,hwmods = "mailbox";
ti,mbox-num-users = <3>;
ti,mbox-num-fifos = <8>;
+ mbox_ipu: mbox_ipu {
+ ti,mbox-tx = <0 0 0>;
+ ti,mbox-rx = <1 0 0>;
+ };
+ mbox_dsp: mbox_dsp {
+ ti,mbox-tx = <3 0 0>;
+ ti,mbox-rx = <2 0 0>;
+ };
};
timer1: timer@4ae18000 {
clock-names = "fck";
};
+ rfbi: encoder@58002000 {
+ compatible = "ti,omap5-rfbi";
+ reg = <0x58002000 0x100>;
+ status = "disabled";
+ ti,hwmods = "dss_rfbi";
+ clocks = <&dss_dss_clk>, <&l3_iclk_div>;
+ clock-names = "fck", "ick";
+ };
+
dsi1: encoder@58004000 {
compatible = "ti,omap5-dsi";
reg = <0x58004000 0x200>,
l3_iclk_div: l3_iclk_div {
#clock-cells = <0>;
- compatible = "fixed-factor-clock";
+ compatible = "ti,divider-clock";
+ ti,max-div = <2>;
+ ti,bit-shift = <4>;
+ reg = <0x100>;
clocks = <&dpll_core_h12x2_ck>;
- clock-mult = <1>;
- clock-div = <1>;
+ ti,index-power-of-two;
};
gpu_l3_iclk: gpu_l3_iclk {
l4_root_clk_div: l4_root_clk_div {
#clock-cells = <0>;
- compatible = "fixed-factor-clock";
+ compatible = "ti,divider-clock";
+ ti,max-div = <2>;
+ ti,bit-shift = <8>;
+ reg = <0x100>;
clocks = <&l3_iclk_div>;
- clock-mult = <1>;
- clock-div = <1>;
+ ti,index-power-of-two;
};
slimbus1_slimbus_clk: slimbus1_slimbus_clk {
regulator-always-on;
};
- clk32kg: regulator-clk32kg {
- compatible = "ti,twl6030-clk32kg";
- };
-
twl_usb_comparator: usb-comparator {
compatible = "ti,twl6030-usb";
interrupts = <4>, <10>;
"mcr p15, 0, r0, c1, c0, 0 @ set SCTLR \n\t" \
"isb \n\t" \
"bl v7_flush_dcache_"__stringify(level)" \n\t" \
- "clrex \n\t" \
"mrc p15, 0, r0, c1, c0, 1 @ get ACTLR \n\t" \
"bic r0, r0, #(1 << 6) @ disable local coherency \n\t" \
"mcr p15, 0, r0, c1, c0, 1 @ set ACTLR \n\t" \
#define ARM_CPU_PART_CORTEX_A12 0x4100c0d0
#define ARM_CPU_PART_CORTEX_A17 0x4100c0e0
#define ARM_CPU_PART_CORTEX_A15 0x4100c0f0
+#define ARM_CPU_PART_MASK 0xff00fff0
#define ARM_CPU_XSCALE_ARCH_MASK 0xe000
#define ARM_CPU_XSCALE_ARCH_V1 0x2000
*/
static inline unsigned int __attribute_const__ read_cpuid_part(void)
{
- return read_cpuid_id() & 0xff00fff0;
+ return read_cpuid_id() & ARM_CPU_PART_MASK;
}
static inline unsigned int __attribute_const__ __deprecated read_cpuid_part_number(void)
#define R_ARM_ABS32 2
#define R_ARM_CALL 28
#define R_ARM_JUMP24 29
+#define R_ARM_TARGET1 38
#define R_ARM_V4BX 40
#define R_ARM_PREL31 42
#define R_ARM_MOVW_ABS_NC 43
#include <linux/cpumask.h>
#include <linux/err.h>
+#include <asm/cpu.h>
#include <asm/cputype.h>
/*
#endif
}
+/**
+ * smp_cpuid_part() - return part id for a given cpu
+ * @cpu: logical cpu id.
+ *
+ * Return: part id of logical cpu passed as argument.
+ */
+static inline unsigned int smp_cpuid_part(int cpu)
+{
+ struct cpuinfo_arm *cpu_info = &per_cpu(cpu_data, cpu);
+
+ return is_smp() ? cpu_info->cpuid & ARM_CPU_PART_MASK :
+ read_cpuid_part();
+}
+
/* all SMP configurations have the extended CPUID registers */
#ifndef CONFIG_MMU
#define tlb_ops_need_broadcast() 0
#endif
.endif
msr spsr_cxsf, \rpsr
-#if defined(CONFIG_CPU_V6)
- ldr r0, [sp]
- strex r1, r2, [sp] @ clear the exclusive monitor
- ldmib sp, {r1 - pc}^ @ load r1 - pc, cpsr
-#elif defined(CONFIG_CPU_32v6K)
- clrex @ clear the exclusive monitor
- ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
-#else
- ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
+#if defined(CONFIG_CPU_V6) || defined(CONFIG_CPU_32v6K)
+ @ We must avoid clrex due to Cortex-A15 erratum #830321
+ sub r0, sp, #4 @ uninhabited address
+ strex r1, r2, [r0] @ clear the exclusive monitor
#endif
+ ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
.endm
.macro restore_user_regs, fast = 0, offset = 0
ldr r1, [sp, #\offset + S_PSR] @ get calling cpsr
ldr lr, [sp, #\offset + S_PC]! @ get pc
msr spsr_cxsf, r1 @ save in spsr_svc
-#if defined(CONFIG_CPU_V6)
+#if defined(CONFIG_CPU_V6) || defined(CONFIG_CPU_32v6K)
+ @ We must avoid clrex due to Cortex-A15 erratum #830321
strex r1, r2, [sp] @ clear the exclusive monitor
-#elif defined(CONFIG_CPU_32v6K)
- clrex @ clear the exclusive monitor
#endif
.if \fast
ldmdb sp, {r1 - lr}^ @ get calling r1 - lr
.endif
ldr lr, [sp, #S_SP] @ top of the stack
ldrd r0, r1, [sp, #S_LR] @ calling lr and pc
- clrex @ clear the exclusive monitor
+
+ @ We must avoid clrex due to Cortex-A15 erratum #830321
+ strex r2, r1, [sp, #S_LR] @ clear the exclusive monitor
+
stmdb lr!, {r0, r1, \rpsr} @ calling lr and rfe context
ldmia sp, {r0 - r12}
mov sp, lr
.endm
#else /* ifdef CONFIG_CPU_V7M */
.macro restore_user_regs, fast = 0, offset = 0
- clrex @ clear the exclusive monitor
mov r2, sp
load_user_sp_lr r2, r3, \offset + S_SP @ calling sp, lr
ldr r1, [sp, #\offset + S_PSR] @ get calling cpsr
ldr lr, [sp, #\offset + S_PC] @ get pc
add sp, sp, #\offset + S_SP
msr spsr_cxsf, r1 @ save in spsr_svc
+
+ @ We must avoid clrex due to Cortex-A15 erratum #830321
+ strex r1, r2, [sp] @ clear the exclusive monitor
+
.if \fast
ldmdb sp, {r1 - r12} @ get calling r1 - r12
.else
break;
case R_ARM_ABS32:
+ case R_ARM_TARGET1:
*(u32 *)loc += sym->st_value;
break;
ifeq ($(CONFIG_ARCH_BRCMSTB),y)
obj-y += brcmstb.o
-obj-$(CONFIG_SMP) += headsmp-brcmstb.o platsmp-brcmstb.o
endif
+++ /dev/null
-/*
- * Copyright (C) 2013-2014 Broadcom Corporation
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#ifndef __BRCMSTB_H__
-#define __BRCMSTB_H__
-
-void brcmstb_secondary_startup(void);
-
-#endif /* __BRCMSTB_H__ */
+++ /dev/null
-/*
- * SMP boot code for secondary CPUs
- * Based on arch/arm/mach-tegra/headsmp.S
- *
- * Copyright (C) 2010 NVIDIA, Inc.
- * Copyright (C) 2013-2014 Broadcom Corporation
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <asm/assembler.h>
-#include <linux/linkage.h>
-#include <linux/init.h>
-
- .section ".text.head", "ax"
-
-ENTRY(brcmstb_secondary_startup)
- /*
- * Ensure CPU is in a sane state by disabling all IRQs and switching
- * into SVC mode.
- */
- setmode PSR_I_BIT | PSR_F_BIT | SVC_MODE, r0
-
- bl v7_invalidate_l1
- b secondary_startup
-ENDPROC(brcmstb_secondary_startup)
+++ /dev/null
-/*
- * Broadcom STB CPU SMP and hotplug support for ARM
- *
- * Copyright (C) 2013-2014 Broadcom Corporation
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/of_address.h>
-#include <linux/of_platform.h>
-#include <linux/printk.h>
-#include <linux/regmap.h>
-#include <linux/smp.h>
-#include <linux/mfd/syscon.h>
-#include <linux/spinlock.h>
-
-#include <asm/cacheflush.h>
-#include <asm/cp15.h>
-#include <asm/mach-types.h>
-#include <asm/smp_plat.h>
-
-#include "brcmstb.h"
-
-enum {
- ZONE_MAN_CLKEN_MASK = BIT(0),
- ZONE_MAN_RESET_CNTL_MASK = BIT(1),
- ZONE_MAN_MEM_PWR_MASK = BIT(4),
- ZONE_RESERVED_1_MASK = BIT(5),
- ZONE_MAN_ISO_CNTL_MASK = BIT(6),
- ZONE_MANUAL_CONTROL_MASK = BIT(7),
- ZONE_PWR_DN_REQ_MASK = BIT(9),
- ZONE_PWR_UP_REQ_MASK = BIT(10),
- ZONE_BLK_RST_ASSERT_MASK = BIT(12),
- ZONE_PWR_OFF_STATE_MASK = BIT(25),
- ZONE_PWR_ON_STATE_MASK = BIT(26),
- ZONE_DPG_PWR_STATE_MASK = BIT(28),
- ZONE_MEM_PWR_STATE_MASK = BIT(29),
- ZONE_RESET_STATE_MASK = BIT(31),
- CPU0_PWR_ZONE_CTRL_REG = 1,
- CPU_RESET_CONFIG_REG = 2,
-};
-
-static void __iomem *cpubiuctrl_block;
-static void __iomem *hif_cont_block;
-static u32 cpu0_pwr_zone_ctrl_reg;
-static u32 cpu_rst_cfg_reg;
-static u32 hif_cont_reg;
-
-#ifdef CONFIG_HOTPLUG_CPU
-static DEFINE_PER_CPU_ALIGNED(int, per_cpu_sw_state);
-
-static int per_cpu_sw_state_rd(u32 cpu)
-{
- sync_cache_r(SHIFT_PERCPU_PTR(&per_cpu_sw_state, per_cpu_offset(cpu)));
- return per_cpu(per_cpu_sw_state, cpu);
-}
-
-static void per_cpu_sw_state_wr(u32 cpu, int val)
-{
- per_cpu(per_cpu_sw_state, cpu) = val;
- dmb();
- sync_cache_w(SHIFT_PERCPU_PTR(&per_cpu_sw_state, per_cpu_offset(cpu)));
- dsb_sev();
-}
-#else
-static inline void per_cpu_sw_state_wr(u32 cpu, int val) { }
-#endif
-
-static void __iomem *pwr_ctrl_get_base(u32 cpu)
-{
- void __iomem *base = cpubiuctrl_block + cpu0_pwr_zone_ctrl_reg;
- base += (cpu_logical_map(cpu) * 4);
- return base;
-}
-
-static u32 pwr_ctrl_rd(u32 cpu)
-{
- void __iomem *base = pwr_ctrl_get_base(cpu);
- return readl_relaxed(base);
-}
-
-static void pwr_ctrl_wr(u32 cpu, u32 val)
-{
- void __iomem *base = pwr_ctrl_get_base(cpu);
- writel(val, base);
-}
-
-static void cpu_rst_cfg_set(u32 cpu, int set)
-{
- u32 val;
- val = readl_relaxed(cpubiuctrl_block + cpu_rst_cfg_reg);
- if (set)
- val |= BIT(cpu_logical_map(cpu));
- else
- val &= ~BIT(cpu_logical_map(cpu));
- writel_relaxed(val, cpubiuctrl_block + cpu_rst_cfg_reg);
-}
-
-static void cpu_set_boot_addr(u32 cpu, unsigned long boot_addr)
-{
- const int reg_ofs = cpu_logical_map(cpu) * 8;
- writel_relaxed(0, hif_cont_block + hif_cont_reg + reg_ofs);
- writel_relaxed(boot_addr, hif_cont_block + hif_cont_reg + 4 + reg_ofs);
-}
-
-static void brcmstb_cpu_boot(u32 cpu)
-{
- pr_info("SMP: Booting CPU%d...\n", cpu);
-
- /*
- * set the reset vector to point to the secondary_startup
- * routine
- */
- cpu_set_boot_addr(cpu, virt_to_phys(brcmstb_secondary_startup));
-
- /* unhalt the cpu */
- cpu_rst_cfg_set(cpu, 0);
-}
-
-static void brcmstb_cpu_power_on(u32 cpu)
-{
- /*
- * The secondary cores power was cut, so we must go through
- * power-on initialization.
- */
- u32 tmp;
-
- pr_info("SMP: Powering up CPU%d...\n", cpu);
-
- /* Request zone power up */
- pwr_ctrl_wr(cpu, ZONE_PWR_UP_REQ_MASK);
-
- /* Wait for the power up FSM to complete */
- do {
- tmp = pwr_ctrl_rd(cpu);
- } while (!(tmp & ZONE_PWR_ON_STATE_MASK));
-
- per_cpu_sw_state_wr(cpu, 1);
-}
-
-static int brcmstb_cpu_get_power_state(u32 cpu)
-{
- int tmp = pwr_ctrl_rd(cpu);
- return (tmp & ZONE_RESET_STATE_MASK) ? 0 : 1;
-}
-
-#ifdef CONFIG_HOTPLUG_CPU
-
-static void brcmstb_cpu_die(u32 cpu)
-{
- v7_exit_coherency_flush(all);
-
- /* Prevent all interrupts from reaching this CPU. */
- arch_local_irq_disable();
-
- /*
- * Final full barrier to ensure everything before this instruction has
- * quiesced.
- */
- isb();
- dsb();
-
- per_cpu_sw_state_wr(cpu, 0);
-
- /* Sit and wait to die */
- wfi();
-
- /* We should never get here... */
- panic("Spurious interrupt on CPU %d received!\n", cpu);
-}
-
-static int brcmstb_cpu_kill(u32 cpu)
-{
- u32 tmp;
-
- pr_info("SMP: Powering down CPU%d...\n", cpu);
-
- while (per_cpu_sw_state_rd(cpu))
- ;
-
- /* Program zone reset */
- pwr_ctrl_wr(cpu, ZONE_RESET_STATE_MASK | ZONE_BLK_RST_ASSERT_MASK |
- ZONE_PWR_DN_REQ_MASK);
-
- /* Verify zone reset */
- tmp = pwr_ctrl_rd(cpu);
- if (!(tmp & ZONE_RESET_STATE_MASK))
- pr_err("%s: Zone reset bit for CPU %d not asserted!\n",
- __func__, cpu);
-
- /* Wait for power down */
- do {
- tmp = pwr_ctrl_rd(cpu);
- } while (!(tmp & ZONE_PWR_OFF_STATE_MASK));
-
- /* Settle-time from Broadcom-internal DVT reference code */
- udelay(7);
-
- /* Assert reset on the CPU */
- cpu_rst_cfg_set(cpu, 1);
-
- return 1;
-}
-
-#endif /* CONFIG_HOTPLUG_CPU */
-
-static int __init setup_hifcpubiuctrl_regs(struct device_node *np)
-{
- int rc = 0;
- char *name;
- struct device_node *syscon_np = NULL;
-
- name = "syscon-cpu";
-
- syscon_np = of_parse_phandle(np, name, 0);
- if (!syscon_np) {
- pr_err("can't find phandle %s\n", name);
- rc = -EINVAL;
- goto cleanup;
- }
-
- cpubiuctrl_block = of_iomap(syscon_np, 0);
- if (!cpubiuctrl_block) {
- pr_err("iomap failed for cpubiuctrl_block\n");
- rc = -EINVAL;
- goto cleanup;
- }
-
- rc = of_property_read_u32_index(np, name, CPU0_PWR_ZONE_CTRL_REG,
- &cpu0_pwr_zone_ctrl_reg);
- if (rc) {
- pr_err("failed to read 1st entry from %s property (%d)\n", name,
- rc);
- rc = -EINVAL;
- goto cleanup;
- }
-
- rc = of_property_read_u32_index(np, name, CPU_RESET_CONFIG_REG,
- &cpu_rst_cfg_reg);
- if (rc) {
- pr_err("failed to read 2nd entry from %s property (%d)\n", name,
- rc);
- rc = -EINVAL;
- goto cleanup;
- }
-
-cleanup:
- if (syscon_np)
- of_node_put(syscon_np);
-
- return rc;
-}
-
-static int __init setup_hifcont_regs(struct device_node *np)
-{
- int rc = 0;
- char *name;
- struct device_node *syscon_np = NULL;
-
- name = "syscon-cont";
-
- syscon_np = of_parse_phandle(np, name, 0);
- if (!syscon_np) {
- pr_err("can't find phandle %s\n", name);
- rc = -EINVAL;
- goto cleanup;
- }
-
- hif_cont_block = of_iomap(syscon_np, 0);
- if (!hif_cont_block) {
- pr_err("iomap failed for hif_cont_block\n");
- rc = -EINVAL;
- goto cleanup;
- }
-
- /* offset is at top of hif_cont_block */
- hif_cont_reg = 0;
-
-cleanup:
- if (syscon_np)
- of_node_put(syscon_np);
-
- return rc;
-}
-
-static void __init brcmstb_cpu_ctrl_setup(unsigned int max_cpus)
-{
- int rc;
- struct device_node *np;
- char *name;
-
- name = "brcm,brcmstb-smpboot";
- np = of_find_compatible_node(NULL, NULL, name);
- if (!np) {
- pr_err("can't find compatible node %s\n", name);
- return;
- }
-
- rc = setup_hifcpubiuctrl_regs(np);
- if (rc)
- return;
-
- rc = setup_hifcont_regs(np);
- if (rc)
- return;
-}
-
-static DEFINE_SPINLOCK(boot_lock);
-
-static void brcmstb_secondary_init(unsigned int cpu)
-{
- /*
- * Synchronise with the boot thread.
- */
- spin_lock(&boot_lock);
- spin_unlock(&boot_lock);
-}
-
-static int brcmstb_boot_secondary(unsigned int cpu, struct task_struct *idle)
-{
- /*
- * set synchronisation state between this boot processor
- * and the secondary one
- */
- spin_lock(&boot_lock);
-
- /* Bring up power to the core if necessary */
- if (brcmstb_cpu_get_power_state(cpu) == 0)
- brcmstb_cpu_power_on(cpu);
-
- brcmstb_cpu_boot(cpu);
-
- /*
- * now the secondary core is starting up let it run its
- * calibrations, then wait for it to finish
- */
- spin_unlock(&boot_lock);
-
- return 0;
-}
-
-static struct smp_operations brcmstb_smp_ops __initdata = {
- .smp_prepare_cpus = brcmstb_cpu_ctrl_setup,
- .smp_secondary_init = brcmstb_secondary_init,
- .smp_boot_secondary = brcmstb_boot_secondary,
-#ifdef CONFIG_HOTPLUG_CPU
- .cpu_kill = brcmstb_cpu_kill,
- .cpu_die = brcmstb_cpu_die,
-#endif
-};
-
-CPU_METHOD_OF_DECLARE(brcmstb_smp, "brcm,brahma-b15", &brcmstb_smp_ops);
"mcr p15, 0, r0, c1, c0, 0 @ set SCTLR\n\t" \
"isb\n\t"\
"bl v7_flush_dcache_"__stringify(level)"\n\t" \
- "clrex\n\t"\
"mrc p15, 0, r0, c1, c0, 1 @ get ACTLR\n\t" \
"bic r0, r0, #(1 << 6) @ disable local coherency\n\t" \
/* Dummy Load of a device register to avoid Erratum 799270 */ \
board_nand_data.nr_parts = nr_parts;
board_nand_data.devsize = nand_type;
- board_nand_data.ecc_opt = OMAP_ECC_HAM1_CODE_HW;
+ board_nand_data.ecc_opt = OMAP_ECC_HAM1_CODE_SW;
gpmc_nand_init(&board_nand_data, gpmc_t);
}
#endif /* CONFIG_MTD_NAND_OMAP2 || CONFIG_MTD_NAND_OMAP2_MODULE */
#ifdef CONFIG_SOC_DRA7XX
static const char *dra74x_boards_compat[] __initconst = {
+ "ti,am5728",
+ "ti,am5726",
"ti,dra742",
"ti,dra7",
NULL,
MACHINE_END
static const char *dra72x_boards_compat[] __initconst = {
+ "ti,am5718",
+ "ti,am5716",
"ti,dra722",
NULL,
};
return 0;
/* legacy platforms support only HAM1 (1-bit Hamming) ECC scheme */
- if (ecc_opt == OMAP_ECC_HAM1_CODE_HW)
+ if (ecc_opt == OMAP_ECC_HAM1_CODE_HW ||
+ ecc_opt == OMAP_ECC_HAM1_CODE_SW)
return 1;
else
return 0;
pr_err("%s: ti,nand-ecc-opt not found\n", __func__);
return -ENODEV;
}
- if (!strcmp(s, "ham1") || !strcmp(s, "sw") ||
- !strcmp(s, "hw") || !strcmp(s, "hw-romcode"))
+
+ if (!strcmp(s, "sw"))
+ gpmc_nand_data->ecc_opt = OMAP_ECC_HAM1_CODE_SW;
+ else if (!strcmp(s, "ham1") ||
+ !strcmp(s, "hw") || !strcmp(s, "hw-romcode"))
gpmc_nand_data->ecc_opt =
OMAP_ECC_HAM1_CODE_HW;
else if (!strcmp(s, "bch4"))
default:
/* Unknown default to latest silicon rev as default*/
- pr_warn("%s: unknown idcode=0x%08x (hawkeye=0x%08x,rev=0x%d)\n",
+ pr_warn("%s: unknown idcode=0x%08x (hawkeye=0x%08x,rev=0x%x)\n",
__func__, idcode, hawkeye, rev);
omap_revision = DRA752_REV_ES1_1;
}
r = clk_get_sys(dev_name(&od->pdev->dev), clk_alias);
if (!IS_ERR(r)) {
- dev_warn(&od->pdev->dev,
+ dev_dbg(&od->pdev->dev,
"alias %s already exists\n", clk_alias);
clk_put(r);
return;
oh->mux->pads_dynamic))) {
omap_hwmod_mux(oh->mux, _HWMOD_STATE_ENABLED);
_reconfigure_io_chain();
+ } else if (oh->flags & HWMOD_FORCE_MSTANDBY) {
+ _reconfigure_io_chain();
}
_add_initiator_dep(oh, mpu_oh);
if (oh->mux && oh->mux->pads_dynamic) {
omap_hwmod_mux(oh->mux, _HWMOD_STATE_IDLE);
_reconfigure_io_chain();
+ } else if (oh->flags & HWMOD_FORCE_MSTANDBY) {
+ _reconfigure_io_chain();
}
oh->_state = _HWMOD_STATE_IDLE;
if (!ois)
return 0;
+ if (ois[0] == NULL) /* Empty list */
+ return 0;
+
if (!linkspace) {
if (_alloc_linkspace(ois)) {
pr_err("omap_hwmod: could not allocate link space\n");
#include "i2c.h"
#include "mmc.h"
#include "wd_timer.h"
+#include "soc.h"
/* Base offset for all DRA7XX interrupts external to MPUSS */
#define DRA7XX_IRQ_GIC_START 32
&dra7xx_l4_per3__usb_otg_ss1,
&dra7xx_l4_per3__usb_otg_ss2,
&dra7xx_l4_per3__usb_otg_ss3,
- &dra7xx_l4_per3__usb_otg_ss4,
&dra7xx_l3_main_1__vcp1,
&dra7xx_l4_per2__vcp1,
&dra7xx_l3_main_1__vcp2,
NULL,
};
+static struct omap_hwmod_ocp_if *dra74x_hwmod_ocp_ifs[] __initdata = {
+ &dra7xx_l4_per3__usb_otg_ss4,
+ NULL,
+};
+
+static struct omap_hwmod_ocp_if *dra72x_hwmod_ocp_ifs[] __initdata = {
+ NULL,
+};
+
int __init dra7xx_hwmod_init(void)
{
+ int ret;
+
omap_hwmod_init();
- return omap_hwmod_register_links(dra7xx_hwmod_ocp_ifs);
+ ret = omap_hwmod_register_links(dra7xx_hwmod_ocp_ifs);
+
+ if (!ret && soc_is_dra74x())
+ return omap_hwmod_register_links(dra74x_hwmod_ocp_ifs);
+ else if (!ret && soc_is_dra72x())
+ return omap_hwmod_register_links(dra72x_hwmod_ocp_ifs);
+
+ return ret;
}
}
}
+
+static void __init omap3_tao3530_legacy_init(void)
+{
+ hsmmc2_internal_input_clk();
+}
#endif /* CONFIG_ARCH_OMAP3 */
#ifdef CONFIG_ARCH_OMAP4
{ "ti,omap3-evm-37xx", omap3_evm_legacy_init, },
{ "ti,omap3-zoom3", omap3_zoom_legacy_init, },
{ "ti,am3517-evm", am3517_evm_legacy_init, },
+ { "technexion,omap3-tao3530", omap3_tao3530_legacy_init, },
#endif
#ifdef CONFIG_ARCH_OMAP4
{ "ti,omap4-sdp", omap4_sdp_legacy_init, },
#define soc_is_omap54xx() 0
#define soc_is_omap543x() 0
#define soc_is_dra7xx() 0
+#define soc_is_dra74x() 0
+#define soc_is_dra72x() 0
#if defined(MULTI_OMAP2)
# if defined(CONFIG_ARCH_OMAP2)
#if defined(CONFIG_SOC_DRA7XX)
#undef soc_is_dra7xx
+#undef soc_is_dra74x
+#undef soc_is_dra72x
#define soc_is_dra7xx() (of_machine_is_compatible("ti,dra7"))
+#define soc_is_dra74x() (of_machine_is_compatible("ti,dra74"))
+#define soc_is_dra72x() (of_machine_is_compatible("ti,dra72"))
#endif
/* Various silicon revisions for omap2 */
static struct clk div4_clks[DIV4_NR] = {
[DIV4_SDH] = SH_CLK_DIV4(&pll1_clk, SDCKCR, 8, 0x0dff, CLK_ENABLE_ON_INIT),
- [DIV4_SD0] = SH_CLK_DIV4(&pll1_clk, SDCKCR, 4, 0x1de0, CLK_ENABLE_ON_INIT),
- [DIV4_SD1] = SH_CLK_DIV4(&pll1_clk, SDCKCR, 0, 0x1de0, CLK_ENABLE_ON_INIT),
+ [DIV4_SD0] = SH_CLK_DIV4(&pll1_clk, SDCKCR, 4, 0x1df0, CLK_ENABLE_ON_INIT),
+ [DIV4_SD1] = SH_CLK_DIV4(&pll1_clk, SDCKCR, 0, 0x1df0, CLK_ENABLE_ON_INIT),
};
/* DIV6 clocks */
static struct clk div4_clks[DIV4_NR] = {
[DIV4_SDH] = SH_CLK_DIV4(&pll1_clk, SDCKCR, 8, 0x0dff, CLK_ENABLE_ON_INIT),
- [DIV4_SD0] = SH_CLK_DIV4(&pll1_clk, SDCKCR, 4, 0x1de0, CLK_ENABLE_ON_INIT),
+ [DIV4_SD0] = SH_CLK_DIV4(&pll1_clk, SDCKCR, 4, 0x1df0, CLK_ENABLE_ON_INIT),
};
/* DIV6 clocks */
CLKDEV_DEV_ID("sh-sci.5", &mstp_clks[MSTP207]), /* SCIFA5 */
CLKDEV_DEV_ID("e6cb0000.serial", &mstp_clks[MSTP207]), /* SCIFA5 */
CLKDEV_DEV_ID("sh-sci.8", &mstp_clks[MSTP206]), /* SCIFB */
- CLKDEV_DEV_ID("0xe6c3000.serial", &mstp_clks[MSTP206]), /* SCIFB */
+ CLKDEV_DEV_ID("e6c3000.serial", &mstp_clks[MSTP206]), /* SCIFB */
CLKDEV_DEV_ID("sh-sci.0", &mstp_clks[MSTP204]), /* SCIFA0 */
CLKDEV_DEV_ID("e6c40000.serial", &mstp_clks[MSTP204]), /* SCIFA0 */
CLKDEV_DEV_ID("sh-sci.1", &mstp_clks[MSTP203]), /* SCIFA1 */
static int ve_init_opp_table(struct device *cpu_dev)
{
- int cluster = topology_physical_package_id(cpu_dev->id);
- int idx, ret = 0, max_opp = info->num_opps[cluster];
- struct ve_spc_opp *opps = info->opps[cluster];
+ int cluster;
+ int idx, ret = 0, max_opp;
+ struct ve_spc_opp *opps;
+
+ cluster = topology_physical_package_id(cpu_dev->id);
+ cluster = cluster < 0 ? 0 : cluster;
+
+ max_opp = info->num_opps[cluster];
+ opps = info->opps[cluster];
for (idx = 0; idx < max_opp; idx++, opps++) {
ret = dev_pm_opp_add(cpu_dev, opps->freq * 1000, opps->u_volt);
spc->hw.init = &init;
spc->cluster = topology_physical_package_id(cpu_dev->id);
+ spc->cluster = spc->cluster < 0 ? 0 : spc->cluster;
+
init.name = dev_name(cpu_dev);
init.ops = &clk_spc_ops;
init.flags = CLK_IS_ROOT | CLK_GET_RATE_NOCACHE;
*/
.align 5
ENTRY(v6_early_abort)
-#ifdef CONFIG_CPU_V6
- sub r1, sp, #4 @ Get unused stack location
- strex r0, r1, [r1] @ Clear the exclusive monitor
-#elif defined(CONFIG_CPU_32v6K)
- clrex
-#endif
mrc p15, 0, r1, c5, c0, 0 @ get FSR
mrc p15, 0, r0, c6, c0, 0 @ get FAR
/*
*/
.align 5
ENTRY(v7_early_abort)
- /*
- * The effect of data aborts on on the exclusive access monitor are
- * UNPREDICTABLE. Do a CLREX to clear the state
- */
- clrex
-
mrc p15, 0, r1, c5, c0, 0 @ get FSR
mrc p15, 0, r0, c6, c0, 0 @ get FAR
);
local_irq_restore(flags);
}
+EXPORT_SYMBOL(flush_icache_range);
void hexagon_clean_dcache_range(unsigned long start, unsigned long end)
{
config KEXEC
bool "kexec system call"
depends on !IA64_HP_SIM && (!SMP || HOTPLUG_CPU)
- select CRYPTO
- select CRYPTO_SHA256
help
kexec is a system call that implements the ability to shutdown your
current kernel, and to start another kernel. It is like a reboot
config KEXEC
bool "kexec system call"
depends on M68KCLASSIC
- select CRYPTO
- select CRYPTO_SHA256
help
kexec is a system call that implements the ability to shutdown your
current kernel, and to start another kernel. It is like a reboot
config KEXEC
bool "Kexec system call"
- select CRYPTO
- select CRYPTO_SHA256
help
kexec is a system call that implements the ability to shutdown your
current kernel, and to start another kernel. It is like a reboot
config KEXEC
bool "kexec system call"
depends on (PPC_BOOK3S || FSL_BOOKE || (44x && !SMP))
- select CRYPTO
- select CRYPTO_SHA256
help
kexec is a system call that implements the ability to shutdown your
current kernel, and to start another kernel. It is like a reboot
config KEXEC
def_bool y
- select CRYPTO
- select CRYPTO_SHA256
config AUDIT_ARCH
def_bool y
#define __NR_sched_setattr 345
#define __NR_sched_getattr 346
#define __NR_renameat2 347
-#define NR_syscalls 348
+#define __NR_seccomp 348
+#define __NR_getrandom 349
+#define __NR_memfd_create 350
+#define NR_syscalls 351
/*
* There are some system calls that are not present on 64 bit, some
COMPAT_SYSCALL_WRAP3(sched_setattr, pid_t, pid, struct sched_attr __user *, attr, unsigned int, flags);
COMPAT_SYSCALL_WRAP4(sched_getattr, pid_t, pid, struct sched_attr __user *, attr, unsigned int, size, unsigned int, flags);
COMPAT_SYSCALL_WRAP5(renameat2, int, olddfd, const char __user *, oldname, int, newdfd, const char __user *, newname, unsigned int, flags);
+COMPAT_SYSCALL_WRAP3(seccomp, unsigned int, op, unsigned int, flags, const char __user *, uargs)
+COMPAT_SYSCALL_WRAP3(getrandom, char __user *, buf, size_t, count, unsigned int, flags)
+COMPAT_SYSCALL_WRAP2(memfd_create, const char __user *, uname, unsigned int, flags)
S390_lowcore.program_new_psw.addr =
PSW_ADDR_AMODE | (unsigned long) s390_base_pgm_handler;
+ /*
+ * Clear subchannel ID and number to signal new kernel that no CCW or
+ * SCSI IPL has been done (for kexec and kdump)
+ */
+ S390_lowcore.subchannel_id = 0;
+ S390_lowcore.subchannel_nr = 0;
+
/* Store status at absolute zero */
store_status();
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/ptrace.h>
+#include <linux/random.h>
#include <linux/user.h>
#include <linux/tty.h>
#include <linux/ioport.h>
#include <asm/diag.h>
#include <asm/os_info.h>
#include <asm/sclp.h>
+#include <asm/sysinfo.h>
#include "entry.h"
/*
#endif
get_cpu_id(&cpu_id);
+ add_device_randomness(&cpu_id, sizeof(cpu_id));
switch (cpu_id.machine) {
case 0x9672:
#if !defined(CONFIG_64BIT)
}
}
+/*
+ * Add system information as device randomness
+ */
+static void __init setup_randomness(void)
+{
+ struct sysinfo_3_2_2 *vmms;
+
+ vmms = (struct sysinfo_3_2_2 *) alloc_page(GFP_KERNEL);
+ if (vmms && stsi(vmms, 3, 2, 2) == 0 && vmms->count)
+ add_device_randomness(&vmms, vmms->count);
+ free_page((unsigned long) vmms);
+}
+
/*
* Setup function called from init/main.c just after the banner
* was printed.
/* Setup zfcpdump support */
setup_zfcpdump();
+
+ /* Add system specific data to the random pool */
+ setup_randomness();
}
#ifdef CONFIG_32BIT
SYSCALL(sys_sched_setattr,sys_sched_setattr,compat_sys_sched_setattr) /* 345 */
SYSCALL(sys_sched_getattr,sys_sched_getattr,compat_sys_sched_getattr)
SYSCALL(sys_renameat2,sys_renameat2,compat_sys_renameat2)
+SYSCALL(sys_seccomp,sys_seccomp,compat_sys_seccomp)
+SYSCALL(sys_getrandom,sys_getrandom,compat_sys_getrandom)
+SYSCALL(sys_memfd_create,sys_memfd_create,compat_sys_memfd_create) /* 350 */
config KEXEC
bool "kexec system call (EXPERIMENTAL)"
depends on SUPERH32 && MMU
- select CRYPTO
- select CRYPTO_SHA256
help
kexec is a system call that implements the ability to shutdown your
current kernel, and to start another kernel. It is like a reboot
cacheop_on_each_cpu(local_flush_icache_range, (void *)&data, 1);
}
+EXPORT_SYMBOL(flush_icache_range);
void flush_icache_page(struct vm_area_struct *vma, struct page *page)
{
config KEXEC
bool "kexec system call"
- select CRYPTO
- select CRYPTO_SHA256
---help---
kexec is a system call that implements the ability to shutdown your
current kernel, and to start another kernel. It is like a reboot
preempt_enable();
}
}
+EXPORT_SYMBOL(flush_icache_range);
/* Called when smp_send_reschedule() triggers IRQ_RESCHEDULE. */
err |= setup_sigframe(frame, regs, set);
if (err == 0)
- err |= setup_return(regs, &ksig->ka, frame->retcode, frame, usig);
+ err |= setup_return(regs, &ksig->ka, frame->retcode, frame,
+ ksig->sig);
return err;
}
err |= __save_altstack(&frame->sig.uc.uc_stack, regs->UCreg_sp);
err |= setup_sigframe(&frame->sig, regs, set);
if (err == 0)
- err |= setup_return(regs, &ksig->ka, frame->sig.retcode, frame, usig);
+ err |= setup_return(regs, &ksig->ka, frame->sig.retcode, frame,
+ ksig->sig);
if (err == 0) {
/*
int syscall)
{
struct thread_info *thread = current_thread_info();
- struct task_struct *tsk = current;
sigset_t *oldset = sigmask_to_save();
int usig = ksig->sig;
int ret;
if (!user_mode(regs))
return;
- if (get_signsl(&ksig)) {
+ if (get_signal(&ksig)) {
handle_signal(&ksig, regs, syscall);
return;
}
obj-y += platform/
obj-y += net/
-ifeq ($(CONFIG_X86_64),y)
-obj-$(CONFIG_KEXEC) += purgatory/
-endif
+obj-$(CONFIG_KEXEC_FILE) += purgatory/
config KEXEC
bool "kexec system call"
- select BUILD_BIN2C
- select CRYPTO
- select CRYPTO_SHA256
---help---
kexec is a system call that implements the ability to shutdown your
current kernel, and to start another kernel. It is like a reboot
interface is strongly in flux, so no good recommendation can be
made.
+config KEXEC_FILE
+ bool "kexec file based system call"
+ select BUILD_BIN2C
+ depends on KEXEC
+ depends on X86_64
+ depends on CRYPTO=y
+ depends on CRYPTO_SHA256=y
+ ---help---
+ This is new version of kexec system call. This system call is
+ file based and takes file descriptors as system call argument
+ for kernel and initramfs as opposed to list of segments as
+ accepted by previous system call.
+
config KEXEC_VERIFY_SIG
bool "Verify kernel signature during kexec_file_load() syscall"
- depends on KEXEC
+ depends on KEXEC_FILE
---help---
This option makes kernel signature verification mandatory for
kexec_file_load() syscall. If kernel is signature can not be
$(Q)$(MAKE) $(build)=arch/x86/syscalls all
archprepare:
-ifeq ($(CONFIG_KEXEC),y)
-# Build only for 64bit. No loaders for 32bit yet.
- ifeq ($(CONFIG_X86_64),y)
+ifeq ($(CONFIG_KEXEC_FILE),y)
$(Q)$(MAKE) $(build)=arch/x86/purgatory arch/x86/purgatory/kexec-purgatory.c
- endif
endif
###
$(Q)rm -rf $(objtree)/arch/x86_64
$(Q)$(MAKE) $(clean)=$(boot)
$(Q)$(MAKE) $(clean)=arch/x86/tools
+ $(Q)$(MAKE) $(clean)=arch/x86/purgatory
PHONY += kvmconfig
kvmconfig:
extern void io_apic_eoi(unsigned int apic, unsigned int vector);
+extern bool mp_should_keep_irq(struct device *dev);
+
#else /* !CONFIG_X86_IO_APIC */
#define io_apic_assign_pci_irqs 0
static inline int pte_special(pte_t pte)
{
- return (pte_flags(pte) & (_PAGE_PRESENT|_PAGE_SPECIAL)) ==
- (_PAGE_PRESENT|_PAGE_SPECIAL);
+ /*
+ * See CONFIG_NUMA_BALANCING pte_numa in include/asm-generic/pgtable.h.
+ * On x86 we have _PAGE_BIT_NUMA == _PAGE_BIT_GLOBAL+1 ==
+ * __PAGE_BIT_SOFTW1 == _PAGE_BIT_SPECIAL.
+ */
+ return (pte_flags(pte) & _PAGE_SPECIAL) &&
+ (pte_flags(pte) & (_PAGE_PRESENT|_PAGE_PROTNONE));
}
static inline unsigned long pte_pfn(pte_t pte)
obj-$(CONFIG_X86_TSC) += trace_clock.o
obj-$(CONFIG_KEXEC) += machine_kexec_$(BITS).o
obj-$(CONFIG_KEXEC) += relocate_kernel_$(BITS).o crash.o
+obj-$(CONFIG_KEXEC_FILE) += kexec-bzimage64.o
obj-$(CONFIG_CRASH_DUMP) += crash_dump_$(BITS).o
obj-y += kprobes/
obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_PCI_MMCONFIG) += mmconf-fam10h_64.o
obj-y += vsmp_64.o
- obj-$(CONFIG_KEXEC) += kexec-bzimage64.o
endif
}
if (flags & IOAPIC_MAP_ALLOC) {
+ /* special handling for legacy IRQs */
+ if (irq < nr_legacy_irqs() && info->count == 1 &&
+ mp_irqdomain_map(domain, irq, pin) != 0)
+ irq = -1;
+
if (irq > 0)
info->count++;
else if (info->count == 0)
info->polarity = 1;
}
info->node = NUMA_NO_NODE;
- info->set = 1;
+
+ /*
+ * setup_IO_APIC_irqs() programs all legacy IRQs with default
+ * trigger and polarity attributes. Don't set the flag for that
+ * case so the first legacy IRQ user could reprogram the pin
+ * with real trigger and polarity attributes.
+ */
+ if (virq >= nr_legacy_irqs() || info->count)
+ info->set = 1;
}
set_io_apic_irq_attr(&attr, ioapic, hwirq, info->trigger,
info->polarity);
return ret;
}
+bool mp_should_keep_irq(struct device *dev)
+{
+ if (dev->power.is_prepared)
+ return true;
+#ifdef CONFIG_PM_RUNTIME
+ if (dev->power.runtime_status == RPM_SUSPENDING)
+ return true;
+#endif
+
+ return false;
+}
+
/* Enable IOAPIC early just for system timer */
void __init pre_init_apic_IRQ0(void)
{
crash_save_cpu(regs, safe_smp_processor_id());
}
-#ifdef CONFIG_X86_64
-
+#ifdef CONFIG_KEXEC_FILE
static int get_nr_ram_ranges_callback(unsigned long start_pfn,
unsigned long nr_pfn, void *arg)
{
return ret;
}
-
-#endif /* CONFIG_X86_64 */
+#endif /* CONFIG_KEXEC_FILE */
set_intr_gate(i, interrupt[i - FIRST_EXTERNAL_VECTOR]);
}
- if (!acpi_ioapic && !of_ioapic)
+ if (!acpi_ioapic && !of_ioapic && nr_legacy_irqs())
setup_irq(2, &irq2);
#ifdef CONFIG_X86_32
#include <asm/debugreg.h>
#include <asm/kexec-bzimage64.h>
+#ifdef CONFIG_KEXEC_FILE
static struct kexec_file_ops *kexec_file_loaders[] = {
&kexec_bzImage64_ops,
};
+#endif
static void free_transition_pgtable(struct kimage *image)
{
);
}
+#ifdef CONFIG_KEXEC_FILE
/* Update purgatory as needed after various image segments have been prepared */
static int arch_update_purgatory(struct kimage *image)
{
return ret;
}
+#else /* !CONFIG_KEXEC_FILE */
+static inline int arch_update_purgatory(struct kimage *image)
+{
+ return 0;
+}
+#endif /* CONFIG_KEXEC_FILE */
int machine_kexec_prepare(struct kimage *image)
{
/* arch-dependent functionality related to kexec file-based syscall */
+#ifdef CONFIG_KEXEC_FILE
int arch_kexec_kernel_image_probe(struct kimage *image, void *buf,
unsigned long buf_len)
{
(int)ELF64_R_TYPE(rel[i].r_info), value);
return -ENOEXEC;
}
+#endif /* CONFIG_KEXEC_FILE */
void __init setup_default_timer_irq(void)
{
+ if (!nr_legacy_irqs())
+ return;
setup_irq(0, &irq0);
}
static void intel_mid_pci_irq_disable(struct pci_dev *dev)
{
- if (!dev->dev.power.is_prepared && dev->irq > 0)
+ if (!mp_should_keep_irq(&dev->dev) && dev->irq > 0)
mp_unmap_irq(dev->irq);
}
static void pirq_disable_irq(struct pci_dev *dev)
{
- if (io_apic_assign_pci_irqs && !dev->dev.power.is_prepared &&
+ if (io_apic_assign_pci_irqs && !mp_should_keep_irq(&dev->dev) &&
dev->irq) {
mp_unmap_irq(dev->irq);
dev->irq = 0;
# sure how to relocate those. Like kexec-tools, use custom flags.
KBUILD_CFLAGS := -fno-strict-aliasing -Wall -Wstrict-prototypes -fno-zero-initialized-in-bss -fno-builtin -ffreestanding -c -MD -Os -mcmodel=large
+KBUILD_CFLAGS += -m$(BITS)
$(obj)/purgatory.ro: $(PURGATORY_OBJS) FORCE
$(call if_changed,ld)
$(call if_changed,bin2c)
-# No loaders for 32bits yet.
-ifeq ($(CONFIG_X86_64),y)
- obj-$(CONFIG_KEXEC) += kexec-purgatory.o
-endif
+obj-$(CONFIG_KEXEC_FILE) += kexec-purgatory.o
config XTENSA
def_bool y
select ARCH_WANT_FRAME_POINTERS
- select HAVE_IDE
- select GENERIC_ATOMIC64
- select GENERIC_CLOCKEVENTS
- select VIRT_TO_BUS
- select GENERIC_IRQ_SHOW
- select GENERIC_SCHED_CLOCK
- select MODULES_USE_ELF_RELA
- select GENERIC_PCI_IOMAP
select ARCH_WANT_IPC_PARSE_VERSION
select ARCH_WANT_OPTIONAL_GPIOLIB
select BUILDTIME_EXTABLE_SORT
select CLONE_BACKWARDS
- select IRQ_DOMAIN
- select HAVE_OPROFILE
+ select COMMON_CLK
+ select GENERIC_ATOMIC64
+ select GENERIC_CLOCKEVENTS
+ select GENERIC_IRQ_SHOW
+ select GENERIC_PCI_IOMAP
+ select GENERIC_SCHED_CLOCK
select HAVE_FUNCTION_TRACER
select HAVE_IRQ_TIME_ACCOUNTING
+ select HAVE_OPROFILE
select HAVE_PERF_EVENTS
- select COMMON_CLK
+ select IRQ_DOMAIN
+ select MODULES_USE_ELF_RELA
+ select VIRT_TO_BUS
help
Xtensa processors are 32-bit RISC machines designed by Tensilica
primarily for embedded systems. These processors are both
def_bool y
config MMU
- def_bool n
+ bool
+ default n if !XTENSA_VARIANT_CUSTOM
+ default XTENSA_VARIANT_MMU if XTENSA_VARIANT_CUSTOM
config VARIANT_IRQ_SWITCH
def_bool n
select VARIANT_IRQ_SWITCH
select ARCH_REQUIRE_GPIOLIB
select XTENSA_CALIBRATE_CCOUNT
+
+config XTENSA_VARIANT_CUSTOM
+ bool "Custom Xtensa processor configuration"
+ select MAY_HAVE_SMP
+ select HAVE_XTENSA_GPIO32
+ help
+ Select this variant to use a custom Xtensa processor configuration.
+ You will be prompted for a processor variant CORENAME.
endchoice
+config XTENSA_VARIANT_CUSTOM_NAME
+ string "Xtensa Processor Custom Core Variant Name"
+ depends on XTENSA_VARIANT_CUSTOM
+ help
+ Provide the name of a custom Xtensa processor variant.
+ This CORENAME selects arch/xtensa/variant/CORENAME.
+ Dont forget you have to select MMU if you have one.
+
+config XTENSA_VARIANT_NAME
+ string
+ default "dc232b" if XTENSA_VARIANT_DC232B
+ default "dc233c" if XTENSA_VARIANT_DC233C
+ default "fsf" if XTENSA_VARIANT_FSF
+ default "s6000" if XTENSA_VARIANT_S6000
+ default XTENSA_VARIANT_CUSTOM_NAME if XTENSA_VARIANT_CUSTOM
+
+config XTENSA_VARIANT_MMU
+ bool "Core variant has a Full MMU (TLB, Pages, Protection, etc)"
+ depends on XTENSA_VARIANT_CUSTOM
+ default y
+ help
+ Build a Conventional Kernel with full MMU support,
+ ie: it supports a TLB with auto-loading, page protection.
+
config XTENSA_UNALIGNED_USER
bool "Unaligned memory access in use space"
help
Say N if you want to disable CPU hotplug.
-config MATH_EMULATION
- bool "Math emulation"
- help
- Can we use information of configuration file?
-
config INITIALIZE_XTENSA_MMU_INSIDE_VMLINUX
bool "Initialize Xtensa MMU inside the Linux kernel code"
+ depends on MMU
default y
help
Earlier version initialized the MMU in the exception vector
config HIGHMEM
bool "High Memory Support"
+ depends on MMU
help
Linux can use the full amount of RAM in the system by
default. However, the default MMUv2 setup only maps the
If unsure, say Y.
+config FAST_SYSCALL_XTENSA
+ bool "Enable fast atomic syscalls"
+ default n
+ help
+ fast_syscall_xtensa is a syscall that can make atomic operations
+ on UP kernel when processor has no s32c1i support.
+
+ This syscall is deprecated. It may have issues when called with
+ invalid arguments. It is provided only for backwards compatibility.
+ Only enable it if your userspace software requires it.
+
+ If unsure, say N.
+
+config FAST_SYSCALL_SPILL_REGISTERS
+ bool "Enable spill registers syscall"
+ default n
+ help
+ fast_syscall_spill_registers is a syscall that spills all active
+ register windows of a calling userspace task onto its stack.
+
+ This syscall is deprecated. It may have issues when called with
+ invalid arguments. It is provided only for backwards compatibility.
+ Only enable it if your userspace software requires it.
+
+ If unsure, say N.
+
endmenu
config XTENSA_CALIBRATE_CCOUNT
config XTENSA_PLATFORM_XT2000
bool "XT2000"
+ select HAVE_IDE
help
XT2000 is the name of Tensilica's feature-rich emulation platform.
This hardware is capable of running a full Linux distribution.
config XTENSA_PLATFORM_S6105
bool "S6105"
+ select HAVE_IDE
select SERIAL_CONSOLE
select NO_IOPORT_MAP
# for more details.
#
# Copyright (C) 2001 - 2005 Tensilica Inc.
+# Copyright (C) 2014 Cadence Design Systems Inc.
#
# This file is included by the global makefile so that you can add your own
# architecture-specific flags and dependencies. Remember to do have actions
# Core configuration.
# (Use VAR=<xtensa_config> to use another default compiler.)
-variant-$(CONFIG_XTENSA_VARIANT_FSF) := fsf
-variant-$(CONFIG_XTENSA_VARIANT_DC232B) := dc232b
-variant-$(CONFIG_XTENSA_VARIANT_DC233C) := dc233c
-variant-$(CONFIG_XTENSA_VARIANT_S6000) := s6000
-variant-$(CONFIG_XTENSA_VARIANT_LINUX_CUSTOM) := custom
+variant-y := $(patsubst "%",%,$(CONFIG_XTENSA_VARIANT_NAME))
VARIANT = $(variant-y)
export VARIANT
/ {
compatible = "cdns,xtensa-kc705";
+ chosen {
+ bootargs = "earlycon=uart8250,mmio32,0xfd050020,115200n8 console=ttyS0,115200n8 ip=dhcp root=/dev/nfs rw debug memmap=0x38000000";
+ };
memory@0 {
device_type = "memory";
- reg = <0x00000000 0x08000000>;
+ reg = <0x00000000 0x38000000>;
};
};
CONFIG_MMU=y
# CONFIG_XTENSA_UNALIGNED_USER is not set
# CONFIG_PREEMPT is not set
-# CONFIG_MATH_EMULATION is not set
# CONFIG_HIGHMEM is not set
#
# CONFIG_XTENSA_VARIANT_S6000 is not set
# CONFIG_XTENSA_UNALIGNED_USER is not set
# CONFIG_PREEMPT is not set
-# CONFIG_MATH_EMULATION is not set
CONFIG_XTENSA_CALIBRATE_CCOUNT=y
CONFIG_SERIAL_CONSOLE=y
CONFIG_XTENSA_ISS_NETWORK=y
# EEPROM support
#
# CONFIG_EEPROM_93CX6 is not set
-CONFIG_HAVE_IDE=y
+# CONFIG_HAVE_IDE is not set
# CONFIG_IDE is not set
#
CONFIG_XTENSA_VARIANT_S6000=y
# CONFIG_XTENSA_UNALIGNED_USER is not set
CONFIG_PREEMPT=y
-# CONFIG_MATH_EMULATION is not set
# CONFIG_HIGHMEM is not set
CONFIG_XTENSA_CALIBRATE_CCOUNT=y
CONFIG_SERIAL_CONSOLE=y
* specials for cache aliasing:
*
* __flush_invalidate_dcache_page_alias(vaddr,paddr)
+ * __invalidate_dcache_page_alias(vaddr,paddr)
* __invalidate_icache_page_alias(vaddr,paddr)
*/
#if defined(CONFIG_MMU) && (DCACHE_WAY_SIZE > PAGE_SIZE)
extern void __flush_invalidate_dcache_page_alias(unsigned long, unsigned long);
+extern void __invalidate_dcache_page_alias(unsigned long, unsigned long);
#else
static inline void __flush_invalidate_dcache_page_alias(unsigned long virt,
unsigned long phys) { }
* Here we define all the compile-time 'special' virtual
* addresses. The point is to have a constant address at
* compile time, but to set the physical address only
- * in the boot process. We allocate these special addresses
- * from the end of the consistent memory region backwards.
+ * in the boot process. We allocate these special addresses
+ * from the start of the consistent memory region upwards.
* Also this lets us do fail-safe vmalloc(), we
* can guarantee that these special addresses and
* vmalloc()-ed addresses never overlap.
#ifdef CONFIG_HIGHMEM
/* reserved pte's for temporary kernel mappings */
FIX_KMAP_BEGIN,
- FIX_KMAP_END = FIX_KMAP_BEGIN + (KM_TYPE_NR * NR_CPUS) - 1,
+ FIX_KMAP_END = FIX_KMAP_BEGIN +
+ (KM_TYPE_NR * NR_CPUS * DCACHE_N_COLORS) - 1,
#endif
__end_of_fixed_addresses
};
#define FIXADDR_SIZE (__end_of_fixed_addresses << PAGE_SHIFT)
#define FIXADDR_START ((FIXADDR_TOP - FIXADDR_SIZE) & PMD_MASK)
-#include <asm-generic/fixmap.h>
+#define __fix_to_virt(x) (FIXADDR_START + ((x) << PAGE_SHIFT))
+#define __virt_to_fix(x) (((x) - FIXADDR_START) >> PAGE_SHIFT)
+
+#ifndef __ASSEMBLY__
+/*
+ * 'index to address' translation. If anyone tries to use the idx
+ * directly without translation, we catch the bug with a NULL-deference
+ * kernel oops. Illegal ranges of incoming indices are caught too.
+ */
+static __always_inline unsigned long fix_to_virt(const unsigned int idx)
+{
+ BUILD_BUG_ON(idx >= __end_of_fixed_addresses);
+ return __fix_to_virt(idx);
+}
+
+static inline unsigned long virt_to_fix(const unsigned long vaddr)
+{
+ BUG_ON(vaddr >= FIXADDR_TOP || vaddr < FIXADDR_START);
+ return __virt_to_fix(vaddr);
+}
+
+#endif
#define kmap_get_fixmap_pte(vaddr) \
pte_offset_kernel( \
#ifndef _XTENSA_HIGHMEM_H
#define _XTENSA_HIGHMEM_H
+#include <linux/wait.h>
#include <asm/cacheflush.h>
#include <asm/fixmap.h>
#include <asm/kmap_types.h>
#include <asm/pgtable.h>
-#define PKMAP_BASE (FIXADDR_START - PMD_SIZE)
-#define LAST_PKMAP PTRS_PER_PTE
+#define PKMAP_BASE ((FIXADDR_START - \
+ (LAST_PKMAP + 1) * PAGE_SIZE) & PMD_MASK)
+#define LAST_PKMAP (PTRS_PER_PTE * DCACHE_N_COLORS)
#define LAST_PKMAP_MASK (LAST_PKMAP - 1)
#define PKMAP_NR(virt) (((virt) - PKMAP_BASE) >> PAGE_SHIFT)
#define PKMAP_ADDR(nr) (PKMAP_BASE + ((nr) << PAGE_SHIFT))
#define kmap_prot PAGE_KERNEL
+#if DCACHE_WAY_SIZE > PAGE_SIZE
+#define get_pkmap_color get_pkmap_color
+static inline int get_pkmap_color(struct page *page)
+{
+ return DCACHE_ALIAS(page_to_phys(page));
+}
+
+extern unsigned int last_pkmap_nr_arr[];
+
+static inline unsigned int get_next_pkmap_nr(unsigned int color)
+{
+ last_pkmap_nr_arr[color] =
+ (last_pkmap_nr_arr[color] + DCACHE_N_COLORS) & LAST_PKMAP_MASK;
+ return last_pkmap_nr_arr[color] + color;
+}
+
+static inline int no_more_pkmaps(unsigned int pkmap_nr, unsigned int color)
+{
+ return pkmap_nr < DCACHE_N_COLORS;
+}
+
+static inline int get_pkmap_entries_count(unsigned int color)
+{
+ return LAST_PKMAP / DCACHE_N_COLORS;
+}
+
+extern wait_queue_head_t pkmap_map_wait_arr[];
+
+static inline wait_queue_head_t *get_pkmap_wait_queue_head(unsigned int color)
+{
+ return pkmap_map_wait_arr + color;
+}
+#endif
+
extern pte_t *pkmap_page_table;
void *kmap_high(struct page *page);
# define DCACHE_ALIAS_EQ(a,b) ((((a) ^ (b)) & DCACHE_ALIAS_MASK) == 0)
#else
# define DCACHE_ALIAS_ORDER 0
+# define DCACHE_ALIAS(a) ((void)(a), 0)
#endif
+#define DCACHE_N_COLORS (1 << DCACHE_ALIAS_ORDER)
#if ICACHE_WAY_SIZE > PAGE_SIZE
# define ICACHE_ALIAS_ORDER (ICACHE_WAY_SHIFT - PAGE_SHIFT)
#endif
struct page;
+struct vm_area_struct;
extern void clear_page(void *page);
extern void copy_page(void *to, void *from);
*/
#if DCACHE_WAY_SIZE > PAGE_SIZE
-extern void clear_user_page(void*, unsigned long, struct page*);
-extern void copy_user_page(void*, void*, unsigned long, struct page*);
+extern void clear_page_alias(void *vaddr, unsigned long paddr);
+extern void copy_page_alias(void *to, void *from,
+ unsigned long to_paddr, unsigned long from_paddr);
+
+#define clear_user_highpage clear_user_highpage
+void clear_user_highpage(struct page *page, unsigned long vaddr);
+#define __HAVE_ARCH_COPY_USER_HIGHPAGE
+void copy_user_highpage(struct page *to, struct page *from,
+ unsigned long vaddr, struct vm_area_struct *vma);
#else
# define clear_user_page(page, vaddr, pg) clear_page(page)
# define copy_user_page(to, from, vaddr, pg) copy_page(to, from)
#define VMALLOC_START 0xC0000000
#define VMALLOC_END 0xC7FEFFFF
#define TLBTEMP_BASE_1 0xC7FF0000
-#define TLBTEMP_BASE_2 0xC7FF8000
+#define TLBTEMP_BASE_2 (TLBTEMP_BASE_1 + DCACHE_WAY_SIZE)
+#if 2 * DCACHE_WAY_SIZE > ICACHE_WAY_SIZE
+#define TLBTEMP_SIZE (2 * DCACHE_WAY_SIZE)
+#else
+#define TLBTEMP_SIZE ICACHE_WAY_SIZE
+#endif
/*
* For the Xtensa architecture, the PTE layout is as follows:
*/
.macro get_fs ad, sp
GET_CURRENT(\ad,\sp)
+#if THREAD_CURRENT_DS > 1020
+ addi \ad, \ad, TASK_THREAD
+ l32i \ad, \ad, THREAD_CURRENT_DS - TASK_THREAD
+#else
l32i \ad, \ad, THREAD_CURRENT_DS
+#endif
.endm
/*
#define TCSETSW 0x5403
#define TCSETSF 0x5404
-#define TCGETA _IOR('t', 23, struct termio)
-#define TCSETA _IOW('t', 24, struct termio)
-#define TCSETAW _IOW('t', 25, struct termio)
-#define TCSETAF _IOW('t', 28, struct termio)
+#define TCGETA 0x80127417 /* _IOR('t', 23, struct termio) */
+#define TCSETA 0x40127418 /* _IOW('t', 24, struct termio) */
+#define TCSETAW 0x40127419 /* _IOW('t', 25, struct termio) */
+#define TCSETAF 0x4012741C /* _IOW('t', 28, struct termio) */
#define TCSBRK _IO('t', 29)
#define TCXONC _IO('t', 30)
#define TCFLSH _IO('t', 31)
-#define TIOCSWINSZ _IOW('t', 103, struct winsize)
-#define TIOCGWINSZ _IOR('t', 104, struct winsize)
+#define TIOCSWINSZ 0x40087467 /* _IOW('t', 103, struct winsize) */
+#define TIOCGWINSZ 0x80087468 /* _IOR('t', 104, struct winsize) */
#define TIOCSTART _IO('t', 110) /* start output, like ^Q */
#define TIOCSTOP _IO('t', 111) /* stop output, like ^S */
#define TIOCOUTQ _IOR('t', 115, int) /* output queue size */
#define TIOCSETD _IOW('T', 35, int)
#define TIOCGETD _IOR('T', 36, int)
#define TCSBRKP _IOW('T', 37, int) /* Needed for POSIX tcsendbreak()*/
-#define TIOCTTYGSTRUCT _IOR('T', 38, struct tty_struct) /* For debugging only*/
#define TIOCSBRK _IO('T', 39) /* BSD compatibility */
#define TIOCCBRK _IO('T', 40) /* BSD compatibility */
#define TIOCGSID _IOR('T', 41, pid_t) /* Return the session ID of FD*/
#define TIOCSERGETLSR _IOR('T', 89, unsigned int) /* Get line status reg. */
/* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */
# define TIOCSER_TEMT 0x01 /* Transmitter physically empty */
-#define TIOCSERGETMULTI _IOR('T', 90, struct serial_multiport_struct) /* Get multiport config */
-#define TIOCSERSETMULTI _IOW('T', 91, struct serial_multiport_struct) /* Set multiport config */
+#define TIOCSERGETMULTI 0x80a8545a /* Get multiport config */
+ /* _IOR('T', 90, struct serial_multiport_struct) */
+#define TIOCSERSETMULTI 0x40a8545b /* Set multiport config */
+ /* _IOW('T', 91, struct serial_multiport_struct) */
#define TIOCMIWAIT _IO('T', 92) /* wait for a change on serial input line(s) */
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
#define __NR_sched_getattr 335
__SYSCALL(335, sys_sched_getattr, 3)
-#define __NR_syscall_count 336
+#define __NR_renameat2 336
+__SYSCALL(336, sys_renameat2, 5)
+
+#define __NR_syscall_count 337
/*
* sysxtensa syscall handler
* this archive for more details.
*
* Copyright (C) 2001 - 2005 Tensilica, Inc.
+ * Copyright (C) 2014 Cadence Design Systems Inc.
*
* Rewritten by Chris Zankel <chris@zankel.net>
*
s32i a0, a2, PT_AREG2
s32i a3, a2, PT_AREG3
+ rsr a3, excsave1
+ movi a4, fast_unaligned_fixup
+ s32i a4, a3, EXC_TABLE_FIXUP
+
/* Keep value of SAR in a0 */
rsr a0, sar
addx8 a5, a6, a5
jx a5 # jump into table
- /* Invalid instruction, CRITICAL! */
-.Linvalid_instruction_load:
- j .Linvalid_instruction
-
/* Load: Load memory address. */
.Lload: movi a3, ~3
/* Set target register. */
1:
-
-#if XCHAL_HAVE_LOOPS
- rsr a5, lend # check if we reached LEND
- bne a7, a5, 1f
- rsr a5, lcount # and LCOUNT != 0
- beqz a5, 1f
- addi a5, a5, -1 # decrement LCOUNT and set
- rsr a7, lbeg # set PC to LBEGIN
- wsr a5, lcount
-#endif
-
-1: wsr a7, epc1 # skip load instruction
extui a4, a4, INSN_T, 4 # extract target register
movi a5, .Lload_table
addx8 a4, a4, a5
mov a3, a14 ; _j 1f; .align 8
mov a3, a15 ; _j 1f; .align 8
+ /* We cannot handle this exception. */
+
+ .extern _kernel_exception
+.Linvalid_instruction_load:
+.Linvalid_instruction_store:
+
+ movi a4, 0
+ rsr a3, excsave1
+ s32i a4, a3, EXC_TABLE_FIXUP
+
+ /* Restore a4...a8 and SAR, set SP, and jump to default exception. */
+
+ l32i a8, a2, PT_AREG8
+ l32i a7, a2, PT_AREG7
+ l32i a6, a2, PT_AREG6
+ l32i a5, a2, PT_AREG5
+ l32i a4, a2, PT_AREG4
+ wsr a0, sar
+ mov a1, a2
+
+ rsr a0, ps
+ bbsi.l a0, PS_UM_BIT, 2f # jump if user mode
+
+ movi a0, _kernel_exception
+ jx a0
+
+2: movi a0, _user_exception
+ jx a0
+
1: # a7: instruction pointer, a4: instruction, a3: value
movi a6, 0 # mask: ffffffff:00000000
/* Get memory address */
1:
-#if XCHAL_HAVE_LOOPS
- rsr a4, lend # check if we reached LEND
- bne a7, a4, 1f
- rsr a4, lcount # and LCOUNT != 0
- beqz a4, 1f
- addi a4, a4, -1 # decrement LCOUNT and set
- rsr a7, lbeg # set PC to LBEGIN
- wsr a4, lcount
-#endif
-
-1: wsr a7, epc1 # skip store instruction
movi a4, ~3
and a4, a4, a8 # align memory address
#endif
__ssa8r a8
- __src_b a7, a5, a6 # lo-mask F..F0..0 (BE) 0..0F..F (LE)
+ __src_b a8, a5, a6 # lo-mask F..F0..0 (BE) 0..0F..F (LE)
__src_b a6, a6, a5 # hi-mask 0..0F..F (BE) F..F0..0 (LE)
#ifdef UNALIGNED_USER_EXCEPTION
l32e a5, a4, -8
#else
l32i a5, a4, 0 # load lower address word
#endif
- and a5, a5, a7 # mask
- __sh a7, a3 # shift value
- or a5, a5, a7 # or with original value
+ and a5, a5, a8 # mask
+ __sh a8, a3 # shift value
+ or a5, a5, a8 # or with original value
#ifdef UNALIGNED_USER_EXCEPTION
s32e a5, a4, -8
- l32e a7, a4, -4
+ l32e a8, a4, -4
#else
s32i a5, a4, 0 # store
- l32i a7, a4, 4 # same for upper address word
+ l32i a8, a4, 4 # same for upper address word
#endif
__sl a5, a3
- and a6, a7, a6
+ and a6, a8, a6
or a6, a6, a5
#ifdef UNALIGNED_USER_EXCEPTION
s32e a6, a4, -4
s32i a6, a4, 4
#endif
- /* Done. restore stack and return */
-
.Lexit:
+#if XCHAL_HAVE_LOOPS
+ rsr a4, lend # check if we reached LEND
+ bne a7, a4, 1f
+ rsr a4, lcount # and LCOUNT != 0
+ beqz a4, 1f
+ addi a4, a4, -1 # decrement LCOUNT and set
+ rsr a7, lbeg # set PC to LBEGIN
+ wsr a4, lcount
+#endif
+
+1: wsr a7, epc1 # skip emulated instruction
+
+ /* Update icount if we're single-stepping in userspace. */
+ rsr a4, icountlevel
+ beqz a4, 1f
+ bgeui a4, LOCKLEVEL + 1, 1f
+ rsr a4, icount
+ addi a4, a4, 1
+ wsr a4, icount
+1:
movi a4, 0
rsr a3, excsave1
s32i a4, a3, EXC_TABLE_FIXUP
l32i a2, a2, PT_AREG2
rfe
- /* We cannot handle this exception. */
+ENDPROC(fast_unaligned)
- .extern _kernel_exception
-.Linvalid_instruction_store:
-.Linvalid_instruction:
+ENTRY(fast_unaligned_fixup)
- /* Restore a4...a8 and SAR, set SP, and jump to default exception. */
+ l32i a2, a3, EXC_TABLE_DOUBLE_SAVE
+ wsr a3, excsave1
l32i a8, a2, PT_AREG8
l32i a7, a2, PT_AREG7
l32i a6, a2, PT_AREG6
l32i a5, a2, PT_AREG5
l32i a4, a2, PT_AREG4
+ l32i a0, a2, PT_AREG2
+ xsr a0, depc # restore depc and a0
wsr a0, sar
- mov a1, a2
+
+ rsr a0, exccause
+ s32i a0, a2, PT_DEPC # mark as a regular exception
rsr a0, ps
- bbsi.l a2, PS_UM_BIT, 1f # jump if user mode
+ bbsi.l a0, PS_UM_BIT, 1f # jump if user mode
- movi a0, _kernel_exception
+ rsr a0, exccause
+ addx4 a0, a0, a3 # find entry in table
+ l32i a0, a0, EXC_TABLE_FAST_KERNEL # load handler
+ l32i a3, a2, PT_AREG3
jx a0
-
-1: movi a0, _user_exception
+1:
+ rsr a0, exccause
+ addx4 a0, a0, a3 # find entry in table
+ l32i a0, a0, EXC_TABLE_FAST_USER # load handler
+ l32i a3, a2, PT_AREG3
jx a0
-ENDPROC(fast_unaligned)
+ENDPROC(fast_unaligned_fixup)
#endif /* XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION */
* j done
*/
+#ifdef CONFIG_FAST_SYSCALL_XTENSA
+
#define TRY \
.section __ex_table, "a"; \
.word 66f, 67f; \
movi a7, 4 # sizeof(unsigned int)
access_ok a3, a7, a0, a2, .Leac # a0: scratch reg, a2: sp
- addi a6, a6, -1 # assuming SYS_XTENSA_ATOMIC_SET = 1
- _bgeui a6, SYS_XTENSA_COUNT - 1, .Lill
- _bnei a6, SYS_XTENSA_ATOMIC_CMP_SWP - 1, .Lnswp
+ _bgeui a6, SYS_XTENSA_COUNT, .Lill
+ _bnei a6, SYS_XTENSA_ATOMIC_CMP_SWP, .Lnswp
/* Fall through for ATOMIC_CMP_SWP. */
l32i a7, a2, PT_AREG7 # restore a7
l32i a0, a2, PT_AREG0 # restore a0
movi a2, 1 # and return 1
- addi a6, a6, 1 # restore a6 (really necessary?)
rfe
1: l32i a7, a2, PT_AREG7 # restore a7
l32i a0, a2, PT_AREG0 # restore a0
movi a2, 0 # return 0 (note that we cannot set
- addi a6, a6, 1 # restore a6 (really necessary?)
rfe
.Lnswp: /* Atomic set, add, and exg_add. */
TRY l32i a7, a3, 0 # orig
+ addi a6, a6, -SYS_XTENSA_ATOMIC_SET
add a0, a4, a7 # + arg
moveqz a0, a4, a6 # set
+ addi a6, a6, SYS_XTENSA_ATOMIC_SET
TRY s32i a0, a3, 0 # write new value
mov a0, a2
mov a2, a7
l32i a7, a0, PT_AREG7 # restore a7
l32i a0, a0, PT_AREG0 # restore a0
- addi a6, a6, 1 # restore a6 (really necessary?)
rfe
CATCH
movi a2, -EFAULT
rfe
-.Lill: l32i a7, a2, PT_AREG0 # restore a7
+.Lill: l32i a7, a2, PT_AREG7 # restore a7
l32i a0, a2, PT_AREG0 # restore a0
movi a2, -EINVAL
rfe
ENDPROC(fast_syscall_xtensa)
+#else /* CONFIG_FAST_SYSCALL_XTENSA */
+
+ENTRY(fast_syscall_xtensa)
+
+ l32i a0, a2, PT_AREG0 # restore a0
+ movi a2, -ENOSYS
+ rfe
+
+ENDPROC(fast_syscall_xtensa)
+
+#endif /* CONFIG_FAST_SYSCALL_XTENSA */
+
/* fast_syscall_spill_registers.
*
* Note: We assume the stack pointer is EXC_TABLE_KSTK in the fixup handler.
*/
+#ifdef CONFIG_FAST_SYSCALL_SPILL_REGISTERS
+
ENTRY(fast_syscall_spill_registers)
/* Register a FIXUP handler (pass current wb as a parameter) */
ENDPROC(fast_syscall_spill_registers_fixup_return)
+#else /* CONFIG_FAST_SYSCALL_SPILL_REGISTERS */
+
+ENTRY(fast_syscall_spill_registers)
+
+ l32i a0, a2, PT_AREG0 # restore a0
+ movi a2, -ENOSYS
+ rfe
+
+ENDPROC(fast_syscall_spill_registers)
+
+#endif /* CONFIG_FAST_SYSCALL_SPILL_REGISTERS */
+
#ifdef CONFIG_MMU
/*
* We should never get here. Bail out!
rsr a0, excvaddr
bltu a0, a3, 2f
- addi a1, a0, -(2 << (DCACHE_ALIAS_ORDER + PAGE_SHIFT))
+ addi a1, a0, -TLBTEMP_SIZE
bgeu a1, a3, 2f
/* Check if we have to restore an ITLB mapping. */
entry a1, 16
- mov a10, a2 # preserve 'prev' (a2)
mov a11, a3 # and 'next' (a3)
l32i a4, a2, TASK_THREAD_INFO
save_xtregs_user a4 a6 a8 a9 a12 a13 THREAD_XTREGS_USER
- s32i a0, a10, THREAD_RA # save return address
- s32i a1, a10, THREAD_SP # save stack pointer
+#if THREAD_RA > 1020 || THREAD_SP > 1020
+ addi a10, a2, TASK_THREAD
+ s32i a0, a10, THREAD_RA - TASK_THREAD # save return address
+ s32i a1, a10, THREAD_SP - TASK_THREAD # save stack pointer
+#else
+ s32i a0, a2, THREAD_RA # save return address
+ s32i a1, a2, THREAD_SP # save stack pointer
+#endif
/* Disable ints while we manipulate the stack pointer. */
load_xtregs_user a5 a6 a8 a9 a12 a13 THREAD_XTREGS_USER
wsr a14, ps
- mov a2, a10 # return 'prev'
rsync
retw
/* We currently don't support coherent memory outside KSEG */
- if (ret < XCHAL_KSEG_CACHED_VADDR
- || ret >= XCHAL_KSEG_CACHED_VADDR + XCHAL_KSEG_SIZE)
- BUG();
+ BUG_ON(ret < XCHAL_KSEG_CACHED_VADDR ||
+ ret > XCHAL_KSEG_CACHED_VADDR + XCHAL_KSEG_SIZE - 1);
if (ret != 0) {
void dma_free_coherent(struct device *hwdev, size_t size,
void *vaddr, dma_addr_t dma_handle)
{
- long addr=(long)vaddr+XCHAL_KSEG_CACHED_VADDR-XCHAL_KSEG_BYPASS_VADDR;
+ unsigned long addr = (unsigned long)vaddr +
+ XCHAL_KSEG_CACHED_VADDR - XCHAL_KSEG_BYPASS_VADDR;
- if (addr < 0 || addr >= XCHAL_KSEG_SIZE)
- BUG();
+ BUG_ON(addr < XCHAL_KSEG_CACHED_VADDR ||
+ addr > XCHAL_KSEG_CACHED_VADDR + XCHAL_KSEG_SIZE - 1);
free_pages(addr, get_order(size));
}
};
on_each_cpu(ipi_flush_icache_range, &fd, 1);
}
+EXPORT_SYMBOL(flush_icache_range);
/* ------------------------------------------------------------------------- */
#if XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION
#ifdef CONFIG_XTENSA_UNALIGNED_USER
{ EXCCAUSE_UNALIGNED, USER, fast_unaligned },
-#else
-{ EXCCAUSE_UNALIGNED, 0, do_unaligned_user },
#endif
+{ EXCCAUSE_UNALIGNED, 0, do_unaligned_user },
{ EXCCAUSE_UNALIGNED, KRNL, fast_unaligned },
#endif
#ifdef CONFIG_MMU
*/
#if XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION
-#ifndef CONFIG_XTENSA_UNALIGNED_USER
void
do_unaligned_user (struct pt_regs *regs)
{
}
#endif
-#endif
void
do_debug(struct pt_regs *regs)
s32i a0, a2, PT_DEPC
_DoubleExceptionVector_handle_exception:
+ addi a0, a0, -EXCCAUSE_UNALIGNED
+ beqz a0, 2f
addx4 a0, a0, a3
- l32i a0, a0, EXC_TABLE_FAST_USER
+ l32i a0, a0, EXC_TABLE_FAST_USER + 4 * EXCCAUSE_UNALIGNED
+ xsr a3, excsave1
+ jx a0
+2:
+ movi a0, user_exception
xsr a3, excsave1
jx a0
.UserExceptionVector.literal)
SECTION_VECTOR (_DoubleExceptionVector_literal,
.DoubleExceptionVector.literal,
- DOUBLEEXC_VECTOR_VADDR - 40,
+ DOUBLEEXC_VECTOR_VADDR - 48,
SIZEOF(.UserExceptionVector.text),
.UserExceptionVector.text)
SECTION_VECTOR (_DoubleExceptionVector_text,
.DoubleExceptionVector.text,
DOUBLEEXC_VECTOR_VADDR,
- 40,
+ 48,
.DoubleExceptionVector.literal)
. = (LOADADDR( .DoubleExceptionVector.text ) + SIZEOF( .DoubleExceptionVector.text ) + 3) & ~ 3;
*
*/
-#if (DCACHE_WAY_SIZE > PAGE_SIZE) && defined(CONFIG_HIGHMEM)
-#error "HIGHMEM is not supported on cores with aliasing cache."
-#endif
+#if (DCACHE_WAY_SIZE > PAGE_SIZE)
+static inline void kmap_invalidate_coherent(struct page *page,
+ unsigned long vaddr)
+{
+ if (!DCACHE_ALIAS_EQ(page_to_phys(page), vaddr)) {
+ unsigned long kvaddr;
+
+ if (!PageHighMem(page)) {
+ kvaddr = (unsigned long)page_to_virt(page);
+
+ __invalidate_dcache_page(kvaddr);
+ } else {
+ kvaddr = TLBTEMP_BASE_1 +
+ (page_to_phys(page) & DCACHE_ALIAS_MASK);
+
+ __invalidate_dcache_page_alias(kvaddr,
+ page_to_phys(page));
+ }
+ }
+}
+
+static inline void *coherent_kvaddr(struct page *page, unsigned long base,
+ unsigned long vaddr, unsigned long *paddr)
+{
+ if (PageHighMem(page) || !DCACHE_ALIAS_EQ(page_to_phys(page), vaddr)) {
+ *paddr = page_to_phys(page);
+ return (void *)(base + (vaddr & DCACHE_ALIAS_MASK));
+ } else {
+ *paddr = 0;
+ return page_to_virt(page);
+ }
+}
+
+void clear_user_highpage(struct page *page, unsigned long vaddr)
+{
+ unsigned long paddr;
+ void *kvaddr = coherent_kvaddr(page, TLBTEMP_BASE_1, vaddr, &paddr);
+
+ pagefault_disable();
+ kmap_invalidate_coherent(page, vaddr);
+ set_bit(PG_arch_1, &page->flags);
+ clear_page_alias(kvaddr, paddr);
+ pagefault_enable();
+}
+
+void copy_user_highpage(struct page *dst, struct page *src,
+ unsigned long vaddr, struct vm_area_struct *vma)
+{
+ unsigned long dst_paddr, src_paddr;
+ void *dst_vaddr = coherent_kvaddr(dst, TLBTEMP_BASE_1, vaddr,
+ &dst_paddr);
+ void *src_vaddr = coherent_kvaddr(src, TLBTEMP_BASE_2, vaddr,
+ &src_paddr);
+
+ pagefault_disable();
+ kmap_invalidate_coherent(dst, vaddr);
+ set_bit(PG_arch_1, &dst->flags);
+ copy_page_alias(dst_vaddr, src_vaddr, dst_paddr, src_paddr);
+ pagefault_enable();
+}
+
+#endif /* DCACHE_WAY_SIZE > PAGE_SIZE */
#if (DCACHE_WAY_SIZE > PAGE_SIZE) && XCHAL_DCACHE_IS_WRITEBACK
if (!alias && !mapping)
return;
- __flush_invalidate_dcache_page((long)page_address(page));
+ virt = TLBTEMP_BASE_1 + (phys & DCACHE_ALIAS_MASK);
+ __flush_invalidate_dcache_page_alias(virt, phys);
virt = TLBTEMP_BASE_1 + (temp & DCACHE_ALIAS_MASK);
#if (DCACHE_WAY_SIZE > PAGE_SIZE) && XCHAL_DCACHE_IS_WRITEBACK
if (!PageReserved(page) && test_bit(PG_arch_1, &page->flags)) {
-
- unsigned long paddr = (unsigned long) page_address(page);
unsigned long phys = page_to_phys(page);
- unsigned long tmp = TLBTEMP_BASE_1 + (addr & DCACHE_ALIAS_MASK);
-
- __flush_invalidate_dcache_page(paddr);
+ unsigned long tmp;
+ tmp = TLBTEMP_BASE_1 + (phys & DCACHE_ALIAS_MASK);
+ __flush_invalidate_dcache_page_alias(tmp, phys);
+ tmp = TLBTEMP_BASE_1 + (addr & DCACHE_ALIAS_MASK);
__flush_invalidate_dcache_page_alias(tmp, phys);
__invalidate_icache_page_alias(tmp, phys);
static pte_t *kmap_pte;
+#if DCACHE_WAY_SIZE > PAGE_SIZE
+unsigned int last_pkmap_nr_arr[DCACHE_N_COLORS];
+wait_queue_head_t pkmap_map_wait_arr[DCACHE_N_COLORS];
+
+static void __init kmap_waitqueues_init(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(pkmap_map_wait_arr); ++i)
+ init_waitqueue_head(pkmap_map_wait_arr + i);
+}
+#else
+static inline void kmap_waitqueues_init(void)
+{
+}
+#endif
+
+static inline enum fixed_addresses kmap_idx(int type, unsigned long color)
+{
+ return (type + KM_TYPE_NR * smp_processor_id()) * DCACHE_N_COLORS +
+ color;
+}
+
void *kmap_atomic(struct page *page)
{
enum fixed_addresses idx;
unsigned long vaddr;
- int type;
pagefault_disable();
if (!PageHighMem(page))
return page_address(page);
- type = kmap_atomic_idx_push();
- idx = type + KM_TYPE_NR * smp_processor_id();
+ idx = kmap_idx(kmap_atomic_idx_push(),
+ DCACHE_ALIAS(page_to_phys(page)));
vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
#ifdef CONFIG_DEBUG_HIGHMEM
- BUG_ON(!pte_none(*(kmap_pte - idx)));
+ BUG_ON(!pte_none(*(kmap_pte + idx)));
#endif
- set_pte(kmap_pte - idx, mk_pte(page, PAGE_KERNEL_EXEC));
+ set_pte(kmap_pte + idx, mk_pte(page, PAGE_KERNEL_EXEC));
return (void *)vaddr;
}
void __kunmap_atomic(void *kvaddr)
{
- int idx, type;
-
if (kvaddr >= (void *)FIXADDR_START &&
kvaddr < (void *)FIXADDR_TOP) {
- type = kmap_atomic_idx();
- idx = type + KM_TYPE_NR * smp_processor_id();
+ int idx = kmap_idx(kmap_atomic_idx(),
+ DCACHE_ALIAS((unsigned long)kvaddr));
/*
* Force other mappings to Oops if they'll try to access this
* is a bad idea also, in case the page changes cacheability
* attributes or becomes a protected page in a hypervisor.
*/
- pte_clear(&init_mm, kvaddr, kmap_pte - idx);
+ pte_clear(&init_mm, kvaddr, kmap_pte + idx);
local_flush_tlb_kernel_range((unsigned long)kvaddr,
(unsigned long)kvaddr + PAGE_SIZE);
/* cache the first kmap pte */
kmap_vstart = __fix_to_virt(FIX_KMAP_BEGIN);
kmap_pte = kmap_get_fixmap_pte(kmap_vstart);
+ kmap_waitqueues_init();
}
#if (DCACHE_WAY_SIZE > PAGE_SIZE)
/*
- * clear_user_page (void *addr, unsigned long vaddr, struct page *page)
- * a2 a3 a4
+ * clear_page_alias(void *addr, unsigned long paddr)
+ * a2 a3
*/
-ENTRY(clear_user_page)
+ENTRY(clear_page_alias)
entry a1, 32
- /* Mark page dirty and determine alias. */
+ /* Skip setting up a temporary DTLB if not aliased low page. */
- movi a7, (1 << PG_ARCH_1)
- l32i a5, a4, PAGE_FLAGS
- xor a6, a2, a3
- extui a3, a3, PAGE_SHIFT, DCACHE_ALIAS_ORDER
- extui a6, a6, PAGE_SHIFT, DCACHE_ALIAS_ORDER
- or a5, a5, a7
- slli a3, a3, PAGE_SHIFT
- s32i a5, a4, PAGE_FLAGS
+ movi a5, PAGE_OFFSET
+ movi a6, 0
+ beqz a3, 1f
- /* Skip setting up a temporary DTLB if not aliased. */
-
- beqz a6, 1f
-
- /* Invalidate kernel page. */
-
- mov a10, a2
- call8 __invalidate_dcache_page
-
- /* Setup a temporary DTLB with the color of the VPN */
-
- movi a4, ((PAGE_KERNEL | _PAGE_HW_WRITE) - PAGE_OFFSET) & 0xffffffff
- movi a5, TLBTEMP_BASE_1 # virt
- add a6, a2, a4 # ppn
- add a2, a5, a3 # add 'color'
+ /* Setup a temporary DTLB for the addr. */
+ addi a6, a3, (PAGE_KERNEL | _PAGE_HW_WRITE)
+ mov a4, a2
wdtlb a6, a2
dsync
/* We need to invalidate the temporary idtlb entry, if any. */
-1: addi a2, a2, -PAGE_SIZE
- idtlb a2
+1: idtlb a4
dsync
retw
-ENDPROC(clear_user_page)
+ENDPROC(clear_page_alias)
/*
- * copy_page_user (void *to, void *from, unsigned long vaddr, struct page *page)
- * a2 a3 a4 a5
+ * copy_page_alias(void *to, void *from,
+ * a2 a3
+ * unsigned long to_paddr, unsigned long from_paddr)
+ * a4 a5
*/
-ENTRY(copy_user_page)
+ENTRY(copy_page_alias)
entry a1, 32
- /* Mark page dirty and determine alias for destination. */
-
- movi a8, (1 << PG_ARCH_1)
- l32i a9, a5, PAGE_FLAGS
- xor a6, a2, a4
- xor a7, a3, a4
- extui a4, a4, PAGE_SHIFT, DCACHE_ALIAS_ORDER
- extui a6, a6, PAGE_SHIFT, DCACHE_ALIAS_ORDER
- extui a7, a7, PAGE_SHIFT, DCACHE_ALIAS_ORDER
- or a9, a9, a8
- slli a4, a4, PAGE_SHIFT
- s32i a9, a5, PAGE_FLAGS
- movi a5, ((PAGE_KERNEL | _PAGE_HW_WRITE) - PAGE_OFFSET) & 0xffffffff
-
- beqz a6, 1f
-
- /* Invalidate dcache */
-
- mov a10, a2
- call8 __invalidate_dcache_page
+ /* Skip setting up a temporary DTLB for destination if not aliased. */
- /* Setup a temporary DTLB with a matching color. */
+ movi a6, 0
+ movi a7, 0
+ beqz a4, 1f
- movi a8, TLBTEMP_BASE_1 # base
- add a6, a2, a5 # ppn
- add a2, a8, a4 # add 'color'
+ /* Setup a temporary DTLB for destination. */
+ addi a6, a4, (PAGE_KERNEL | _PAGE_HW_WRITE)
wdtlb a6, a2
dsync
- /* Skip setting up a temporary DTLB for destination if not aliased. */
+ /* Skip setting up a temporary DTLB for source if not aliased. */
-1: beqz a7, 1f
+1: beqz a5, 1f
- /* Setup a temporary DTLB with a matching color. */
+ /* Setup a temporary DTLB for source. */
- movi a8, TLBTEMP_BASE_2 # base
- add a7, a3, a5 # ppn
- add a3, a8, a4
+ addi a7, a5, PAGE_KERNEL
addi a8, a3, 1 # way1
wdtlb a7, a8
retw
-ENDPROC(copy_user_page)
+ENDPROC(copy_page_alias)
#endif
retw
ENDPROC(__flush_invalidate_dcache_page_alias)
+
+/*
+ * void __invalidate_dcache_page_alias (addr, phys)
+ * a2 a3
+ */
+
+ENTRY(__invalidate_dcache_page_alias)
+
+ entry sp, 16
+
+ movi a7, 0 # required for exception handler
+ addi a6, a3, (PAGE_KERNEL | _PAGE_HW_WRITE)
+ mov a4, a2
+ wdtlb a6, a2
+ dsync
+
+ ___invalidate_dcache_page a2 a3
+
+ idtlb a4
+ dsync
+
+ retw
+
+ENDPROC(__invalidate_dcache_page_alias)
#endif
ENTRY(__tlbtemp_mapping_itlb)
#include <asm/io.h>
#if defined(CONFIG_HIGHMEM)
-static void * __init init_pmd(unsigned long vaddr)
+static void * __init init_pmd(unsigned long vaddr, unsigned long n_pages)
{
pgd_t *pgd = pgd_offset_k(vaddr);
pmd_t *pmd = pmd_offset(pgd, vaddr);
+ pte_t *pte;
+ unsigned long i;
- if (pmd_none(*pmd)) {
- unsigned i;
- pte_t *pte = alloc_bootmem_low_pages(PAGE_SIZE);
+ n_pages = ALIGN(n_pages, PTRS_PER_PTE);
- for (i = 0; i < 1024; i++)
- pte_clear(NULL, 0, pte + i);
+ pr_debug("%s: vaddr: 0x%08lx, n_pages: %ld\n",
+ __func__, vaddr, n_pages);
- set_pmd(pmd, __pmd(((unsigned long)pte) & PAGE_MASK));
- BUG_ON(pte != pte_offset_kernel(pmd, 0));
- pr_debug("%s: vaddr: 0x%08lx, pmd: 0x%p, pte: 0x%p\n",
- __func__, vaddr, pmd, pte);
- return pte;
- } else {
- return pte_offset_kernel(pmd, 0);
+ pte = alloc_bootmem_low_pages(n_pages * sizeof(pte_t));
+
+ for (i = 0; i < n_pages; ++i)
+ pte_clear(NULL, 0, pte + i);
+
+ for (i = 0; i < n_pages; i += PTRS_PER_PTE, ++pmd) {
+ pte_t *cur_pte = pte + i;
+
+ BUG_ON(!pmd_none(*pmd));
+ set_pmd(pmd, __pmd(((unsigned long)cur_pte) & PAGE_MASK));
+ BUG_ON(cur_pte != pte_offset_kernel(pmd, 0));
+ pr_debug("%s: pmd: 0x%p, pte: 0x%p\n",
+ __func__, pmd, cur_pte);
}
+ return pte;
}
static void __init fixedrange_init(void)
{
- BUILD_BUG_ON(FIXADDR_SIZE > PMD_SIZE);
- init_pmd(__fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK);
+ init_pmd(__fix_to_virt(0), __end_of_fixed_addresses);
}
#endif
memset(swapper_pg_dir, 0, PAGE_SIZE);
#ifdef CONFIG_HIGHMEM
fixedrange_init();
- pkmap_page_table = init_pmd(PKMAP_BASE);
+ pkmap_page_table = init_pmd(PKMAP_BASE, LAST_PKMAP);
kmap_init();
#endif
}
*/
if (error) {
bio->bi_end_io = bip->bip_end_io;
- bio_endio(bio, error);
+ bio_endio_nodec(bio, error);
return;
}
rq->__sector = (sector_t) -1;
rq->bio = rq->biotail = NULL;
memset(rq->__cmd, 0, sizeof(rq->__cmd));
- rq->cmd = rq->__cmd;
}
EXPORT_SYMBOL(blk_rq_set_block_pc);
*/
void blk_mq_freeze_queue(struct request_queue *q)
{
+ bool freeze;
+
spin_lock_irq(q->queue_lock);
- q->mq_freeze_depth++;
+ freeze = !q->mq_freeze_depth++;
spin_unlock_irq(q->queue_lock);
- percpu_ref_kill(&q->mq_usage_counter);
- blk_mq_run_queues(q, false);
+ if (freeze) {
+ percpu_ref_kill(&q->mq_usage_counter);
+ blk_mq_run_queues(q, false);
+ }
wait_event(q->mq_freeze_wq, percpu_ref_is_zero(&q->mq_usage_counter));
}
static void blk_mq_unfreeze_queue(struct request_queue *q)
{
- bool wake = false;
+ bool wake;
spin_lock_irq(q->queue_lock);
wake = !--q->mq_freeze_depth;
/* tag was already set */
rq->errors = 0;
+ rq->cmd = rq->__cmd;
+
rq->extra_len = 0;
rq->sense_len = 0;
rq->resid_len = 0;
blk_account_io_start(rq, 1);
}
+static inline bool hctx_allow_merges(struct blk_mq_hw_ctx *hctx)
+{
+ return (hctx->flags & BLK_MQ_F_SHOULD_MERGE) &&
+ !blk_queue_nomerges(hctx->queue);
+}
+
static inline bool blk_mq_merge_queue_io(struct blk_mq_hw_ctx *hctx,
struct blk_mq_ctx *ctx,
struct request *rq, struct bio *bio)
{
- struct request_queue *q = hctx->queue;
-
- if (!(hctx->flags & BLK_MQ_F_SHOULD_MERGE)) {
+ if (!hctx_allow_merges(hctx)) {
blk_mq_bio_to_request(rq, bio);
spin_lock(&ctx->lock);
insert_rq:
spin_unlock(&ctx->lock);
return false;
} else {
+ struct request_queue *q = hctx->queue;
+
spin_lock(&ctx->lock);
if (!blk_mq_attempt_merge(q, ctx, bio)) {
blk_mq_bio_to_request(rq, bio);
hctx->tags = set->tags[i];
/*
- * Allocate space for all possible cpus to avoid allocation in
+ * Allocate space for all possible cpus to avoid allocation at
* runtime
*/
hctx->ctxs = kmalloc_node(nr_cpu_ids * sizeof(void *),
queue_for_each_hw_ctx(q, hctx, i) {
/*
- * If not software queues are mapped to this hardware queue,
- * disable it and free the request entries
+ * If no software queues are mapped to this hardware queue,
+ * disable it and free the request entries.
*/
if (!hctx->nr_ctx) {
struct blk_mq_tag_set *set = q->tag_set;
{
struct blk_mq_tag_set *set = q->tag_set;
- blk_mq_freeze_queue(q);
-
mutex_lock(&set->tag_list_lock);
list_del_init(&q->tag_set_list);
blk_mq_update_tag_set_depth(set);
mutex_unlock(&set->tag_list_lock);
-
- blk_mq_unfreeze_queue(q);
}
static void blk_mq_add_queue_tag_set(struct blk_mq_tag_set *set,
rb_insert_color(&cfqg->rb_node, &st->rb);
}
+/*
+ * This has to be called only on activation of cfqg
+ */
static void
cfq_update_group_weight(struct cfq_group *cfqg)
{
- BUG_ON(!RB_EMPTY_NODE(&cfqg->rb_node));
-
if (cfqg->new_weight) {
cfqg->weight = cfqg->new_weight;
cfqg->new_weight = 0;
}
+}
+
+static void
+cfq_update_group_leaf_weight(struct cfq_group *cfqg)
+{
+ BUG_ON(!RB_EMPTY_NODE(&cfqg->rb_node));
if (cfqg->new_leaf_weight) {
cfqg->leaf_weight = cfqg->new_leaf_weight;
/* add to the service tree */
BUG_ON(!RB_EMPTY_NODE(&cfqg->rb_node));
- cfq_update_group_weight(cfqg);
+ /*
+ * Update leaf_weight. We cannot update weight at this point
+ * because cfqg might already have been activated and is
+ * contributing its current weight to the parent's child_weight.
+ */
+ cfq_update_group_leaf_weight(cfqg);
__cfq_group_service_tree_add(st, cfqg);
/*
*/
while ((parent = cfqg_parent(pos))) {
if (propagate) {
+ cfq_update_group_weight(pos);
propagate = !parent->nr_active++;
parent->children_weight += pos->weight;
}
r = blk_rq_unmap_user(bio);
if (!ret)
ret = r;
- blk_put_request(rq);
return ret;
}
if (hdr->interface_id != 'S')
return -EINVAL;
- if (hdr->cmd_len > BLK_MAX_CDB)
- return -EINVAL;
if (hdr->dxfer_len > (queue_max_hw_sectors(q) << 9))
return -EIO;
if (hdr->flags & SG_FLAG_Q_AT_HEAD)
at_head = 1;
+ ret = -ENOMEM;
rq = blk_get_request(q, writing ? WRITE : READ, GFP_KERNEL);
if (!rq)
- return -ENOMEM;
+ goto out;
blk_rq_set_block_pc(rq);
- if (blk_fill_sghdr_rq(q, rq, hdr, mode)) {
- blk_put_request(rq);
- return -EFAULT;
+ if (hdr->cmd_len > BLK_MAX_CDB) {
+ rq->cmd = kzalloc(hdr->cmd_len, GFP_KERNEL);
+ if (!rq->cmd)
+ goto out_put_request;
}
+ ret = -EFAULT;
+ if (blk_fill_sghdr_rq(q, rq, hdr, mode))
+ goto out_free_cdb;
+
+ ret = 0;
if (hdr->iovec_count) {
size_t iov_data_len;
struct iovec *iov = NULL;
0, NULL, &iov);
if (ret < 0) {
kfree(iov);
- goto out;
+ goto out_free_cdb;
}
iov_data_len = ret;
GFP_KERNEL);
if (ret)
- goto out;
+ goto out_free_cdb;
bio = rq->bio;
memset(sense, 0, sizeof(sense));
hdr->duration = jiffies_to_msecs(jiffies - start_time);
- return blk_complete_sghdr_rq(rq, hdr, bio);
-out:
+ ret = blk_complete_sghdr_rq(rq, hdr, bio);
+
+out_free_cdb:
+ if (rq->cmd != rq->__cmd)
+ kfree(rq->cmd);
+out_put_request:
blk_put_request(rq);
+out:
return ret;
}
}
rq = blk_get_request(q, in_len ? WRITE : READ, __GFP_WAIT);
+ if (!rq) {
+ err = -ENOMEM;
+ goto error;
+ }
+ blk_rq_set_block_pc(rq);
cmdlen = COMMAND_SIZE(opcode);
memset(sense, 0, sizeof(sense));
rq->sense = sense;
rq->sense_len = 0;
- blk_rq_set_block_pc(rq);
blk_execute_rq(q, disk, rq, 0);
error:
kfree(buffer);
- blk_put_request(rq);
+ if (rq)
+ blk_put_request(rq);
return err;
}
EXPORT_SYMBOL_GPL(sg_scsi_ioctl);
.setup = lpss_i2c_setup,
};
+static struct lpss_shared_clock bsw_pwm_clock = {
+ .name = "pwm_clk",
+ .rate = 19200000,
+};
+
+static struct lpss_device_desc bsw_pwm_dev_desc = {
+ .clk_required = true,
+ .save_ctx = true,
+ .shared_clock = &bsw_pwm_clock,
+};
+
#else
#define LPSS_ADDR(desc) (0UL)
{ "INT33B2", },
{ "INT33FC", },
+ /* Braswell LPSS devices */
+ { "80862288", LPSS_ADDR(bsw_pwm_dev_desc) },
+ { "8086228A", LPSS_ADDR(byt_uart_dev_desc) },
+ { "8086228E", LPSS_ADDR(byt_spi_dev_desc) },
+ { "808622C1", LPSS_ADDR(byt_i2c_dev_desc) },
+
{ "INT3430", LPSS_ADDR(lpt_dev_desc) },
{ "INT3431", LPSS_ADDR(lpt_dev_desc) },
{ "INT3432", LPSS_ADDR(lpt_i2c_dev_desc) },
t->rdata[t->ri++] = acpi_ec_read_data(ec);
if (t->rlen == t->ri) {
t->flags |= ACPI_EC_COMMAND_COMPLETE;
+ if (t->command == ACPI_EC_COMMAND_QUERY)
+ pr_debug("hardware QR_EC completion\n");
wakeup = true;
}
} else
}
return wakeup;
} else {
- if ((status & ACPI_EC_FLAG_IBF) == 0) {
+ /*
+ * There is firmware refusing to respond QR_EC when SCI_EVT
+ * is not set, for which case, we complete the QR_EC
+ * without issuing it to the firmware.
+ * https://bugzilla.kernel.org/show_bug.cgi?id=86211
+ */
+ if (!(status & ACPI_EC_FLAG_SCI) &&
+ (t->command == ACPI_EC_COMMAND_QUERY)) {
+ t->flags |= ACPI_EC_COMMAND_POLL;
+ t->rdata[t->ri++] = 0x00;
+ t->flags |= ACPI_EC_COMMAND_COMPLETE;
+ pr_debug("software QR_EC completion\n");
+ wakeup = true;
+ } else if ((status & ACPI_EC_FLAG_IBF) == 0) {
acpi_ec_write_cmd(ec, t->command);
t->flags |= ACPI_EC_COMMAND_POLL;
} else
/* following two actions should be kept atomic */
ec->curr = t;
start_transaction(ec);
- if (ec->curr->command == ACPI_EC_COMMAND_QUERY)
- clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags);
spin_unlock_irqrestore(&ec->lock, tmp);
ret = ec_poll(ec);
spin_lock_irqsave(&ec->lock, tmp);
+ if (ec->curr->command == ACPI_EC_COMMAND_QUERY)
+ clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags);
ec->curr = NULL;
spin_unlock_irqrestore(&ec->lock, tmp);
return ret;
/* Keep IOAPIC pin configuration when suspending */
if (dev->dev.power.is_prepared)
return;
+#ifdef CONFIG_PM_RUNTIME
+ if (dev->dev.power.runtime_status == RPM_SUSPENDING)
+ return;
+#endif
entry = acpi_pci_irq_lookup(dev, pin);
if (!entry)
device->driver->ops.notify(device, event);
}
-static acpi_status acpi_device_notify_fixed(void *data)
+static void acpi_device_notify_fixed(void *data)
{
struct acpi_device *device = data;
/* Fixed hardware devices have no handles */
acpi_device_notify(NULL, ACPI_FIXED_HARDWARE_EVENT, device);
+}
+
+static acpi_status acpi_device_fixed_event(void *data)
+{
+ acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_device_notify_fixed, data);
return AE_OK;
}
if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON)
status =
acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
- acpi_device_notify_fixed,
+ acpi_device_fixed_event,
device);
else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON)
status =
acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
- acpi_device_notify_fixed,
+ acpi_device_fixed_event,
device);
else
status = acpi_install_notify_handler(device->handle,
{
if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON)
acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
- acpi_device_notify_fixed);
+ acpi_device_fixed_event);
else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON)
acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
- acpi_device_notify_fixed);
+ acpi_device_fixed_event);
else
acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
acpi_device_notify);
struct acpi_driver *acpi_drv = to_acpi_driver(dev->driver);
int ret;
- if (acpi_dev->handler)
+ if (acpi_dev->handler && !acpi_is_pnp_device(acpi_dev))
return -EINVAL;
if (!acpi_drv->ops.add)
int rd_size = CONFIG_BLK_DEV_RAM_SIZE;
static int max_part;
static int part_shift;
+static int part_show = 0;
module_param(rd_nr, int, S_IRUGO);
MODULE_PARM_DESC(rd_nr, "Maximum number of brd devices");
module_param(rd_size, int, S_IRUGO);
MODULE_PARM_DESC(rd_size, "Size of each RAM disk in kbytes.");
module_param(max_part, int, S_IRUGO);
MODULE_PARM_DESC(max_part, "Maximum number of partitions per RAM disk");
+module_param(part_show, int, S_IRUGO);
+MODULE_PARM_DESC(part_show, "Control RAM disk visibility in /proc/partitions");
MODULE_LICENSE("GPL");
MODULE_ALIAS_BLOCKDEV_MAJOR(RAMDISK_MAJOR);
MODULE_ALIAS("rd");
disk->fops = &brd_fops;
disk->private_data = brd;
disk->queue = brd->brd_queue;
- disk->flags |= GENHD_FL_SUPPRESS_PARTITION_INFO;
+ if (!part_show)
+ disk->flags |= GENHD_FL_SUPPRESS_PARTITION_INFO;
sprintf(disk->disk_name, "ram%d", i);
set_capacity(disk, rd_size * 2);
.probe = ace_probe,
.remove = ace_remove,
.driver = {
- .owner = THIS_MODULE,
.name = "xsysace",
.of_match_table = ace_of_match,
},
/* Should NEVER happen. Return bio error if it does. */
if (unlikely(ret)) {
pr_err("Decompression failed! err=%d, page=%u\n", ret, index);
- atomic64_inc(&zram->stats.failed_reads);
return ret;
}
zcomp_strm_release(zram->comp, zstrm);
if (is_partial_io(bvec))
kfree(uncmem);
- if (ret)
- atomic64_inc(&zram->stats.failed_writes);
return ret;
}
ret = zram_bvec_write(zram, bvec, index, offset);
}
+ if (unlikely(ret)) {
+ if (rw == READ)
+ atomic64_inc(&zram->stats.failed_reads);
+ else
+ atomic64_inc(&zram->stats.failed_writes);
+ }
+
return ret;
}
atomic64_t compr_data_size; /* compressed size of pages stored */
atomic64_t num_reads; /* failed + successful */
atomic64_t num_writes; /* --do-- */
- atomic64_t failed_reads; /* should NEVER! happen */
+ atomic64_t failed_reads; /* can happen when memory is too low */
atomic64_t failed_writes; /* can happen when memory is too low */
atomic64_t invalid_io; /* non-page-aligned I/O requests */
atomic64_t notify_free; /* no. of swap slot free notifications */
ICPU(0x3f, core_params),
ICPU(0x45, core_params),
ICPU(0x46, core_params),
+ ICPU(0x4c, byt_params),
ICPU(0x4f, core_params),
ICPU(0x56, core_params),
{}
add_timer_on(&cpu->timer, cpunum);
- pr_info("Intel pstate controlling: cpu %d\n", cpunum);
+ pr_debug("Intel pstate controlling: cpu %d\n", cpunum);
return 0;
}
return val >> 8;
}
-static int __init s5pv210_cpu_init(struct cpufreq_policy *policy)
+static int s5pv210_cpu_init(struct cpufreq_policy *policy)
{
unsigned long mem_type;
int ret;
return idx;
}
-static int __init bl_idle_driver_init(struct cpuidle_driver *drv, int cpu_id)
+static int __init bl_idle_driver_init(struct cpuidle_driver *drv, int part_id)
{
- struct cpuinfo_arm *cpu_info;
struct cpumask *cpumask;
- unsigned long cpuid;
int cpu;
cpumask = kzalloc(cpumask_size(), GFP_KERNEL);
if (!cpumask)
return -ENOMEM;
- for_each_possible_cpu(cpu) {
- cpu_info = &per_cpu(cpu_data, cpu);
- cpuid = is_smp() ? cpu_info->cpuid : read_cpuid_id();
-
- /* read cpu id part number */
- if ((cpuid & 0xFFF0) == cpu_id)
+ for_each_possible_cpu(cpu)
+ if (smp_cpuid_part(cpu) == part_id)
cpumask_set_cpu(cpu, cpumask);
- }
drv->cpumask = cpumask;
EXPORT_TRACEPOINT_SYMBOL(fence_annotate_wait_on);
EXPORT_TRACEPOINT_SYMBOL(fence_emit);
-/**
+/*
* fence context counter: each execution context should have its own
* fence context, this allows checking if fences belong to the same
* context or not. One device can have multiple separate contexts,
{0x25, 0x65, 0x80}, /* 16: VCLK88.75 */
{0x77, 0x58, 0x80}, /* 17: VCLK119 */
{0x32, 0x67, 0x80}, /* 18: VCLK85_5 */
+ {0x6a, 0x6d, 0x80}, /* 19: VCLK97_75 */
};
static struct ast_vbios_stdtable vbios_stdtable[] = {
return -EINVAL;
/* overflow checks for 32bit size calculations */
+ /* NOTE: DIV_ROUND_UP() can overflow */
cpp = DIV_ROUND_UP(args->bpp, 8);
- if (cpp > 0xffffffffU / args->width)
+ if (!cpp || cpp > 0xffffffffU / args->width)
return -EINVAL;
stride = cpp * args->width;
if (args->height > 0xffffffffU / stride)
struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
DBG("%s", mdp4_crtc->name);
/* make sure we hold a ref to mdp clks while setting up mode: */
+ drm_crtc_vblank_get(crtc);
mdp4_enable(get_kms(crtc));
mdp4_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
}
crtc_flush(crtc);
/* drop the ref to mdp clk's that we got in prepare: */
mdp4_disable(get_kms(crtc));
+ drm_crtc_vblank_put(crtc);
}
static int mdp4_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
for (i = 0; i < ARRAY_SIZE(devnames); i++) {
struct device *dev;
- int ret;
dev = bus_find_device_by_name(&platform_bus_type,
NULL, devnames[i]);
if (!dev) {
- dev_info(master, "still waiting for %s\n", devnames[i]);
+ dev_info(&pdev->dev, "still waiting for %s\n", devnames[i]);
return -EPROBE_DEFER;
}
ret = msm_gem_get_iova_locked(fbdev->bo, 0, &paddr);
if (ret) {
dev_err(dev->dev, "failed to get buffer obj iova: %d\n", ret);
- goto fail;
+ goto fail_unlock;
}
fbi = framebuffer_alloc(0, dev->dev);
static int msm_fault_handler(struct iommu_domain *iommu, struct device *dev,
unsigned long iova, int flags, void *arg)
{
- DBG("*** fault: iova=%08lx, flags=%d", iova, flags);
- return -ENOSYS;
+ pr_warn_ratelimited("*** fault: iova=%08lx, flags=%d\n", iova, flags);
+ return 0;
}
static int msm_iommu_attach(struct msm_mmu *mmu, const char **names, int cnt)
WREG32(0x15D8, 0);
WREG32(0x15DC, 0);
- /* empty context1-15 */
- /* FIXME start with 4G, once using 2 level pt switch to full
- * vm size space
- */
+ /* restore context1-15 */
/* set vm size, must be a multiple of 4 */
WREG32(VM_CONTEXT1_PAGE_TABLE_START_ADDR, 0);
WREG32(VM_CONTEXT1_PAGE_TABLE_END_ADDR, rdev->vm_manager.max_pfn);
for (i = 1; i < 16; i++) {
if (i < 8)
WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (i << 2),
- rdev->gart.table_addr >> 12);
+ rdev->vm_manager.saved_table_addr[i]);
else
WREG32(VM_CONTEXT8_PAGE_TABLE_BASE_ADDR + ((i - 8) << 2),
- rdev->gart.table_addr >> 12);
+ rdev->vm_manager.saved_table_addr[i]);
}
/* enable context1-15 */
*/
static void cik_pcie_gart_disable(struct radeon_device *rdev)
{
+ unsigned i;
+
+ for (i = 1; i < 16; ++i) {
+ uint32_t reg;
+ if (i < 8)
+ reg = VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (i << 2);
+ else
+ reg = VM_CONTEXT8_PAGE_TABLE_BASE_ADDR + ((i - 8) << 2);
+ rdev->vm_manager.saved_table_addr[i] = RREG32(reg);
+ }
+
/* Disable all tables */
WREG32(VM_CONTEXT0_CNTL, 0);
WREG32(VM_CONTEXT1_CNTL, 0);
int ret, i;
u16 tmp16;
+ if (pci_is_root_bus(rdev->pdev->bus))
+ return;
+
if (radeon_pcie_gen2 == 0)
return;
if (orig != data)
WREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL, data);
- if (!disable_clkreq) {
+ if (!disable_clkreq &&
+ !pci_is_root_bus(rdev->pdev->bus)) {
struct pci_dev *root = rdev->pdev->bus->self;
u32 lnkcap;
WREG32(VM_CONTEXT0_PAGE_TABLE_START_ADDR + (i << 2), 0);
WREG32(VM_CONTEXT0_PAGE_TABLE_END_ADDR + (i << 2), rdev->vm_manager.max_pfn);
WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (i << 2),
- rdev->gart.table_addr >> 12);
+ rdev->vm_manager.saved_table_addr[i]);
}
/* enable context1-7 */
static void cayman_pcie_gart_disable(struct radeon_device *rdev)
{
+ unsigned i;
+
+ for (i = 1; i < 8; ++i) {
+ rdev->vm_manager.saved_table_addr[i] = RREG32(
+ VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (i << 2));
+ }
+
/* Disable all tables */
WREG32(VM_CONTEXT0_CNTL, 0);
WREG32(VM_CONTEXT1_CNTL, 0);
{
u32 tiling_config;
u32 ramcfg;
- u32 cc_rb_backend_disable;
u32 cc_gc_shader_pipe_config;
u32 tmp;
int i, j;
}
tiling_config |= BANK_SWAPS(1);
- cc_rb_backend_disable = RREG32(CC_RB_BACKEND_DISABLE) & 0x00ff0000;
- tmp = R6XX_MAX_BACKENDS -
- r600_count_pipe_bits((cc_rb_backend_disable >> 16) & R6XX_MAX_BACKENDS_MASK);
- if (tmp < rdev->config.r600.max_backends) {
- rdev->config.r600.max_backends = tmp;
- }
-
cc_gc_shader_pipe_config = RREG32(CC_GC_SHADER_PIPE_CONFIG) & 0x00ffff00;
- tmp = R6XX_MAX_PIPES -
- r600_count_pipe_bits((cc_gc_shader_pipe_config >> 8) & R6XX_MAX_PIPES_MASK);
- if (tmp < rdev->config.r600.max_pipes) {
- rdev->config.r600.max_pipes = tmp;
- }
- tmp = R6XX_MAX_SIMDS -
- r600_count_pipe_bits((cc_gc_shader_pipe_config >> 16) & R6XX_MAX_SIMDS_MASK);
- if (tmp < rdev->config.r600.max_simds) {
- rdev->config.r600.max_simds = tmp;
- }
tmp = rdev->config.r600.max_simds -
r600_count_pipe_bits((cc_gc_shader_pipe_config >> 16) & R6XX_MAX_SIMDS_MASK);
rdev->config.r600.active_simds = tmp;
disabled_rb_mask = (RREG32(CC_RB_BACKEND_DISABLE) >> 16) & R6XX_MAX_BACKENDS_MASK;
+ tmp = 0;
+ for (i = 0; i < rdev->config.r600.max_backends; i++)
+ tmp |= (1 << i);
+ /* if all the backends are disabled, fix it up here */
+ if ((disabled_rb_mask & tmp) == tmp) {
+ for (i = 0; i < rdev->config.r600.max_backends; i++)
+ disabled_rb_mask &= ~(1 << i);
+ }
tmp = (tiling_config & PIPE_TILING__MASK) >> PIPE_TILING__SHIFT;
tmp = r6xx_remap_render_backend(rdev, tmp, rdev->config.r600.max_backends,
R6XX_MAX_BACKENDS, disabled_rb_mask);
u64 vram_base_offset;
/* is vm enabled? */
bool enabled;
+ /* for hw to save the PD addr on suspend/resume */
+ uint32_t saved_table_addr[RADEON_NUM_VM];
};
/*
u32 hdp_host_path_cntl;
u32 sq_dyn_gpr_size_simd_ab_0;
u32 gb_tiling_config = 0;
- u32 cc_rb_backend_disable = 0;
u32 cc_gc_shader_pipe_config = 0;
u32 mc_arb_ramcfg;
u32 db_debug4, tmp;
WREG32(SPI_CONFIG_CNTL, 0);
}
- cc_rb_backend_disable = RREG32(CC_RB_BACKEND_DISABLE) & 0x00ff0000;
- tmp = R7XX_MAX_BACKENDS - r600_count_pipe_bits(cc_rb_backend_disable >> 16);
- if (tmp < rdev->config.rv770.max_backends) {
- rdev->config.rv770.max_backends = tmp;
- }
-
cc_gc_shader_pipe_config = RREG32(CC_GC_SHADER_PIPE_CONFIG) & 0xffffff00;
- tmp = R7XX_MAX_PIPES - r600_count_pipe_bits((cc_gc_shader_pipe_config >> 8) & R7XX_MAX_PIPES_MASK);
- if (tmp < rdev->config.rv770.max_pipes) {
- rdev->config.rv770.max_pipes = tmp;
- }
- tmp = R7XX_MAX_SIMDS - r600_count_pipe_bits((cc_gc_shader_pipe_config >> 16) & R7XX_MAX_SIMDS_MASK);
- if (tmp < rdev->config.rv770.max_simds) {
- rdev->config.rv770.max_simds = tmp;
- }
tmp = rdev->config.rv770.max_simds -
r600_count_pipe_bits((cc_gc_shader_pipe_config >> 16) & R7XX_MAX_SIMDS_MASK);
rdev->config.rv770.active_simds = tmp;
rdev->config.rv770.tiling_npipes = rdev->config.rv770.max_tile_pipes;
disabled_rb_mask = (RREG32(CC_RB_BACKEND_DISABLE) >> 16) & R7XX_MAX_BACKENDS_MASK;
+ tmp = 0;
+ for (i = 0; i < rdev->config.rv770.max_backends; i++)
+ tmp |= (1 << i);
+ /* if all the backends are disabled, fix it up here */
+ if ((disabled_rb_mask & tmp) == tmp) {
+ for (i = 0; i < rdev->config.rv770.max_backends; i++)
+ disabled_rb_mask &= ~(1 << i);
+ }
tmp = (gb_tiling_config & PIPE_TILING__MASK) >> PIPE_TILING__SHIFT;
tmp = r6xx_remap_render_backend(rdev, tmp, rdev->config.rv770.max_backends,
R7XX_MAX_BACKENDS, disabled_rb_mask);
for (i = 1; i < 16; i++) {
if (i < 8)
WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (i << 2),
- rdev->gart.table_addr >> 12);
+ rdev->vm_manager.saved_table_addr[i]);
else
WREG32(VM_CONTEXT8_PAGE_TABLE_BASE_ADDR + ((i - 8) << 2),
- rdev->gart.table_addr >> 12);
+ rdev->vm_manager.saved_table_addr[i]);
}
/* enable context1-15 */
static void si_pcie_gart_disable(struct radeon_device *rdev)
{
+ unsigned i;
+
+ for (i = 1; i < 16; ++i) {
+ uint32_t reg;
+ if (i < 8)
+ reg = VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (i << 2);
+ else
+ reg = VM_CONTEXT8_PAGE_TABLE_BASE_ADDR + ((i - 8) << 2);
+ rdev->vm_manager.saved_table_addr[i] = RREG32(reg);
+ }
+
/* Disable all tables */
WREG32(VM_CONTEXT0_CNTL, 0);
WREG32(VM_CONTEXT1_CNTL, 0);
int ret, i;
u16 tmp16;
+ if (pci_is_root_bus(rdev->pdev->bus))
+ return;
+
if (radeon_pcie_gen2 == 0)
return;
if (orig != data)
WREG32_PIF_PHY1(PB1_PIF_CNTL, data);
- if (!disable_clkreq) {
+ if (!disable_clkreq &&
+ !pci_is_root_bus(rdev->pdev->bus)) {
struct pci_dev *root = rdev->pdev->bus->self;
u32 lnkcap;
config DRM_STI
tristate "DRM Support for STMicroelectronics SoC stiH41x Series"
depends on DRM && (SOC_STIH415 || SOC_STIH416 || ARCH_MULTIPLATFORM)
+ select RESET_CONTROLLER
select DRM_KMS_HELPER
select DRM_GEM_CMA_HELPER
select DRM_KMS_CMA_HELPER
master = platform_device_register_resndata(dev,
DRIVER_NAME "__master", -1,
NULL, 0, NULL, 0);
- if (!master)
- return -EINVAL;
+ if (IS_ERR(master))
+ return PTR_ERR(master);
platform_set_drvdata(pdev, master);
return 0;
return -ENOMEM;
}
hda->regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
- if (IS_ERR(hda->regs))
- return PTR_ERR(hda->regs);
+ if (!hda->regs)
+ return -ENOMEM;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"video-dacs-ctrl");
if (res) {
hda->video_dacs_ctrl = devm_ioremap_nocache(dev, res->start,
resource_size(res));
- if (IS_ERR(hda->video_dacs_ctrl))
- return PTR_ERR(hda->video_dacs_ctrl);
+ if (!hda->video_dacs_ctrl)
+ return -ENOMEM;
} else {
/* If no existing video-dacs-ctrl resource continue the probe */
DRM_DEBUG_DRIVER("No video-dacs-ctrl resource\n");
return 0;
}
-static struct of_device_id hda_of_match[] = {
+static const struct of_device_id hda_of_match[] = {
{ .compatible = "st,stih416-hda", },
{ .compatible = "st,stih407-hda", },
{ /* end node */ }
.unbind = sti_hdmi_unbind,
};
-static struct of_device_id hdmi_of_match[] = {
+static const struct of_device_id hdmi_of_match[] = {
{
.compatible = "st,stih416-hdmi",
.data = &tx3g0c55phy_ops,
return -ENOMEM;
}
hdmi->regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
- if (IS_ERR(hdmi->regs))
- return PTR_ERR(hdmi->regs);
+ if (!hdmi->regs)
+ return -ENOMEM;
if (of_device_is_compatible(np, "st,stih416-hdmi")) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
}
hdmi->syscfg = devm_ioremap_nocache(dev, res->start,
resource_size(res));
- if (IS_ERR(hdmi->syscfg))
- return PTR_ERR(hdmi->syscfg);
+ if (!hdmi->syscfg)
+ return -ENOMEM;
}
return -ENOMEM;
}
tvout->regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
- if (IS_ERR(tvout->regs))
- return PTR_ERR(tvout->regs);
+ if (!tvout->regs)
+ return -ENOMEM;
/* get reset resources */
tvout->reset = devm_reset_control_get(dev, "tvout");
return 0;
}
-static struct of_device_id tvout_of_match[] = {
+static const struct of_device_id tvout_of_match[] = {
{ .compatible = "st,stih416-tvout", },
{ .compatible = "st,stih407-tvout", },
{ /* end node */ }
struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
struct dj_report *dj_report = (struct dj_report *) data;
unsigned long flags;
- bool report_processed = false;
dbg_hid("%s, size:%d\n", __func__, size);
* device (via hid_input_report() ) and return 1 so hid-core does not do
* anything else with it.
*/
+
+ /* case 1) */
+ if (data[0] != REPORT_ID_DJ_SHORT)
+ return false;
+
if ((dj_report->device_index < DJ_DEVICE_INDEX_MIN) ||
(dj_report->device_index > DJ_DEVICE_INDEX_MAX)) {
- dev_err(&hdev->dev, "%s: invalid device index:%d\n",
+ /*
+ * Device index is wrong, bail out.
+ * This driver can ignore safely the receiver notifications,
+ * so ignore those reports too.
+ */
+ if (dj_report->device_index != DJ_RECEIVER_INDEX)
+ dev_err(&hdev->dev, "%s: invalid device index:%d\n",
__func__, dj_report->device_index);
return false;
}
spin_lock_irqsave(&djrcv_dev->lock, flags);
- if (dj_report->report_id == REPORT_ID_DJ_SHORT) {
- switch (dj_report->report_type) {
- case REPORT_TYPE_NOTIF_DEVICE_PAIRED:
- case REPORT_TYPE_NOTIF_DEVICE_UNPAIRED:
- logi_dj_recv_queue_notification(djrcv_dev, dj_report);
- break;
- case REPORT_TYPE_NOTIF_CONNECTION_STATUS:
- if (dj_report->report_params[CONNECTION_STATUS_PARAM_STATUS] ==
- STATUS_LINKLOSS) {
- logi_dj_recv_forward_null_report(djrcv_dev, dj_report);
- }
- break;
- default:
- logi_dj_recv_forward_report(djrcv_dev, dj_report);
+ switch (dj_report->report_type) {
+ case REPORT_TYPE_NOTIF_DEVICE_PAIRED:
+ case REPORT_TYPE_NOTIF_DEVICE_UNPAIRED:
+ logi_dj_recv_queue_notification(djrcv_dev, dj_report);
+ break;
+ case REPORT_TYPE_NOTIF_CONNECTION_STATUS:
+ if (dj_report->report_params[CONNECTION_STATUS_PARAM_STATUS] ==
+ STATUS_LINKLOSS) {
+ logi_dj_recv_forward_null_report(djrcv_dev, dj_report);
}
- report_processed = true;
+ break;
+ default:
+ logi_dj_recv_forward_report(djrcv_dev, dj_report);
}
spin_unlock_irqrestore(&djrcv_dev->lock, flags);
- return report_processed;
+ return true;
}
static int logi_dj_probe(struct hid_device *hdev,
#define DJ_MAX_PAIRED_DEVICES 6
#define DJ_MAX_NUMBER_NOTIFICATIONS 8
+#define DJ_RECEIVER_INDEX 0
#define DJ_DEVICE_INDEX_MIN 1
#define DJ_DEVICE_INDEX_MAX 6
if (size < 4 || ((size - 4) % 9) != 0)
return 0;
npoints = (size - 4) / 9;
+ if (npoints > 15) {
+ hid_warn(hdev, "invalid size value (%d) for TRACKPAD_REPORT_ID\n",
+ size);
+ return 0;
+ }
msc->ntouches = 0;
for (ii = 0; ii < npoints; ii++)
magicmouse_emit_touch(msc, ii, data + ii * 9 + 4);
if (size < 6 || ((size - 6) % 8) != 0)
return 0;
npoints = (size - 6) / 8;
+ if (npoints > 15) {
+ hid_warn(hdev, "invalid size value (%d) for MOUSE_REPORT_ID\n",
+ size);
+ return 0;
+ }
msc->ntouches = 0;
for (ii = 0; ii < npoints; ii++)
magicmouse_emit_touch(msc, ii, data + ii * 8 + 6);
if (!data)
return 1;
+ if (size > 64) {
+ hid_warn(hdev, "invalid size value (%d) for picolcd raw event\n",
+ size);
+ return 0;
+ }
+
if (report->id == REPORT_KEY_STATE) {
if (data->input_keys)
ret = picolcd_raw_keypad(data, report, raw_data+1, size-1);
unsigned int key_size, opt_params;
unsigned long long tmpll;
int ret;
+ size_t iv_size_padding;
struct dm_arg_set as;
const char *opt_string;
char dummy;
cc->dmreq_start = sizeof(struct ablkcipher_request);
cc->dmreq_start += crypto_ablkcipher_reqsize(any_tfm(cc));
- cc->dmreq_start = ALIGN(cc->dmreq_start, crypto_tfm_ctx_alignment());
- cc->dmreq_start += crypto_ablkcipher_alignmask(any_tfm(cc)) &
- ~(crypto_tfm_ctx_alignment() - 1);
+ cc->dmreq_start = ALIGN(cc->dmreq_start, __alignof__(struct dm_crypt_request));
+
+ if (crypto_ablkcipher_alignmask(any_tfm(cc)) < CRYPTO_MINALIGN) {
+ /* Allocate the padding exactly */
+ iv_size_padding = -(cc->dmreq_start + sizeof(struct dm_crypt_request))
+ & crypto_ablkcipher_alignmask(any_tfm(cc));
+ } else {
+ /*
+ * If the cipher requires greater alignment than kmalloc
+ * alignment, we don't know the exact position of the
+ * initialization vector. We must assume worst case.
+ */
+ iv_size_padding = crypto_ablkcipher_alignmask(any_tfm(cc));
+ }
cc->req_pool = mempool_create_kmalloc_pool(MIN_IOS, cc->dmreq_start +
- sizeof(struct dm_crypt_request) + cc->iv_size);
+ sizeof(struct dm_crypt_request) + iv_size_padding + cc->iv_size);
if (!cc->req_pool) {
ti->error = "Cannot allocate crypt request mempool";
goto bad;
}
cc->per_bio_data_size = ti->per_bio_data_size =
- sizeof(struct dm_crypt_io) + cc->dmreq_start +
- sizeof(struct dm_crypt_request) + cc->iv_size;
+ ALIGN(sizeof(struct dm_crypt_io) + cc->dmreq_start +
+ sizeof(struct dm_crypt_request) + iv_size_padding + cc->iv_size,
+ ARCH_KMALLOC_MINALIGN);
cc->page_pool = mempool_create_page_pool(MIN_POOL_PAGES, 0);
if (!cc->page_pool) {
if (ret)
return ret;
-#if CONFIG_DEBUG_FS
+#ifdef CONFIG_DEBUG_FS
/* Pass to debugfs */
ab8500_debug_resources[0].start = ab8500->irq;
ab8500_debug_resources[0].end = ab8500->irq;
}
i2c_set_clientdata(client, chip);
- snprintf(client->name, I2C_NAME_SIZE, "Chip_0x%d", client->addr);
+ snprintf(client->name, I2C_NAME_SIZE, "Chip_0x%x", client->addr);
chip->client = client;
/* Reset the chip */
default:
omap->nports = OMAP3_HS_USB_PORTS;
dev_dbg(dev,
- "USB HOST Rev:0x%d not recognized, assuming %d ports\n",
+ "USB HOST Rev:0x%x not recognized, assuming %d ports\n",
omap->usbhs_rev, omap->nports);
break;
}
* above.
*/
static struct twl4030_resconfig omap3_idle_rconfig[] = {
- TWL_REMAP_SLEEP(RES_VAUX1, DEV_GRP_NULL, 0, 0),
- TWL_REMAP_SLEEP(RES_VAUX2, DEV_GRP_NULL, 0, 0),
- TWL_REMAP_SLEEP(RES_VAUX3, DEV_GRP_NULL, 0, 0),
- TWL_REMAP_SLEEP(RES_VAUX4, DEV_GRP_NULL, 0, 0),
- TWL_REMAP_SLEEP(RES_VMMC1, DEV_GRP_NULL, 0, 0),
- TWL_REMAP_SLEEP(RES_VMMC2, DEV_GRP_NULL, 0, 0),
+ TWL_REMAP_SLEEP(RES_VAUX1, TWL4030_RESCONFIG_UNDEF, 0, 0),
+ TWL_REMAP_SLEEP(RES_VAUX2, TWL4030_RESCONFIG_UNDEF, 0, 0),
+ TWL_REMAP_SLEEP(RES_VAUX3, TWL4030_RESCONFIG_UNDEF, 0, 0),
+ TWL_REMAP_SLEEP(RES_VAUX4, TWL4030_RESCONFIG_UNDEF, 0, 0),
+ TWL_REMAP_SLEEP(RES_VMMC1, TWL4030_RESCONFIG_UNDEF, 0, 0),
+ TWL_REMAP_SLEEP(RES_VMMC2, TWL4030_RESCONFIG_UNDEF, 0, 0),
TWL_REMAP_OFF(RES_VPLL1, DEV_GRP_P1, 3, 1),
TWL_REMAP_SLEEP(RES_VPLL2, DEV_GRP_P1, 0, 0),
- TWL_REMAP_SLEEP(RES_VSIM, DEV_GRP_NULL, 0, 0),
- TWL_REMAP_SLEEP(RES_VDAC, DEV_GRP_NULL, 0, 0),
+ TWL_REMAP_SLEEP(RES_VSIM, TWL4030_RESCONFIG_UNDEF, 0, 0),
+ TWL_REMAP_SLEEP(RES_VDAC, TWL4030_RESCONFIG_UNDEF, 0, 0),
TWL_REMAP_SLEEP(RES_VINTANA1, TWL_DEV_GRP_P123, 1, 2),
TWL_REMAP_SLEEP(RES_VINTANA2, TWL_DEV_GRP_P123, 0, 2),
TWL_REMAP_SLEEP(RES_VINTDIG, TWL_DEV_GRP_P123, 1, 2),
TWL_REMAP_SLEEP(RES_VIO, TWL_DEV_GRP_P123, 2, 2),
TWL_REMAP_OFF(RES_VDD1, DEV_GRP_P1, 4, 1),
TWL_REMAP_OFF(RES_VDD2, DEV_GRP_P1, 3, 1),
- TWL_REMAP_SLEEP(RES_VUSB_1V5, DEV_GRP_NULL, 0, 0),
- TWL_REMAP_SLEEP(RES_VUSB_1V8, DEV_GRP_NULL, 0, 0),
+ TWL_REMAP_SLEEP(RES_VUSB_1V5, TWL4030_RESCONFIG_UNDEF, 0, 0),
+ TWL_REMAP_SLEEP(RES_VUSB_1V8, TWL4030_RESCONFIG_UNDEF, 0, 0),
TWL_REMAP_SLEEP(RES_VUSB_3V1, TWL_DEV_GRP_P123, 0, 0),
/* Resource #20 USB charge pump skipped */
TWL_REMAP_SLEEP(RES_REGEN, TWL_DEV_GRP_P123, 2, 1),
cl->timer_count = MEI_CONNECT_TIMEOUT;
list_add_tail(&cb->list, &dev->ctrl_rd_list.list);
} else {
+ cl->state = MEI_FILE_INITIALIZING;
list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
}
ndev = (struct mei_nfc_dev *) cldev->priv_data;
dev = ndev->cl->dev;
+ err = -ENOMEM;
mei_buf = kzalloc(length + MEI_NFC_HEADER_SIZE, GFP_KERNEL);
if (!mei_buf)
- return -ENOMEM;
+ goto out;
hdr = (struct mei_nfc_hci_hdr *) mei_buf;
hdr->cmd = MEI_NFC_CMD_HCI_SEND;
hdr->data_size = length;
memcpy(mei_buf + MEI_NFC_HEADER_SIZE, buf, length);
-
err = __mei_cl_send(ndev->cl, mei_buf, length + MEI_NFC_HEADER_SIZE);
if (err < 0)
- return err;
-
- kfree(mei_buf);
+ goto out;
if (!wait_event_interruptible_timeout(ndev->send_wq,
ndev->recv_req_id == ndev->req_id, HZ)) {
} else {
ndev->req_id++;
}
-
+out:
+ kfree(mei_buf);
return err;
}
u32 val;
val = readl(info->reg.gpmc_ecc_config);
- if (((val >> ECC_CONFIG_CS_SHIFT) & ~CS_MASK) != info->gpmc_cs)
+ if (((val >> ECC_CONFIG_CS_SHIFT) & CS_MASK) != info->gpmc_cs)
return -EINVAL;
/* read ecc result */
}
/* populate MTD interface based on ECC scheme */
- nand_chip->ecc.layout = &omap_oobinfo;
ecclayout = &omap_oobinfo;
switch (info->ecc_opt) {
+ case OMAP_ECC_HAM1_CODE_SW:
+ nand_chip->ecc.mode = NAND_ECC_SOFT;
+ break;
+
case OMAP_ECC_HAM1_CODE_HW:
pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n");
nand_chip->ecc.mode = NAND_ECC_HW;
nand_chip->ecc.priv = nand_bch_init(mtd,
nand_chip->ecc.size,
nand_chip->ecc.bytes,
- &nand_chip->ecc.layout);
+ &ecclayout);
if (!nand_chip->ecc.priv) {
pr_err("nand: error: unable to use s/w BCH library\n");
err = -EINVAL;
nand_chip->ecc.priv = nand_bch_init(mtd,
nand_chip->ecc.size,
nand_chip->ecc.bytes,
- &nand_chip->ecc.layout);
+ &ecclayout);
if (!nand_chip->ecc.priv) {
pr_err("nand: error: unable to use s/w BCH library\n");
err = -EINVAL;
goto return_error;
}
+ if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW)
+ goto scan_tail;
+
/* all OOB bytes from oobfree->offset till end off OOB are free */
ecclayout->oobfree->length = mtd->oobsize - ecclayout->oobfree->offset;
/* check if NAND device's OOB is enough to store ECC signatures */
err = -EINVAL;
goto return_error;
}
+ nand_chip->ecc.layout = ecclayout;
+scan_tail:
/* second phase scan */
if (nand_scan_tail(mtd)) {
err = -ENXIO;
} else
seq_printf(s, " %-9s", chip->get(chip, offset) ? "hi" : "lo");
- if (pctldev)
- mode = abx500_get_mode(pctldev, chip, offset);
+ mode = abx500_get_mode(pctldev, chip, offset);
seq_printf(s, " %s", (mode < 0) ? "unknown" : modes[mode]);
static void at91_pin_dbg(const struct device *dev, const struct at91_pmx_pin *pin)
{
if (pin->mux) {
- dev_dbg(dev, "pio%c%d configured as periph%c with conf = 0x%lu\n",
+ dev_dbg(dev, "pio%c%d configured as periph%c with conf = 0x%lx\n",
pin->bank + 'A', pin->pin, pin->mux - 1 + 'A', pin->conf);
} else {
- dev_dbg(dev, "pio%c%d configured as gpio with conf = 0x%lu\n",
+ dev_dbg(dev, "pio%c%d configured as gpio with conf = 0x%lx\n",
pin->bank + 'A', pin->pin, pin->conf);
}
}
int reg, ret, mask;
unsigned long flags;
u8 bit;
- u32 data;
+ u32 data, rmask;
if (iomux_num > 3)
return -EINVAL;
spin_lock_irqsave(&bank->slock, flags);
data = (mask << (bit + 16));
+ rmask = data | (data >> 16);
data |= (mux & mask) << bit;
- ret = regmap_write(regmap, reg, data);
+ ret = regmap_update_bits(regmap, reg, rmask, data);
spin_unlock_irqrestore(&bank->slock, flags);
struct regmap *regmap;
unsigned long flags;
int reg, ret, i;
- u32 data;
+ u32 data, rmask;
u8 bit;
rk3288_calc_drv_reg_and_bit(bank, pin_num, ®map, ®, &bit);
/* enable the write to the equivalent lower bits */
data = ((1 << RK3288_DRV_BITS_PER_PIN) - 1) << (bit + 16);
+ rmask = data | (data >> 16);
data |= (ret << bit);
- ret = regmap_write(regmap, reg, data);
+ ret = regmap_update_bits(regmap, reg, rmask, data);
spin_unlock_irqrestore(&bank->slock, flags);
return ret;
int reg, ret;
unsigned long flags;
u8 bit;
- u32 data;
+ u32 data, rmask;
dev_dbg(info->dev, "setting pull of GPIO%d-%d to %d\n",
bank->bank_num, pin_num, pull);
/* enable the write to the equivalent lower bits */
data = ((1 << RK3188_PULL_BITS_PER_PIN) - 1) << (bit + 16);
+ rmask = data | (data >> 16);
switch (pull) {
case PIN_CONFIG_BIAS_DISABLE:
return -EINVAL;
}
- ret = regmap_write(regmap, reg, data);
+ ret = regmap_update_bits(regmap, reg, rmask, data);
spin_unlock_irqrestore(&bank->slock, flags);
break;
if (args->args_count <= 0)
return ERR_PTR(-EINVAL);
- if (index > ARRAY_SIZE(padctl->phys))
+ if (index >= ARRAY_SIZE(padctl->phys))
return ERR_PTR(-EINVAL);
return padctl->phys[index];
padctl->provider = devm_of_phy_provider_register(&pdev->dev,
tegra_xusb_padctl_xlate);
- if (err < 0) {
+ if (IS_ERR(padctl->provider)) {
+ err = PTR_ERR(padctl->provider);
dev_err(&pdev->dev, "failed to register PHYs: %d\n", err);
goto unregister;
}
struct irq_chip *chip = irq_data_get_irq_chip(irqd);
struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
- struct samsung_pin_bank_type *bank_type = bank->type;
struct samsung_pinctrl_drv_data *d = bank->drvdata;
- unsigned int pin = irqd->hwirq;
- unsigned int shift = EXYNOS_EINT_CON_LEN * pin;
+ unsigned int shift = EXYNOS_EINT_CON_LEN * irqd->hwirq;
unsigned int con, trig_type;
unsigned long reg_con = our_chip->eint_con + bank->eint_offset;
- unsigned long flags;
- unsigned int mask;
switch (type) {
case IRQ_TYPE_EDGE_RISING:
con |= trig_type << shift;
writel(con, d->virt_base + reg_con);
+ return 0;
+}
+
+static int exynos_irq_request_resources(struct irq_data *irqd)
+{
+ struct irq_chip *chip = irq_data_get_irq_chip(irqd);
+ struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
+ struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
+ struct samsung_pin_bank_type *bank_type = bank->type;
+ struct samsung_pinctrl_drv_data *d = bank->drvdata;
+ unsigned int shift = EXYNOS_EINT_CON_LEN * irqd->hwirq;
+ unsigned long reg_con = our_chip->eint_con + bank->eint_offset;
+ unsigned long flags;
+ unsigned int mask;
+ unsigned int con;
+ int ret;
+
+ ret = gpio_lock_as_irq(&bank->gpio_chip, irqd->hwirq);
+ if (ret) {
+ dev_err(bank->gpio_chip.dev, "unable to lock pin %s-%lu IRQ\n",
+ bank->name, irqd->hwirq);
+ return ret;
+ }
+
reg_con = bank->pctl_offset + bank_type->reg_offset[PINCFG_TYPE_FUNC];
- shift = pin * bank_type->fld_width[PINCFG_TYPE_FUNC];
+ shift = irqd->hwirq * bank_type->fld_width[PINCFG_TYPE_FUNC];
mask = (1 << bank_type->fld_width[PINCFG_TYPE_FUNC]) - 1;
spin_lock_irqsave(&bank->slock, flags);
spin_unlock_irqrestore(&bank->slock, flags);
+ exynos_irq_unmask(irqd);
+
return 0;
}
+static void exynos_irq_release_resources(struct irq_data *irqd)
+{
+ struct irq_chip *chip = irq_data_get_irq_chip(irqd);
+ struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
+ struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
+ struct samsung_pin_bank_type *bank_type = bank->type;
+ struct samsung_pinctrl_drv_data *d = bank->drvdata;
+ unsigned int shift = EXYNOS_EINT_CON_LEN * irqd->hwirq;
+ unsigned long reg_con = our_chip->eint_con + bank->eint_offset;
+ unsigned long flags;
+ unsigned int mask;
+ unsigned int con;
+
+ reg_con = bank->pctl_offset + bank_type->reg_offset[PINCFG_TYPE_FUNC];
+ shift = irqd->hwirq * bank_type->fld_width[PINCFG_TYPE_FUNC];
+ mask = (1 << bank_type->fld_width[PINCFG_TYPE_FUNC]) - 1;
+
+ exynos_irq_mask(irqd);
+
+ spin_lock_irqsave(&bank->slock, flags);
+
+ con = readl(d->virt_base + reg_con);
+ con &= ~(mask << shift);
+ con |= FUNC_INPUT << shift;
+ writel(con, d->virt_base + reg_con);
+
+ spin_unlock_irqrestore(&bank->slock, flags);
+
+ gpio_unlock_as_irq(&bank->gpio_chip, irqd->hwirq);
+}
+
/*
* irq_chip for gpio interrupts.
*/
.irq_mask = exynos_irq_mask,
.irq_ack = exynos_irq_ack,
.irq_set_type = exynos_irq_set_type,
+ .irq_request_resources = exynos_irq_request_resources,
+ .irq_release_resources = exynos_irq_release_resources,
},
.eint_con = EXYNOS_GPIO_ECON_OFFSET,
.eint_mask = EXYNOS_GPIO_EMASK_OFFSET,
.irq_ack = exynos_irq_ack,
.irq_set_type = exynos_irq_set_type,
.irq_set_wake = exynos_wkup_irq_set_wake,
+ .irq_request_resources = exynos_irq_request_resources,
+ .irq_release_resources = exynos_irq_release_resources,
},
.eint_con = EXYNOS_WKUP_ECON_OFFSET,
.eint_mask = EXYNOS_WKUP_EMASK_OFFSET,
#include <linux/gpio.h>
/* pinmux function number for pin as gpio output line */
+#define FUNC_INPUT 0x0
#define FUNC_OUTPUT 0x1
/**
};
static const char * const can0_groups[] = {
- "can0_data_a",
+ "can0_data",
"can0_data_b",
"can0_data_c",
"can0_data_d",
"can0_data_e",
"can0_data_f",
- "can_clk_a",
+ "can_clk",
"can_clk_b",
"can_clk_c",
"can_clk_d",
};
static const char * const can1_groups[] = {
- "can1_data_a",
+ "can1_data",
"can1_data_b",
"can1_data_c",
"can1_data_d",
- "can_clk_a",
+ "can_clk",
"can_clk_b",
"can_clk_c",
"can_clk_d",
info->device_type = s5m87xx->device_type;
info->wtsr_smpl = s5m87xx->wtsr_smpl;
- info->irq = regmap_irq_get_virq(s5m87xx->irq_data, alarm_irq);
- if (info->irq <= 0) {
- ret = -EINVAL;
- dev_err(&pdev->dev, "Failed to get virtual IRQ %d\n",
+ if (s5m87xx->irq_data) {
+ info->irq = regmap_irq_get_virq(s5m87xx->irq_data, alarm_irq);
+ if (info->irq <= 0) {
+ ret = -EINVAL;
+ dev_err(&pdev->dev, "Failed to get virtual IRQ %d\n",
alarm_irq);
- goto err;
+ goto err;
+ }
}
platform_set_drvdata(pdev, info);
goto err;
}
+ if (!info->irq) {
+ dev_info(&pdev->dev, "Alarm IRQ not available\n");
+ return 0;
+ }
+
ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL,
s5m_rtc_alarm_irq, 0, "rtc-alarm0",
info);
struct s5m_rtc_info *info = dev_get_drvdata(dev);
int ret = 0;
- if (device_may_wakeup(dev))
+ if (info->irq && device_may_wakeup(dev))
ret = disable_irq_wake(info->irq);
return ret;
struct s5m_rtc_info *info = dev_get_drvdata(dev);
int ret = 0;
- if (device_may_wakeup(dev))
+ if (info->irq && device_may_wakeup(dev))
ret = enable_irq_wake(info->irq);
return ret;
const unsigned char *buf, int count)
{
struct raw3215_info *raw;
+ int i, written;
if (!tty)
return 0;
raw = (struct raw3215_info *) tty->driver_data;
- raw3215_write(raw, buf, count);
- return count;
+ written = count;
+ while (count > 0) {
+ for (i = 0; i < count; i++)
+ if (buf[i] == '\t' || buf[i] == '\n')
+ break;
+ raw3215_write(raw, buf, i);
+ count -= i;
+ buf += i;
+ if (count > 0) {
+ raw3215_putchar(raw, *buf);
+ count--;
+ buf++;
+ }
+ }
+ return written;
}
/*
driver->subtype = SYSTEM_TYPE_TTY;
driver->init_termios = tty_std_termios;
driver->init_termios.c_iflag = IGNBRK | IGNPAR;
- driver->init_termios.c_oflag = ONLCR | XTABS;
+ driver->init_termios.c_oflag = ONLCR;
driver->init_termios.c_lflag = ISIG;
driver->flags = TTY_DRIVER_REAL_RAW;
tty_set_operations(driver, &tty3215_ops);
driver->subtype = SYSTEM_TYPE_TTY;
driver->init_termios = tty_std_termios;
driver->init_termios.c_iflag = IGNBRK | IGNPAR;
- driver->init_termios.c_oflag = ONLCR | XTABS;
+ driver->init_termios.c_oflag = ONLCR;
driver->init_termios.c_lflag = ISIG | ECHO;
driver->flags = TTY_DRIVER_REAL_RAW;
tty_set_operations(driver, &sclp_ops);
cmd->tag = req->tag;
- req->cmd = req->__cmd;
cmd->cmnd = req->cmd;
cmd->prot_op = SCSI_PROT_NORMAL;
spi_bitbang_stop(&hw->bitbang);
free_irq(hw->irq, hw);
iounmap((void __iomem *)hw->regs);
- release_mem_region(r->start, sizeof(psc_spi_t));
+ release_mem_region(hw->ioarea->start, sizeof(psc_spi_t));
if (hw->usedma) {
au1550_spi_dma_rxtmp_free(hw);
flags, dev_name(&spi->dev));
internal_cs = false;
}
- }
- if (retval) {
- dev_err(&spi->dev, "GPIO %d setup failed (%d)\n",
- spi->cs_gpio, retval);
- return retval;
- }
+ if (retval) {
+ dev_err(&spi->dev, "GPIO %d setup failed (%d)\n",
+ spi->cs_gpio, retval);
+ return retval;
+ }
- if (internal_cs)
- set_io_bits(dspi->base + SPIPC0, 1 << spi->chip_select);
+ if (internal_cs)
+ set_io_bits(dspi->base + SPIPC0, 1 << spi->chip_select);
+ }
if (spi->mode & SPI_READY)
set_io_bits(dspi->base + SPIPC0, SPIPC0_SPIENA_MASK);
if (ret)
return ret;
+ dws->regs = pcim_iomap_table(pdev)[pci_bar];
+
dws->bus_num = 0;
dws->num_cs = 4;
dws->irq = pdev->irq;
transfer_list);
if (!last_transfer->cs_change)
- spi_chip_sel(dws, dws->cur_msg->spi, 0);
+ spi_chip_sel(dws, msg->spi, 0);
spi_finalize_current_message(dws->master);
}
disable_fifo:
if (t->rx_buf != NULL)
chconf &= ~OMAP2_MCSPI_CHCONF_FFER;
- else
+
+ if (t->tx_buf != NULL)
chconf &= ~OMAP2_MCSPI_CHCONF_FFET;
mcspi_write_chconf0(spi, chconf);
{ "INT3430", 0 },
{ "INT3431", 0 },
{ "80860F0E", 0 },
+ { "8086228E", 0 },
{ },
};
MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match);
}
/* div doesn't support odd number */
- div = rs->max_freq / rs->speed;
+ div = max_t(u32, rs->max_freq / rs->speed, 1);
div = (div + 1) & 0xfffe;
spi_enable_chip(rs, 0);
rs->dma_tx.addr = (dma_addr_t)(mem->start + ROCKCHIP_SPI_TXDR);
rs->dma_rx.addr = (dma_addr_t)(mem->start + ROCKCHIP_SPI_RXDR);
rs->dma_tx.direction = DMA_MEM_TO_DEV;
- rs->dma_tx.direction = DMA_DEV_TO_MEM;
+ rs->dma_rx.direction = DMA_DEV_TO_MEM;
master->can_dma = rockchip_spi_can_dma;
master->dma_tx = rs->dma_tx.ch;
dma_cookie_t cookie;
int ret;
- if (tx) {
- desc_tx = dmaengine_prep_slave_sg(rspi->master->dma_tx,
- tx->sgl, tx->nents, DMA_TO_DEVICE,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
- if (!desc_tx)
- goto no_dma;
-
- irq_mask |= SPCR_SPTIE;
- }
+ /* First prepare and submit the DMA request(s), as this may fail */
if (rx) {
desc_rx = dmaengine_prep_slave_sg(rspi->master->dma_rx,
rx->sgl, rx->nents, DMA_FROM_DEVICE,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
- if (!desc_rx)
- goto no_dma;
+ if (!desc_rx) {
+ ret = -EAGAIN;
+ goto no_dma_rx;
+ }
+
+ desc_rx->callback = rspi_dma_complete;
+ desc_rx->callback_param = rspi;
+ cookie = dmaengine_submit(desc_rx);
+ if (dma_submit_error(cookie)) {
+ ret = cookie;
+ goto no_dma_rx;
+ }
irq_mask |= SPCR_SPRIE;
}
+ if (tx) {
+ desc_tx = dmaengine_prep_slave_sg(rspi->master->dma_tx,
+ tx->sgl, tx->nents, DMA_TO_DEVICE,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc_tx) {
+ ret = -EAGAIN;
+ goto no_dma_tx;
+ }
+
+ if (rx) {
+ /* No callback */
+ desc_tx->callback = NULL;
+ } else {
+ desc_tx->callback = rspi_dma_complete;
+ desc_tx->callback_param = rspi;
+ }
+ cookie = dmaengine_submit(desc_tx);
+ if (dma_submit_error(cookie)) {
+ ret = cookie;
+ goto no_dma_tx;
+ }
+
+ irq_mask |= SPCR_SPTIE;
+ }
+
/*
* DMAC needs SPxIE, but if SPxIE is set, the IRQ routine will be
* called. So, this driver disables the IRQ while DMA transfer.
rspi_enable_irq(rspi, irq_mask);
rspi->dma_callbacked = 0;
- if (rx) {
- desc_rx->callback = rspi_dma_complete;
- desc_rx->callback_param = rspi;
- cookie = dmaengine_submit(desc_rx);
- if (dma_submit_error(cookie))
- return cookie;
+ /* Now start DMA */
+ if (rx)
dma_async_issue_pending(rspi->master->dma_rx);
- }
- if (tx) {
- if (rx) {
- /* No callback */
- desc_tx->callback = NULL;
- } else {
- desc_tx->callback = rspi_dma_complete;
- desc_tx->callback_param = rspi;
- }
- cookie = dmaengine_submit(desc_tx);
- if (dma_submit_error(cookie))
- return cookie;
+ if (tx)
dma_async_issue_pending(rspi->master->dma_tx);
- }
ret = wait_event_interruptible_timeout(rspi->wait,
rspi->dma_callbacked, HZ);
if (ret > 0 && rspi->dma_callbacked)
ret = 0;
- else if (!ret)
+ else if (!ret) {
+ dev_err(&rspi->master->dev, "DMA timeout\n");
ret = -ETIMEDOUT;
+ if (tx)
+ dmaengine_terminate_all(rspi->master->dma_tx);
+ if (rx)
+ dmaengine_terminate_all(rspi->master->dma_rx);
+ }
rspi_disable_irq(rspi, irq_mask);
return ret;
-no_dma:
- pr_warn_once("%s %s: DMA not available, falling back to PIO\n",
- dev_driver_string(&rspi->master->dev),
- dev_name(&rspi->master->dev));
- return -EAGAIN;
+no_dma_tx:
+ if (rx)
+ dmaengine_terminate_all(rspi->master->dma_rx);
+no_dma_rx:
+ if (ret == -EAGAIN) {
+ pr_warn_once("%s %s: DMA not available, falling back to PIO\n",
+ dev_driver_string(&rspi->master->dev),
+ dev_name(&rspi->master->dev));
+ }
+ return ret;
}
static void rspi_receive_init(const struct rspi_data *rspi)
dma_cookie_t cookie;
int ret;
- if (tx) {
- ier_bits |= IER_TDREQE | IER_TDMAE;
- dma_sync_single_for_device(p->master->dma_tx->device->dev,
- p->tx_dma_addr, len, DMA_TO_DEVICE);
- desc_tx = dmaengine_prep_slave_single(p->master->dma_tx,
- p->tx_dma_addr, len, DMA_TO_DEVICE,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
- if (!desc_tx)
- return -EAGAIN;
- }
-
+ /* First prepare and submit the DMA request(s), as this may fail */
if (rx) {
ier_bits |= IER_RDREQE | IER_RDMAE;
desc_rx = dmaengine_prep_slave_single(p->master->dma_rx,
p->rx_dma_addr, len, DMA_FROM_DEVICE,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
- if (!desc_rx)
- return -EAGAIN;
- }
-
- /* 1 stage FIFO watermarks for DMA */
- sh_msiof_write(p, FCTR, FCTR_TFWM_1 | FCTR_RFWM_1);
-
- /* setup msiof transfer mode registers (32-bit words) */
- sh_msiof_spi_set_mode_regs(p, tx, rx, 32, len / 4);
-
- sh_msiof_write(p, IER, ier_bits);
-
- reinit_completion(&p->done);
+ if (!desc_rx) {
+ ret = -EAGAIN;
+ goto no_dma_rx;
+ }
- if (rx) {
desc_rx->callback = sh_msiof_dma_complete;
desc_rx->callback_param = p;
cookie = dmaengine_submit(desc_rx);
if (dma_submit_error(cookie)) {
ret = cookie;
- goto stop_ier;
+ goto no_dma_rx;
}
- dma_async_issue_pending(p->master->dma_rx);
}
if (tx) {
+ ier_bits |= IER_TDREQE | IER_TDMAE;
+ dma_sync_single_for_device(p->master->dma_tx->device->dev,
+ p->tx_dma_addr, len, DMA_TO_DEVICE);
+ desc_tx = dmaengine_prep_slave_single(p->master->dma_tx,
+ p->tx_dma_addr, len, DMA_TO_DEVICE,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc_tx) {
+ ret = -EAGAIN;
+ goto no_dma_tx;
+ }
+
if (rx) {
/* No callback */
desc_tx->callback = NULL;
cookie = dmaengine_submit(desc_tx);
if (dma_submit_error(cookie)) {
ret = cookie;
- goto stop_rx;
+ goto no_dma_tx;
}
- dma_async_issue_pending(p->master->dma_tx);
}
+ /* 1 stage FIFO watermarks for DMA */
+ sh_msiof_write(p, FCTR, FCTR_TFWM_1 | FCTR_RFWM_1);
+
+ /* setup msiof transfer mode registers (32-bit words) */
+ sh_msiof_spi_set_mode_regs(p, tx, rx, 32, len / 4);
+
+ sh_msiof_write(p, IER, ier_bits);
+
+ reinit_completion(&p->done);
+
+ /* Now start DMA */
+ if (rx)
+ dma_async_issue_pending(p->master->dma_rx);
+ if (tx)
+ dma_async_issue_pending(p->master->dma_tx);
+
ret = sh_msiof_spi_start(p, rx);
if (ret) {
dev_err(&p->pdev->dev, "failed to start hardware\n");
- goto stop_tx;
+ goto stop_dma;
}
/* wait for tx fifo to be emptied / rx fifo to be filled */
stop_reset:
sh_msiof_reset_str(p);
sh_msiof_spi_stop(p, rx);
-stop_tx:
+stop_dma:
if (tx)
dmaengine_terminate_all(p->master->dma_tx);
-stop_rx:
+no_dma_tx:
if (rx)
dmaengine_terminate_all(p->master->dma_rx);
-stop_ier:
sh_msiof_write(p, IER, 0);
+no_dma_rx:
return ret;
}
/**
* spi_finalize_current_transfer - report completion of a transfer
+ * @master: the master reporting completion
*
* Called by SPI drivers using the core transfer_one_message()
* implementation to notify it that the current interrupt driven
source "drivers/staging/slicoss/Kconfig"
-source "drivers/staging/usbip/Kconfig"
-
source "drivers/staging/wlan-ng/Kconfig"
source "drivers/staging/comedi/Kconfig"
obj-y += media/
obj-$(CONFIG_ET131X) += et131x/
obj-$(CONFIG_SLICOSS) += slicoss/
-obj-$(CONFIG_USBIP_CORE) += usbip/
obj-$(CONFIG_PRISM2_USB) += wlan-ng/
obj-$(CONFIG_COMEDI) += comedi/
obj-$(CONFIG_FB_OLPC_DCON) += olpc_dcon/
if (unlikely(ret)) {
pr_err("failed to register misc device for log '%s'!\n",
log->misc.name);
- goto out_free_log;
+ goto out_free_misc_name;
}
pr_info("created %luK log '%s'\n",
return 0;
+out_free_misc_name:
+ kfree(log->misc.name);
+
out_free_log:
kfree(log);
* @reg: the register to read
* @value: 16-bit value to write
*/
-static int et131x_mii_write(struct et131x_adapter *adapter, u8 reg, u16 value)
+static int et131x_mii_write(struct et131x_adapter *adapter, u8 addr, u8 reg,
+ u16 value)
{
struct mac_regs __iomem *mac = &adapter->regs->mac;
- struct phy_device *phydev = adapter->phydev;
int status = 0;
- u8 addr;
u32 delay = 0;
u32 mii_addr;
u32 mii_cmd;
u32 mii_indicator;
- if (!phydev)
- return -EIO;
-
- addr = phydev->addr;
-
/* Save a local copy of the registers we are dealing with so we can
* set them back
*/
struct net_device *netdev = bus->priv;
struct et131x_adapter *adapter = netdev_priv(netdev);
- return et131x_mii_write(adapter, reg, value);
-}
-
-static int et131x_mdio_reset(struct mii_bus *bus)
-{
- struct net_device *netdev = bus->priv;
- struct et131x_adapter *adapter = netdev_priv(netdev);
-
- et131x_mii_write(adapter, MII_BMCR, BMCR_RESET);
-
- return 0;
+ return et131x_mii_write(adapter, phy_addr, reg, value);
}
/* et1310_phy_power_switch - PHY power control
static void et1310_phy_power_switch(struct et131x_adapter *adapter, bool down)
{
u16 data;
+ struct phy_device *phydev = adapter->phydev;
et131x_mii_read(adapter, MII_BMCR, &data);
data &= ~BMCR_PDOWN;
if (down)
data |= BMCR_PDOWN;
- et131x_mii_write(adapter, MII_BMCR, data);
+ et131x_mii_write(adapter, phydev->addr, MII_BMCR, data);
}
/* et131x_xcvr_init - Init the phy if we are setting it into force mode */
static void et131x_xcvr_init(struct et131x_adapter *adapter)
{
u16 lcr2;
+ struct phy_device *phydev = adapter->phydev;
/* Set the LED behavior such that LED 1 indicates speed (off =
* 10Mbits, blink = 100Mbits, on = 1000Mbits) and LED 2 indicates
else
lcr2 |= (LED_VAL_LINKON << LED_TXRX_SHIFT);
- et131x_mii_write(adapter, PHY_LED_2, lcr2);
+ et131x_mii_write(adapter, phydev->addr, PHY_LED_2, lcr2);
}
}
et131x_mii_read(adapter, PHY_MPHY_CONTROL_REG,
®ister18);
- et131x_mii_write(adapter, PHY_MPHY_CONTROL_REG,
- register18 | 0x4);
- et131x_mii_write(adapter, PHY_INDEX_REG,
+ et131x_mii_write(adapter, phydev->addr,
+ PHY_MPHY_CONTROL_REG, register18 | 0x4);
+ et131x_mii_write(adapter, phydev->addr, PHY_INDEX_REG,
register18 | 0x8402);
- et131x_mii_write(adapter, PHY_DATA_REG,
+ et131x_mii_write(adapter, phydev->addr, PHY_DATA_REG,
register18 | 511);
- et131x_mii_write(adapter, PHY_MPHY_CONTROL_REG,
- register18);
+ et131x_mii_write(adapter, phydev->addr,
+ PHY_MPHY_CONTROL_REG, register18);
}
et1310_config_flow_control(adapter);
et131x_mii_read(adapter, PHY_CONFIG, ®);
reg &= ~ET_PHY_CONFIG_TX_FIFO_DEPTH;
reg |= ET_PHY_CONFIG_FIFO_DEPTH_32;
- et131x_mii_write(adapter, PHY_CONFIG, reg);
+ et131x_mii_write(adapter, phydev->addr, PHY_CONFIG,
+ reg);
}
et131x_set_rx_dma_timer(adapter);
et131x_mii_read(adapter, PHY_MPHY_CONTROL_REG,
®ister18);
- et131x_mii_write(adapter, PHY_MPHY_CONTROL_REG,
- register18 | 0x4);
- et131x_mii_write(adapter, PHY_INDEX_REG,
- register18 | 0x8402);
- et131x_mii_write(adapter, PHY_DATA_REG,
- register18 | 511);
- et131x_mii_write(adapter, PHY_MPHY_CONTROL_REG,
- register18);
+ et131x_mii_write(adapter, phydev->addr,
+ PHY_MPHY_CONTROL_REG, register18 | 0x4);
+ et131x_mii_write(adapter, phydev->addr,
+ PHY_INDEX_REG, register18 | 0x8402);
+ et131x_mii_write(adapter, phydev->addr,
+ PHY_DATA_REG, register18 | 511);
+ et131x_mii_write(adapter, phydev->addr,
+ PHY_MPHY_CONTROL_REG, register18);
}
/* Free the packets being actively sent & stopped */
/* Copy address into the net_device struct */
memcpy(netdev->dev_addr, adapter->addr, ETH_ALEN);
- /* Init variable for counting how long we do not have link status */
- adapter->boot_coma = 0;
- et1310_disable_phy_coma(adapter);
-
rc = -ENOMEM;
/* Setup the mii_bus struct */
adapter->mii_bus->priv = netdev;
adapter->mii_bus->read = et131x_mdio_read;
adapter->mii_bus->write = et131x_mdio_write;
- adapter->mii_bus->reset = et131x_mdio_reset;
adapter->mii_bus->irq = kmalloc_array(PHY_MAX_ADDR, sizeof(int),
GFP_KERNEL);
if (!adapter->mii_bus->irq)
/* Setup et1310 as per the documentation */
et131x_adapter_setup(adapter);
+ /* Init variable for counting how long we do not have link status */
+ adapter->boot_coma = 0;
+ et1310_disable_phy_coma(adapter);
+
/* We can enable interrupts now
*
* NOTE - Because registration of interrupt handler is done in the
return -ENOMEM;
strncpy(sched->ws_name, name, CFS_WS_NAME_LEN);
+ sched->ws_name[CFS_WS_NAME_LEN - 1] = '\0';
sched->ws_cptab = cptab;
sched->ws_cpt = cpt;
*/
#define DEBUG_SUBSYSTEM S_CLASS
-# include <asm/atomic.h>
+# include <linux/atomic.h>
#include "../include/obd_support.h"
#include "../include/obd_class.h"
{USB_DEVICE(USB_VENDER_ID_REALTEK, 0x0179)}, /* 8188ETV */
/*=== Customer ID ===*/
/****** 8188EUS ********/
+ {USB_DEVICE(0x056e, 0x4008)}, /* Elecom WDC-150SU2M */
{USB_DEVICE(0x07b8, 0x8179)}, /* Abocom - Abocom */
{USB_DEVICE(0x2001, 0x330F)}, /* DLink DWA-125 REV D1 */
{USB_DEVICE(0x2001, 0x3310)}, /* Dlink DWA-123 REV D1 */
+ {USB_DEVICE(0x0df6, 0x0076)}, /* Sitecom N150 v2 */
{} /* Terminating entry */
};
+++ /dev/null
-config USBIP_CORE
- tristate "USB/IP support"
- depends on USB && NET
- ---help---
- This enables pushing USB packets over IP to allow remote
- machines direct access to USB devices. It provides the
- USB/IP core that is required by both drivers.
-
- For more details, and to get the userspace utility
- programs, please see <http://usbip.sourceforge.net/>.
-
- To compile this as a module, choose M here: the module will
- be called usbip-core.
-
- If unsure, say N.
-
-config USBIP_VHCI_HCD
- tristate "VHCI hcd"
- depends on USBIP_CORE
- ---help---
- This enables the USB/IP virtual host controller driver,
- which is run on the remote machine.
-
- To compile this driver as a module, choose M here: the
- module will be called vhci-hcd.
-
-config USBIP_HOST
- tristate "Host driver"
- depends on USBIP_CORE
- ---help---
- This enables the USB/IP host driver, which is run on the
- machine that is sharing the USB devices.
-
- To compile this driver as a module, choose M here: the
- module will be called usbip-host.
-
-config USBIP_DEBUG
- bool "Debug messages for USB/IP"
- depends on USBIP_CORE
- ---help---
- This enables the debug messages from the USB/IP drivers.
+++ /dev/null
-ccflags-$(CONFIG_USBIP_DEBUG) := -DDEBUG
-
-obj-$(CONFIG_USBIP_CORE) += usbip-core.o
-usbip-core-y := usbip_common.o usbip_event.o
-
-obj-$(CONFIG_USBIP_VHCI_HCD) += vhci-hcd.o
-vhci-hcd-y := vhci_sysfs.o vhci_tx.o vhci_rx.o vhci_hcd.o
-
-obj-$(CONFIG_USBIP_HOST) += usbip-host.o
-usbip-host-y := stub_dev.o stub_main.o stub_rx.o stub_tx.o
+++ /dev/null
-TODO:
- - more discussion about the protocol
- - testing
- - review of the userspace interface
- - document the protocol
-
-Please send patches for this code to Greg Kroah-Hartman <greg@kroah.com>
+++ /dev/null
-/*
- * Copyright (C) 2003-2008 Takahiro Hirofuchi
- *
- * This 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 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 __USBIP_STUB_H
-#define __USBIP_STUB_H
-
-#include <linux/list.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/types.h>
-#include <linux/usb.h>
-#include <linux/wait.h>
-
-#define STUB_BUSID_OTHER 0
-#define STUB_BUSID_REMOV 1
-#define STUB_BUSID_ADDED 2
-#define STUB_BUSID_ALLOC 3
-
-struct stub_device {
- struct usb_interface *interface;
- struct usb_device *udev;
-
- struct usbip_device ud;
- __u32 devid;
-
- /*
- * stub_priv preserves private data of each urb.
- * It is allocated as stub_priv_cache and assigned to urb->context.
- *
- * stub_priv is always linked to any one of 3 lists;
- * priv_init: linked to this until the comletion of a urb.
- * priv_tx : linked to this after the completion of a urb.
- * priv_free: linked to this after the sending of the result.
- *
- * Any of these list operations should be locked by priv_lock.
- */
- spinlock_t priv_lock;
- struct list_head priv_init;
- struct list_head priv_tx;
- struct list_head priv_free;
-
- /* see comments for unlinking in stub_rx.c */
- struct list_head unlink_tx;
- struct list_head unlink_free;
-
- wait_queue_head_t tx_waitq;
-};
-
-/* private data into urb->priv */
-struct stub_priv {
- unsigned long seqnum;
- struct list_head list;
- struct stub_device *sdev;
- struct urb *urb;
-
- int unlinking;
-};
-
-struct stub_unlink {
- unsigned long seqnum;
- struct list_head list;
- __u32 status;
-};
-
-/* same as SYSFS_BUS_ID_SIZE */
-#define BUSID_SIZE 32
-
-struct bus_id_priv {
- char name[BUSID_SIZE];
- char status;
- int interf_count;
- struct stub_device *sdev;
- struct usb_device *udev;
- char shutdown_busid;
-};
-
-/* stub_priv is allocated from stub_priv_cache */
-extern struct kmem_cache *stub_priv_cache;
-
-/* stub_dev.c */
-extern struct usb_device_driver stub_driver;
-
-/* stub_main.c */
-struct bus_id_priv *get_busid_priv(const char *busid);
-int del_match_busid(char *busid);
-void stub_device_cleanup_urbs(struct stub_device *sdev);
-
-/* stub_rx.c */
-int stub_rx_loop(void *data);
-
-/* stub_tx.c */
-void stub_enqueue_ret_unlink(struct stub_device *sdev, __u32 seqnum,
- __u32 status);
-void stub_complete(struct urb *urb);
-int stub_tx_loop(void *data);
-
-#endif /* __USBIP_STUB_H */
+++ /dev/null
-/*
- * Copyright (C) 2003-2008 Takahiro Hirofuchi
- *
- * This 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 is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
- */
-
-#include <linux/device.h>
-#include <linux/file.h>
-#include <linux/kthread.h>
-#include <linux/module.h>
-
-#include "usbip_common.h"
-#include "stub.h"
-
-/*
- * Define device IDs here if you want to explicitly limit exportable devices.
- * In most cases, wildcard matching will be okay because driver binding can be
- * changed dynamically by a userland program.
- */
-static struct usb_device_id stub_table[] = {
-#if 0
- /* just an example */
- { USB_DEVICE(0x05ac, 0x0301) }, /* Mac 1 button mouse */
- { USB_DEVICE(0x0430, 0x0009) }, /* Plat Home Keyboard */
- { USB_DEVICE(0x059b, 0x0001) }, /* Iomega USB Zip 100 */
- { USB_DEVICE(0x04b3, 0x4427) }, /* IBM USB CD-ROM */
- { USB_DEVICE(0x05a9, 0xa511) }, /* LifeView USB cam */
- { USB_DEVICE(0x55aa, 0x0201) }, /* Imation card reader */
- { USB_DEVICE(0x046d, 0x0870) }, /* Qcam Express(QV-30) */
- { USB_DEVICE(0x04bb, 0x0101) }, /* IO-DATA HD 120GB */
- { USB_DEVICE(0x04bb, 0x0904) }, /* IO-DATA USB-ET/TX */
- { USB_DEVICE(0x04bb, 0x0201) }, /* IO-DATA USB-ET/TX */
- { USB_DEVICE(0x08bb, 0x2702) }, /* ONKYO USB Speaker */
- { USB_DEVICE(0x046d, 0x08b2) }, /* Logicool Qcam 4000 Pro */
-#endif
- /* magic for wild card */
- { .driver_info = 1 },
- { 0, } /* Terminating entry */
-};
-MODULE_DEVICE_TABLE(usb, stub_table);
-
-/*
- * usbip_status shows the status of usbip-host as long as this driver is bound
- * to the target device.
- */
-static ssize_t usbip_status_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct stub_device *sdev = dev_get_drvdata(dev);
- int status;
-
- if (!sdev) {
- dev_err(dev, "sdev is null\n");
- return -ENODEV;
- }
-
- spin_lock_irq(&sdev->ud.lock);
- status = sdev->ud.status;
- spin_unlock_irq(&sdev->ud.lock);
-
- return snprintf(buf, PAGE_SIZE, "%d\n", status);
-}
-static DEVICE_ATTR_RO(usbip_status);
-
-/*
- * usbip_sockfd gets a socket descriptor of an established TCP connection that
- * is used to transfer usbip requests by kernel threads. -1 is a magic number
- * by which usbip connection is finished.
- */
-static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct stub_device *sdev = dev_get_drvdata(dev);
- int sockfd = 0;
- struct socket *socket;
- int rv;
-
- if (!sdev) {
- dev_err(dev, "sdev is null\n");
- return -ENODEV;
- }
-
- rv = sscanf(buf, "%d", &sockfd);
- if (rv != 1)
- return -EINVAL;
-
- if (sockfd != -1) {
- int err;
-
- dev_info(dev, "stub up\n");
-
- spin_lock_irq(&sdev->ud.lock);
-
- if (sdev->ud.status != SDEV_ST_AVAILABLE) {
- dev_err(dev, "not ready\n");
- goto err;
- }
-
- socket = sockfd_lookup(sockfd, &err);
- if (!socket)
- goto err;
-
- sdev->ud.tcp_socket = socket;
-
- spin_unlock_irq(&sdev->ud.lock);
-
- sdev->ud.tcp_rx = kthread_get_run(stub_rx_loop, &sdev->ud,
- "stub_rx");
- sdev->ud.tcp_tx = kthread_get_run(stub_tx_loop, &sdev->ud,
- "stub_tx");
-
- spin_lock_irq(&sdev->ud.lock);
- sdev->ud.status = SDEV_ST_USED;
- spin_unlock_irq(&sdev->ud.lock);
-
- } else {
- dev_info(dev, "stub down\n");
-
- spin_lock_irq(&sdev->ud.lock);
- if (sdev->ud.status != SDEV_ST_USED)
- goto err;
-
- spin_unlock_irq(&sdev->ud.lock);
-
- usbip_event_add(&sdev->ud, SDEV_EVENT_DOWN);
- }
-
- return count;
-
-err:
- spin_unlock_irq(&sdev->ud.lock);
- return -EINVAL;
-}
-static DEVICE_ATTR(usbip_sockfd, S_IWUSR, NULL, store_sockfd);
-
-static int stub_add_files(struct device *dev)
-{
- int err = 0;
-
- err = device_create_file(dev, &dev_attr_usbip_status);
- if (err)
- goto err_status;
-
- err = device_create_file(dev, &dev_attr_usbip_sockfd);
- if (err)
- goto err_sockfd;
-
- err = device_create_file(dev, &dev_attr_usbip_debug);
- if (err)
- goto err_debug;
-
- return 0;
-
-err_debug:
- device_remove_file(dev, &dev_attr_usbip_sockfd);
-err_sockfd:
- device_remove_file(dev, &dev_attr_usbip_status);
-err_status:
- return err;
-}
-
-static void stub_remove_files(struct device *dev)
-{
- device_remove_file(dev, &dev_attr_usbip_status);
- device_remove_file(dev, &dev_attr_usbip_sockfd);
- device_remove_file(dev, &dev_attr_usbip_debug);
-}
-
-static void stub_shutdown_connection(struct usbip_device *ud)
-{
- struct stub_device *sdev = container_of(ud, struct stub_device, ud);
-
- /*
- * When removing an exported device, kernel panic sometimes occurred
- * and then EIP was sk_wait_data of stub_rx thread. Is this because
- * sk_wait_data returned though stub_rx thread was already finished by
- * step 1?
- */
- if (ud->tcp_socket) {
- dev_dbg(&sdev->udev->dev, "shutdown tcp_socket %p\n",
- ud->tcp_socket);
- kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR);
- }
-
- /* 1. stop threads */
- if (ud->tcp_rx) {
- kthread_stop_put(ud->tcp_rx);
- ud->tcp_rx = NULL;
- }
- if (ud->tcp_tx) {
- kthread_stop_put(ud->tcp_tx);
- ud->tcp_tx = NULL;
- }
-
- /*
- * 2. close the socket
- *
- * tcp_socket is freed after threads are killed so that usbip_xmit does
- * not touch NULL socket.
- */
- if (ud->tcp_socket) {
- sockfd_put(ud->tcp_socket);
- ud->tcp_socket = NULL;
- }
-
- /* 3. free used data */
- stub_device_cleanup_urbs(sdev);
-
- /* 4. free stub_unlink */
- {
- unsigned long flags;
- struct stub_unlink *unlink, *tmp;
-
- spin_lock_irqsave(&sdev->priv_lock, flags);
- list_for_each_entry_safe(unlink, tmp, &sdev->unlink_tx, list) {
- list_del(&unlink->list);
- kfree(unlink);
- }
- list_for_each_entry_safe(unlink, tmp, &sdev->unlink_free,
- list) {
- list_del(&unlink->list);
- kfree(unlink);
- }
- spin_unlock_irqrestore(&sdev->priv_lock, flags);
- }
-}
-
-static void stub_device_reset(struct usbip_device *ud)
-{
- struct stub_device *sdev = container_of(ud, struct stub_device, ud);
- struct usb_device *udev = sdev->udev;
- int ret;
-
- dev_dbg(&udev->dev, "device reset");
-
- ret = usb_lock_device_for_reset(udev, sdev->interface);
- if (ret < 0) {
- dev_err(&udev->dev, "lock for reset\n");
- spin_lock_irq(&ud->lock);
- ud->status = SDEV_ST_ERROR;
- spin_unlock_irq(&ud->lock);
- return;
- }
-
- /* try to reset the device */
- ret = usb_reset_device(udev);
- usb_unlock_device(udev);
-
- spin_lock_irq(&ud->lock);
- if (ret) {
- dev_err(&udev->dev, "device reset\n");
- ud->status = SDEV_ST_ERROR;
- } else {
- dev_info(&udev->dev, "device reset\n");
- ud->status = SDEV_ST_AVAILABLE;
- }
- spin_unlock_irq(&ud->lock);
-}
-
-static void stub_device_unusable(struct usbip_device *ud)
-{
- spin_lock_irq(&ud->lock);
- ud->status = SDEV_ST_ERROR;
- spin_unlock_irq(&ud->lock);
-}
-
-/**
- * stub_device_alloc - allocate a new stub_device struct
- * @interface: usb_interface of a new device
- *
- * Allocates and initializes a new stub_device struct.
- */
-static struct stub_device *stub_device_alloc(struct usb_device *udev)
-{
- struct stub_device *sdev;
- int busnum = udev->bus->busnum;
- int devnum = udev->devnum;
-
- dev_dbg(&udev->dev, "allocating stub device");
-
- /* yes, it's a new device */
- sdev = kzalloc(sizeof(struct stub_device), GFP_KERNEL);
- if (!sdev)
- return NULL;
-
- sdev->udev = usb_get_dev(udev);
-
- /*
- * devid is defined with devnum when this driver is first allocated.
- * devnum may change later if a device is reset. However, devid never
- * changes during a usbip connection.
- */
- sdev->devid = (busnum << 16) | devnum;
- sdev->ud.side = USBIP_STUB;
- sdev->ud.status = SDEV_ST_AVAILABLE;
- spin_lock_init(&sdev->ud.lock);
- sdev->ud.tcp_socket = NULL;
-
- INIT_LIST_HEAD(&sdev->priv_init);
- INIT_LIST_HEAD(&sdev->priv_tx);
- INIT_LIST_HEAD(&sdev->priv_free);
- INIT_LIST_HEAD(&sdev->unlink_free);
- INIT_LIST_HEAD(&sdev->unlink_tx);
- spin_lock_init(&sdev->priv_lock);
-
- init_waitqueue_head(&sdev->tx_waitq);
-
- sdev->ud.eh_ops.shutdown = stub_shutdown_connection;
- sdev->ud.eh_ops.reset = stub_device_reset;
- sdev->ud.eh_ops.unusable = stub_device_unusable;
-
- usbip_start_eh(&sdev->ud);
-
- dev_dbg(&udev->dev, "register new device\n");
-
- return sdev;
-}
-
-static void stub_device_free(struct stub_device *sdev)
-{
- kfree(sdev);
-}
-
-static int stub_probe(struct usb_device *udev)
-{
- struct stub_device *sdev = NULL;
- const char *udev_busid = dev_name(&udev->dev);
- int err = 0;
- struct bus_id_priv *busid_priv;
- int rc;
-
- dev_dbg(&udev->dev, "Enter\n");
-
- /* check we should claim or not by busid_table */
- busid_priv = get_busid_priv(udev_busid);
- if (!busid_priv || (busid_priv->status == STUB_BUSID_REMOV) ||
- (busid_priv->status == STUB_BUSID_OTHER)) {
- dev_info(&udev->dev,
- "%s is not in match_busid table... skip!\n",
- udev_busid);
-
- /*
- * Return value should be ENODEV or ENOXIO to continue trying
- * other matched drivers by the driver core.
- * See driver_probe_device() in driver/base/dd.c
- */
- return -ENODEV;
- }
-
- if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) {
- dev_dbg(&udev->dev, "%s is a usb hub device... skip!\n",
- udev_busid);
- return -ENODEV;
- }
-
- if (!strcmp(udev->bus->bus_name, "vhci_hcd")) {
- dev_dbg(&udev->dev,
- "%s is attached on vhci_hcd... skip!\n",
- udev_busid);
-
- return -ENODEV;
- }
-
- /* ok, this is my device */
- sdev = stub_device_alloc(udev);
- if (!sdev)
- return -ENOMEM;
-
- dev_info(&udev->dev,
- "usbip-host: register new device (bus %u dev %u)\n",
- udev->bus->busnum, udev->devnum);
-
- busid_priv->shutdown_busid = 0;
-
- /* set private data to usb_device */
- dev_set_drvdata(&udev->dev, sdev);
- busid_priv->sdev = sdev;
- busid_priv->udev = udev;
-
- /*
- * Claim this hub port.
- * It doesn't matter what value we pass as owner
- * (struct dev_state) as long as it is unique.
- */
- rc = usb_hub_claim_port(udev->parent, udev->portnum,
- (struct usb_dev_state *) udev);
- if (rc) {
- dev_dbg(&udev->dev, "unable to claim port\n");
- return rc;
- }
-
- err = stub_add_files(&udev->dev);
- if (err) {
- dev_err(&udev->dev, "stub_add_files for %s\n", udev_busid);
- dev_set_drvdata(&udev->dev, NULL);
- usb_put_dev(udev);
- kthread_stop_put(sdev->ud.eh);
-
- busid_priv->sdev = NULL;
- stub_device_free(sdev);
- return err;
- }
- busid_priv->status = STUB_BUSID_ALLOC;
-
- return 0;
-}
-
-static void shutdown_busid(struct bus_id_priv *busid_priv)
-{
- if (busid_priv->sdev && !busid_priv->shutdown_busid) {
- busid_priv->shutdown_busid = 1;
- usbip_event_add(&busid_priv->sdev->ud, SDEV_EVENT_REMOVED);
-
- /* wait for the stop of the event handler */
- usbip_stop_eh(&busid_priv->sdev->ud);
- }
-}
-
-/*
- * called in usb_disconnect() or usb_deregister()
- * but only if actconfig(active configuration) exists
- */
-static void stub_disconnect(struct usb_device *udev)
-{
- struct stub_device *sdev;
- const char *udev_busid = dev_name(&udev->dev);
- struct bus_id_priv *busid_priv;
- int rc;
-
- dev_dbg(&udev->dev, "Enter\n");
-
- busid_priv = get_busid_priv(udev_busid);
- if (!busid_priv) {
- BUG();
- return;
- }
-
- sdev = dev_get_drvdata(&udev->dev);
-
- /* get stub_device */
- if (!sdev) {
- dev_err(&udev->dev, "could not get device");
- return;
- }
-
- dev_set_drvdata(&udev->dev, NULL);
-
- /*
- * NOTE: rx/tx threads are invoked for each usb_device.
- */
- stub_remove_files(&udev->dev);
-
- /* release port */
- rc = usb_hub_release_port(udev->parent, udev->portnum,
- (struct usb_dev_state *) udev);
- if (rc) {
- dev_dbg(&udev->dev, "unable to release port\n");
- return;
- }
-
- /* If usb reset is called from event handler */
- if (busid_priv->sdev->ud.eh == current)
- return;
-
- /* shutdown the current connection */
- shutdown_busid(busid_priv);
-
- usb_put_dev(sdev->udev);
-
- /* free sdev */
- busid_priv->sdev = NULL;
- stub_device_free(sdev);
-
- if (busid_priv->status == STUB_BUSID_ALLOC) {
- busid_priv->status = STUB_BUSID_ADDED;
- } else {
- busid_priv->status = STUB_BUSID_OTHER;
- del_match_busid((char *)udev_busid);
- }
-}
-
-#ifdef CONFIG_PM
-
-/* These functions need usb_port_suspend and usb_port_resume,
- * which reside in drivers/usb/core/usb.h. Skip for now. */
-
-static int stub_suspend(struct usb_device *udev, pm_message_t message)
-{
- dev_dbg(&udev->dev, "stub_suspend\n");
-
- return 0;
-}
-
-static int stub_resume(struct usb_device *udev, pm_message_t message)
-{
- dev_dbg(&udev->dev, "stub_resume\n");
-
- return 0;
-}
-
-#endif /* CONFIG_PM */
-
-struct usb_device_driver stub_driver = {
- .name = "usbip-host",
- .probe = stub_probe,
- .disconnect = stub_disconnect,
-#ifdef CONFIG_PM
- .suspend = stub_suspend,
- .resume = stub_resume,
-#endif
- .supports_autosuspend = 0,
-};
+++ /dev/null
-/*
- * Copyright (C) 2003-2008 Takahiro Hirofuchi
- *
- * This 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 is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
- */
-
-#include <linux/string.h>
-#include <linux/module.h>
-#include <linux/device.h>
-
-#include "usbip_common.h"
-#include "stub.h"
-
-#define DRIVER_AUTHOR "Takahiro Hirofuchi"
-#define DRIVER_DESC "USB/IP Host Driver"
-
-struct kmem_cache *stub_priv_cache;
-/*
- * busid_tables defines matching busids that usbip can grab. A user can change
- * dynamically what device is locally used and what device is exported to a
- * remote host.
- */
-#define MAX_BUSID 16
-static struct bus_id_priv busid_table[MAX_BUSID];
-static spinlock_t busid_table_lock;
-
-static void init_busid_table(void)
-{
- /*
- * This also sets the bus_table[i].status to
- * STUB_BUSID_OTHER, which is 0.
- */
- memset(busid_table, 0, sizeof(busid_table));
-
- spin_lock_init(&busid_table_lock);
-}
-
-/*
- * Find the index of the busid by name.
- * Must be called with busid_table_lock held.
- */
-static int get_busid_idx(const char *busid)
-{
- int i;
- int idx = -1;
-
- for (i = 0; i < MAX_BUSID; i++)
- if (busid_table[i].name[0])
- if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) {
- idx = i;
- break;
- }
- return idx;
-}
-
-struct bus_id_priv *get_busid_priv(const char *busid)
-{
- int idx;
- struct bus_id_priv *bid = NULL;
-
- spin_lock(&busid_table_lock);
- idx = get_busid_idx(busid);
- if (idx >= 0)
- bid = &(busid_table[idx]);
- spin_unlock(&busid_table_lock);
-
- return bid;
-}
-
-static int add_match_busid(char *busid)
-{
- int i;
- int ret = -1;
-
- spin_lock(&busid_table_lock);
- /* already registered? */
- if (get_busid_idx(busid) >= 0) {
- ret = 0;
- goto out;
- }
-
- for (i = 0; i < MAX_BUSID; i++)
- if (!busid_table[i].name[0]) {
- strlcpy(busid_table[i].name, busid, BUSID_SIZE);
- if ((busid_table[i].status != STUB_BUSID_ALLOC) &&
- (busid_table[i].status != STUB_BUSID_REMOV))
- busid_table[i].status = STUB_BUSID_ADDED;
- ret = 0;
- break;
- }
-
-out:
- spin_unlock(&busid_table_lock);
-
- return ret;
-}
-
-int del_match_busid(char *busid)
-{
- int idx;
- int ret = -1;
-
- spin_lock(&busid_table_lock);
- idx = get_busid_idx(busid);
- if (idx < 0)
- goto out;
-
- /* found */
- ret = 0;
-
- if (busid_table[idx].status == STUB_BUSID_OTHER)
- memset(busid_table[idx].name, 0, BUSID_SIZE);
-
- if ((busid_table[idx].status != STUB_BUSID_OTHER) &&
- (busid_table[idx].status != STUB_BUSID_ADDED))
- busid_table[idx].status = STUB_BUSID_REMOV;
-
-out:
- spin_unlock(&busid_table_lock);
-
- return ret;
-}
-
-static ssize_t show_match_busid(struct device_driver *drv, char *buf)
-{
- int i;
- char *out = buf;
-
- spin_lock(&busid_table_lock);
- for (i = 0; i < MAX_BUSID; i++)
- if (busid_table[i].name[0])
- out += sprintf(out, "%s ", busid_table[i].name);
- spin_unlock(&busid_table_lock);
- out += sprintf(out, "\n");
-
- return out - buf;
-}
-
-static ssize_t store_match_busid(struct device_driver *dev, const char *buf,
- size_t count)
-{
- int len;
- char busid[BUSID_SIZE];
-
- if (count < 5)
- return -EINVAL;
-
- /* busid needs to include \0 termination */
- len = strlcpy(busid, buf + 4, BUSID_SIZE);
- if (sizeof(busid) <= len)
- return -EINVAL;
-
- if (!strncmp(buf, "add ", 4)) {
- if (add_match_busid(busid) < 0)
- return -ENOMEM;
-
- pr_debug("add busid %s\n", busid);
- return count;
- }
-
- if (!strncmp(buf, "del ", 4)) {
- if (del_match_busid(busid) < 0)
- return -ENODEV;
-
- pr_debug("del busid %s\n", busid);
- return count;
- }
-
- return -EINVAL;
-}
-static DRIVER_ATTR(match_busid, S_IRUSR | S_IWUSR, show_match_busid,
- store_match_busid);
-
-static ssize_t rebind_store(struct device_driver *dev, const char *buf,
- size_t count)
-{
- int ret;
- int len;
- struct bus_id_priv *bid;
-
- /* buf length should be less that BUSID_SIZE */
- len = strnlen(buf, BUSID_SIZE);
-
- if (!(len < BUSID_SIZE))
- return -EINVAL;
-
- bid = get_busid_priv(buf);
- if (!bid)
- return -ENODEV;
-
- ret = device_attach(&bid->udev->dev);
- if (ret < 0) {
- dev_err(&bid->udev->dev, "rebind failed\n");
- return ret;
- }
-
- return count;
-}
-
-static DRIVER_ATTR_WO(rebind);
-
-static struct stub_priv *stub_priv_pop_from_listhead(struct list_head *listhead)
-{
- struct stub_priv *priv, *tmp;
-
- list_for_each_entry_safe(priv, tmp, listhead, list) {
- list_del(&priv->list);
- return priv;
- }
-
- return NULL;
-}
-
-static struct stub_priv *stub_priv_pop(struct stub_device *sdev)
-{
- unsigned long flags;
- struct stub_priv *priv;
-
- spin_lock_irqsave(&sdev->priv_lock, flags);
-
- priv = stub_priv_pop_from_listhead(&sdev->priv_init);
- if (priv)
- goto done;
-
- priv = stub_priv_pop_from_listhead(&sdev->priv_tx);
- if (priv)
- goto done;
-
- priv = stub_priv_pop_from_listhead(&sdev->priv_free);
-
-done:
- spin_unlock_irqrestore(&sdev->priv_lock, flags);
-
- return priv;
-}
-
-void stub_device_cleanup_urbs(struct stub_device *sdev)
-{
- struct stub_priv *priv;
- struct urb *urb;
-
- dev_dbg(&sdev->udev->dev, "free sdev %p\n", sdev);
-
- while ((priv = stub_priv_pop(sdev))) {
- urb = priv->urb;
- dev_dbg(&sdev->udev->dev, "free urb %p\n", urb);
- usb_kill_urb(urb);
-
- kmem_cache_free(stub_priv_cache, priv);
-
- kfree(urb->transfer_buffer);
- kfree(urb->setup_packet);
- usb_free_urb(urb);
- }
-}
-
-static int __init usbip_host_init(void)
-{
- int ret;
-
- init_busid_table();
-
- stub_priv_cache = KMEM_CACHE(stub_priv, SLAB_HWCACHE_ALIGN);
- if (!stub_priv_cache) {
- pr_err("kmem_cache_create failed\n");
- return -ENOMEM;
- }
-
- ret = usb_register_device_driver(&stub_driver, THIS_MODULE);
- if (ret) {
- pr_err("usb_register failed %d\n", ret);
- goto err_usb_register;
- }
-
- ret = driver_create_file(&stub_driver.drvwrap.driver,
- &driver_attr_match_busid);
- if (ret) {
- pr_err("driver_create_file failed\n");
- goto err_create_file;
- }
-
- ret = driver_create_file(&stub_driver.drvwrap.driver,
- &driver_attr_rebind);
- if (ret) {
- pr_err("driver_create_file failed\n");
- goto err_create_file;
- }
-
- pr_info(DRIVER_DESC " v" USBIP_VERSION "\n");
- return ret;
-
-err_create_file:
- usb_deregister_device_driver(&stub_driver);
-err_usb_register:
- kmem_cache_destroy(stub_priv_cache);
- return ret;
-}
-
-static void __exit usbip_host_exit(void)
-{
- driver_remove_file(&stub_driver.drvwrap.driver,
- &driver_attr_match_busid);
-
- driver_remove_file(&stub_driver.drvwrap.driver,
- &driver_attr_rebind);
-
- /*
- * deregister() calls stub_disconnect() for all devices. Device
- * specific data is cleared in stub_disconnect().
- */
- usb_deregister_device_driver(&stub_driver);
-
- kmem_cache_destroy(stub_priv_cache);
-}
-
-module_init(usbip_host_init);
-module_exit(usbip_host_exit);
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
-MODULE_VERSION(USBIP_VERSION);
+++ /dev/null
-/*
- * Copyright (C) 2003-2008 Takahiro Hirofuchi
- *
- * This 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 is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
- */
-
-#include <asm/byteorder.h>
-#include <linux/kthread.h>
-#include <linux/usb.h>
-#include <linux/usb/hcd.h>
-
-#include "usbip_common.h"
-#include "stub.h"
-
-static int is_clear_halt_cmd(struct urb *urb)
-{
- struct usb_ctrlrequest *req;
-
- req = (struct usb_ctrlrequest *) urb->setup_packet;
-
- return (req->bRequest == USB_REQ_CLEAR_FEATURE) &&
- (req->bRequestType == USB_RECIP_ENDPOINT) &&
- (req->wValue == USB_ENDPOINT_HALT);
-}
-
-static int is_set_interface_cmd(struct urb *urb)
-{
- struct usb_ctrlrequest *req;
-
- req = (struct usb_ctrlrequest *) urb->setup_packet;
-
- return (req->bRequest == USB_REQ_SET_INTERFACE) &&
- (req->bRequestType == USB_RECIP_INTERFACE);
-}
-
-static int is_set_configuration_cmd(struct urb *urb)
-{
- struct usb_ctrlrequest *req;
-
- req = (struct usb_ctrlrequest *) urb->setup_packet;
-
- return (req->bRequest == USB_REQ_SET_CONFIGURATION) &&
- (req->bRequestType == USB_RECIP_DEVICE);
-}
-
-static int is_reset_device_cmd(struct urb *urb)
-{
- struct usb_ctrlrequest *req;
- __u16 value;
- __u16 index;
-
- req = (struct usb_ctrlrequest *) urb->setup_packet;
- value = le16_to_cpu(req->wValue);
- index = le16_to_cpu(req->wIndex);
-
- if ((req->bRequest == USB_REQ_SET_FEATURE) &&
- (req->bRequestType == USB_RT_PORT) &&
- (value == USB_PORT_FEAT_RESET)) {
- usbip_dbg_stub_rx("reset_device_cmd, port %u\n", index);
- return 1;
- } else
- return 0;
-}
-
-static int tweak_clear_halt_cmd(struct urb *urb)
-{
- struct usb_ctrlrequest *req;
- int target_endp;
- int target_dir;
- int target_pipe;
- int ret;
-
- req = (struct usb_ctrlrequest *) urb->setup_packet;
-
- /*
- * The stalled endpoint is specified in the wIndex value. The endpoint
- * of the urb is the target of this clear_halt request (i.e., control
- * endpoint).
- */
- target_endp = le16_to_cpu(req->wIndex) & 0x000f;
-
- /* the stalled endpoint direction is IN or OUT?. USB_DIR_IN is 0x80. */
- target_dir = le16_to_cpu(req->wIndex) & 0x0080;
-
- if (target_dir)
- target_pipe = usb_rcvctrlpipe(urb->dev, target_endp);
- else
- target_pipe = usb_sndctrlpipe(urb->dev, target_endp);
-
- ret = usb_clear_halt(urb->dev, target_pipe);
- if (ret < 0)
- dev_err(&urb->dev->dev,
- "usb_clear_halt error: devnum %d endp %d ret %d\n",
- urb->dev->devnum, target_endp, ret);
- else
- dev_info(&urb->dev->dev,
- "usb_clear_halt done: devnum %d endp %d\n",
- urb->dev->devnum, target_endp);
-
- return ret;
-}
-
-static int tweak_set_interface_cmd(struct urb *urb)
-{
- struct usb_ctrlrequest *req;
- __u16 alternate;
- __u16 interface;
- int ret;
-
- req = (struct usb_ctrlrequest *) urb->setup_packet;
- alternate = le16_to_cpu(req->wValue);
- interface = le16_to_cpu(req->wIndex);
-
- usbip_dbg_stub_rx("set_interface: inf %u alt %u\n",
- interface, alternate);
-
- ret = usb_set_interface(urb->dev, interface, alternate);
- if (ret < 0)
- dev_err(&urb->dev->dev,
- "usb_set_interface error: inf %u alt %u ret %d\n",
- interface, alternate, ret);
- else
- dev_info(&urb->dev->dev,
- "usb_set_interface done: inf %u alt %u\n",
- interface, alternate);
-
- return ret;
-}
-
-static int tweak_set_configuration_cmd(struct urb *urb)
-{
- struct stub_priv *priv = (struct stub_priv *) urb->context;
- struct stub_device *sdev = priv->sdev;
- struct usb_ctrlrequest *req;
- __u16 config;
- int err;
-
- req = (struct usb_ctrlrequest *) urb->setup_packet;
- config = le16_to_cpu(req->wValue);
-
- err = usb_set_configuration(sdev->udev, config);
- if (err && err != -ENODEV)
- dev_err(&sdev->udev->dev, "can't set config #%d, error %d\n",
- config, err);
- return 0;
-}
-
-static int tweak_reset_device_cmd(struct urb *urb)
-{
- struct stub_priv *priv = (struct stub_priv *) urb->context;
- struct stub_device *sdev = priv->sdev;
-
- dev_info(&urb->dev->dev, "usb_queue_reset_device\n");
-
- /*
- * With the implementation of pre_reset and post_reset the driver no
- * longer unbinds. This allows the use of synchronous reset.
- */
-
- if (usb_lock_device_for_reset(sdev->udev, sdev->interface) < 0) {
- dev_err(&urb->dev->dev, "could not obtain lock to reset device\n");
- return 0;
- }
- usb_reset_device(sdev->udev);
- usb_unlock_device(sdev->udev);
-
- return 0;
-}
-
-/*
- * clear_halt, set_interface, and set_configuration require special tricks.
- */
-static void tweak_special_requests(struct urb *urb)
-{
- if (!urb || !urb->setup_packet)
- return;
-
- if (usb_pipetype(urb->pipe) != PIPE_CONTROL)
- return;
-
- if (is_clear_halt_cmd(urb))
- /* tweak clear_halt */
- tweak_clear_halt_cmd(urb);
-
- else if (is_set_interface_cmd(urb))
- /* tweak set_interface */
- tweak_set_interface_cmd(urb);
-
- else if (is_set_configuration_cmd(urb))
- /* tweak set_configuration */
- tweak_set_configuration_cmd(urb);
-
- else if (is_reset_device_cmd(urb))
- tweak_reset_device_cmd(urb);
- else
- usbip_dbg_stub_rx("no need to tweak\n");
-}
-
-/*
- * stub_recv_unlink() unlinks the URB by a call to usb_unlink_urb().
- * By unlinking the urb asynchronously, stub_rx can continuously
- * process coming urbs. Even if the urb is unlinked, its completion
- * handler will be called and stub_tx will send a return pdu.
- *
- * See also comments about unlinking strategy in vhci_hcd.c.
- */
-static int stub_recv_cmd_unlink(struct stub_device *sdev,
- struct usbip_header *pdu)
-{
- int ret;
- unsigned long flags;
- struct stub_priv *priv;
-
- spin_lock_irqsave(&sdev->priv_lock, flags);
-
- list_for_each_entry(priv, &sdev->priv_init, list) {
- if (priv->seqnum != pdu->u.cmd_unlink.seqnum)
- continue;
-
- dev_info(&priv->urb->dev->dev, "unlink urb %p\n",
- priv->urb);
-
- /*
- * This matched urb is not completed yet (i.e., be in
- * flight in usb hcd hardware/driver). Now we are
- * cancelling it. The unlinking flag means that we are
- * now not going to return the normal result pdu of a
- * submission request, but going to return a result pdu
- * of the unlink request.
- */
- priv->unlinking = 1;
-
- /*
- * In the case that unlinking flag is on, prev->seqnum
- * is changed from the seqnum of the cancelling urb to
- * the seqnum of the unlink request. This will be used
- * to make the result pdu of the unlink request.
- */
- priv->seqnum = pdu->base.seqnum;
-
- spin_unlock_irqrestore(&sdev->priv_lock, flags);
-
- /*
- * usb_unlink_urb() is now out of spinlocking to avoid
- * spinlock recursion since stub_complete() is
- * sometimes called in this context but not in the
- * interrupt context. If stub_complete() is executed
- * before we call usb_unlink_urb(), usb_unlink_urb()
- * will return an error value. In this case, stub_tx
- * will return the result pdu of this unlink request
- * though submission is completed and actual unlinking
- * is not executed. OK?
- */
- /* In the above case, urb->status is not -ECONNRESET,
- * so a driver in a client host will know the failure
- * of the unlink request ?
- */
- ret = usb_unlink_urb(priv->urb);
- if (ret != -EINPROGRESS)
- dev_err(&priv->urb->dev->dev,
- "failed to unlink a urb %p, ret %d\n",
- priv->urb, ret);
-
- return 0;
- }
-
- usbip_dbg_stub_rx("seqnum %d is not pending\n",
- pdu->u.cmd_unlink.seqnum);
-
- /*
- * The urb of the unlink target is not found in priv_init queue. It was
- * already completed and its results is/was going to be sent by a
- * CMD_RET pdu. In this case, usb_unlink_urb() is not needed. We only
- * return the completeness of this unlink request to vhci_hcd.
- */
- stub_enqueue_ret_unlink(sdev, pdu->base.seqnum, 0);
-
- spin_unlock_irqrestore(&sdev->priv_lock, flags);
-
- return 0;
-}
-
-static int valid_request(struct stub_device *sdev, struct usbip_header *pdu)
-{
- struct usbip_device *ud = &sdev->ud;
- int valid = 0;
-
- if (pdu->base.devid == sdev->devid) {
- spin_lock_irq(&ud->lock);
- if (ud->status == SDEV_ST_USED) {
- /* A request is valid. */
- valid = 1;
- }
- spin_unlock_irq(&ud->lock);
- }
-
- return valid;
-}
-
-static struct stub_priv *stub_priv_alloc(struct stub_device *sdev,
- struct usbip_header *pdu)
-{
- struct stub_priv *priv;
- struct usbip_device *ud = &sdev->ud;
- unsigned long flags;
-
- spin_lock_irqsave(&sdev->priv_lock, flags);
-
- priv = kmem_cache_zalloc(stub_priv_cache, GFP_ATOMIC);
- if (!priv) {
- dev_err(&sdev->interface->dev, "alloc stub_priv\n");
- spin_unlock_irqrestore(&sdev->priv_lock, flags);
- usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
- return NULL;
- }
-
- priv->seqnum = pdu->base.seqnum;
- priv->sdev = sdev;
-
- /*
- * After a stub_priv is linked to a list_head,
- * our error handler can free allocated data.
- */
- list_add_tail(&priv->list, &sdev->priv_init);
-
- spin_unlock_irqrestore(&sdev->priv_lock, flags);
-
- return priv;
-}
-
-static int get_pipe(struct stub_device *sdev, int epnum, int dir)
-{
- struct usb_device *udev = sdev->udev;
- struct usb_host_endpoint *ep;
- struct usb_endpoint_descriptor *epd = NULL;
-
- if (dir == USBIP_DIR_IN)
- ep = udev->ep_in[epnum & 0x7f];
- else
- ep = udev->ep_out[epnum & 0x7f];
- if (!ep) {
- dev_err(&sdev->interface->dev, "no such endpoint?, %d\n",
- epnum);
- BUG();
- }
-
- epd = &ep->desc;
- if (usb_endpoint_xfer_control(epd)) {
- if (dir == USBIP_DIR_OUT)
- return usb_sndctrlpipe(udev, epnum);
- else
- return usb_rcvctrlpipe(udev, epnum);
- }
-
- if (usb_endpoint_xfer_bulk(epd)) {
- if (dir == USBIP_DIR_OUT)
- return usb_sndbulkpipe(udev, epnum);
- else
- return usb_rcvbulkpipe(udev, epnum);
- }
-
- if (usb_endpoint_xfer_int(epd)) {
- if (dir == USBIP_DIR_OUT)
- return usb_sndintpipe(udev, epnum);
- else
- return usb_rcvintpipe(udev, epnum);
- }
-
- if (usb_endpoint_xfer_isoc(epd)) {
- if (dir == USBIP_DIR_OUT)
- return usb_sndisocpipe(udev, epnum);
- else
- return usb_rcvisocpipe(udev, epnum);
- }
-
- /* NOT REACHED */
- dev_err(&sdev->interface->dev, "get pipe, epnum %d\n", epnum);
- return 0;
-}
-
-static void masking_bogus_flags(struct urb *urb)
-{
- int xfertype;
- struct usb_device *dev;
- struct usb_host_endpoint *ep;
- int is_out;
- unsigned int allowed;
-
- if (!urb || urb->hcpriv || !urb->complete)
- return;
- dev = urb->dev;
- if ((!dev) || (dev->state < USB_STATE_UNAUTHENTICATED))
- return;
-
- ep = (usb_pipein(urb->pipe) ? dev->ep_in : dev->ep_out)
- [usb_pipeendpoint(urb->pipe)];
- if (!ep)
- return;
-
- xfertype = usb_endpoint_type(&ep->desc);
- if (xfertype == USB_ENDPOINT_XFER_CONTROL) {
- struct usb_ctrlrequest *setup =
- (struct usb_ctrlrequest *) urb->setup_packet;
-
- if (!setup)
- return;
- is_out = !(setup->bRequestType & USB_DIR_IN) ||
- !setup->wLength;
- } else {
- is_out = usb_endpoint_dir_out(&ep->desc);
- }
-
- /* enforce simple/standard policy */
- allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT |
- URB_DIR_MASK | URB_FREE_BUFFER);
- switch (xfertype) {
- case USB_ENDPOINT_XFER_BULK:
- if (is_out)
- allowed |= URB_ZERO_PACKET;
- /* FALLTHROUGH */
- case USB_ENDPOINT_XFER_CONTROL:
- allowed |= URB_NO_FSBR; /* only affects UHCI */
- /* FALLTHROUGH */
- default: /* all non-iso endpoints */
- if (!is_out)
- allowed |= URB_SHORT_NOT_OK;
- break;
- case USB_ENDPOINT_XFER_ISOC:
- allowed |= URB_ISO_ASAP;
- break;
- }
- urb->transfer_flags &= allowed;
-}
-
-static void stub_recv_cmd_submit(struct stub_device *sdev,
- struct usbip_header *pdu)
-{
- int ret;
- struct stub_priv *priv;
- struct usbip_device *ud = &sdev->ud;
- struct usb_device *udev = sdev->udev;
- int pipe = get_pipe(sdev, pdu->base.ep, pdu->base.direction);
-
- priv = stub_priv_alloc(sdev, pdu);
- if (!priv)
- return;
-
- /* setup a urb */
- if (usb_pipeisoc(pipe))
- priv->urb = usb_alloc_urb(pdu->u.cmd_submit.number_of_packets,
- GFP_KERNEL);
- else
- priv->urb = usb_alloc_urb(0, GFP_KERNEL);
-
- if (!priv->urb) {
- dev_err(&sdev->interface->dev, "malloc urb\n");
- usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
- return;
- }
-
- /* allocate urb transfer buffer, if needed */
- if (pdu->u.cmd_submit.transfer_buffer_length > 0) {
- priv->urb->transfer_buffer =
- kzalloc(pdu->u.cmd_submit.transfer_buffer_length,
- GFP_KERNEL);
- if (!priv->urb->transfer_buffer) {
- usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
- return;
- }
- }
-
- /* copy urb setup packet */
- priv->urb->setup_packet = kmemdup(&pdu->u.cmd_submit.setup, 8,
- GFP_KERNEL);
- if (!priv->urb->setup_packet) {
- dev_err(&sdev->interface->dev, "allocate setup_packet\n");
- usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
- return;
- }
-
- /* set other members from the base header of pdu */
- priv->urb->context = (void *) priv;
- priv->urb->dev = udev;
- priv->urb->pipe = pipe;
- priv->urb->complete = stub_complete;
-
- usbip_pack_pdu(pdu, priv->urb, USBIP_CMD_SUBMIT, 0);
-
-
- if (usbip_recv_xbuff(ud, priv->urb) < 0)
- return;
-
- if (usbip_recv_iso(ud, priv->urb) < 0)
- return;
-
- /* no need to submit an intercepted request, but harmless? */
- tweak_special_requests(priv->urb);
-
- masking_bogus_flags(priv->urb);
- /* urb is now ready to submit */
- ret = usb_submit_urb(priv->urb, GFP_KERNEL);
-
- if (ret == 0)
- usbip_dbg_stub_rx("submit urb ok, seqnum %u\n",
- pdu->base.seqnum);
- else {
- dev_err(&sdev->interface->dev, "submit_urb error, %d\n", ret);
- usbip_dump_header(pdu);
- usbip_dump_urb(priv->urb);
-
- /*
- * Pessimistic.
- * This connection will be discarded.
- */
- usbip_event_add(ud, SDEV_EVENT_ERROR_SUBMIT);
- }
-
- usbip_dbg_stub_rx("Leave\n");
-}
-
-/* recv a pdu */
-static void stub_rx_pdu(struct usbip_device *ud)
-{
- int ret;
- struct usbip_header pdu;
- struct stub_device *sdev = container_of(ud, struct stub_device, ud);
- struct device *dev = &sdev->udev->dev;
-
- usbip_dbg_stub_rx("Enter\n");
-
- memset(&pdu, 0, sizeof(pdu));
-
- /* receive a pdu header */
- ret = usbip_recv(ud->tcp_socket, &pdu, sizeof(pdu));
- if (ret != sizeof(pdu)) {
- dev_err(dev, "recv a header, %d\n", ret);
- usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
- return;
- }
-
- usbip_header_correct_endian(&pdu, 0);
-
- if (usbip_dbg_flag_stub_rx)
- usbip_dump_header(&pdu);
-
- if (!valid_request(sdev, &pdu)) {
- dev_err(dev, "recv invalid request\n");
- usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
- return;
- }
-
- switch (pdu.base.command) {
- case USBIP_CMD_UNLINK:
- stub_recv_cmd_unlink(sdev, &pdu);
- break;
-
- case USBIP_CMD_SUBMIT:
- stub_recv_cmd_submit(sdev, &pdu);
- break;
-
- default:
- /* NOTREACHED */
- dev_err(dev, "unknown pdu\n");
- usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
- break;
- }
-}
-
-int stub_rx_loop(void *data)
-{
- struct usbip_device *ud = data;
-
- while (!kthread_should_stop()) {
- if (usbip_event_happened(ud))
- break;
-
- stub_rx_pdu(ud);
- }
-
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) 2003-2008 Takahiro Hirofuchi
- *
- * This 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 is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
- */
-
-#include <linux/kthread.h>
-#include <linux/socket.h>
-
-#include "usbip_common.h"
-#include "stub.h"
-
-static void stub_free_priv_and_urb(struct stub_priv *priv)
-{
- struct urb *urb = priv->urb;
-
- kfree(urb->setup_packet);
- kfree(urb->transfer_buffer);
- list_del(&priv->list);
- kmem_cache_free(stub_priv_cache, priv);
- usb_free_urb(urb);
-}
-
-/* be in spin_lock_irqsave(&sdev->priv_lock, flags) */
-void stub_enqueue_ret_unlink(struct stub_device *sdev, __u32 seqnum,
- __u32 status)
-{
- struct stub_unlink *unlink;
-
- unlink = kzalloc(sizeof(struct stub_unlink), GFP_ATOMIC);
- if (!unlink) {
- usbip_event_add(&sdev->ud, VDEV_EVENT_ERROR_MALLOC);
- return;
- }
-
- unlink->seqnum = seqnum;
- unlink->status = status;
-
- list_add_tail(&unlink->list, &sdev->unlink_tx);
-}
-
-/**
- * stub_complete - completion handler of a usbip urb
- * @urb: pointer to the urb completed
- *
- * When a urb has completed, the USB core driver calls this function mostly in
- * the interrupt context. To return the result of a urb, the completed urb is
- * linked to the pending list of returning.
- *
- */
-void stub_complete(struct urb *urb)
-{
- struct stub_priv *priv = (struct stub_priv *) urb->context;
- struct stub_device *sdev = priv->sdev;
- unsigned long flags;
-
- usbip_dbg_stub_tx("complete! status %d\n", urb->status);
-
- switch (urb->status) {
- case 0:
- /* OK */
- break;
- case -ENOENT:
- dev_info(&urb->dev->dev,
- "stopped by a call to usb_kill_urb() because of cleaning up a virtual connection\n");
- return;
- case -ECONNRESET:
- dev_info(&urb->dev->dev,
- "unlinked by a call to usb_unlink_urb()\n");
- break;
- case -EPIPE:
- dev_info(&urb->dev->dev, "endpoint %d is stalled\n",
- usb_pipeendpoint(urb->pipe));
- break;
- case -ESHUTDOWN:
- dev_info(&urb->dev->dev, "device removed?\n");
- break;
- default:
- dev_info(&urb->dev->dev,
- "urb completion with non-zero status %d\n",
- urb->status);
- break;
- }
-
- /* link a urb to the queue of tx. */
- spin_lock_irqsave(&sdev->priv_lock, flags);
- if (priv->unlinking) {
- stub_enqueue_ret_unlink(sdev, priv->seqnum, urb->status);
- stub_free_priv_and_urb(priv);
- } else {
- list_move_tail(&priv->list, &sdev->priv_tx);
- }
- spin_unlock_irqrestore(&sdev->priv_lock, flags);
-
- /* wake up tx_thread */
- wake_up(&sdev->tx_waitq);
-}
-
-static inline void setup_base_pdu(struct usbip_header_basic *base,
- __u32 command, __u32 seqnum)
-{
- base->command = command;
- base->seqnum = seqnum;
- base->devid = 0;
- base->ep = 0;
- base->direction = 0;
-}
-
-static void setup_ret_submit_pdu(struct usbip_header *rpdu, struct urb *urb)
-{
- struct stub_priv *priv = (struct stub_priv *) urb->context;
-
- setup_base_pdu(&rpdu->base, USBIP_RET_SUBMIT, priv->seqnum);
- usbip_pack_pdu(rpdu, urb, USBIP_RET_SUBMIT, 1);
-}
-
-static void setup_ret_unlink_pdu(struct usbip_header *rpdu,
- struct stub_unlink *unlink)
-{
- setup_base_pdu(&rpdu->base, USBIP_RET_UNLINK, unlink->seqnum);
- rpdu->u.ret_unlink.status = unlink->status;
-}
-
-static struct stub_priv *dequeue_from_priv_tx(struct stub_device *sdev)
-{
- unsigned long flags;
- struct stub_priv *priv, *tmp;
-
- spin_lock_irqsave(&sdev->priv_lock, flags);
-
- list_for_each_entry_safe(priv, tmp, &sdev->priv_tx, list) {
- list_move_tail(&priv->list, &sdev->priv_free);
- spin_unlock_irqrestore(&sdev->priv_lock, flags);
- return priv;
- }
-
- spin_unlock_irqrestore(&sdev->priv_lock, flags);
-
- return NULL;
-}
-
-static int stub_send_ret_submit(struct stub_device *sdev)
-{
- unsigned long flags;
- struct stub_priv *priv, *tmp;
-
- struct msghdr msg;
- size_t txsize;
-
- size_t total_size = 0;
-
- while ((priv = dequeue_from_priv_tx(sdev)) != NULL) {
- int ret;
- struct urb *urb = priv->urb;
- struct usbip_header pdu_header;
- struct usbip_iso_packet_descriptor *iso_buffer = NULL;
- struct kvec *iov = NULL;
- int iovnum = 0;
-
- txsize = 0;
- memset(&pdu_header, 0, sizeof(pdu_header));
- memset(&msg, 0, sizeof(msg));
-
- if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)
- iovnum = 2 + urb->number_of_packets;
- else
- iovnum = 2;
-
- iov = kcalloc(iovnum, sizeof(struct kvec), GFP_KERNEL);
-
- if (!iov) {
- usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_MALLOC);
- return -1;
- }
-
- iovnum = 0;
-
- /* 1. setup usbip_header */
- setup_ret_submit_pdu(&pdu_header, urb);
- usbip_dbg_stub_tx("setup txdata seqnum: %d urb: %p\n",
- pdu_header.base.seqnum, urb);
- usbip_header_correct_endian(&pdu_header, 1);
-
- iov[iovnum].iov_base = &pdu_header;
- iov[iovnum].iov_len = sizeof(pdu_header);
- iovnum++;
- txsize += sizeof(pdu_header);
-
- /* 2. setup transfer buffer */
- if (usb_pipein(urb->pipe) &&
- usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS &&
- urb->actual_length > 0) {
- iov[iovnum].iov_base = urb->transfer_buffer;
- iov[iovnum].iov_len = urb->actual_length;
- iovnum++;
- txsize += urb->actual_length;
- } else if (usb_pipein(urb->pipe) &&
- usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
- /*
- * For isochronous packets: actual length is the sum of
- * the actual length of the individual, packets, but as
- * the packet offsets are not changed there will be
- * padding between the packets. To optimally use the
- * bandwidth the padding is not transmitted.
- */
-
- int i;
-
- for (i = 0; i < urb->number_of_packets; i++) {
- iov[iovnum].iov_base = urb->transfer_buffer +
- urb->iso_frame_desc[i].offset;
- iov[iovnum].iov_len =
- urb->iso_frame_desc[i].actual_length;
- iovnum++;
- txsize += urb->iso_frame_desc[i].actual_length;
- }
-
- if (txsize != sizeof(pdu_header) + urb->actual_length) {
- dev_err(&sdev->interface->dev,
- "actual length of urb %d does not match iso packet sizes %zu\n",
- urb->actual_length,
- txsize-sizeof(pdu_header));
- kfree(iov);
- usbip_event_add(&sdev->ud,
- SDEV_EVENT_ERROR_TCP);
- return -1;
- }
- }
-
- /* 3. setup iso_packet_descriptor */
- if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
- ssize_t len = 0;
-
- iso_buffer = usbip_alloc_iso_desc_pdu(urb, &len);
- if (!iso_buffer) {
- usbip_event_add(&sdev->ud,
- SDEV_EVENT_ERROR_MALLOC);
- kfree(iov);
- return -1;
- }
-
- iov[iovnum].iov_base = iso_buffer;
- iov[iovnum].iov_len = len;
- txsize += len;
- iovnum++;
- }
-
- ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg,
- iov, iovnum, txsize);
- if (ret != txsize) {
- dev_err(&sdev->interface->dev,
- "sendmsg failed!, retval %d for %zd\n",
- ret, txsize);
- kfree(iov);
- kfree(iso_buffer);
- usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP);
- return -1;
- }
-
- kfree(iov);
- kfree(iso_buffer);
-
- total_size += txsize;
- }
-
- spin_lock_irqsave(&sdev->priv_lock, flags);
- list_for_each_entry_safe(priv, tmp, &sdev->priv_free, list) {
- stub_free_priv_and_urb(priv);
- }
- spin_unlock_irqrestore(&sdev->priv_lock, flags);
-
- return total_size;
-}
-
-static struct stub_unlink *dequeue_from_unlink_tx(struct stub_device *sdev)
-{
- unsigned long flags;
- struct stub_unlink *unlink, *tmp;
-
- spin_lock_irqsave(&sdev->priv_lock, flags);
-
- list_for_each_entry_safe(unlink, tmp, &sdev->unlink_tx, list) {
- list_move_tail(&unlink->list, &sdev->unlink_free);
- spin_unlock_irqrestore(&sdev->priv_lock, flags);
- return unlink;
- }
-
- spin_unlock_irqrestore(&sdev->priv_lock, flags);
-
- return NULL;
-}
-
-static int stub_send_ret_unlink(struct stub_device *sdev)
-{
- unsigned long flags;
- struct stub_unlink *unlink, *tmp;
-
- struct msghdr msg;
- struct kvec iov[1];
- size_t txsize;
-
- size_t total_size = 0;
-
- while ((unlink = dequeue_from_unlink_tx(sdev)) != NULL) {
- int ret;
- struct usbip_header pdu_header;
-
- txsize = 0;
- memset(&pdu_header, 0, sizeof(pdu_header));
- memset(&msg, 0, sizeof(msg));
- memset(&iov, 0, sizeof(iov));
-
- usbip_dbg_stub_tx("setup ret unlink %lu\n", unlink->seqnum);
-
- /* 1. setup usbip_header */
- setup_ret_unlink_pdu(&pdu_header, unlink);
- usbip_header_correct_endian(&pdu_header, 1);
-
- iov[0].iov_base = &pdu_header;
- iov[0].iov_len = sizeof(pdu_header);
- txsize += sizeof(pdu_header);
-
- ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg, iov,
- 1, txsize);
- if (ret != txsize) {
- dev_err(&sdev->interface->dev,
- "sendmsg failed!, retval %d for %zd\n",
- ret, txsize);
- usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP);
- return -1;
- }
-
- usbip_dbg_stub_tx("send txdata\n");
- total_size += txsize;
- }
-
- spin_lock_irqsave(&sdev->priv_lock, flags);
-
- list_for_each_entry_safe(unlink, tmp, &sdev->unlink_free, list) {
- list_del(&unlink->list);
- kfree(unlink);
- }
-
- spin_unlock_irqrestore(&sdev->priv_lock, flags);
-
- return total_size;
-}
-
-int stub_tx_loop(void *data)
-{
- struct usbip_device *ud = data;
- struct stub_device *sdev = container_of(ud, struct stub_device, ud);
-
- while (!kthread_should_stop()) {
- if (usbip_event_happened(ud))
- break;
-
- /*
- * send_ret_submit comes earlier than send_ret_unlink. stub_rx
- * looks at only priv_init queue. If the completion of a URB is
- * earlier than the receive of CMD_UNLINK, priv is moved to
- * priv_tx queue and stub_rx does not find the target priv. In
- * this case, vhci_rx receives the result of the submit request
- * and then receives the result of the unlink request. The
- * result of the submit is given back to the usbcore as the
- * completion of the unlink request. The request of the
- * unlink is ignored. This is ok because a driver who calls
- * usb_unlink_urb() understands the unlink was too late by
- * getting the status of the given-backed URB which has the
- * status of usb_submit_urb().
- */
- if (stub_send_ret_submit(sdev) < 0)
- break;
-
- if (stub_send_ret_unlink(sdev) < 0)
- break;
-
- wait_event_interruptible(sdev->tx_waitq,
- (!list_empty(&sdev->priv_tx) ||
- !list_empty(&sdev->unlink_tx) ||
- kthread_should_stop()));
- }
-
- return 0;
-}
+++ /dev/null
-/*
- * usbip.h
- *
- * USBIP uapi defines and function prototypes etc.
-*/
-
-#ifndef _UAPI_LINUX_USBIP_H
-#define _UAPI_LINUX_USBIP_H
-
-/* usbip device status - exported in usbip device sysfs status */
-enum usbip_device_status {
- /* sdev is available. */
- SDEV_ST_AVAILABLE = 0x01,
- /* sdev is now used. */
- SDEV_ST_USED,
- /* sdev is unusable because of a fatal error. */
- SDEV_ST_ERROR,
-
- /* vdev does not connect a remote device. */
- VDEV_ST_NULL,
- /* vdev is used, but the USB address is not assigned yet */
- VDEV_ST_NOTASSIGNED,
- VDEV_ST_USED,
- VDEV_ST_ERROR
-};
-#endif /* _UAPI_LINUX_USBIP_H */
+++ /dev/null
-/*
- * Copyright (C) 2003-2008 Takahiro Hirofuchi
- *
- * This 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 is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
- */
-
-#include <asm/byteorder.h>
-#include <linux/file.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/stat.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <net/sock.h>
-
-#include "usbip_common.h"
-
-#define DRIVER_AUTHOR "Takahiro Hirofuchi <hirofuchi@users.sourceforge.net>"
-#define DRIVER_DESC "USB/IP Core"
-
-#ifdef CONFIG_USBIP_DEBUG
-unsigned long usbip_debug_flag = 0xffffffff;
-#else
-unsigned long usbip_debug_flag;
-#endif
-EXPORT_SYMBOL_GPL(usbip_debug_flag);
-module_param(usbip_debug_flag, ulong, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(usbip_debug_flag, "debug flags (defined in usbip_common.h)");
-
-/* FIXME */
-struct device_attribute dev_attr_usbip_debug;
-EXPORT_SYMBOL_GPL(dev_attr_usbip_debug);
-
-static ssize_t usbip_debug_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return sprintf(buf, "%lx\n", usbip_debug_flag);
-}
-
-static ssize_t usbip_debug_store(struct device *dev,
- struct device_attribute *attr, const char *buf,
- size_t count)
-{
- if (sscanf(buf, "%lx", &usbip_debug_flag) != 1)
- return -EINVAL;
- return count;
-}
-DEVICE_ATTR_RW(usbip_debug);
-
-static void usbip_dump_buffer(char *buff, int bufflen)
-{
- print_hex_dump(KERN_DEBUG, "usbip-core", DUMP_PREFIX_OFFSET, 16, 4,
- buff, bufflen, false);
-}
-
-static void usbip_dump_pipe(unsigned int p)
-{
- unsigned char type = usb_pipetype(p);
- unsigned char ep = usb_pipeendpoint(p);
- unsigned char dev = usb_pipedevice(p);
- unsigned char dir = usb_pipein(p);
-
- pr_debug("dev(%d) ep(%d) [%s] ", dev, ep, dir ? "IN" : "OUT");
-
- switch (type) {
- case PIPE_ISOCHRONOUS:
- pr_debug("ISO\n");
- break;
- case PIPE_INTERRUPT:
- pr_debug("INT\n");
- break;
- case PIPE_CONTROL:
- pr_debug("CTRL\n");
- break;
- case PIPE_BULK:
- pr_debug("BULK\n");
- break;
- default:
- pr_debug("ERR\n");
- break;
- }
-}
-
-static void usbip_dump_usb_device(struct usb_device *udev)
-{
- struct device *dev = &udev->dev;
- int i;
-
- dev_dbg(dev, " devnum(%d) devpath(%s) usb speed(%s)",
- udev->devnum, udev->devpath, usb_speed_string(udev->speed));
-
- pr_debug("tt %p, ttport %d\n", udev->tt, udev->ttport);
-
- dev_dbg(dev, " ");
- for (i = 0; i < 16; i++)
- pr_debug(" %2u", i);
- pr_debug("\n");
-
- dev_dbg(dev, " toggle0(IN) :");
- for (i = 0; i < 16; i++)
- pr_debug(" %2u", (udev->toggle[0] & (1 << i)) ? 1 : 0);
- pr_debug("\n");
-
- dev_dbg(dev, " toggle1(OUT):");
- for (i = 0; i < 16; i++)
- pr_debug(" %2u", (udev->toggle[1] & (1 << i)) ? 1 : 0);
- pr_debug("\n");
-
- dev_dbg(dev, " epmaxp_in :");
- for (i = 0; i < 16; i++) {
- if (udev->ep_in[i])
- pr_debug(" %2u",
- le16_to_cpu(udev->ep_in[i]->desc.wMaxPacketSize));
- }
- pr_debug("\n");
-
- dev_dbg(dev, " epmaxp_out :");
- for (i = 0; i < 16; i++) {
- if (udev->ep_out[i])
- pr_debug(" %2u",
- le16_to_cpu(udev->ep_out[i]->desc.wMaxPacketSize));
- }
- pr_debug("\n");
-
- dev_dbg(dev, "parent %p, bus %p\n", udev->parent, udev->bus);
-
- dev_dbg(dev,
- "descriptor %p, config %p, actconfig %p, rawdescriptors %p\n",
- &udev->descriptor, udev->config,
- udev->actconfig, udev->rawdescriptors);
-
- dev_dbg(dev, "have_langid %d, string_langid %d\n",
- udev->have_langid, udev->string_langid);
-
- dev_dbg(dev, "maxchild %d\n", udev->maxchild);
-}
-
-static void usbip_dump_request_type(__u8 rt)
-{
- switch (rt & USB_RECIP_MASK) {
- case USB_RECIP_DEVICE:
- pr_debug("DEVICE");
- break;
- case USB_RECIP_INTERFACE:
- pr_debug("INTERF");
- break;
- case USB_RECIP_ENDPOINT:
- pr_debug("ENDPOI");
- break;
- case USB_RECIP_OTHER:
- pr_debug("OTHER ");
- break;
- default:
- pr_debug("------");
- break;
- }
-}
-
-static void usbip_dump_usb_ctrlrequest(struct usb_ctrlrequest *cmd)
-{
- if (!cmd) {
- pr_debug(" : null pointer\n");
- return;
- }
-
- pr_debug(" ");
- pr_debug("bRequestType(%02X) bRequest(%02X) wValue(%04X) wIndex(%04X) wLength(%04X) ",
- cmd->bRequestType, cmd->bRequest,
- cmd->wValue, cmd->wIndex, cmd->wLength);
- pr_debug("\n ");
-
- if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
- pr_debug("STANDARD ");
- switch (cmd->bRequest) {
- case USB_REQ_GET_STATUS:
- pr_debug("GET_STATUS\n");
- break;
- case USB_REQ_CLEAR_FEATURE:
- pr_debug("CLEAR_FEAT\n");
- break;
- case USB_REQ_SET_FEATURE:
- pr_debug("SET_FEAT\n");
- break;
- case USB_REQ_SET_ADDRESS:
- pr_debug("SET_ADDRRS\n");
- break;
- case USB_REQ_GET_DESCRIPTOR:
- pr_debug("GET_DESCRI\n");
- break;
- case USB_REQ_SET_DESCRIPTOR:
- pr_debug("SET_DESCRI\n");
- break;
- case USB_REQ_GET_CONFIGURATION:
- pr_debug("GET_CONFIG\n");
- break;
- case USB_REQ_SET_CONFIGURATION:
- pr_debug("SET_CONFIG\n");
- break;
- case USB_REQ_GET_INTERFACE:
- pr_debug("GET_INTERF\n");
- break;
- case USB_REQ_SET_INTERFACE:
- pr_debug("SET_INTERF\n");
- break;
- case USB_REQ_SYNCH_FRAME:
- pr_debug("SYNC_FRAME\n");
- break;
- default:
- pr_debug("REQ(%02X)\n", cmd->bRequest);
- break;
- }
- usbip_dump_request_type(cmd->bRequestType);
- } else if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) {
- pr_debug("CLASS\n");
- } else if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) {
- pr_debug("VENDOR\n");
- } else if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_RESERVED) {
- pr_debug("RESERVED\n");
- }
-}
-
-void usbip_dump_urb(struct urb *urb)
-{
- struct device *dev;
-
- if (!urb) {
- pr_debug("urb: null pointer!!\n");
- return;
- }
-
- if (!urb->dev) {
- pr_debug("urb->dev: null pointer!!\n");
- return;
- }
-
- dev = &urb->dev->dev;
-
- dev_dbg(dev, " urb :%p\n", urb);
- dev_dbg(dev, " dev :%p\n", urb->dev);
-
- usbip_dump_usb_device(urb->dev);
-
- dev_dbg(dev, " pipe :%08x ", urb->pipe);
-
- usbip_dump_pipe(urb->pipe);
-
- dev_dbg(dev, " status :%d\n", urb->status);
- dev_dbg(dev, " transfer_flags :%08X\n", urb->transfer_flags);
- dev_dbg(dev, " transfer_buffer :%p\n", urb->transfer_buffer);
- dev_dbg(dev, " transfer_buffer_length:%d\n",
- urb->transfer_buffer_length);
- dev_dbg(dev, " actual_length :%d\n", urb->actual_length);
- dev_dbg(dev, " setup_packet :%p\n", urb->setup_packet);
-
- if (urb->setup_packet && usb_pipetype(urb->pipe) == PIPE_CONTROL)
- usbip_dump_usb_ctrlrequest(
- (struct usb_ctrlrequest *)urb->setup_packet);
-
- dev_dbg(dev, " start_frame :%d\n", urb->start_frame);
- dev_dbg(dev, " number_of_packets :%d\n", urb->number_of_packets);
- dev_dbg(dev, " interval :%d\n", urb->interval);
- dev_dbg(dev, " error_count :%d\n", urb->error_count);
- dev_dbg(dev, " context :%p\n", urb->context);
- dev_dbg(dev, " complete :%p\n", urb->complete);
-}
-EXPORT_SYMBOL_GPL(usbip_dump_urb);
-
-void usbip_dump_header(struct usbip_header *pdu)
-{
- pr_debug("BASE: cmd %u seq %u devid %u dir %u ep %u\n",
- pdu->base.command,
- pdu->base.seqnum,
- pdu->base.devid,
- pdu->base.direction,
- pdu->base.ep);
-
- switch (pdu->base.command) {
- case USBIP_CMD_SUBMIT:
- pr_debug("USBIP_CMD_SUBMIT: x_flags %u x_len %u sf %u #p %d iv %d\n",
- pdu->u.cmd_submit.transfer_flags,
- pdu->u.cmd_submit.transfer_buffer_length,
- pdu->u.cmd_submit.start_frame,
- pdu->u.cmd_submit.number_of_packets,
- pdu->u.cmd_submit.interval);
- break;
- case USBIP_CMD_UNLINK:
- pr_debug("USBIP_CMD_UNLINK: seq %u\n",
- pdu->u.cmd_unlink.seqnum);
- break;
- case USBIP_RET_SUBMIT:
- pr_debug("USBIP_RET_SUBMIT: st %d al %u sf %d #p %d ec %d\n",
- pdu->u.ret_submit.status,
- pdu->u.ret_submit.actual_length,
- pdu->u.ret_submit.start_frame,
- pdu->u.ret_submit.number_of_packets,
- pdu->u.ret_submit.error_count);
- break;
- case USBIP_RET_UNLINK:
- pr_debug("USBIP_RET_UNLINK: status %d\n",
- pdu->u.ret_unlink.status);
- break;
- default:
- /* NOT REACHED */
- pr_err("unknown command\n");
- break;
- }
-}
-EXPORT_SYMBOL_GPL(usbip_dump_header);
-
-/* Receive data over TCP/IP. */
-int usbip_recv(struct socket *sock, void *buf, int size)
-{
- int result;
- struct msghdr msg;
- struct kvec iov;
- int total = 0;
-
- /* for blocks of if (usbip_dbg_flag_xmit) */
- char *bp = buf;
- int osize = size;
-
- usbip_dbg_xmit("enter\n");
-
- if (!sock || !buf || !size) {
- pr_err("invalid arg, sock %p buff %p size %d\n", sock, buf,
- size);
- return -EINVAL;
- }
-
- do {
- sock->sk->sk_allocation = GFP_NOIO;
- iov.iov_base = buf;
- iov.iov_len = size;
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
- msg.msg_control = NULL;
- msg.msg_controllen = 0;
- msg.msg_flags = MSG_NOSIGNAL;
-
- result = kernel_recvmsg(sock, &msg, &iov, 1, size, MSG_WAITALL);
- if (result <= 0) {
- pr_debug("receive sock %p buf %p size %u ret %d total %d\n",
- sock, buf, size, result, total);
- goto err;
- }
-
- size -= result;
- buf += result;
- total += result;
- } while (size > 0);
-
- if (usbip_dbg_flag_xmit) {
- if (!in_interrupt())
- pr_debug("%-10s:", current->comm);
- else
- pr_debug("interrupt :");
-
- pr_debug("receiving....\n");
- usbip_dump_buffer(bp, osize);
- pr_debug("received, osize %d ret %d size %d total %d\n",
- osize, result, size, total);
- }
-
- return total;
-
-err:
- return result;
-}
-EXPORT_SYMBOL_GPL(usbip_recv);
-
-/* there may be more cases to tweak the flags. */
-static unsigned int tweak_transfer_flags(unsigned int flags)
-{
- flags &= ~URB_NO_TRANSFER_DMA_MAP;
- return flags;
-}
-
-static void usbip_pack_cmd_submit(struct usbip_header *pdu, struct urb *urb,
- int pack)
-{
- struct usbip_header_cmd_submit *spdu = &pdu->u.cmd_submit;
-
- /*
- * Some members are not still implemented in usbip. I hope this issue
- * will be discussed when usbip is ported to other operating systems.
- */
- if (pack) {
- spdu->transfer_flags =
- tweak_transfer_flags(urb->transfer_flags);
- spdu->transfer_buffer_length = urb->transfer_buffer_length;
- spdu->start_frame = urb->start_frame;
- spdu->number_of_packets = urb->number_of_packets;
- spdu->interval = urb->interval;
- } else {
- urb->transfer_flags = spdu->transfer_flags;
- urb->transfer_buffer_length = spdu->transfer_buffer_length;
- urb->start_frame = spdu->start_frame;
- urb->number_of_packets = spdu->number_of_packets;
- urb->interval = spdu->interval;
- }
-}
-
-static void usbip_pack_ret_submit(struct usbip_header *pdu, struct urb *urb,
- int pack)
-{
- struct usbip_header_ret_submit *rpdu = &pdu->u.ret_submit;
-
- if (pack) {
- rpdu->status = urb->status;
- rpdu->actual_length = urb->actual_length;
- rpdu->start_frame = urb->start_frame;
- rpdu->number_of_packets = urb->number_of_packets;
- rpdu->error_count = urb->error_count;
- } else {
- urb->status = rpdu->status;
- urb->actual_length = rpdu->actual_length;
- urb->start_frame = rpdu->start_frame;
- urb->number_of_packets = rpdu->number_of_packets;
- urb->error_count = rpdu->error_count;
- }
-}
-
-void usbip_pack_pdu(struct usbip_header *pdu, struct urb *urb, int cmd,
- int pack)
-{
- switch (cmd) {
- case USBIP_CMD_SUBMIT:
- usbip_pack_cmd_submit(pdu, urb, pack);
- break;
- case USBIP_RET_SUBMIT:
- usbip_pack_ret_submit(pdu, urb, pack);
- break;
- default:
- /* NOT REACHED */
- pr_err("unknown command\n");
- break;
- }
-}
-EXPORT_SYMBOL_GPL(usbip_pack_pdu);
-
-static void correct_endian_basic(struct usbip_header_basic *base, int send)
-{
- if (send) {
- base->command = cpu_to_be32(base->command);
- base->seqnum = cpu_to_be32(base->seqnum);
- base->devid = cpu_to_be32(base->devid);
- base->direction = cpu_to_be32(base->direction);
- base->ep = cpu_to_be32(base->ep);
- } else {
- base->command = be32_to_cpu(base->command);
- base->seqnum = be32_to_cpu(base->seqnum);
- base->devid = be32_to_cpu(base->devid);
- base->direction = be32_to_cpu(base->direction);
- base->ep = be32_to_cpu(base->ep);
- }
-}
-
-static void correct_endian_cmd_submit(struct usbip_header_cmd_submit *pdu,
- int send)
-{
- if (send) {
- pdu->transfer_flags = cpu_to_be32(pdu->transfer_flags);
-
- cpu_to_be32s(&pdu->transfer_buffer_length);
- cpu_to_be32s(&pdu->start_frame);
- cpu_to_be32s(&pdu->number_of_packets);
- cpu_to_be32s(&pdu->interval);
- } else {
- pdu->transfer_flags = be32_to_cpu(pdu->transfer_flags);
-
- be32_to_cpus(&pdu->transfer_buffer_length);
- be32_to_cpus(&pdu->start_frame);
- be32_to_cpus(&pdu->number_of_packets);
- be32_to_cpus(&pdu->interval);
- }
-}
-
-static void correct_endian_ret_submit(struct usbip_header_ret_submit *pdu,
- int send)
-{
- if (send) {
- cpu_to_be32s(&pdu->status);
- cpu_to_be32s(&pdu->actual_length);
- cpu_to_be32s(&pdu->start_frame);
- cpu_to_be32s(&pdu->number_of_packets);
- cpu_to_be32s(&pdu->error_count);
- } else {
- be32_to_cpus(&pdu->status);
- be32_to_cpus(&pdu->actual_length);
- be32_to_cpus(&pdu->start_frame);
- be32_to_cpus(&pdu->number_of_packets);
- be32_to_cpus(&pdu->error_count);
- }
-}
-
-static void correct_endian_cmd_unlink(struct usbip_header_cmd_unlink *pdu,
- int send)
-{
- if (send)
- pdu->seqnum = cpu_to_be32(pdu->seqnum);
- else
- pdu->seqnum = be32_to_cpu(pdu->seqnum);
-}
-
-static void correct_endian_ret_unlink(struct usbip_header_ret_unlink *pdu,
- int send)
-{
- if (send)
- cpu_to_be32s(&pdu->status);
- else
- be32_to_cpus(&pdu->status);
-}
-
-void usbip_header_correct_endian(struct usbip_header *pdu, int send)
-{
- __u32 cmd = 0;
-
- if (send)
- cmd = pdu->base.command;
-
- correct_endian_basic(&pdu->base, send);
-
- if (!send)
- cmd = pdu->base.command;
-
- switch (cmd) {
- case USBIP_CMD_SUBMIT:
- correct_endian_cmd_submit(&pdu->u.cmd_submit, send);
- break;
- case USBIP_RET_SUBMIT:
- correct_endian_ret_submit(&pdu->u.ret_submit, send);
- break;
- case USBIP_CMD_UNLINK:
- correct_endian_cmd_unlink(&pdu->u.cmd_unlink, send);
- break;
- case USBIP_RET_UNLINK:
- correct_endian_ret_unlink(&pdu->u.ret_unlink, send);
- break;
- default:
- /* NOT REACHED */
- pr_err("unknown command\n");
- break;
- }
-}
-EXPORT_SYMBOL_GPL(usbip_header_correct_endian);
-
-static void usbip_iso_packet_correct_endian(
- struct usbip_iso_packet_descriptor *iso, int send)
-{
- /* does not need all members. but copy all simply. */
- if (send) {
- iso->offset = cpu_to_be32(iso->offset);
- iso->length = cpu_to_be32(iso->length);
- iso->status = cpu_to_be32(iso->status);
- iso->actual_length = cpu_to_be32(iso->actual_length);
- } else {
- iso->offset = be32_to_cpu(iso->offset);
- iso->length = be32_to_cpu(iso->length);
- iso->status = be32_to_cpu(iso->status);
- iso->actual_length = be32_to_cpu(iso->actual_length);
- }
-}
-
-static void usbip_pack_iso(struct usbip_iso_packet_descriptor *iso,
- struct usb_iso_packet_descriptor *uiso, int pack)
-{
- if (pack) {
- iso->offset = uiso->offset;
- iso->length = uiso->length;
- iso->status = uiso->status;
- iso->actual_length = uiso->actual_length;
- } else {
- uiso->offset = iso->offset;
- uiso->length = iso->length;
- uiso->status = iso->status;
- uiso->actual_length = iso->actual_length;
- }
-}
-
-/* must free buffer */
-struct usbip_iso_packet_descriptor*
-usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen)
-{
- struct usbip_iso_packet_descriptor *iso;
- int np = urb->number_of_packets;
- ssize_t size = np * sizeof(*iso);
- int i;
-
- iso = kzalloc(size, GFP_KERNEL);
- if (!iso)
- return NULL;
-
- for (i = 0; i < np; i++) {
- usbip_pack_iso(&iso[i], &urb->iso_frame_desc[i], 1);
- usbip_iso_packet_correct_endian(&iso[i], 1);
- }
-
- *bufflen = size;
-
- return iso;
-}
-EXPORT_SYMBOL_GPL(usbip_alloc_iso_desc_pdu);
-
-/* some members of urb must be substituted before. */
-int usbip_recv_iso(struct usbip_device *ud, struct urb *urb)
-{
- void *buff;
- struct usbip_iso_packet_descriptor *iso;
- int np = urb->number_of_packets;
- int size = np * sizeof(*iso);
- int i;
- int ret;
- int total_length = 0;
-
- if (!usb_pipeisoc(urb->pipe))
- return 0;
-
- /* my Bluetooth dongle gets ISO URBs which are np = 0 */
- if (np == 0)
- return 0;
-
- buff = kzalloc(size, GFP_KERNEL);
- if (!buff)
- return -ENOMEM;
-
- ret = usbip_recv(ud->tcp_socket, buff, size);
- if (ret != size) {
- dev_err(&urb->dev->dev, "recv iso_frame_descriptor, %d\n",
- ret);
- kfree(buff);
-
- if (ud->side == USBIP_STUB)
- usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
- else
- usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
-
- return -EPIPE;
- }
-
- iso = (struct usbip_iso_packet_descriptor *) buff;
- for (i = 0; i < np; i++) {
- usbip_iso_packet_correct_endian(&iso[i], 0);
- usbip_pack_iso(&iso[i], &urb->iso_frame_desc[i], 0);
- total_length += urb->iso_frame_desc[i].actual_length;
- }
-
- kfree(buff);
-
- if (total_length != urb->actual_length) {
- dev_err(&urb->dev->dev,
- "total length of iso packets %d not equal to actual length of buffer %d\n",
- total_length, urb->actual_length);
-
- if (ud->side == USBIP_STUB)
- usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
- else
- usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
-
- return -EPIPE;
- }
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(usbip_recv_iso);
-
-/*
- * This functions restores the padding which was removed for optimizing
- * the bandwidth during transfer over tcp/ip
- *
- * buffer and iso packets need to be stored and be in propeper endian in urb
- * before calling this function
- */
-void usbip_pad_iso(struct usbip_device *ud, struct urb *urb)
-{
- int np = urb->number_of_packets;
- int i;
- int actualoffset = urb->actual_length;
-
- if (!usb_pipeisoc(urb->pipe))
- return;
-
- /* if no packets or length of data is 0, then nothing to unpack */
- if (np == 0 || urb->actual_length == 0)
- return;
-
- /*
- * if actual_length is transfer_buffer_length then no padding is
- * present.
- */
- if (urb->actual_length == urb->transfer_buffer_length)
- return;
-
- /*
- * loop over all packets from last to first (to prevent overwritting
- * memory when padding) and move them into the proper place
- */
- for (i = np-1; i > 0; i--) {
- actualoffset -= urb->iso_frame_desc[i].actual_length;
- memmove(urb->transfer_buffer + urb->iso_frame_desc[i].offset,
- urb->transfer_buffer + actualoffset,
- urb->iso_frame_desc[i].actual_length);
- }
-}
-EXPORT_SYMBOL_GPL(usbip_pad_iso);
-
-/* some members of urb must be substituted before. */
-int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb)
-{
- int ret;
- int size;
-
- if (ud->side == USBIP_STUB) {
- /* the direction of urb must be OUT. */
- if (usb_pipein(urb->pipe))
- return 0;
-
- size = urb->transfer_buffer_length;
- } else {
- /* the direction of urb must be IN. */
- if (usb_pipeout(urb->pipe))
- return 0;
-
- size = urb->actual_length;
- }
-
- /* no need to recv xbuff */
- if (!(size > 0))
- return 0;
-
- ret = usbip_recv(ud->tcp_socket, urb->transfer_buffer, size);
- if (ret != size) {
- dev_err(&urb->dev->dev, "recv xbuf, %d\n", ret);
- if (ud->side == USBIP_STUB) {
- usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
- } else {
- usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
- return -EPIPE;
- }
- }
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(usbip_recv_xbuff);
-
-static int __init usbip_core_init(void)
-{
- pr_info(DRIVER_DESC " v" USBIP_VERSION "\n");
- return 0;
-}
-
-static void __exit usbip_core_exit(void)
-{
- return;
-}
-
-module_init(usbip_core_init);
-module_exit(usbip_core_exit);
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
-MODULE_VERSION(USBIP_VERSION);
+++ /dev/null
-/*
- * Copyright (C) 2003-2008 Takahiro Hirofuchi
- *
- * This 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 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 __USBIP_COMMON_H
-#define __USBIP_COMMON_H
-
-#include <linux/compiler.h>
-#include <linux/device.h>
-#include <linux/interrupt.h>
-#include <linux/net.h>
-#include <linux/printk.h>
-#include <linux/spinlock.h>
-#include <linux/types.h>
-#include <linux/usb.h>
-#include <linux/wait.h>
-#include "uapi/usbip.h"
-
-#define USBIP_VERSION "1.0.0"
-
-#undef pr_fmt
-
-#ifdef DEBUG
-#define pr_fmt(fmt) KBUILD_MODNAME ": %s:%d: " fmt, __func__, __LINE__
-#else
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#endif
-
-enum {
- usbip_debug_xmit = (1 << 0),
- usbip_debug_sysfs = (1 << 1),
- usbip_debug_urb = (1 << 2),
- usbip_debug_eh = (1 << 3),
-
- usbip_debug_stub_cmp = (1 << 8),
- usbip_debug_stub_dev = (1 << 9),
- usbip_debug_stub_rx = (1 << 10),
- usbip_debug_stub_tx = (1 << 11),
-
- usbip_debug_vhci_rh = (1 << 8),
- usbip_debug_vhci_hc = (1 << 9),
- usbip_debug_vhci_rx = (1 << 10),
- usbip_debug_vhci_tx = (1 << 11),
- usbip_debug_vhci_sysfs = (1 << 12)
-};
-
-#define usbip_dbg_flag_xmit (usbip_debug_flag & usbip_debug_xmit)
-#define usbip_dbg_flag_vhci_rh (usbip_debug_flag & usbip_debug_vhci_rh)
-#define usbip_dbg_flag_vhci_hc (usbip_debug_flag & usbip_debug_vhci_hc)
-#define usbip_dbg_flag_vhci_rx (usbip_debug_flag & usbip_debug_vhci_rx)
-#define usbip_dbg_flag_vhci_tx (usbip_debug_flag & usbip_debug_vhci_tx)
-#define usbip_dbg_flag_stub_rx (usbip_debug_flag & usbip_debug_stub_rx)
-#define usbip_dbg_flag_stub_tx (usbip_debug_flag & usbip_debug_stub_tx)
-#define usbip_dbg_flag_vhci_sysfs (usbip_debug_flag & usbip_debug_vhci_sysfs)
-
-extern unsigned long usbip_debug_flag;
-extern struct device_attribute dev_attr_usbip_debug;
-
-#define usbip_dbg_with_flag(flag, fmt, args...) \
- do { \
- if (flag & usbip_debug_flag) \
- pr_debug(fmt, ##args); \
- } while (0)
-
-#define usbip_dbg_sysfs(fmt, args...) \
- usbip_dbg_with_flag(usbip_debug_sysfs, fmt , ##args)
-#define usbip_dbg_xmit(fmt, args...) \
- usbip_dbg_with_flag(usbip_debug_xmit, fmt , ##args)
-#define usbip_dbg_urb(fmt, args...) \
- usbip_dbg_with_flag(usbip_debug_urb, fmt , ##args)
-#define usbip_dbg_eh(fmt, args...) \
- usbip_dbg_with_flag(usbip_debug_eh, fmt , ##args)
-
-#define usbip_dbg_vhci_rh(fmt, args...) \
- usbip_dbg_with_flag(usbip_debug_vhci_rh, fmt , ##args)
-#define usbip_dbg_vhci_hc(fmt, args...) \
- usbip_dbg_with_flag(usbip_debug_vhci_hc, fmt , ##args)
-#define usbip_dbg_vhci_rx(fmt, args...) \
- usbip_dbg_with_flag(usbip_debug_vhci_rx, fmt , ##args)
-#define usbip_dbg_vhci_tx(fmt, args...) \
- usbip_dbg_with_flag(usbip_debug_vhci_tx, fmt , ##args)
-#define usbip_dbg_vhci_sysfs(fmt, args...) \
- usbip_dbg_with_flag(usbip_debug_vhci_sysfs, fmt , ##args)
-
-#define usbip_dbg_stub_cmp(fmt, args...) \
- usbip_dbg_with_flag(usbip_debug_stub_cmp, fmt , ##args)
-#define usbip_dbg_stub_rx(fmt, args...) \
- usbip_dbg_with_flag(usbip_debug_stub_rx, fmt , ##args)
-#define usbip_dbg_stub_tx(fmt, args...) \
- usbip_dbg_with_flag(usbip_debug_stub_tx, fmt , ##args)
-
-/*
- * USB/IP request headers
- *
- * Each request is transferred across the network to its counterpart, which
- * facilitates the normal USB communication. The values contained in the headers
- * are basically the same as in a URB. Currently, four request types are
- * defined:
- *
- * - USBIP_CMD_SUBMIT: a USB request block, corresponds to usb_submit_urb()
- * (client to server)
- *
- * - USBIP_RET_SUBMIT: the result of USBIP_CMD_SUBMIT
- * (server to client)
- *
- * - USBIP_CMD_UNLINK: an unlink request of a pending USBIP_CMD_SUBMIT,
- * corresponds to usb_unlink_urb()
- * (client to server)
- *
- * - USBIP_RET_UNLINK: the result of USBIP_CMD_UNLINK
- * (server to client)
- *
- */
-#define USBIP_CMD_SUBMIT 0x0001
-#define USBIP_CMD_UNLINK 0x0002
-#define USBIP_RET_SUBMIT 0x0003
-#define USBIP_RET_UNLINK 0x0004
-
-#define USBIP_DIR_OUT 0x00
-#define USBIP_DIR_IN 0x01
-
-/**
- * struct usbip_header_basic - data pertinent to every request
- * @command: the usbip request type
- * @seqnum: sequential number that identifies requests; incremented per
- * connection
- * @devid: specifies a remote USB device uniquely instead of busnum and devnum;
- * in the stub driver, this value is ((busnum << 16) | devnum)
- * @direction: direction of the transfer
- * @ep: endpoint number
- */
-struct usbip_header_basic {
- __u32 command;
- __u32 seqnum;
- __u32 devid;
- __u32 direction;
- __u32 ep;
-} __packed;
-
-/**
- * struct usbip_header_cmd_submit - USBIP_CMD_SUBMIT packet header
- * @transfer_flags: URB flags
- * @transfer_buffer_length: the data size for (in) or (out) transfer
- * @start_frame: initial frame for isochronous or interrupt transfers
- * @number_of_packets: number of isochronous packets
- * @interval: maximum time for the request on the server-side host controller
- * @setup: setup data for a control request
- */
-struct usbip_header_cmd_submit {
- __u32 transfer_flags;
- __s32 transfer_buffer_length;
-
- /* it is difficult for usbip to sync frames (reserved only?) */
- __s32 start_frame;
- __s32 number_of_packets;
- __s32 interval;
-
- unsigned char setup[8];
-} __packed;
-
-/**
- * struct usbip_header_ret_submit - USBIP_RET_SUBMIT packet header
- * @status: return status of a non-iso request
- * @actual_length: number of bytes transferred
- * @start_frame: initial frame for isochronous or interrupt transfers
- * @number_of_packets: number of isochronous packets
- * @error_count: number of errors for isochronous transfers
- */
-struct usbip_header_ret_submit {
- __s32 status;
- __s32 actual_length;
- __s32 start_frame;
- __s32 number_of_packets;
- __s32 error_count;
-} __packed;
-
-/**
- * struct usbip_header_cmd_unlink - USBIP_CMD_UNLINK packet header
- * @seqnum: the URB seqnum to unlink
- */
-struct usbip_header_cmd_unlink {
- __u32 seqnum;
-} __packed;
-
-/**
- * struct usbip_header_ret_unlink - USBIP_RET_UNLINK packet header
- * @status: return status of the request
- */
-struct usbip_header_ret_unlink {
- __s32 status;
-} __packed;
-
-/**
- * struct usbip_header - common header for all usbip packets
- * @base: the basic header
- * @u: packet type dependent header
- */
-struct usbip_header {
- struct usbip_header_basic base;
-
- union {
- struct usbip_header_cmd_submit cmd_submit;
- struct usbip_header_ret_submit ret_submit;
- struct usbip_header_cmd_unlink cmd_unlink;
- struct usbip_header_ret_unlink ret_unlink;
- } u;
-} __packed;
-
-/*
- * This is the same as usb_iso_packet_descriptor but packed for pdu.
- */
-struct usbip_iso_packet_descriptor {
- __u32 offset;
- __u32 length; /* expected length */
- __u32 actual_length;
- __u32 status;
-} __packed;
-
-enum usbip_side {
- USBIP_VHCI,
- USBIP_STUB,
-};
-
-/* event handler */
-#define USBIP_EH_SHUTDOWN (1 << 0)
-#define USBIP_EH_BYE (1 << 1)
-#define USBIP_EH_RESET (1 << 2)
-#define USBIP_EH_UNUSABLE (1 << 3)
-
-#define SDEV_EVENT_REMOVED (USBIP_EH_SHUTDOWN | USBIP_EH_RESET | USBIP_EH_BYE)
-#define SDEV_EVENT_DOWN (USBIP_EH_SHUTDOWN | USBIP_EH_RESET)
-#define SDEV_EVENT_ERROR_TCP (USBIP_EH_SHUTDOWN | USBIP_EH_RESET)
-#define SDEV_EVENT_ERROR_SUBMIT (USBIP_EH_SHUTDOWN | USBIP_EH_RESET)
-#define SDEV_EVENT_ERROR_MALLOC (USBIP_EH_SHUTDOWN | USBIP_EH_UNUSABLE)
-
-#define VDEV_EVENT_REMOVED (USBIP_EH_SHUTDOWN | USBIP_EH_BYE)
-#define VDEV_EVENT_DOWN (USBIP_EH_SHUTDOWN | USBIP_EH_RESET)
-#define VDEV_EVENT_ERROR_TCP (USBIP_EH_SHUTDOWN | USBIP_EH_RESET)
-#define VDEV_EVENT_ERROR_MALLOC (USBIP_EH_SHUTDOWN | USBIP_EH_UNUSABLE)
-
-/* a common structure for stub_device and vhci_device */
-struct usbip_device {
- enum usbip_side side;
- enum usbip_device_status status;
-
- /* lock for status */
- spinlock_t lock;
-
- struct socket *tcp_socket;
-
- struct task_struct *tcp_rx;
- struct task_struct *tcp_tx;
-
- unsigned long event;
- struct task_struct *eh;
- wait_queue_head_t eh_waitq;
-
- struct eh_ops {
- void (*shutdown)(struct usbip_device *);
- void (*reset)(struct usbip_device *);
- void (*unusable)(struct usbip_device *);
- } eh_ops;
-};
-
-#define kthread_get_run(threadfn, data, namefmt, ...) \
-({ \
- struct task_struct *__k \
- = kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \
- if (!IS_ERR(__k)) { \
- get_task_struct(__k); \
- wake_up_process(__k); \
- } \
- __k; \
-})
-
-#define kthread_stop_put(k) \
- do { \
- kthread_stop(k); \
- put_task_struct(k); \
- } while (0)
-
-/* usbip_common.c */
-void usbip_dump_urb(struct urb *purb);
-void usbip_dump_header(struct usbip_header *pdu);
-
-int usbip_recv(struct socket *sock, void *buf, int size);
-
-void usbip_pack_pdu(struct usbip_header *pdu, struct urb *urb, int cmd,
- int pack);
-void usbip_header_correct_endian(struct usbip_header *pdu, int send);
-
-struct usbip_iso_packet_descriptor*
-usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen);
-
-/* some members of urb must be substituted before. */
-int usbip_recv_iso(struct usbip_device *ud, struct urb *urb);
-void usbip_pad_iso(struct usbip_device *ud, struct urb *urb);
-int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb);
-
-/* usbip_event.c */
-int usbip_start_eh(struct usbip_device *ud);
-void usbip_stop_eh(struct usbip_device *ud);
-void usbip_event_add(struct usbip_device *ud, unsigned long event);
-int usbip_event_happened(struct usbip_device *ud);
-
-static inline int interface_to_busnum(struct usb_interface *interface)
-{
- struct usb_device *udev = interface_to_usbdev(interface);
-
- return udev->bus->busnum;
-}
-
-static inline int interface_to_devnum(struct usb_interface *interface)
-{
- struct usb_device *udev = interface_to_usbdev(interface);
-
- return udev->devnum;
-}
-
-#endif /* __USBIP_COMMON_H */
+++ /dev/null
-/*
- * Copyright (C) 2003-2008 Takahiro Hirofuchi
- *
- * This 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 is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
- */
-
-#include <linux/kthread.h>
-#include <linux/export.h>
-
-#include "usbip_common.h"
-
-static int event_handler(struct usbip_device *ud)
-{
- usbip_dbg_eh("enter\n");
-
- /*
- * Events are handled by only this thread.
- */
- while (usbip_event_happened(ud)) {
- usbip_dbg_eh("pending event %lx\n", ud->event);
-
- /*
- * NOTE: shutdown must come first.
- * Shutdown the device.
- */
- if (ud->event & USBIP_EH_SHUTDOWN) {
- ud->eh_ops.shutdown(ud);
- ud->event &= ~USBIP_EH_SHUTDOWN;
- }
-
- /* Reset the device. */
- if (ud->event & USBIP_EH_RESET) {
- ud->eh_ops.reset(ud);
- ud->event &= ~USBIP_EH_RESET;
- }
-
- /* Mark the device as unusable. */
- if (ud->event & USBIP_EH_UNUSABLE) {
- ud->eh_ops.unusable(ud);
- ud->event &= ~USBIP_EH_UNUSABLE;
- }
-
- /* Stop the error handler. */
- if (ud->event & USBIP_EH_BYE)
- return -1;
- }
-
- return 0;
-}
-
-static int event_handler_loop(void *data)
-{
- struct usbip_device *ud = data;
-
- while (!kthread_should_stop()) {
- wait_event_interruptible(ud->eh_waitq,
- usbip_event_happened(ud) ||
- kthread_should_stop());
- usbip_dbg_eh("wakeup\n");
-
- if (event_handler(ud) < 0)
- break;
- }
-
- return 0;
-}
-
-int usbip_start_eh(struct usbip_device *ud)
-{
- init_waitqueue_head(&ud->eh_waitq);
- ud->event = 0;
-
- ud->eh = kthread_run(event_handler_loop, ud, "usbip_eh");
- if (IS_ERR(ud->eh)) {
- pr_warn("Unable to start control thread\n");
- return PTR_ERR(ud->eh);
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(usbip_start_eh);
-
-void usbip_stop_eh(struct usbip_device *ud)
-{
- if (ud->eh == current)
- return; /* do not wait for myself */
-
- kthread_stop(ud->eh);
- usbip_dbg_eh("usbip_eh has finished\n");
-}
-EXPORT_SYMBOL_GPL(usbip_stop_eh);
-
-void usbip_event_add(struct usbip_device *ud, unsigned long event)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&ud->lock, flags);
- ud->event |= event;
- wake_up(&ud->eh_waitq);
- spin_unlock_irqrestore(&ud->lock, flags);
-}
-EXPORT_SYMBOL_GPL(usbip_event_add);
-
-int usbip_event_happened(struct usbip_device *ud)
-{
- int happened = 0;
-
- spin_lock(&ud->lock);
- if (ud->event != 0)
- happened = 1;
- spin_unlock(&ud->lock);
-
- return happened;
-}
-EXPORT_SYMBOL_GPL(usbip_event_happened);
+++ /dev/null
-PRELIMINARY DRAFT, MAY CONTAIN MISTAKES!
-28 Jun 2011
-
-The USB/IP protocol follows a server/client architecture. The server exports the
-USB devices and the clients imports them. The device driver for the exported
-USB device runs on the client machine.
-
-The client may ask for the list of the exported USB devices. To get the list the
-client opens a TCP/IP connection towards the server, and sends an OP_REQ_DEVLIST
-packet on top of the TCP/IP connection (so the actual OP_REQ_DEVLIST may be sent
-in one or more pieces at the low level transport layer). The server sends back
-the OP_REP_DEVLIST packet which lists the exported USB devices. Finally the
-TCP/IP connection is closed.
-
- virtual host controller usb host
- "client" "server"
- (imports USB devices) (exports USB devices)
- | |
- | OP_REQ_DEVLIST |
- | ----------------------------------------------> |
- | |
- | OP_REP_DEVLIST |
- | <---------------------------------------------- |
- | |
-
-Once the client knows the list of exported USB devices it may decide to use one
-of them. First the client opens a TCP/IP connection towards the server and
-sends an OP_REQ_IMPORT packet. The server replies with OP_REP_IMPORT. If the
-import was successful the TCP/IP connection remains open and will be used
-to transfer the URB traffic between the client and the server. The client may
-send two types of packets: the USBIP_CMD_SUBMIT to submit an URB, and
-USBIP_CMD_UNLINK to unlink a previously submitted URB. The answers of the
-server may be USBIP_RET_SUBMIT and USBIP_RET_UNLINK respectively.
-
- virtual host controller usb host
- "client" "server"
- (imports USB devices) (exports USB devices)
- | |
- | OP_REQ_IMPORT |
- | ----------------------------------------------> |
- | |
- | OP_REP_IMPORT |
- | <---------------------------------------------- |
- | |
- | |
- | USBIP_CMD_SUBMIT(seqnum = n) |
- | ----------------------------------------------> |
- | |
- | USBIP_RET_SUBMIT(seqnum = n) |
- | <---------------------------------------------- |
- | . |
- | : |
- | |
- | USBIP_CMD_SUBMIT(seqnum = m) |
- | ----------------------------------------------> |
- | |
- | USBIP_CMD_SUBMIT(seqnum = m+1) |
- | ----------------------------------------------> |
- | |
- | USBIP_CMD_SUBMIT(seqnum = m+2) |
- | ----------------------------------------------> |
- | |
- | USBIP_RET_SUBMIT(seqnum = m) |
- | <---------------------------------------------- |
- | |
- | USBIP_CMD_SUBMIT(seqnum = m+3) |
- | ----------------------------------------------> |
- | |
- | USBIP_RET_SUBMIT(seqnum = m+1) |
- | <---------------------------------------------- |
- | |
- | USBIP_CMD_SUBMIT(seqnum = m+4) |
- | ----------------------------------------------> |
- | |
- | USBIP_RET_SUBMIT(seqnum = m+2) |
- | <---------------------------------------------- |
- | . |
- | : |
- | |
- | USBIP_CMD_UNLINK |
- | ----------------------------------------------> |
- | |
- | USBIP_RET_UNLINK |
- | <---------------------------------------------- |
- | |
-
-The fields are in network (big endian) byte order meaning that the most significant
-byte (MSB) is stored at the lowest address.
-
-
-OP_REQ_DEVLIST: Retrieve the list of exported USB devices.
-
- Offset | Length | Value | Description
------------+--------+------------+---------------------------------------------------
- 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0
------------+--------+------------+---------------------------------------------------
- 2 | 2 | 0x8005 | Command code: Retrieve the list of exported USB
- | | | devices.
------------+--------+------------+---------------------------------------------------
- 4 | 4 | 0x00000000 | Status: unused, shall be set to 0
-
-OP_REP_DEVLIST: Reply with the list of exported USB devices.
-
- Offset | Length | Value | Description
------------+--------+------------+---------------------------------------------------
- 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0.
------------+--------+------------+---------------------------------------------------
- 2 | 2 | 0x0005 | Reply code: The list of exported USB devices.
------------+--------+------------+---------------------------------------------------
- 4 | 4 | 0x00000000 | Status: 0 for OK
------------+--------+------------+---------------------------------------------------
- 8 | 4 | n | Number of exported devices: 0 means no exported
- | | | devices.
------------+--------+------------+---------------------------------------------------
- 0x0C | | | From now on the exported n devices are described,
- | | | if any. If no devices are exported the message
- | | | ends with the previous "number of exported
- | | | devices" field.
------------+--------+------------+---------------------------------------------------
- | 256 | | path: Path of the device on the host exporting the
- | | | USB device, string closed with zero byte, e.g.
- | | | "/sys/devices/pci0000:00/0000:00:1d.1/usb3/3-2"
- | | | The unused bytes shall be filled with zero
- | | | bytes.
------------+--------+------------+---------------------------------------------------
- 0x10C | 32 | | busid: Bus ID of the exported device, string
- | | | closed with zero byte, e.g. "3-2". The unused
- | | | bytes shall be filled with zero bytes.
------------+--------+------------+---------------------------------------------------
- 0x12C | 4 | | busnum
------------+--------+------------+---------------------------------------------------
- 0x130 | 4 | | devnum
------------+--------+------------+---------------------------------------------------
- 0x134 | 4 | | speed
------------+--------+------------+---------------------------------------------------
- 0x138 | 2 | | idVendor
------------+--------+------------+---------------------------------------------------
- 0x13A | 2 | | idProduct
------------+--------+------------+---------------------------------------------------
- 0x13C | 2 | | bcdDevice
------------+--------+------------+---------------------------------------------------
- 0x13E | 1 | | bDeviceClass
------------+--------+------------+---------------------------------------------------
- 0x13F | 1 | | bDeviceSubClass
------------+--------+------------+---------------------------------------------------
- 0x140 | 1 | | bDeviceProtocol
------------+--------+------------+---------------------------------------------------
- 0x141 | 1 | | bConfigurationValue
------------+--------+------------+---------------------------------------------------
- 0x142 | 1 | | bNumConfigurations
------------+--------+------------+---------------------------------------------------
- 0x143 | 1 | | bNumInterfaces
------------+--------+------------+---------------------------------------------------
- 0x144 | | m_0 | From now on each interface is described, all
- | | | together bNumInterfaces times, with the
- | | | the following 4 fields:
------------+--------+------------+---------------------------------------------------
- | 1 | | bInterfaceClass
------------+--------+------------+---------------------------------------------------
- 0x145 | 1 | | bInterfaceSubClass
------------+--------+------------+---------------------------------------------------
- 0x146 | 1 | | bInterfaceProtocol
------------+--------+------------+---------------------------------------------------
- 0x147 | 1 | | padding byte for alignment, shall be set to zero
------------+--------+------------+---------------------------------------------------
- 0xC + | | | The second exported USB device starts at i=1
- i*0x138 + | | | with the busid field.
- m_(i-1)*4 | | |
-
-OP_REQ_IMPORT: Request to import (attach) a remote USB device.
-
- Offset | Length | Value | Description
------------+--------+------------+---------------------------------------------------
- 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0
------------+--------+------------+---------------------------------------------------
- 2 | 2 | 0x8003 | Command code: import a remote USB device.
------------+--------+------------+---------------------------------------------------
- 4 | 4 | 0x00000000 | Status: unused, shall be set to 0
------------+--------+------------+---------------------------------------------------
- 8 | 32 | | busid: the busid of the exported device on the
- | | | remote host. The possible values are taken
- | | | from the message field OP_REP_DEVLIST.busid.
- | | | A string closed with zero, the unused bytes
- | | | shall be filled with zeros.
------------+--------+------------+---------------------------------------------------
-
-OP_REP_IMPORT: Reply to import (attach) a remote USB device.
-
- Offset | Length | Value | Description
------------+--------+------------+---------------------------------------------------
- 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0
------------+--------+------------+---------------------------------------------------
- 2 | 2 | 0x0003 | Reply code: Reply to import.
------------+--------+------------+---------------------------------------------------
- 4 | 4 | 0x00000000 | Status: 0 for OK
- | | | 1 for error
------------+--------+------------+---------------------------------------------------
- 8 | | | From now on comes the details of the imported
- | | | device, if the previous status field was OK (0),
- | | | otherwise the reply ends with the status field.
------------+--------+------------+---------------------------------------------------
- | 256 | | path: Path of the device on the host exporting the
- | | | USB device, string closed with zero byte, e.g.
- | | | "/sys/devices/pci0000:00/0000:00:1d.1/usb3/3-2"
- | | | The unused bytes shall be filled with zero
- | | | bytes.
------------+--------+------------+---------------------------------------------------
- 0x108 | 32 | | busid: Bus ID of the exported device, string
- | | | closed with zero byte, e.g. "3-2". The unused
- | | | bytes shall be filled with zero bytes.
------------+--------+------------+---------------------------------------------------
- 0x128 | 4 | | busnum
------------+--------+------------+---------------------------------------------------
- 0x12C | 4 | | devnum
------------+--------+------------+---------------------------------------------------
- 0x130 | 4 | | speed
------------+--------+------------+---------------------------------------------------
- 0x134 | 2 | | idVendor
------------+--------+------------+---------------------------------------------------
- 0x136 | 2 | | idProduct
------------+--------+------------+---------------------------------------------------
- 0x138 | 2 | | bcdDevice
------------+--------+------------+---------------------------------------------------
- 0x139 | 1 | | bDeviceClass
------------+--------+------------+---------------------------------------------------
- 0x13A | 1 | | bDeviceSubClass
------------+--------+------------+---------------------------------------------------
- 0x13B | 1 | | bDeviceProtocol
------------+--------+------------+---------------------------------------------------
- 0x13C | 1 | | bConfigurationValue
------------+--------+------------+---------------------------------------------------
- 0x13D | 1 | | bNumConfigurations
------------+--------+------------+---------------------------------------------------
- 0x13E | 1 | | bNumInterfaces
-
-USBIP_CMD_SUBMIT: Submit an URB
-
- Offset | Length | Value | Description
------------+--------+------------+---------------------------------------------------
- 0 | 4 | 0x00000001 | command: Submit an URB
------------+--------+------------+---------------------------------------------------
- 4 | 4 | | seqnum: the sequence number of the URB to submit
------------+--------+------------+---------------------------------------------------
- 8 | 4 | | devid
------------+--------+------------+---------------------------------------------------
- 0xC | 4 | | direction: 0: USBIP_DIR_OUT
- | | | 1: USBIP_DIR_IN
------------+--------+------------+---------------------------------------------------
- 0x10 | 4 | | ep: endpoint number, possible values are: 0...15
------------+--------+------------+---------------------------------------------------
- 0x14 | 4 | | transfer_flags: possible values depend on the
- | | | URB transfer type, see below
------------+--------+------------+---------------------------------------------------
- 0x18 | 4 | | transfer_buffer_length
------------+--------+------------+---------------------------------------------------
- 0x1C | 4 | | start_frame: specify the selected frame to
- | | | transmit an ISO frame, ignored if URB_ISO_ASAP
- | | | is specified at transfer_flags
------------+--------+------------+---------------------------------------------------
- 0x20 | 4 | | number_of_packets: number of ISO packets
------------+--------+------------+---------------------------------------------------
- 0x24 | 4 | | interval: maximum time for the request on the
- | | | server-side host controller
------------+--------+------------+---------------------------------------------------
- 0x28 | 8 | | setup: data bytes for USB setup, filled with
- | | | zeros if not used
------------+--------+------------+---------------------------------------------------
- 0x30 | | | URB data. For ISO transfers the padding between
- | | | each ISO packets is not transmitted.
-
-
- Allowed transfer_flags | value | control | interrupt | bulk | isochronous
- -------------------------+------------+---------+-----------+----------+-------------
- URB_SHORT_NOT_OK | 0x00000001 | only in | only in | only in | no
- URB_ISO_ASAP | 0x00000002 | no | no | no | yes
- URB_NO_TRANSFER_DMA_MAP | 0x00000004 | yes | yes | yes | yes
- URB_NO_FSBR | 0x00000020 | yes | no | no | no
- URB_ZERO_PACKET | 0x00000040 | no | no | only out | no
- URB_NO_INTERRUPT | 0x00000080 | yes | yes | yes | yes
- URB_FREE_BUFFER | 0x00000100 | yes | yes | yes | yes
- URB_DIR_MASK | 0x00000200 | yes | yes | yes | yes
-
-
-USBIP_RET_SUBMIT: Reply for submitting an URB
-
- Offset | Length | Value | Description
------------+--------+------------+---------------------------------------------------
- 0 | 4 | 0x00000003 | command
------------+--------+------------+---------------------------------------------------
- 4 | 4 | | seqnum: URB sequence number
------------+--------+------------+---------------------------------------------------
- 8 | 4 | | devid
------------+--------+------------+---------------------------------------------------
- 0xC | 4 | | direction: 0: USBIP_DIR_OUT
- | | | 1: USBIP_DIR_IN
------------+--------+------------+---------------------------------------------------
- 0x10 | 4 | | ep: endpoint number
------------+--------+------------+---------------------------------------------------
- 0x14 | 4 | | status: zero for successful URB transaction,
- | | | otherwise some kind of error happened.
------------+--------+------------+---------------------------------------------------
- 0x18 | 4 | n | actual_length: number of URB data bytes
------------+--------+------------+---------------------------------------------------
- 0x1C | 4 | | start_frame: for an ISO frame the actually
- | | | selected frame for transmit.
------------+--------+------------+---------------------------------------------------
- 0x20 | 4 | | number_of_packets
------------+--------+------------+---------------------------------------------------
- 0x24 | 4 | | error_count
------------+--------+------------+---------------------------------------------------
- 0x28 | 8 | | setup: data bytes for USB setup, filled with
- | | | zeros if not used
------------+--------+------------+---------------------------------------------------
- 0x30 | n | | URB data bytes. For ISO transfers the padding
- | | | between each ISO packets is not transmitted.
-
-USBIP_CMD_UNLINK: Unlink an URB
-
- Offset | Length | Value | Description
------------+--------+------------+---------------------------------------------------
- 0 | 4 | 0x00000002 | command: URB unlink command
------------+--------+------------+---------------------------------------------------
- 4 | 4 | | seqnum: URB sequence number to unlink: FIXME: is this so?
------------+--------+------------+---------------------------------------------------
- 8 | 4 | | devid
------------+--------+------------+---------------------------------------------------
- 0xC | 4 | | direction: 0: USBIP_DIR_OUT
- | | | 1: USBIP_DIR_IN
------------+--------+------------+---------------------------------------------------
- 0x10 | 4 | | ep: endpoint number: zero
------------+--------+------------+---------------------------------------------------
- 0x14 | 4 | | seqnum: the URB sequence number given previously
- | | | at USBIP_CMD_SUBMIT.seqnum field
------------+--------+------------+---------------------------------------------------
- 0x30 | n | | URB data bytes. For ISO transfers the padding
- | | | between each ISO packets is not transmitted.
-
-USBIP_RET_UNLINK: Reply for URB unlink
-
- Offset | Length | Value | Description
------------+--------+------------+---------------------------------------------------
- 0 | 4 | 0x00000004 | command: reply for the URB unlink command
------------+--------+------------+---------------------------------------------------
- 4 | 4 | | seqnum: the unlinked URB sequence number
------------+--------+------------+---------------------------------------------------
- 8 | 4 | | devid
------------+--------+------------+---------------------------------------------------
- 0xC | 4 | | direction: 0: USBIP_DIR_OUT
- | | | 1: USBIP_DIR_IN
------------+--------+------------+---------------------------------------------------
- 0x10 | 4 | | ep: endpoint number
------------+--------+------------+---------------------------------------------------
- 0x14 | 4 | | status: This is the value contained in the
- | | | urb->status in the URB completition handler.
- | | | FIXME: a better explanation needed.
------------+--------+------------+---------------------------------------------------
- 0x30 | n | | URB data bytes. For ISO transfers the padding
- | | | between each ISO packets is not transmitted.
+++ /dev/null
-Makefile
-Makefile.in
-aclocal.m4
-autom4te.cache/
-config.guess
-config.h
-config.h.in
-config.log
-config.status
-config.sub
-configure
-depcomp
-install-sh
-libsrc/Makefile
-libsrc/Makefile.in
-libtool
-ltmain.sh
-missing
-src/Makefile
-src/Makefile.in
-stamp-h1
-libsrc/libusbip.la
-libsrc/libusbip_la-names.lo
-libsrc/libusbip_la-usbip_common.lo
-libsrc/libusbip_la-usbip_host_driver.lo
-libsrc/libusbip_la-vhci_driver.lo
-src/usbip
-src/usbipd
+++ /dev/null
-Takahiro Hirofuchi
-Robert Leibl
-matt mooney <mfm@muteddisk.com>
+++ /dev/null
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users. This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it. (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.) You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
- To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have. You must make sure that they, too, receive or can get the
-source code. And you must show them these terms so they know their
-rights.
-
- We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
- Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software. If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
- Finally, any free program is threatened constantly by software
-patents. We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
- 1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
- 2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
- a) Accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of Sections
- 1 and 2 above on a medium customarily used for software interchange; or,
-
- b) Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable. However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
- 4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
- 5. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
- 6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
- 7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
- 8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded. In such case, this License incorporates
-the limitation as if written in the body of this License.
-
- 9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation. If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
- 10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission. For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this. Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
- NO WARRANTY
-
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) year name of author
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
- <signature of Ty Coon>, 1 April 1989
- Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Library General
-Public License instead of this License.
+++ /dev/null
-Installation Instructions
-*************************
-
-Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
-2006, 2007 Free Software Foundation, Inc.
-
-This file is free documentation; the Free Software Foundation gives
-unlimited permission to copy, distribute and modify it.
-
-Basic Installation
-==================
-
-Briefly, the shell commands `./configure; make; make install' should
-configure, build, and install this package. The following
-more-detailed instructions are generic; see the `README' file for
-instructions specific to this package.
-
- The `configure' shell script attempts to guess correct values for
-various system-dependent variables used during compilation. It uses
-those values to create a `Makefile' in each directory of the package.
-It may also create one or more `.h' files containing system-dependent
-definitions. Finally, it creates a shell script `config.status' that
-you can run in the future to recreate the current configuration, and a
-file `config.log' containing compiler output (useful mainly for
-debugging `configure').
-
- It can also use an optional file (typically called `config.cache'
-and enabled with `--cache-file=config.cache' or simply `-C') that saves
-the results of its tests to speed up reconfiguring. Caching is
-disabled by default to prevent problems with accidental use of stale
-cache files.
-
- If you need to do unusual things to compile the package, please try
-to figure out how `configure' could check whether to do them, and mail
-diffs or instructions to the address given in the `README' so they can
-be considered for the next release. If you are using the cache, and at
-some point `config.cache' contains results you don't want to keep, you
-may remove or edit it.
-
- The file `configure.ac' (or `configure.in') is used to create
-`configure' by a program called `autoconf'. You need `configure.ac' if
-you want to change it or regenerate `configure' using a newer version
-of `autoconf'.
-
-The simplest way to compile this package is:
-
- 1. `cd' to the directory containing the package's source code and type
- `./configure' to configure the package for your system.
-
- Running `configure' might take a while. While running, it prints
- some messages telling which features it is checking for.
-
- 2. Type `make' to compile the package.
-
- 3. Optionally, type `make check' to run any self-tests that come with
- the package.
-
- 4. Type `make install' to install the programs and any data files and
- documentation.
-
- 5. You can remove the program binaries and object files from the
- source code directory by typing `make clean'. To also remove the
- files that `configure' created (so you can compile the package for
- a different kind of computer), type `make distclean'. There is
- also a `make maintainer-clean' target, but that is intended mainly
- for the package's developers. If you use it, you may have to get
- all sorts of other programs in order to regenerate files that came
- with the distribution.
-
- 6. Often, you can also type `make uninstall' to remove the installed
- files again.
-
-Compilers and Options
-=====================
-
-Some systems require unusual options for compilation or linking that the
-`configure' script does not know about. Run `./configure --help' for
-details on some of the pertinent environment variables.
-
- You can give `configure' initial values for configuration parameters
-by setting variables in the command line or in the environment. Here
-is an example:
-
- ./configure CC=c99 CFLAGS=-g LIBS=-lposix
-
- *Note Defining Variables::, for more details.
-
-Compiling For Multiple Architectures
-====================================
-
-You can compile the package for more than one kind of computer at the
-same time, by placing the object files for each architecture in their
-own directory. To do this, you can use GNU `make'. `cd' to the
-directory where you want the object files and executables to go and run
-the `configure' script. `configure' automatically checks for the
-source code in the directory that `configure' is in and in `..'.
-
- With a non-GNU `make', it is safer to compile the package for one
-architecture at a time in the source code directory. After you have
-installed the package for one architecture, use `make distclean' before
-reconfiguring for another architecture.
-
-Installation Names
-==================
-
-By default, `make install' installs the package's commands under
-`/usr/local/bin', include files under `/usr/local/include', etc. You
-can specify an installation prefix other than `/usr/local' by giving
-`configure' the option `--prefix=PREFIX'.
-
- You can specify separate installation prefixes for
-architecture-specific files and architecture-independent files. If you
-pass the option `--exec-prefix=PREFIX' to `configure', the package uses
-PREFIX as the prefix for installing programs and libraries.
-Documentation and other data files still use the regular prefix.
-
- In addition, if you use an unusual directory layout you can give
-options like `--bindir=DIR' to specify different values for particular
-kinds of files. Run `configure --help' for a list of the directories
-you can set and what kinds of files go in them.
-
- If the package supports it, you can cause programs to be installed
-with an extra prefix or suffix on their names by giving `configure' the
-option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
-
-Optional Features
-=================
-
-Some packages pay attention to `--enable-FEATURE' options to
-`configure', where FEATURE indicates an optional part of the package.
-They may also pay attention to `--with-PACKAGE' options, where PACKAGE
-is something like `gnu-as' or `x' (for the X Window System). The
-`README' should mention any `--enable-' and `--with-' options that the
-package recognizes.
-
- For packages that use the X Window System, `configure' can usually
-find the X include and library files automatically, but if it doesn't,
-you can use the `configure' options `--x-includes=DIR' and
-`--x-libraries=DIR' to specify their locations.
-
-Specifying the System Type
-==========================
-
-There may be some features `configure' cannot figure out automatically,
-but needs to determine by the type of machine the package will run on.
-Usually, assuming the package is built to be run on the _same_
-architectures, `configure' can figure that out, but if it prints a
-message saying it cannot guess the machine type, give it the
-`--build=TYPE' option. TYPE can either be a short name for the system
-type, such as `sun4', or a canonical name which has the form:
-
- CPU-COMPANY-SYSTEM
-
-where SYSTEM can have one of these forms:
-
- OS KERNEL-OS
-
- See the file `config.sub' for the possible values of each field. If
-`config.sub' isn't included in this package, then this package doesn't
-need to know the machine type.
-
- If you are _building_ compiler tools for cross-compiling, you should
-use the option `--target=TYPE' to select the type of system they will
-produce code for.
-
- If you want to _use_ a cross compiler, that generates code for a
-platform different from the build platform, you should specify the
-"host" platform (i.e., that on which the generated programs will
-eventually be run) with `--host=TYPE'.
-
-Sharing Defaults
-================
-
-If you want to set default values for `configure' scripts to share, you
-can create a site shell script called `config.site' that gives default
-values for variables like `CC', `cache_file', and `prefix'.
-`configure' looks for `PREFIX/share/config.site' if it exists, then
-`PREFIX/etc/config.site' if it exists. Or, you can set the
-`CONFIG_SITE' environment variable to the location of the site script.
-A warning: not all `configure' scripts look for a site script.
-
-Defining Variables
-==================
-
-Variables not defined in a site shell script can be set in the
-environment passed to `configure'. However, some packages may run
-configure again during the build, and the customized values of these
-variables may be lost. In order to avoid this problem, you should set
-them in the `configure' command line, using `VAR=value'. For example:
-
- ./configure CC=/usr/local2/bin/gcc
-
-causes the specified `gcc' to be used as the C compiler (unless it is
-overridden in the site shell script).
-
-Unfortunately, this technique does not work for `CONFIG_SHELL' due to
-an Autoconf bug. Until the bug is fixed you can use this workaround:
-
- CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
-
-`configure' Invocation
-======================
-
-`configure' recognizes the following options to control how it operates.
-
-`--help'
-`-h'
- Print a summary of the options to `configure', and exit.
-
-`--version'
-`-V'
- Print the version of Autoconf used to generate the `configure'
- script, and exit.
-
-`--cache-file=FILE'
- Enable the cache: use and save the results of the tests in FILE,
- traditionally `config.cache'. FILE defaults to `/dev/null' to
- disable caching.
-
-`--config-cache'
-`-C'
- Alias for `--cache-file=config.cache'.
-
-`--quiet'
-`--silent'
-`-q'
- Do not print messages saying which checks are being made. To
- suppress all normal output, redirect it to `/dev/null' (any error
- messages will still be shown).
-
-`--srcdir=DIR'
- Look for the package's source code in directory DIR. Usually
- `configure' can determine that directory automatically.
-
-`configure' also accepts some other, not widely useful, options. Run
-`configure --help' for more details.
-
+++ /dev/null
-SUBDIRS := libsrc src
-includedir = @includedir@/usbip
-include_HEADERS := $(addprefix libsrc/, \
- usbip_common.h vhci_driver.h usbip_host_driver.h)
-
-dist_man_MANS := $(addprefix doc/, usbip.8 usbipd.8)
+++ /dev/null
-#
-# README for usbip-utils
-#
-# Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
-# 2005-2008 Takahiro Hirofuchi
-
-
-[Requirements]
- - USB/IP device drivers
- Found in the staging directory of the Linux kernel.
-
- - libudev >= 2.0
- libudev library
-
- - libwrap0-dev
- tcp wrapper library
-
- - gcc >= 4.0
-
- - libtool, automake >= 1.9, autoconf >= 2.5.0, pkg-config
-
-[Optional]
- - hwdata
- Contains USB device identification data.
-
-
-[Install]
- 0. Generate configuration scripts.
- $ ./autogen.sh
-
- 1. Compile & install the userspace utilities.
- $ ./configure [--with-tcp-wrappers=no] [--with-usbids-dir=<dir>]
- $ make install
-
- 2. Compile & install USB/IP drivers.
-
-
-[Usage]
- server:# (Physically attach your USB device.)
-
- server:# insmod usbip-core.ko
- server:# insmod usbip-host.ko
-
- server:# usbipd -D
- - Start usbip daemon.
-
- server:# usbip list -l
- - List driver assignments for USB devices.
-
- server:# usbip bind --busid 1-2
- - Bind usbip-host.ko to the device with busid 1-2.
- - The USB device 1-2 is now exportable to other hosts!
- - Use `usbip unbind --busid 1-2' to stop exporting the device.
-
- client:# insmod usbip-core.ko
- client:# insmod vhci-hcd.ko
-
- client:# usbip list --remote <host>
- - List exported USB devices on the <host>.
-
- client:# usbip attach --remote <host> --busid 1-2
- - Connect the remote USB device.
-
- client:# usbip port
- - Show virtual port status.
-
- client:# usbip detach --port <port>
- - Detach the USB device.
-
-
-[Example]
----------------------------
- SERVER SIDE
----------------------------
-Physically attach your USB devices to this host.
-
- trois:# insmod path/to/usbip-core.ko
- trois:# insmod path/to/usbip-host.ko
- trois:# usbipd -D
-
-In another terminal, let's look up what USB devices are physically
-attached to this host.
-
- trois:# usbip list -l
- Local USB devices
- =================
- - busid 1-1 (05a9:a511)
- 1-1:1.0 -> ov511
-
- - busid 3-2 (0711:0902)
- 3-2:1.0 -> none
-
- - busid 3-3.1 (08bb:2702)
- 3-3.1:1.0 -> snd-usb-audio
- 3-3.1:1.1 -> snd-usb-audio
-
- - busid 3-3.2 (04bb:0206)
- 3-3.2:1.0 -> usb-storage
-
- - busid 3-3 (0409:0058)
- 3-3:1.0 -> hub
-
- - busid 4-1 (046d:08b2)
- 4-1:1.0 -> none
- 4-1:1.1 -> none
- 4-1:1.2 -> none
-
- - busid 5-2 (058f:9254)
- 5-2:1.0 -> hub
-
-A USB storage device of busid 3-3.2 is now bound to the usb-storage
-driver. To export this device, we first mark the device as
-"exportable"; the device is bound to the usbip-host driver. Please
-remember you can not export a USB hub.
-
-Mark the device of busid 3-3.2 as exportable:
-
- trois:# usbip --debug bind --busid 3-3.2
- ...
- usbip debug: usbip_bind.c:162:[unbind_other] 3-3.2:1.0 -> usb-storage
- ...
- bind device on busid 3-3.2: complete
-
- trois:# usbip list -l
- Local USB devices
- =================
- ...
-
- - busid 3-3.2 (04bb:0206)
- 3-3.2:1.0 -> usbip-host
- ...
-
----------------------------
- CLIENT SIDE
----------------------------
-First, let's list available remote devices that are marked as
-exportable on the host.
-
- deux:# insmod path/to/usbip-core.ko
- deux:# insmod path/to/vhci-hcd.ko
-
- deux:# usbip list --remote 10.0.0.3
- Exportable USB devices
- ======================
- - 10.0.0.3
- 1-1: Prolific Technology, Inc. : unknown product (067b:3507)
- : /sys/devices/pci0000:00/0000:00:1f.2/usb1/1-1
- : (Defined at Interface level) / unknown subclass / unknown protocol (00/00/00)
- : 0 - Mass Storage / SCSI / Bulk (Zip) (08/06/50)
-
- 1-2.2.1: Apple Computer, Inc. : unknown product (05ac:0203)
- : /sys/devices/pci0000:00/0000:00:1f.2/usb1/1-2/1-2.2/1-2.2.1
- : (Defined at Interface level) / unknown subclass / unknown protocol (00/00/00)
- : 0 - Human Interface Devices / Boot Interface Subclass / Keyboard (03/01/01)
-
- 1-2.2.3: OmniVision Technologies, Inc. : OV511+ WebCam (05a9:a511)
- : /sys/devices/pci0000:00/0000:00:1f.2/usb1/1-2/1-2.2/1-2.2.3
- : (Defined at Interface level) / unknown subclass / unknown protocol (00/00/00)
- : 0 - Vendor Specific Class / unknown subclass / unknown protocol (ff/00/00)
-
- 3-1: Logitech, Inc. : QuickCam Pro 4000 (046d:08b2)
- : /sys/devices/pci0000:00/0000:00:1e.0/0000:02:0a.0/usb3/3-1
- : (Defined at Interface level) / unknown subclass / unknown protocol (00/00/00)
- : 0 - Data / unknown subclass / unknown protocol (0a/ff/00)
- : 1 - Audio / Control Device / unknown protocol (01/01/00)
- : 2 - Audio / Streaming / unknown protocol (01/02/00)
-
-Attach a remote USB device:
-
- deux:# usbip attach --remote 10.0.0.3 --busid 1-1
- port 0 attached
-
-Show the devices attached to this client:
-
- deux:# usbip port
- Port 00: <Port in Use> at Full Speed(12Mbps)
- Prolific Technology, Inc. : unknown product (067b:3507)
- 6-1 -> usbip://10.0.0.3:3240/1-1 (remote bus/dev 001/004)
- 6-1:1.0 used by usb-storage
- /sys/class/scsi_device/0:0:0:0/device
- /sys/class/scsi_host/host0/device
- /sys/block/sda/device
-
-Detach the imported device:
-
- deux:# usbip detach --port 0
- port 0 detached
-
-
-[Checklist]
- - See 'Debug Tips' on the project wiki.
- - http://usbip.wiki.sourceforge.net/how-to-debug-usbip
- - usbip-host.ko must be bound to the target device.
- - See /proc/bus/usb/devices and find "Driver=..." lines of the device.
- - Shutdown firewall.
- - usbip now uses TCP port 3240.
- - Disable SELinux.
- - Check the kernel and daemon messages.
-
-
-[Contact]
- Mailing List: linux-usb@vger.kernel.org
+++ /dev/null
-#!/bin/sh -x
-
-#aclocal
-#autoheader
-#libtoolize --copy --force
-#automake-1.9 -acf
-#autoconf
-
-autoreconf -i -f -v
+++ /dev/null
-#!/bin/sh
-
-if [ -r Makefile ]; then
- make distclean
-fi
-
-FILES="aclocal.m4 autom4te.cache compile config.guess config.h.in config.log \
- config.status config.sub configure cscope.out depcomp install-sh \
- libsrc/Makefile libsrc/Makefile.in libtool ltmain.sh Makefile \
- Makefile.in missing src/Makefile src/Makefile.in"
-
-rm -vRf $FILES
+++ /dev/null
-dnl Process this file with autoconf to produce a configure script.
-
-AC_PREREQ(2.59)
-AC_INIT([usbip-utils], [2.0], [linux-usb@vger.kernel.org])
-AC_DEFINE([USBIP_VERSION], [0x00000111], [binary-coded decimal version number])
-
-CURRENT=0
-REVISION=1
-AGE=0
-AC_SUBST([LIBUSBIP_VERSION], [$CURRENT:$REVISION:$AGE], [library version])
-
-AC_CONFIG_SRCDIR([src/usbipd.c])
-AC_CONFIG_HEADERS([config.h])
-
-AM_INIT_AUTOMAKE([foreign])
-LT_INIT
-
-# Silent build for automake >= 1.11
-m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
-
-AC_SUBST([EXTRA_CFLAGS], ["-Wall -Werror -Wextra -std=gnu99"])
-
-# Checks for programs.
-AC_PROG_CC
-AC_PROG_INSTALL
-AC_PROG_MAKE_SET
-
-# Checks for header files.
-AC_HEADER_DIRENT
-AC_HEADER_STDC
-AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdint.h stdlib.h dnl
- string.h sys/socket.h syslog.h unistd.h])
-
-# Checks for typedefs, structures, and compiler characteristics.
-AC_TYPE_INT32_T
-AC_TYPE_SIZE_T
-AC_TYPE_SSIZE_T
-AC_TYPE_UINT16_T
-AC_TYPE_UINT32_T
-AC_TYPE_UINT8_T
-
-# Checks for library functions.
-AC_FUNC_REALLOC
-AC_CHECK_FUNCS([memset mkdir regcomp socket strchr strerror strstr dnl
- strtoul])
-
-AC_CHECK_HEADER([libudev.h],
- [AC_CHECK_LIB([udev], [udev_new],
- [LIBS="$LIBS -ludev"],
- [AC_MSG_ERROR([Missing udev library!])])],
- [AC_MSG_ERROR([Missing /usr/include/libudev.h])])
-
-# Checks for libwrap library.
-AC_MSG_CHECKING([whether to use the libwrap (TCP wrappers) library])
-AC_ARG_WITH([tcp-wrappers],
- [AS_HELP_STRING([--with-tcp-wrappers],
- [use the libwrap (TCP wrappers) library])],
- dnl [ACTION-IF-GIVEN]
- [if test "$withval" = "yes"; then
- AC_MSG_RESULT([yes])
- AC_MSG_CHECKING([for hosts_access in -lwrap])
- saved_LIBS="$LIBS"
- LIBS="-lwrap $saved_LIBS"
- AC_TRY_LINK(
- [int hosts_access(); int allow_severity, deny_severity;],
- [hosts_access()],
- [AC_MSG_RESULT([yes]);
- AC_DEFINE([HAVE_LIBWRAP], [1],
- [use tcp wrapper]) wrap_LIB="-lwrap"],
- [AC_MSG_RESULT([not found]); exit 1])
- else
- AC_MSG_RESULT([no]);
- fi],
- dnl [ACTION-IF-NOT-GIVEN]
- [AC_MSG_RESULT([(default)])
- AC_MSG_CHECKING([for hosts_access in -lwrap])
- saved_LIBS="$LIBS"
- LIBS="-lwrap $saved_LIBS"
- AC_TRY_LINK(
- [int hosts_access(); int allow_severity, deny_severity;],
- [hosts_access()],
- [AC_MSG_RESULT([yes]);
- AC_DEFINE([HAVE_LIBWRAP], [1], [use tcp wrapper])],
- [AC_MSG_RESULT([no]); LIBS="$saved_LIBS"])])
-
-# Sets directory containing usb.ids.
-AC_ARG_WITH([usbids-dir],
- [AS_HELP_STRING([--with-usbids-dir=DIR],
- [where usb.ids is found (default /usr/share/hwdata/)])],
- [USBIDS_DIR=$withval], [USBIDS_DIR="/usr/share/hwdata/"])
-AC_SUBST([USBIDS_DIR])
-
-# use _FORTIFY_SOURCE
-AC_MSG_CHECKING([whether to use fortify])
-AC_ARG_WITH([fortify],
- [AS_HELP_STRING([--with-fortify],
- [use _FORTIFY_SROUCE option when compiling)])],
- dnl [ACTION-IF-GIVEN]
- [if test "$withval" = "yes"; then
- AC_MSG_RESULT([yes])
- CFLAGS="$CFLAGS -D_FORTIFY_SOURCE -O"
- else
- AC_MSG_RESULT([no])
- CFLAGS="$CFLAGS -U_FORTIFY_SOURCE"
- fi
- ],
- dnl [ACTION-IF-NOT-GIVEN]
- [AC_MSG_RESULT([default])])
-
-AC_CONFIG_FILES([Makefile libsrc/Makefile src/Makefile])
-AC_OUTPUT
+++ /dev/null
-.TH USBIP "8" "February 2009" "usbip" "System Administration Utilities"
-.SH NAME
-usbip \- manage USB/IP devices
-.SH SYNOPSIS
-.B usbip
-[\fIoptions\fR] <\fIcommand\fR> <\fIargs\fR>
-
-.SH DESCRIPTION
-On a USB/IP server, devices can be listed, bound, and unbound using
-this program. On a USB/IP client, devices exported by USB/IP servers
-can be listed, attached and detached.
-
-.SH OPTIONS
-.HP
-\fB\-\-debug\fR
-.IP
-Print debugging information.
-.PP
-
-.HP
-\fB\-\-log\fR
-.IP
-Log to syslog.
-.PP
-
-.HP
-\fB\-\-tcp-port PORT\fR
-.IP
-Connect to PORT on remote host (used for attach and list --remote).
-.PP
-
-.SH COMMANDS
-.HP
-\fBversion\fR
-.IP
-Show version and exit.
-.PP
-
-.HP
-\fBhelp\fR [\fIcommand\fR]
-.IP
-Print the program help message, or help on a specific command, and
-then exit.
-.PP
-
-.HP
-\fBattach\fR \-\-remote=<\fIhost\fR> \-\-busid=<\fIbus_id\fR>
-.IP
-Attach a remote USB device.
-.PP
-
-.HP
-\fBdetach\fR \-\-port=<\fIport\fR>
-.IP
-Detach an imported USB device.
-.PP
-
-.HP
-\fBbind\fR \-\-busid=<\fIbusid\fR>
-.IP
-Make a device exportable.
-.PP
-
-.HP
-\fBunbind\fR \-\-busid=<\fIbusid\fR>
-.IP
-Stop exporting a device so it can be used by a local driver.
-.PP
-
-.HP
-\fBlist\fR \-\-remote=<\fIhost\fR>
-.IP
-List USB devices exported by a remote host.
-.PP
-
-.HP
-\fBlist\fR \-\-local
-.IP
-List local USB devices.
-.PP
-
-
-.SH EXAMPLES
-
- client:# usbip list --remote=server
- - List exportable usb devices on the server.
-
- client:# usbip attach --remote=server --busid=1-2
- - Connect the remote USB device.
-
- client:# usbip detach --port=0
- - Detach the usb device.
-
-.SH "SEE ALSO"
-\fBusbipd\fP\fB(8)\fB\fP
+++ /dev/null
-.TH USBIP "8" "February 2009" "usbip" "System Administration Utilities"
-.SH NAME
-usbipd \- USB/IP server daemon
-.SH SYNOPSIS
-.B usbipd
-[\fIoptions\fR]
-
-.SH DESCRIPTION
-.B usbipd
-provides USB/IP clients access to exported USB devices.
-
-Devices have to explicitly be exported using
-.B usbip bind
-before usbipd makes them available to other hosts.
-
-The daemon accepts connections from USB/IP clients
-on TCP port 3240 by default.
-
-.SH OPTIONS
-.HP
-\fB\-4\fR, \fB\-\-ipv4\fR
-.IP
-Bind to IPv4. Default is both.
-.PP
-
-.HP
-\fB\-6\fR, \fB\-\-ipv6\fR
-.IP
-Bind to IPv6. Default is both.
-.PP
-
-.HP
-\fB\-D\fR, \fB\-\-daemon\fR
-.IP
-Run as a daemon process.
-.PP
-
-.HP
-\fB\-d\fR, \fB\-\-debug\fR
-.IP
-Print debugging information.
-.PP
-
-.HP
-\fB\-PFILE\fR, \fB\-\-pid FILE\fR
-.IP
-Write process id to FILE.
-.br
-If no FILE specified, use /var/run/usbipd.pid
-.PP
-
-\fB\-tPORT\fR, \fB\-\-tcp\-port PORT\fR
-.IP
-Listen on TCP/IP port PORT.
-.PP
-
-\fB\-h\fR, \fB\-\-help\fR
-.IP
-Print the program help message and exit.
-.PP
-
-.HP
-\fB\-v\fR, \fB\-\-version\fR
-.IP
-Show version.
-.PP
-
-.SH LIMITATIONS
-
-.B usbipd
-offers no authentication or authorization for USB/IP. Any
-USB/IP client can connect and use exported devices.
-
-.SH EXAMPLES
-
- server:# modprobe usbip
-
- server:# usbipd -D
- - Start usbip daemon.
-
- server:# usbip list --local
- - List driver assignments for usb devices.
-
- server:# usbip bind --busid=1-2
- - Bind usbip-host.ko to the device of busid 1-2.
- - A usb device 1-2 is now exportable to other hosts!
- - Use 'usbip unbind --busid=1-2' when you want to shutdown exporting and use the device locally.
-
-.SH "SEE ALSO"
-\fBusbip\fP\fB(8)\fB\fP
-
+++ /dev/null
-libusbip_la_CPPFLAGS = -DUSBIDS_FILE='"@USBIDS_DIR@/usb.ids"'
-libusbip_la_CFLAGS = @EXTRA_CFLAGS@
-libusbip_la_LDFLAGS = -version-info @LIBUSBIP_VERSION@
-
-lib_LTLIBRARIES := libusbip.la
-libusbip_la_SOURCES := names.c names.h usbip_host_driver.c usbip_host_driver.h \
- usbip_common.c usbip_common.h vhci_driver.c vhci_driver.h \
- sysfs_utils.c sysfs_utils.h
+++ /dev/null
-#ifndef _LIST_H
-#define _LIST_H
-
-/* Stripped down implementation of linked list taken
- * from the Linux Kernel.
- */
-
-/*
- * Simple doubly linked list implementation.
- *
- * Some of the internal functions ("__xxx") are useful when
- * manipulating whole lists rather than single entries, as
- * sometimes we already know the next/prev entries and we can
- * generate better code by using them directly rather than
- * using the generic single-entry routines.
- */
-
-struct list_head {
- struct list_head *next, *prev;
-};
-
-#define LIST_HEAD_INIT(name) { &(name), &(name) }
-
-#define LIST_HEAD(name) \
- struct list_head name = LIST_HEAD_INIT(name)
-
-static inline void INIT_LIST_HEAD(struct list_head *list)
-{
- list->next = list;
- list->prev = list;
-}
-
-/*
- * Insert a new entry between two known consecutive entries.
- *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
- */
-static inline void __list_add(struct list_head *new,
- struct list_head *prev,
- struct list_head *next)
-{
- next->prev = new;
- new->next = next;
- new->prev = prev;
- prev->next = new;
-}
-
-/**
- * list_add - add a new entry
- * @new: new entry to be added
- * @head: list head to add it after
- *
- * Insert a new entry after the specified head.
- * This is good for implementing stacks.
- */
-static inline void list_add(struct list_head *new, struct list_head *head)
-{
- __list_add(new, head, head->next);
-}
-
-/*
- * Delete a list entry by making the prev/next entries
- * point to each other.
- *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
- */
-static inline void __list_del(struct list_head * prev, struct list_head * next)
-{
- next->prev = prev;
- prev->next = next;
-}
-
-#define POISON_POINTER_DELTA 0
-#define LIST_POISON1 ((void *) 0x00100100 + POISON_POINTER_DELTA)
-#define LIST_POISON2 ((void *) 0x00200200 + POISON_POINTER_DELTA)
-
-/**
- * list_del - deletes entry from list.
- * @entry: the element to delete from the list.
- * Note: list_empty() on entry does not return true after this, the entry is
- * in an undefined state.
- */
-static inline void __list_del_entry(struct list_head *entry)
-{
- __list_del(entry->prev, entry->next);
-}
-
-static inline void list_del(struct list_head *entry)
-{
- __list_del(entry->prev, entry->next);
- entry->next = LIST_POISON1;
- entry->prev = LIST_POISON2;
-}
-
-/**
- * list_entry - get the struct for this entry
- * @ptr: the &struct list_head pointer.
- * @type: the type of the struct this is embedded in.
- * @member: the name of the list_struct within the struct.
- */
-#define list_entry(ptr, type, member) \
- container_of(ptr, type, member)
-/**
- * list_for_each - iterate over a list
- * @pos: the &struct list_head to use as a loop cursor.
- * @head: the head for your list.
- */
-#define list_for_each(pos, head) \
- for (pos = (head)->next; pos != (head); pos = pos->next)
-
-/**
- * list_for_each_safe - iterate over a list safe against removal of list entry
- * @pos: the &struct list_head to use as a loop cursor.
- * @n: another &struct list_head to use as temporary storage
- * @head: the head for your list.
- */
-#define list_for_each_safe(pos, n, head) \
- for (pos = (head)->next, n = pos->next; pos != (head); \
- pos = n, n = pos->next)
-
-#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
-
-/**
- * container_of - cast a member of a structure out to the containing structure
- * @ptr: the pointer to the member.
- * @type: the type of the container struct this is embedded in.
- * @member: the name of the member within the struct.
- *
- */
-#define container_of(ptr, type, member) ({ \
- const typeof( ((type *)0)->member ) *__mptr = (ptr); \
- (type *)( (char *)__mptr - offsetof(type,member) );})
-
-#endif
+++ /dev/null
-/*
- * names.c -- USB name database manipulation routines
- *
- * Copyright (C) 1999, 2000 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.
- *
- * 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.
- *
- *
- *
- *
- *
- * Copyright (C) 2005 Takahiro Hirofuchi
- * - names_deinit() is added.
- *
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <string.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <ctype.h>
-
-#include "names.h"
-#include "usbip_common.h"
-
-struct vendor {
- struct vendor *next;
- u_int16_t vendorid;
- char name[1];
-};
-
-struct product {
- struct product *next;
- u_int16_t vendorid, productid;
- char name[1];
-};
-
-struct class {
- struct class *next;
- u_int8_t classid;
- char name[1];
-};
-
-struct subclass {
- struct subclass *next;
- u_int8_t classid, subclassid;
- char name[1];
-};
-
-struct protocol {
- struct protocol *next;
- u_int8_t classid, subclassid, protocolid;
- char name[1];
-};
-
-struct genericstrtable {
- struct genericstrtable *next;
- unsigned int num;
- char name[1];
-};
-
-
-#define HASH1 0x10
-#define HASH2 0x02
-#define HASHSZ 16
-
-static unsigned int hashnum(unsigned int num)
-{
- unsigned int mask1 = HASH1 << 27, mask2 = HASH2 << 27;
-
- for (; mask1 >= HASH1; mask1 >>= 1, mask2 >>= 1)
- if (num & mask1)
- num ^= mask2;
- return num & (HASHSZ-1);
-}
-
-
-static struct vendor *vendors[HASHSZ] = { NULL, };
-static struct product *products[HASHSZ] = { NULL, };
-static struct class *classes[HASHSZ] = { NULL, };
-static struct subclass *subclasses[HASHSZ] = { NULL, };
-static struct protocol *protocols[HASHSZ] = { NULL, };
-
-const char *names_vendor(u_int16_t vendorid)
-{
- struct vendor *v;
-
- v = vendors[hashnum(vendorid)];
- for (; v; v = v->next)
- if (v->vendorid == vendorid)
- return v->name;
- return NULL;
-}
-
-const char *names_product(u_int16_t vendorid, u_int16_t productid)
-{
- struct product *p;
-
- p = products[hashnum((vendorid << 16) | productid)];
- for (; p; p = p->next)
- if (p->vendorid == vendorid && p->productid == productid)
- return p->name;
- return NULL;
-}
-
-const char *names_class(u_int8_t classid)
-{
- struct class *c;
-
- c = classes[hashnum(classid)];
- for (; c; c = c->next)
- if (c->classid == classid)
- return c->name;
- return NULL;
-}
-
-const char *names_subclass(u_int8_t classid, u_int8_t subclassid)
-{
- struct subclass *s;
-
- s = subclasses[hashnum((classid << 8) | subclassid)];
- for (; s; s = s->next)
- if (s->classid == classid && s->subclassid == subclassid)
- return s->name;
- return NULL;
-}
-
-const char *names_protocol(u_int8_t classid, u_int8_t subclassid,
- u_int8_t protocolid)
-{
- struct protocol *p;
-
- p = protocols[hashnum((classid << 16) | (subclassid << 8)
- | protocolid)];
- for (; p; p = p->next)
- if (p->classid == classid && p->subclassid == subclassid &&
- p->protocolid == protocolid)
- return p->name;
- return NULL;
-}
-
-/* add a cleanup function by takahiro */
-struct pool {
- struct pool *next;
- void *mem;
-};
-
-static struct pool *pool_head;
-
-static void *my_malloc(size_t size)
-{
- struct pool *p;
-
- p = calloc(1, sizeof(struct pool));
- if (!p)
- return NULL;
-
- p->mem = calloc(1, size);
- if (!p->mem) {
- free(p);
- return NULL;
- }
-
- p->next = pool_head;
- pool_head = p;
-
- return p->mem;
-}
-
-void names_free(void)
-{
- struct pool *pool;
-
- if (!pool_head)
- return;
-
- for (pool = pool_head; pool != NULL; ) {
- struct pool *tmp;
-
- if (pool->mem)
- free(pool->mem);
-
- tmp = pool;
- pool = pool->next;
- free(tmp);
- }
-}
-
-static int new_vendor(const char *name, u_int16_t vendorid)
-{
- struct vendor *v;
- unsigned int h = hashnum(vendorid);
-
- v = vendors[h];
- for (; v; v = v->next)
- if (v->vendorid == vendorid)
- return -1;
- v = my_malloc(sizeof(struct vendor) + strlen(name));
- if (!v)
- return -1;
- strcpy(v->name, name);
- v->vendorid = vendorid;
- v->next = vendors[h];
- vendors[h] = v;
- return 0;
-}
-
-static int new_product(const char *name, u_int16_t vendorid,
- u_int16_t productid)
-{
- struct product *p;
- unsigned int h = hashnum((vendorid << 16) | productid);
-
- p = products[h];
- for (; p; p = p->next)
- if (p->vendorid == vendorid && p->productid == productid)
- return -1;
- p = my_malloc(sizeof(struct product) + strlen(name));
- if (!p)
- return -1;
- strcpy(p->name, name);
- p->vendorid = vendorid;
- p->productid = productid;
- p->next = products[h];
- products[h] = p;
- return 0;
-}
-
-static int new_class(const char *name, u_int8_t classid)
-{
- struct class *c;
- unsigned int h = hashnum(classid);
-
- c = classes[h];
- for (; c; c = c->next)
- if (c->classid == classid)
- return -1;
- c = my_malloc(sizeof(struct class) + strlen(name));
- if (!c)
- return -1;
- strcpy(c->name, name);
- c->classid = classid;
- c->next = classes[h];
- classes[h] = c;
- return 0;
-}
-
-static int new_subclass(const char *name, u_int8_t classid, u_int8_t subclassid)
-{
- struct subclass *s;
- unsigned int h = hashnum((classid << 8) | subclassid);
-
- s = subclasses[h];
- for (; s; s = s->next)
- if (s->classid == classid && s->subclassid == subclassid)
- return -1;
- s = my_malloc(sizeof(struct subclass) + strlen(name));
- if (!s)
- return -1;
- strcpy(s->name, name);
- s->classid = classid;
- s->subclassid = subclassid;
- s->next = subclasses[h];
- subclasses[h] = s;
- return 0;
-}
-
-static int new_protocol(const char *name, u_int8_t classid, u_int8_t subclassid,
- u_int8_t protocolid)
-{
- struct protocol *p;
- unsigned int h = hashnum((classid << 16) | (subclassid << 8)
- | protocolid);
-
- p = protocols[h];
- for (; p; p = p->next)
- if (p->classid == classid && p->subclassid == subclassid
- && p->protocolid == protocolid)
- return -1;
- p = my_malloc(sizeof(struct protocol) + strlen(name));
- if (!p)
- return -1;
- strcpy(p->name, name);
- p->classid = classid;
- p->subclassid = subclassid;
- p->protocolid = protocolid;
- p->next = protocols[h];
- protocols[h] = p;
- return 0;
-}
-
-static void parse(FILE *f)
-{
- char buf[512], *cp;
- unsigned int linectr = 0;
- int lastvendor = -1;
- int lastclass = -1;
- int lastsubclass = -1;
- int lasthut = -1;
- int lastlang = -1;
- unsigned int u;
-
- while (fgets(buf, sizeof(buf), f)) {
- linectr++;
- /* remove line ends */
- cp = strchr(buf, '\r');
- if (cp)
- *cp = 0;
- cp = strchr(buf, '\n');
- if (cp)
- *cp = 0;
- if (buf[0] == '#' || !buf[0])
- continue;
- cp = buf;
- if (buf[0] == 'P' && buf[1] == 'H' && buf[2] == 'Y' &&
- buf[3] == 'S' && buf[4] == 'D' &&
- buf[5] == 'E' && buf[6] == 'S' && /*isspace(buf[7])*/
- buf[7] == ' ') {
- continue;
- }
- if (buf[0] == 'P' && buf[1] == 'H' &&
- buf[2] == 'Y' && /*isspace(buf[3])*/ buf[3] == ' ') {
- continue;
- }
- if (buf[0] == 'B' && buf[1] == 'I' && buf[2] == 'A' &&
- buf[3] == 'S' && /*isspace(buf[4])*/ buf[4] == ' ') {
- continue;
- }
- if (buf[0] == 'L' && /*isspace(buf[1])*/ buf[1] == ' ') {
- lasthut = lastclass = lastvendor = lastsubclass = -1;
- /*
- * set 1 as pseudo-id to indicate that the parser is
- * in a `L' section.
- */
- lastlang = 1;
- continue;
- }
- if (buf[0] == 'C' && /*isspace(buf[1])*/ buf[1] == ' ') {
- /* class spec */
- cp = buf+2;
- while (isspace(*cp))
- cp++;
- if (!isxdigit(*cp)) {
- err("Invalid class spec at line %u", linectr);
- continue;
- }
- u = strtoul(cp, &cp, 16);
- while (isspace(*cp))
- cp++;
- if (!*cp) {
- err("Invalid class spec at line %u", linectr);
- continue;
- }
- if (new_class(cp, u))
- err("Duplicate class spec at line %u class %04x %s",
- linectr, u, cp);
- dbg("line %5u class %02x %s", linectr, u, cp);
- lasthut = lastlang = lastvendor = lastsubclass = -1;
- lastclass = u;
- continue;
- }
- if (buf[0] == 'A' && buf[1] == 'T' && isspace(buf[2])) {
- /* audio terminal type spec */
- continue;
- }
- if (buf[0] == 'H' && buf[1] == 'C' && buf[2] == 'C'
- && isspace(buf[3])) {
- /* HID Descriptor bCountryCode */
- continue;
- }
- if (isxdigit(*cp)) {
- /* vendor */
- u = strtoul(cp, &cp, 16);
- while (isspace(*cp))
- cp++;
- if (!*cp) {
- err("Invalid vendor spec at line %u", linectr);
- continue;
- }
- if (new_vendor(cp, u))
- err("Duplicate vendor spec at line %u vendor %04x %s",
- linectr, u, cp);
- dbg("line %5u vendor %04x %s", linectr, u, cp);
- lastvendor = u;
- lasthut = lastlang = lastclass = lastsubclass = -1;
- continue;
- }
- if (buf[0] == '\t' && isxdigit(buf[1])) {
- /* product or subclass spec */
- u = strtoul(buf+1, &cp, 16);
- while (isspace(*cp))
- cp++;
- if (!*cp) {
- err("Invalid product/subclass spec at line %u",
- linectr);
- continue;
- }
- if (lastvendor != -1) {
- if (new_product(cp, lastvendor, u))
- err("Duplicate product spec at line %u product %04x:%04x %s",
- linectr, lastvendor, u, cp);
- dbg("line %5u product %04x:%04x %s", linectr,
- lastvendor, u, cp);
- continue;
- }
- if (lastclass != -1) {
- if (new_subclass(cp, lastclass, u))
- err("Duplicate subclass spec at line %u class %02x:%02x %s",
- linectr, lastclass, u, cp);
- dbg("line %5u subclass %02x:%02x %s", linectr,
- lastclass, u, cp);
- lastsubclass = u;
- continue;
- }
- if (lasthut != -1) {
- /* do not store hut */
- continue;
- }
- if (lastlang != -1) {
- /* do not store langid */
- continue;
- }
- err("Product/Subclass spec without prior Vendor/Class spec at line %u",
- linectr);
- continue;
- }
- if (buf[0] == '\t' && buf[1] == '\t' && isxdigit(buf[2])) {
- /* protocol spec */
- u = strtoul(buf+2, &cp, 16);
- while (isspace(*cp))
- cp++;
- if (!*cp) {
- err("Invalid protocol spec at line %u",
- linectr);
- continue;
- }
- if (lastclass != -1 && lastsubclass != -1) {
- if (new_protocol(cp, lastclass, lastsubclass,
- u))
- err("Duplicate protocol spec at line %u class %02x:%02x:%02x %s",
- linectr, lastclass, lastsubclass,
- u, cp);
- dbg("line %5u protocol %02x:%02x:%02x %s",
- linectr, lastclass, lastsubclass, u, cp);
- continue;
- }
- err("Protocol spec without prior Class and Subclass spec at line %u",
- linectr);
- continue;
- }
- if (buf[0] == 'H' && buf[1] == 'I' &&
- buf[2] == 'D' && /*isspace(buf[3])*/ buf[3] == ' ') {
- continue;
- }
- if (buf[0] == 'H' && buf[1] == 'U' &&
- buf[2] == 'T' && /*isspace(buf[3])*/ buf[3] == ' ') {
- lastlang = lastclass = lastvendor = lastsubclass = -1;
- /*
- * set 1 as pseudo-id to indicate that the parser is
- * in a `HUT' section.
- */
- lasthut = 1;
- continue;
- }
- if (buf[0] == 'R' && buf[1] == ' ')
- continue;
-
- if (buf[0] == 'V' && buf[1] == 'T')
- continue;
-
- err("Unknown line at line %u", linectr);
- }
-}
-
-
-int names_init(char *n)
-{
- FILE *f;
-
- f = fopen(n, "r");
- if (!f)
- return errno;
-
- parse(f);
- fclose(f);
- return 0;
-}
+++ /dev/null
-/*
- * names.h -- USB name database manipulation routines
- *
- * Copyright (C) 1999, 2000 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.
- *
- * 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.
- *
- *
- *
- * Copyright (C) 2005 Takahiro Hirofuchi
- * - names_free() is added.
- */
-
-#ifndef _NAMES_H
-#define _NAMES_H
-
-#include <sys/types.h>
-
-/* used by usbip_common.c */
-extern const char *names_vendor(u_int16_t vendorid);
-extern const char *names_product(u_int16_t vendorid, u_int16_t productid);
-extern const char *names_class(u_int8_t classid);
-extern const char *names_subclass(u_int8_t classid, u_int8_t subclassid);
-extern const char *names_protocol(u_int8_t classid, u_int8_t subclassid,
- u_int8_t protocolid);
-extern int names_init(char *n);
-extern void names_free(void);
-
-#endif /* _NAMES_H */
+++ /dev/null
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <errno.h>
-
-#include "sysfs_utils.h"
-#include "usbip_common.h"
-
-int write_sysfs_attribute(const char *attr_path, const char *new_value,
- size_t len)
-{
- int fd;
- int length;
-
- fd = open(attr_path, O_WRONLY);
- if (fd < 0) {
- dbg("error opening attribute %s", attr_path);
- return -1;
- }
-
- length = write(fd, new_value, len);
- if (length < 0) {
- dbg("error writing to attribute %s", attr_path);
- close(fd);
- return -1;
- }
-
- close(fd);
-
- return 0;
-}
+++ /dev/null
-
-#ifndef __SYSFS_UTILS_H
-#define __SYSFS_UTILS_H
-
-int write_sysfs_attribute(const char *attr_path, const char *new_value,
- size_t len);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) 2005-2007 Takahiro Hirofuchi
- */
-
-#include <libudev.h>
-#include "usbip_common.h"
-#include "names.h"
-
-#undef PROGNAME
-#define PROGNAME "libusbip"
-
-int usbip_use_syslog;
-int usbip_use_stderr;
-int usbip_use_debug;
-
-extern struct udev *udev_context;
-
-struct speed_string {
- int num;
- char *speed;
- char *desc;
-};
-
-static const struct speed_string speed_strings[] = {
- { USB_SPEED_UNKNOWN, "unknown", "Unknown Speed"},
- { USB_SPEED_LOW, "1.5", "Low Speed(1.5Mbps)" },
- { USB_SPEED_FULL, "12", "Full Speed(12Mbps)" },
- { USB_SPEED_HIGH, "480", "High Speed(480Mbps)" },
- { USB_SPEED_WIRELESS, "53.3-480", "Wireless"},
- { USB_SPEED_SUPER, "5000", "Super Speed(5000Mbps)" },
- { 0, NULL, NULL }
-};
-
-struct portst_string {
- int num;
- char *desc;
-};
-
-static struct portst_string portst_strings[] = {
- { SDEV_ST_AVAILABLE, "Device Available" },
- { SDEV_ST_USED, "Device in Use" },
- { SDEV_ST_ERROR, "Device Error"},
- { VDEV_ST_NULL, "Port Available"},
- { VDEV_ST_NOTASSIGNED, "Port Initializing"},
- { VDEV_ST_USED, "Port in Use"},
- { VDEV_ST_ERROR, "Port Error"},
- { 0, NULL}
-};
-
-const char *usbip_status_string(int32_t status)
-{
- for (int i = 0; portst_strings[i].desc != NULL; i++)
- if (portst_strings[i].num == status)
- return portst_strings[i].desc;
-
- return "Unknown Status";
-}
-
-const char *usbip_speed_string(int num)
-{
- for (int i = 0; speed_strings[i].speed != NULL; i++)
- if (speed_strings[i].num == num)
- return speed_strings[i].desc;
-
- return "Unknown Speed";
-}
-
-
-#define DBG_UDEV_INTEGER(name)\
- dbg("%-20s = %x", to_string(name), (int) udev->name)
-
-#define DBG_UINF_INTEGER(name)\
- dbg("%-20s = %x", to_string(name), (int) uinf->name)
-
-void dump_usb_interface(struct usbip_usb_interface *uinf)
-{
- char buff[100];
-
- usbip_names_get_class(buff, sizeof(buff),
- uinf->bInterfaceClass,
- uinf->bInterfaceSubClass,
- uinf->bInterfaceProtocol);
- dbg("%-20s = %s", "Interface(C/SC/P)", buff);
-}
-
-void dump_usb_device(struct usbip_usb_device *udev)
-{
- char buff[100];
-
- dbg("%-20s = %s", "path", udev->path);
- dbg("%-20s = %s", "busid", udev->busid);
-
- usbip_names_get_class(buff, sizeof(buff),
- udev->bDeviceClass,
- udev->bDeviceSubClass,
- udev->bDeviceProtocol);
- dbg("%-20s = %s", "Device(C/SC/P)", buff);
-
- DBG_UDEV_INTEGER(bcdDevice);
-
- usbip_names_get_product(buff, sizeof(buff),
- udev->idVendor,
- udev->idProduct);
- dbg("%-20s = %s", "Vendor/Product", buff);
-
- DBG_UDEV_INTEGER(bNumConfigurations);
- DBG_UDEV_INTEGER(bNumInterfaces);
-
- dbg("%-20s = %s", "speed",
- usbip_speed_string(udev->speed));
-
- DBG_UDEV_INTEGER(busnum);
- DBG_UDEV_INTEGER(devnum);
-}
-
-
-int read_attr_value(struct udev_device *dev, const char *name,
- const char *format)
-{
- const char *attr;
- int num = 0;
- int ret;
-
- attr = udev_device_get_sysattr_value(dev, name);
- if (!attr) {
- err("udev_device_get_sysattr_value failed");
- goto err;
- }
-
- /* The client chooses the device configuration
- * when attaching it so right after being bound
- * to usbip-host on the server the device will
- * have no configuration.
- * Therefore, attributes such as bConfigurationValue
- * and bNumInterfaces will not exist and sscanf will
- * fail. Check for these cases and don't treat them
- * as errors.
- */
-
- ret = sscanf(attr, format, &num);
- if (ret < 1) {
- if (strcmp(name, "bConfigurationValue") &&
- strcmp(name, "bNumInterfaces")) {
- err("sscanf failed for attribute %s", name);
- goto err;
- }
- }
-
-err:
-
- return num;
-}
-
-
-int read_attr_speed(struct udev_device *dev)
-{
- const char *speed;
-
- speed = udev_device_get_sysattr_value(dev, "speed");
- if (!speed) {
- err("udev_device_get_sysattr_value failed");
- goto err;
- }
-
- for (int i = 0; speed_strings[i].speed != NULL; i++) {
- if (!strcmp(speed, speed_strings[i].speed))
- return speed_strings[i].num;
- }
-
-err:
-
- return USB_SPEED_UNKNOWN;
-}
-
-#define READ_ATTR(object, type, dev, name, format) \
- do { \
- (object)->name = (type) read_attr_value(dev, to_string(name), \
- format); \
- } while (0)
-
-
-int read_usb_device(struct udev_device *sdev, struct usbip_usb_device *udev)
-{
- uint32_t busnum, devnum;
- const char *path, *name;
-
- READ_ATTR(udev, uint8_t, sdev, bDeviceClass, "%02x\n");
- READ_ATTR(udev, uint8_t, sdev, bDeviceSubClass, "%02x\n");
- READ_ATTR(udev, uint8_t, sdev, bDeviceProtocol, "%02x\n");
-
- READ_ATTR(udev, uint16_t, sdev, idVendor, "%04x\n");
- READ_ATTR(udev, uint16_t, sdev, idProduct, "%04x\n");
- READ_ATTR(udev, uint16_t, sdev, bcdDevice, "%04x\n");
-
- READ_ATTR(udev, uint8_t, sdev, bConfigurationValue, "%02x\n");
- READ_ATTR(udev, uint8_t, sdev, bNumConfigurations, "%02x\n");
- READ_ATTR(udev, uint8_t, sdev, bNumInterfaces, "%02x\n");
-
- READ_ATTR(udev, uint8_t, sdev, devnum, "%d\n");
- udev->speed = read_attr_speed(sdev);
-
- path = udev_device_get_syspath(sdev);
- name = udev_device_get_sysname(sdev);
-
- strncpy(udev->path, path, SYSFS_PATH_MAX);
- strncpy(udev->busid, name, SYSFS_BUS_ID_SIZE);
-
- sscanf(name, "%u-%u", &busnum, &devnum);
- udev->busnum = busnum;
-
- return 0;
-}
-
-int read_usb_interface(struct usbip_usb_device *udev, int i,
- struct usbip_usb_interface *uinf)
-{
- char busid[SYSFS_BUS_ID_SIZE];
- struct udev_device *sif;
-
- sprintf(busid, "%s:%d.%d", udev->busid, udev->bConfigurationValue, i);
-
- sif = udev_device_new_from_subsystem_sysname(udev_context, "usb", busid);
- if (!sif) {
- err("udev_device_new_from_subsystem_sysname %s failed", busid);
- return -1;
- }
-
- READ_ATTR(uinf, uint8_t, sif, bInterfaceClass, "%02x\n");
- READ_ATTR(uinf, uint8_t, sif, bInterfaceSubClass, "%02x\n");
- READ_ATTR(uinf, uint8_t, sif, bInterfaceProtocol, "%02x\n");
-
- return 0;
-}
-
-int usbip_names_init(char *f)
-{
- return names_init(f);
-}
-
-void usbip_names_free(void)
-{
- names_free();
-}
-
-void usbip_names_get_product(char *buff, size_t size, uint16_t vendor,
- uint16_t product)
-{
- const char *prod, *vend;
-
- prod = names_product(vendor, product);
- if (!prod)
- prod = "unknown product";
-
-
- vend = names_vendor(vendor);
- if (!vend)
- vend = "unknown vendor";
-
- snprintf(buff, size, "%s : %s (%04x:%04x)", vend, prod, vendor, product);
-}
-
-void usbip_names_get_class(char *buff, size_t size, uint8_t class,
- uint8_t subclass, uint8_t protocol)
-{
- const char *c, *s, *p;
-
- if (class == 0 && subclass == 0 && protocol == 0) {
- snprintf(buff, size, "(Defined at Interface level) (%02x/%02x/%02x)", class, subclass, protocol);
- return;
- }
-
- p = names_protocol(class, subclass, protocol);
- if (!p)
- p = "unknown protocol";
-
- s = names_subclass(class, subclass);
- if (!s)
- s = "unknown subclass";
-
- c = names_class(class);
- if (!c)
- c = "unknown class";
-
- snprintf(buff, size, "%s / %s / %s (%02x/%02x/%02x)", c, s, p, class, subclass, protocol);
-}
+++ /dev/null
-/*
- * Copyright (C) 2005-2007 Takahiro Hirofuchi
- */
-
-#ifndef __USBIP_COMMON_H
-#define __USBIP_COMMON_H
-
-#include <libudev.h>
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <syslog.h>
-#include <unistd.h>
-#include <linux/usb/ch9.h>
-#include "../../uapi/usbip.h"
-
-#ifndef USBIDS_FILE
-#define USBIDS_FILE "/usr/share/hwdata/usb.ids"
-#endif
-
-#ifndef VHCI_STATE_PATH
-#define VHCI_STATE_PATH "/var/run/vhci_hcd"
-#endif
-
-/* kernel module names */
-#define USBIP_CORE_MOD_NAME "usbip-core"
-#define USBIP_HOST_DRV_NAME "usbip-host"
-#define USBIP_VHCI_DRV_NAME "vhci_hcd"
-
-/* sysfs constants */
-#define SYSFS_MNT_PATH "/sys"
-#define SYSFS_BUS_NAME "bus"
-#define SYSFS_BUS_TYPE "usb"
-#define SYSFS_DRIVERS_NAME "drivers"
-
-#define SYSFS_PATH_MAX 256
-#define SYSFS_BUS_ID_SIZE 32
-
-extern int usbip_use_syslog;
-extern int usbip_use_stderr;
-extern int usbip_use_debug ;
-
-#define PROGNAME "usbip"
-
-#define pr_fmt(fmt) "%s: %s: " fmt "\n", PROGNAME
-#define dbg_fmt(fmt) pr_fmt("%s:%d:[%s] " fmt), "debug", \
- __FILE__, __LINE__, __func__
-
-#define err(fmt, args...) \
- do { \
- if (usbip_use_syslog) { \
- syslog(LOG_ERR, pr_fmt(fmt), "error", ##args); \
- } \
- if (usbip_use_stderr) { \
- fprintf(stderr, pr_fmt(fmt), "error", ##args); \
- } \
- } while (0)
-
-#define info(fmt, args...) \
- do { \
- if (usbip_use_syslog) { \
- syslog(LOG_INFO, pr_fmt(fmt), "info", ##args); \
- } \
- if (usbip_use_stderr) { \
- fprintf(stderr, pr_fmt(fmt), "info", ##args); \
- } \
- } while (0)
-
-#define dbg(fmt, args...) \
- do { \
- if (usbip_use_debug) { \
- if (usbip_use_syslog) { \
- syslog(LOG_DEBUG, dbg_fmt(fmt), ##args); \
- } \
- if (usbip_use_stderr) { \
- fprintf(stderr, dbg_fmt(fmt), ##args); \
- } \
- } \
- } while (0)
-
-#define BUG() \
- do { \
- err("sorry, it's a bug!"); \
- abort(); \
- } while (0)
-
-struct usbip_usb_interface {
- uint8_t bInterfaceClass;
- uint8_t bInterfaceSubClass;
- uint8_t bInterfaceProtocol;
- uint8_t padding; /* alignment */
-} __attribute__((packed));
-
-struct usbip_usb_device {
- char path[SYSFS_PATH_MAX];
- char busid[SYSFS_BUS_ID_SIZE];
-
- uint32_t busnum;
- uint32_t devnum;
- uint32_t speed;
-
- uint16_t idVendor;
- uint16_t idProduct;
- uint16_t bcdDevice;
-
- uint8_t bDeviceClass;
- uint8_t bDeviceSubClass;
- uint8_t bDeviceProtocol;
- uint8_t bConfigurationValue;
- uint8_t bNumConfigurations;
- uint8_t bNumInterfaces;
-} __attribute__((packed));
-
-#define to_string(s) #s
-
-void dump_usb_interface(struct usbip_usb_interface *);
-void dump_usb_device(struct usbip_usb_device *);
-int read_usb_device(struct udev_device *sdev, struct usbip_usb_device *udev);
-int read_attr_value(struct udev_device *dev, const char *name,
- const char *format);
-int read_usb_interface(struct usbip_usb_device *udev, int i,
- struct usbip_usb_interface *uinf);
-
-const char *usbip_speed_string(int num);
-const char *usbip_status_string(int32_t status);
-
-int usbip_names_init(char *);
-void usbip_names_free(void);
-void usbip_names_get_product(char *buff, size_t size, uint16_t vendor,
- uint16_t product);
-void usbip_names_get_class(char *buff, size_t size, uint8_t class,
- uint8_t subclass, uint8_t protocol);
-
-#endif /* __USBIP_COMMON_H */
+++ /dev/null
-/*
- * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
- * 2005-2007 Takahiro Hirofuchi
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-#include <errno.h>
-#include <unistd.h>
-
-#include <libudev.h>
-
-#include "usbip_common.h"
-#include "usbip_host_driver.h"
-#include "list.h"
-#include "sysfs_utils.h"
-
-#undef PROGNAME
-#define PROGNAME "libusbip"
-
-struct usbip_host_driver *host_driver;
-struct udev *udev_context;
-
-static int32_t read_attr_usbip_status(struct usbip_usb_device *udev)
-{
- char status_attr_path[SYSFS_PATH_MAX];
- int fd;
- int length;
- char status;
- int value = 0;
-
- snprintf(status_attr_path, SYSFS_PATH_MAX, "%s/usbip_status",
- udev->path);
-
- fd = open(status_attr_path, O_RDONLY);
- if (fd < 0) {
- err("error opening attribute %s", status_attr_path);
- return -1;
- }
-
- length = read(fd, &status, 1);
- if (length < 0) {
- err("error reading attribute %s", status_attr_path);
- close(fd);
- return -1;
- }
-
- value = atoi(&status);
-
- return value;
-}
-
-static
-struct usbip_exported_device *usbip_exported_device_new(const char *sdevpath)
-{
- struct usbip_exported_device *edev = NULL;
- struct usbip_exported_device *edev_old;
- size_t size;
- int i;
-
- edev = calloc(1, sizeof(struct usbip_exported_device));
-
- edev->sudev = udev_device_new_from_syspath(udev_context, sdevpath);
- if (!edev->sudev) {
- err("udev_device_new_from_syspath: %s", sdevpath);
- goto err;
- }
-
- read_usb_device(edev->sudev, &edev->udev);
-
- edev->status = read_attr_usbip_status(&edev->udev);
- if (edev->status < 0)
- goto err;
-
- /* reallocate buffer to include usb interface data */
- size = sizeof(struct usbip_exported_device) +
- edev->udev.bNumInterfaces * sizeof(struct usbip_usb_interface);
-
- edev_old = edev;
- edev = realloc(edev, size);
- if (!edev) {
- edev = edev_old;
- dbg("realloc failed");
- goto err;
- }
-
- for (i = 0; i < edev->udev.bNumInterfaces; i++)
- read_usb_interface(&edev->udev, i, &edev->uinf[i]);
-
- return edev;
-err:
- if (edev->sudev)
- udev_device_unref(edev->sudev);
- if (edev)
- free(edev);
-
- return NULL;
-}
-
-static int refresh_exported_devices(void)
-{
- struct usbip_exported_device *edev;
- struct udev_enumerate *enumerate;
- struct udev_list_entry *devices, *dev_list_entry;
- struct udev_device *dev;
- const char *path;
- const char *driver;
-
- enumerate = udev_enumerate_new(udev_context);
- udev_enumerate_add_match_subsystem(enumerate, "usb");
- udev_enumerate_scan_devices(enumerate);
-
- devices = udev_enumerate_get_list_entry(enumerate);
-
- udev_list_entry_foreach(dev_list_entry, devices) {
- path = udev_list_entry_get_name(dev_list_entry);
- dev = udev_device_new_from_syspath(udev_context, path);
- if (dev == NULL)
- continue;
-
- /* Check whether device uses usbip-host driver. */
- driver = udev_device_get_driver(dev);
- if (driver != NULL && !strcmp(driver, USBIP_HOST_DRV_NAME)) {
- edev = usbip_exported_device_new(path);
- if (!edev) {
- dbg("usbip_exported_device_new failed");
- continue;
- }
-
- list_add(&edev->node, &host_driver->edev_list);
- host_driver->ndevs++;
- }
- }
-
- return 0;
-}
-
-static void usbip_exported_device_destroy(void)
-{
- struct list_head *i, *tmp;
- struct usbip_exported_device *edev;
-
- list_for_each_safe(i, tmp, &host_driver->edev_list) {
- edev = list_entry(i, struct usbip_exported_device, node);
- list_del(i);
- free(edev);
- }
-}
-
-int usbip_host_driver_open(void)
-{
- int rc;
-
- udev_context = udev_new();
- if (!udev_context) {
- err("udev_new failed");
- return -1;
- }
-
- host_driver = calloc(1, sizeof(*host_driver));
-
- host_driver->ndevs = 0;
- INIT_LIST_HEAD(&host_driver->edev_list);
-
- rc = refresh_exported_devices();
- if (rc < 0)
- goto err_free_host_driver;
-
- return 0;
-
-err_free_host_driver:
- free(host_driver);
- host_driver = NULL;
-
- udev_unref(udev_context);
-
- return -1;
-}
-
-void usbip_host_driver_close(void)
-{
- if (!host_driver)
- return;
-
- usbip_exported_device_destroy();
-
- free(host_driver);
- host_driver = NULL;
-
- udev_unref(udev_context);
-}
-
-int usbip_host_refresh_device_list(void)
-{
- int rc;
-
- usbip_exported_device_destroy();
-
- host_driver->ndevs = 0;
- INIT_LIST_HEAD(&host_driver->edev_list);
-
- rc = refresh_exported_devices();
- if (rc < 0)
- return -1;
-
- return 0;
-}
-
-int usbip_host_export_device(struct usbip_exported_device *edev, int sockfd)
-{
- char attr_name[] = "usbip_sockfd";
- char sockfd_attr_path[SYSFS_PATH_MAX];
- char sockfd_buff[30];
- int ret;
-
- if (edev->status != SDEV_ST_AVAILABLE) {
- dbg("device not available: %s", edev->udev.busid);
- switch (edev->status) {
- case SDEV_ST_ERROR:
- dbg("status SDEV_ST_ERROR");
- break;
- case SDEV_ST_USED:
- dbg("status SDEV_ST_USED");
- break;
- default:
- dbg("status unknown: 0x%x", edev->status);
- }
- return -1;
- }
-
- /* only the first interface is true */
- snprintf(sockfd_attr_path, sizeof(sockfd_attr_path), "%s/%s",
- edev->udev.path, attr_name);
-
- snprintf(sockfd_buff, sizeof(sockfd_buff), "%d\n", sockfd);
-
- ret = write_sysfs_attribute(sockfd_attr_path, sockfd_buff,
- strlen(sockfd_buff));
- if (ret < 0) {
- err("write_sysfs_attribute failed: sockfd %s to %s",
- sockfd_buff, sockfd_attr_path);
- return ret;
- }
-
- info("connect: %s", edev->udev.busid);
-
- return ret;
-}
-
-struct usbip_exported_device *usbip_host_get_device(int num)
-{
- struct list_head *i;
- struct usbip_exported_device *edev;
- int cnt = 0;
-
- list_for_each(i, &host_driver->edev_list) {
- edev = list_entry(i, struct usbip_exported_device, node);
- if (num == cnt)
- return edev;
- else
- cnt++;
- }
-
- return NULL;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
- * 2005-2007 Takahiro Hirofuchi
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef __USBIP_HOST_DRIVER_H
-#define __USBIP_HOST_DRIVER_H
-
-#include <stdint.h>
-#include "usbip_common.h"
-#include "list.h"
-
-struct usbip_host_driver {
- int ndevs;
- /* list of exported device */
- struct list_head edev_list;
-};
-
-struct usbip_exported_device {
- struct udev_device *sudev;
- int32_t status;
- struct usbip_usb_device udev;
- struct list_head node;
- struct usbip_usb_interface uinf[];
-};
-
-extern struct usbip_host_driver *host_driver;
-
-int usbip_host_driver_open(void);
-void usbip_host_driver_close(void);
-
-int usbip_host_refresh_device_list(void);
-int usbip_host_export_device(struct usbip_exported_device *edev, int sockfd);
-struct usbip_exported_device *usbip_host_get_device(int num);
-
-#endif /* __USBIP_HOST_DRIVER_H */
+++ /dev/null
-/*
- * Copyright (C) 2005-2007 Takahiro Hirofuchi
- */
-
-#include "usbip_common.h"
-#include "vhci_driver.h"
-#include <limits.h>
-#include <netdb.h>
-#include <libudev.h>
-#include "sysfs_utils.h"
-
-#undef PROGNAME
-#define PROGNAME "libusbip"
-
-struct usbip_vhci_driver *vhci_driver;
-struct udev *udev_context;
-
-static struct usbip_imported_device *
-imported_device_init(struct usbip_imported_device *idev, char *busid)
-{
- struct udev_device *sudev;
-
- sudev = udev_device_new_from_subsystem_sysname(udev_context,
- "usb", busid);
- if (!sudev) {
- dbg("udev_device_new_from_subsystem_sysname failed: %s", busid);
- goto err;
- }
- read_usb_device(sudev, &idev->udev);
- udev_device_unref(sudev);
-
- return idev;
-
-err:
- return NULL;
-}
-
-
-
-static int parse_status(const char *value)
-{
- int ret = 0;
- char *c;
-
-
- for (int i = 0; i < vhci_driver->nports; i++)
- memset(&vhci_driver->idev[i], 0, sizeof(vhci_driver->idev[i]));
-
-
- /* skip a header line */
- c = strchr(value, '\n');
- if (!c)
- return -1;
- c++;
-
- while (*c != '\0') {
- int port, status, speed, devid;
- unsigned long socket;
- char lbusid[SYSFS_BUS_ID_SIZE];
-
- ret = sscanf(c, "%d %d %d %x %lx %31s\n",
- &port, &status, &speed,
- &devid, &socket, lbusid);
-
- if (ret < 5) {
- dbg("sscanf failed: %d", ret);
- BUG();
- }
-
- dbg("port %d status %d speed %d devid %x",
- port, status, speed, devid);
- dbg("socket %lx lbusid %s", socket, lbusid);
-
-
- /* if a device is connected, look at it */
- {
- struct usbip_imported_device *idev = &vhci_driver->idev[port];
-
- idev->port = port;
- idev->status = status;
-
- idev->devid = devid;
-
- idev->busnum = (devid >> 16);
- idev->devnum = (devid & 0x0000ffff);
-
- if (idev->status != VDEV_ST_NULL
- && idev->status != VDEV_ST_NOTASSIGNED) {
- idev = imported_device_init(idev, lbusid);
- if (!idev) {
- dbg("imported_device_init failed");
- return -1;
- }
- }
- }
-
-
- /* go to the next line */
- c = strchr(c, '\n');
- if (!c)
- break;
- c++;
- }
-
- dbg("exit");
-
- return 0;
-}
-
-static int refresh_imported_device_list(void)
-{
- const char *attr_status;
-
- attr_status = udev_device_get_sysattr_value(vhci_driver->hc_device,
- "status");
- if (!attr_status) {
- err("udev_device_get_sysattr_value failed");
- return -1;
- }
-
- return parse_status(attr_status);
-}
-
-static int get_nports(void)
-{
- char *c;
- int nports = 0;
- const char *attr_status;
-
- attr_status = udev_device_get_sysattr_value(vhci_driver->hc_device,
- "status");
- if (!attr_status) {
- err("udev_device_get_sysattr_value failed");
- return -1;
- }
-
- /* skip a header line */
- c = strchr(attr_status, '\n');
- if (!c)
- return 0;
- c++;
-
- while (*c != '\0') {
- /* go to the next line */
- c = strchr(c, '\n');
- if (!c)
- return nports;
- c++;
- nports += 1;
- }
-
- return nports;
-}
-
-/*
- * Read the given port's record.
- *
- * To avoid buffer overflow we will read the entire line and
- * validate each part's size. The initial buffer is padded by 4 to
- * accommodate the 2 spaces, 1 newline and an additional character
- * which is needed to properly validate the 3rd part without it being
- * truncated to an acceptable length.
- */
-static int read_record(int rhport, char *host, unsigned long host_len,
- char *port, unsigned long port_len, char *busid)
-{
- int part;
- FILE *file;
- char path[PATH_MAX+1];
- char *buffer, *start, *end;
- char delim[] = {' ', ' ', '\n'};
- int max_len[] = {(int)host_len, (int)port_len, SYSFS_BUS_ID_SIZE};
- size_t buffer_len = host_len + port_len + SYSFS_BUS_ID_SIZE + 4;
-
- buffer = malloc(buffer_len);
- if (!buffer)
- return -1;
-
- snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport);
-
- file = fopen(path, "r");
- if (!file) {
- err("fopen");
- free(buffer);
- return -1;
- }
-
- if (fgets(buffer, buffer_len, file) == NULL) {
- err("fgets");
- free(buffer);
- fclose(file);
- return -1;
- }
- fclose(file);
-
- /* validate the length of each of the 3 parts */
- start = buffer;
- for (part = 0; part < 3; part++) {
- end = strchr(start, delim[part]);
- if (end == NULL || (end - start) > max_len[part]) {
- free(buffer);
- return -1;
- }
- start = end + 1;
- }
-
- if (sscanf(buffer, "%s %s %s\n", host, port, busid) != 3) {
- err("sscanf");
- free(buffer);
- return -1;
- }
-
- free(buffer);
-
- return 0;
-}
-
-/* ---------------------------------------------------------------------- */
-
-int usbip_vhci_driver_open(void)
-{
- udev_context = udev_new();
- if (!udev_context) {
- err("udev_new failed");
- return -1;
- }
-
- vhci_driver = calloc(1, sizeof(struct usbip_vhci_driver));
-
- /* will be freed in usbip_driver_close() */
- vhci_driver->hc_device =
- udev_device_new_from_subsystem_sysname(udev_context,
- USBIP_VHCI_BUS_TYPE,
- USBIP_VHCI_DRV_NAME);
- if (!vhci_driver->hc_device) {
- err("udev_device_new_from_subsystem_sysname failed");
- goto err;
- }
-
- vhci_driver->nports = get_nports();
-
- dbg("available ports: %d", vhci_driver->nports);
-
- if (refresh_imported_device_list())
- goto err;
-
- return 0;
-
-err:
- udev_device_unref(vhci_driver->hc_device);
-
- if (vhci_driver)
- free(vhci_driver);
-
- vhci_driver = NULL;
-
- udev_unref(udev_context);
-
- return -1;
-}
-
-
-void usbip_vhci_driver_close(void)
-{
- if (!vhci_driver)
- return;
-
- udev_device_unref(vhci_driver->hc_device);
-
- free(vhci_driver);
-
- vhci_driver = NULL;
-
- udev_unref(udev_context);
-}
-
-
-int usbip_vhci_refresh_device_list(void)
-{
-
- if (refresh_imported_device_list())
- goto err;
-
- return 0;
-err:
- dbg("failed to refresh device list");
- return -1;
-}
-
-
-int usbip_vhci_get_free_port(void)
-{
- for (int i = 0; i < vhci_driver->nports; i++) {
- if (vhci_driver->idev[i].status == VDEV_ST_NULL)
- return i;
- }
-
- return -1;
-}
-
-int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
- uint32_t speed) {
- char buff[200]; /* what size should be ? */
- char attach_attr_path[SYSFS_PATH_MAX];
- char attr_attach[] = "attach";
- const char *path;
- int ret;
-
- snprintf(buff, sizeof(buff), "%u %d %u %u",
- port, sockfd, devid, speed);
- dbg("writing: %s", buff);
-
- path = udev_device_get_syspath(vhci_driver->hc_device);
- snprintf(attach_attr_path, sizeof(attach_attr_path), "%s/%s",
- path, attr_attach);
- dbg("attach attribute path: %s", attach_attr_path);
-
- ret = write_sysfs_attribute(attach_attr_path, buff, strlen(buff));
- if (ret < 0) {
- dbg("write_sysfs_attribute failed");
- return -1;
- }
-
- dbg("attached port: %d", port);
-
- return 0;
-}
-
-static unsigned long get_devid(uint8_t busnum, uint8_t devnum)
-{
- return (busnum << 16) | devnum;
-}
-
-/* will be removed */
-int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum,
- uint8_t devnum, uint32_t speed)
-{
- int devid = get_devid(busnum, devnum);
-
- return usbip_vhci_attach_device2(port, sockfd, devid, speed);
-}
-
-int usbip_vhci_detach_device(uint8_t port)
-{
- char detach_attr_path[SYSFS_PATH_MAX];
- char attr_detach[] = "detach";
- char buff[200]; /* what size should be ? */
- const char *path;
- int ret;
-
- snprintf(buff, sizeof(buff), "%u", port);
- dbg("writing: %s", buff);
-
- path = udev_device_get_syspath(vhci_driver->hc_device);
- snprintf(detach_attr_path, sizeof(detach_attr_path), "%s/%s",
- path, attr_detach);
- dbg("detach attribute path: %s", detach_attr_path);
-
- ret = write_sysfs_attribute(detach_attr_path, buff, strlen(buff));
- if (ret < 0) {
- dbg("write_sysfs_attribute failed");
- return -1;
- }
-
- dbg("detached port: %d", port);
-
- return 0;
-}
-
-int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev)
-{
- char product_name[100];
- char host[NI_MAXHOST] = "unknown host";
- char serv[NI_MAXSERV] = "unknown port";
- char remote_busid[SYSFS_BUS_ID_SIZE];
- int ret;
- int read_record_error = 0;
-
- if (idev->status == VDEV_ST_NULL || idev->status == VDEV_ST_NOTASSIGNED)
- return 0;
-
- ret = read_record(idev->port, host, sizeof(host), serv, sizeof(serv),
- remote_busid);
- if (ret) {
- err("read_record");
- read_record_error = 1;
- }
-
- printf("Port %02d: <%s> at %s\n", idev->port,
- usbip_status_string(idev->status),
- usbip_speed_string(idev->udev.speed));
-
- usbip_names_get_product(product_name, sizeof(product_name),
- idev->udev.idVendor, idev->udev.idProduct);
-
- printf(" %s\n", product_name);
-
- if (!read_record_error) {
- printf("%10s -> usbip://%s:%s/%s\n", idev->udev.busid,
- host, serv, remote_busid);
- printf("%10s -> remote bus/dev %03d/%03d\n", " ",
- idev->busnum, idev->devnum);
- } else {
- printf("%10s -> unknown host, remote port and remote busid\n",
- idev->udev.busid);
- printf("%10s -> remote bus/dev %03d/%03d\n", " ",
- idev->busnum, idev->devnum);
- }
-
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) 2005-2007 Takahiro Hirofuchi
- */
-
-#ifndef __VHCI_DRIVER_H
-#define __VHCI_DRIVER_H
-
-#include <libudev.h>
-#include <stdint.h>
-
-#include "usbip_common.h"
-
-#define USBIP_VHCI_BUS_TYPE "platform"
-#define MAXNPORT 128
-
-struct usbip_imported_device {
- uint8_t port;
- uint32_t status;
-
- uint32_t devid;
-
- uint8_t busnum;
- uint8_t devnum;
-
- /* usbip_class_device list */
- struct usbip_usb_device udev;
-};
-
-struct usbip_vhci_driver {
-
- /* /sys/devices/platform/vhci_hcd */
- struct udev_device *hc_device;
-
- int nports;
- struct usbip_imported_device idev[MAXNPORT];
-};
-
-
-extern struct usbip_vhci_driver *vhci_driver;
-
-int usbip_vhci_driver_open(void);
-void usbip_vhci_driver_close(void);
-
-int usbip_vhci_refresh_device_list(void);
-
-
-int usbip_vhci_get_free_port(void);
-int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
- uint32_t speed);
-
-/* will be removed */
-int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum,
- uint8_t devnum, uint32_t speed);
-
-int usbip_vhci_detach_device(uint8_t port);
-
-int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev);
-
-#endif /* __VHCI_DRIVER_H */
+++ /dev/null
-AM_CPPFLAGS = -I$(top_srcdir)/libsrc -DUSBIDS_FILE='"@USBIDS_DIR@/usb.ids"'
-AM_CFLAGS = @EXTRA_CFLAGS@
-LDADD = $(top_builddir)/libsrc/libusbip.la
-
-sbin_PROGRAMS := usbip usbipd
-
-usbip_SOURCES := usbip.h utils.h usbip.c utils.c usbip_network.c \
- usbip_attach.c usbip_detach.c usbip_list.c \
- usbip_bind.c usbip_unbind.c usbip_port.c
-
-usbipd_SOURCES := usbip_network.h usbipd.c usbip_network.c
+++ /dev/null
-/*
- * command structure borrowed from udev
- * (git://git.kernel.org/pub/scm/linux/hotplug/udev.git)
- *
- * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
- * 2005-2007 Takahiro Hirofuchi
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <getopt.h>
-#include <syslog.h>
-
-#include "usbip_common.h"
-#include "usbip_network.h"
-#include "usbip.h"
-
-static int usbip_help(int argc, char *argv[]);
-static int usbip_version(int argc, char *argv[]);
-
-static const char usbip_version_string[] = PACKAGE_STRING;
-
-static const char usbip_usage_string[] =
- "usbip [--debug] [--log] [--tcp-port PORT] [version]\n"
- " [help] <command> <args>\n";
-
-static void usbip_usage(void)
-{
- printf("usage: %s", usbip_usage_string);
-}
-
-struct command {
- const char *name;
- int (*fn)(int argc, char *argv[]);
- const char *help;
- void (*usage)(void);
-};
-
-static const struct command cmds[] = {
- {
- .name = "help",
- .fn = usbip_help,
- .help = NULL,
- .usage = NULL
- },
- {
- .name = "version",
- .fn = usbip_version,
- .help = NULL,
- .usage = NULL
- },
- {
- .name = "attach",
- .fn = usbip_attach,
- .help = "Attach a remote USB device",
- .usage = usbip_attach_usage
- },
- {
- .name = "detach",
- .fn = usbip_detach,
- .help = "Detach a remote USB device",
- .usage = usbip_detach_usage
- },
- {
- .name = "list",
- .fn = usbip_list,
- .help = "List exportable or local USB devices",
- .usage = usbip_list_usage
- },
- {
- .name = "bind",
- .fn = usbip_bind,
- .help = "Bind device to " USBIP_HOST_DRV_NAME ".ko",
- .usage = usbip_bind_usage
- },
- {
- .name = "unbind",
- .fn = usbip_unbind,
- .help = "Unbind device from " USBIP_HOST_DRV_NAME ".ko",
- .usage = usbip_unbind_usage
- },
- {
- .name = "port",
- .fn = usbip_port_show,
- .help = "Show imported USB devices",
- .usage = NULL
- },
- { NULL, NULL, NULL, NULL }
-};
-
-static int usbip_help(int argc, char *argv[])
-{
- const struct command *cmd;
- int i;
- int ret = 0;
-
- if (argc > 1 && argv++) {
- for (i = 0; cmds[i].name != NULL; i++)
- if (!strcmp(cmds[i].name, argv[0]) && cmds[i].usage) {
- cmds[i].usage();
- goto done;
- }
- ret = -1;
- }
-
- usbip_usage();
- printf("\n");
- for (cmd = cmds; cmd->name != NULL; cmd++)
- if (cmd->help != NULL)
- printf(" %-10s %s\n", cmd->name, cmd->help);
- printf("\n");
-done:
- return ret;
-}
-
-static int usbip_version(int argc, char *argv[])
-{
- (void) argc;
- (void) argv;
-
- printf(PROGNAME " (%s)\n", usbip_version_string);
- return 0;
-}
-
-static int run_command(const struct command *cmd, int argc, char *argv[])
-{
- dbg("running command: `%s'", cmd->name);
- return cmd->fn(argc, argv);
-}
-
-int main(int argc, char *argv[])
-{
- static const struct option opts[] = {
- { "debug", no_argument, NULL, 'd' },
- { "log", no_argument, NULL, 'l' },
- { "tcp-port", required_argument, NULL, 't' },
- { NULL, 0, NULL, 0 }
- };
-
- char *cmd;
- int opt;
- int i, rc = -1;
-
- usbip_use_stderr = 1;
- opterr = 0;
- for (;;) {
- opt = getopt_long(argc, argv, "+dlt:", opts, NULL);
-
- if (opt == -1)
- break;
-
- switch (opt) {
- case 'd':
- usbip_use_debug = 1;
- break;
- case 'l':
- usbip_use_syslog = 1;
- openlog("", LOG_PID, LOG_USER);
- break;
- case 't':
- usbip_setup_port_number(optarg);
- break;
- case '?':
- printf("usbip: invalid option\n");
- default:
- usbip_usage();
- goto out;
- }
- }
-
- cmd = argv[optind];
- if (cmd) {
- for (i = 0; cmds[i].name != NULL; i++)
- if (!strcmp(cmds[i].name, cmd)) {
- argc -= optind;
- argv += optind;
- optind = 0;
- rc = run_command(&cmds[i], argc, argv);
- goto out;
- }
- }
-
- /* invalid command */
- usbip_help(0, NULL);
-out:
- return (rc > -1 ? EXIT_SUCCESS : EXIT_FAILURE);
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
- * 2005-2007 Takahiro Hirofuchi
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef __USBIP_H
-#define __USBIP_H
-
-#ifdef HAVE_CONFIG_H
-#include "../config.h"
-#endif
-
-/* usbip commands */
-int usbip_attach(int argc, char *argv[]);
-int usbip_detach(int argc, char *argv[]);
-int usbip_list(int argc, char *argv[]);
-int usbip_bind(int argc, char *argv[]);
-int usbip_unbind(int argc, char *argv[]);
-int usbip_port_show(int argc, char *argv[]);
-
-void usbip_attach_usage(void);
-void usbip_detach_usage(void);
-void usbip_list_usage(void);
-void usbip_bind_usage(void);
-void usbip_unbind_usage(void);
-
-#endif /* __USBIP_H */
+++ /dev/null
-/*
- * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
- * 2005-2007 Takahiro Hirofuchi
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-#include <sys/stat.h>
-
-#include <limits.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <fcntl.h>
-#include <getopt.h>
-#include <unistd.h>
-#include <errno.h>
-
-#include "vhci_driver.h"
-#include "usbip_common.h"
-#include "usbip_network.h"
-#include "usbip.h"
-
-static const char usbip_attach_usage_string[] =
- "usbip attach <args>\n"
- " -r, --remote=<host> The machine with exported USB devices\n"
- " -b, --busid=<busid> Busid of the device on <host>\n";
-
-void usbip_attach_usage(void)
-{
- printf("usage: %s", usbip_attach_usage_string);
-}
-
-#define MAX_BUFF 100
-static int record_connection(char *host, char *port, char *busid, int rhport)
-{
- int fd;
- char path[PATH_MAX+1];
- char buff[MAX_BUFF+1];
- int ret;
-
- ret = mkdir(VHCI_STATE_PATH, 0700);
- if (ret < 0) {
- /* if VHCI_STATE_PATH exists, then it better be a directory */
- if (errno == EEXIST) {
- struct stat s;
-
- ret = stat(VHCI_STATE_PATH, &s);
- if (ret < 0)
- return -1;
- if (!(s.st_mode & S_IFDIR))
- return -1;
- } else
- return -1;
- }
-
- snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport);
-
- fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU);
- if (fd < 0)
- return -1;
-
- snprintf(buff, MAX_BUFF, "%s %s %s\n",
- host, port, busid);
-
- ret = write(fd, buff, strlen(buff));
- if (ret != (ssize_t) strlen(buff)) {
- close(fd);
- return -1;
- }
-
- close(fd);
-
- return 0;
-}
-
-static int import_device(int sockfd, struct usbip_usb_device *udev)
-{
- int rc;
- int port;
-
- rc = usbip_vhci_driver_open();
- if (rc < 0) {
- err("open vhci_driver");
- return -1;
- }
-
- port = usbip_vhci_get_free_port();
- if (port < 0) {
- err("no free port");
- usbip_vhci_driver_close();
- return -1;
- }
-
- rc = usbip_vhci_attach_device(port, sockfd, udev->busnum,
- udev->devnum, udev->speed);
- if (rc < 0) {
- err("import device");
- usbip_vhci_driver_close();
- return -1;
- }
-
- usbip_vhci_driver_close();
-
- return port;
-}
-
-static int query_import_device(int sockfd, char *busid)
-{
- int rc;
- struct op_import_request request;
- struct op_import_reply reply;
- uint16_t code = OP_REP_IMPORT;
-
- memset(&request, 0, sizeof(request));
- memset(&reply, 0, sizeof(reply));
-
- /* send a request */
- rc = usbip_net_send_op_common(sockfd, OP_REQ_IMPORT, 0);
- if (rc < 0) {
- err("send op_common");
- return -1;
- }
-
- strncpy(request.busid, busid, SYSFS_BUS_ID_SIZE-1);
-
- PACK_OP_IMPORT_REQUEST(0, &request);
-
- rc = usbip_net_send(sockfd, (void *) &request, sizeof(request));
- if (rc < 0) {
- err("send op_import_request");
- return -1;
- }
-
- /* receive a reply */
- rc = usbip_net_recv_op_common(sockfd, &code);
- if (rc < 0) {
- err("recv op_common");
- return -1;
- }
-
- rc = usbip_net_recv(sockfd, (void *) &reply, sizeof(reply));
- if (rc < 0) {
- err("recv op_import_reply");
- return -1;
- }
-
- PACK_OP_IMPORT_REPLY(0, &reply);
-
- /* check the reply */
- if (strncmp(reply.udev.busid, busid, SYSFS_BUS_ID_SIZE)) {
- err("recv different busid %s", reply.udev.busid);
- return -1;
- }
-
- /* import a device */
- return import_device(sockfd, &reply.udev);
-}
-
-static int attach_device(char *host, char *busid)
-{
- int sockfd;
- int rc;
- int rhport;
-
- sockfd = usbip_net_tcp_connect(host, usbip_port_string);
- if (sockfd < 0) {
- err("tcp connect");
- return -1;
- }
-
- rhport = query_import_device(sockfd, busid);
- if (rhport < 0) {
- err("query");
- return -1;
- }
-
- close(sockfd);
-
- rc = record_connection(host, usbip_port_string, busid, rhport);
- if (rc < 0) {
- err("record connection");
- return -1;
- }
-
- return 0;
-}
-
-int usbip_attach(int argc, char *argv[])
-{
- static const struct option opts[] = {
- { "remote", required_argument, NULL, 'r' },
- { "busid", required_argument, NULL, 'b' },
- { NULL, 0, NULL, 0 }
- };
- char *host = NULL;
- char *busid = NULL;
- int opt;
- int ret = -1;
-
- for (;;) {
- opt = getopt_long(argc, argv, "r:b:", opts, NULL);
-
- if (opt == -1)
- break;
-
- switch (opt) {
- case 'r':
- host = optarg;
- break;
- case 'b':
- busid = optarg;
- break;
- default:
- goto err_out;
- }
- }
-
- if (!host || !busid)
- goto err_out;
-
- ret = attach_device(host, busid);
- goto out;
-
-err_out:
- usbip_attach_usage();
-out:
- return ret;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
- * 2005-2007 Takahiro Hirofuchi
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-#include <libudev.h>
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <getopt.h>
-
-#include "usbip_common.h"
-#include "utils.h"
-#include "usbip.h"
-#include "sysfs_utils.h"
-
-enum unbind_status {
- UNBIND_ST_OK,
- UNBIND_ST_USBIP_HOST,
- UNBIND_ST_FAILED
-};
-
-static const char usbip_bind_usage_string[] =
- "usbip bind <args>\n"
- " -b, --busid=<busid> Bind " USBIP_HOST_DRV_NAME ".ko to device "
- "on <busid>\n";
-
-void usbip_bind_usage(void)
-{
- printf("usage: %s", usbip_bind_usage_string);
-}
-
-/* call at unbound state */
-static int bind_usbip(char *busid)
-{
- char attr_name[] = "bind";
- char bind_attr_path[SYSFS_PATH_MAX];
- int rc = -1;
-
- snprintf(bind_attr_path, sizeof(bind_attr_path), "%s/%s/%s/%s/%s/%s",
- SYSFS_MNT_PATH, SYSFS_BUS_NAME, SYSFS_BUS_TYPE,
- SYSFS_DRIVERS_NAME, USBIP_HOST_DRV_NAME, attr_name);
-
- rc = write_sysfs_attribute(bind_attr_path, busid, strlen(busid));
- if (rc < 0) {
- err("error binding device %s to driver: %s", busid,
- strerror(errno));
- return -1;
- }
-
- return 0;
-}
-
-/* buggy driver may cause dead lock */
-static int unbind_other(char *busid)
-{
- enum unbind_status status = UNBIND_ST_OK;
-
- char attr_name[] = "unbind";
- char unbind_attr_path[SYSFS_PATH_MAX];
- int rc = -1;
-
- struct udev *udev;
- struct udev_device *dev;
- const char *driver;
- const char *bDevClass;
-
- /* Create libudev context. */
- udev = udev_new();
-
- /* Get the device. */
- dev = udev_device_new_from_subsystem_sysname(udev, "usb", busid);
- if (!dev) {
- dbg("unable to find device with bus ID %s", busid);
- goto err_close_busid_dev;
- }
-
- /* Check what kind of device it is. */
- bDevClass = udev_device_get_sysattr_value(dev, "bDeviceClass");
- if (!bDevClass) {
- dbg("unable to get bDevClass device attribute");
- goto err_close_busid_dev;
- }
-
- if (!strncmp(bDevClass, "09", strlen(bDevClass))) {
- dbg("skip unbinding of hub");
- goto err_close_busid_dev;
- }
-
- /* Get the device driver. */
- driver = udev_device_get_driver(dev);
- if (!driver) {
- /* No driver bound to this device. */
- goto out;
- }
-
- if (!strncmp(USBIP_HOST_DRV_NAME, driver,
- strlen(USBIP_HOST_DRV_NAME))) {
- /* Already bound to usbip-host. */
- status = UNBIND_ST_USBIP_HOST;
- goto out;
- }
-
- /* Unbind device from driver. */
- snprintf(unbind_attr_path, sizeof(unbind_attr_path), "%s/%s/%s/%s/%s/%s",
- SYSFS_MNT_PATH, SYSFS_BUS_NAME, SYSFS_BUS_TYPE,
- SYSFS_DRIVERS_NAME, driver, attr_name);
-
- rc = write_sysfs_attribute(unbind_attr_path, busid, strlen(busid));
- if (rc < 0) {
- err("error unbinding device %s from driver", busid);
- goto err_close_busid_dev;
- }
-
- goto out;
-
-err_close_busid_dev:
- status = UNBIND_ST_FAILED;
-out:
- udev_device_unref(dev);
- udev_unref(udev);
-
- return status;
-}
-
-static int bind_device(char *busid)
-{
- int rc;
- struct udev *udev;
- struct udev_device *dev;
-
- /* Check whether the device with this bus ID exists. */
- udev = udev_new();
- dev = udev_device_new_from_subsystem_sysname(udev, "usb", busid);
- if (!dev) {
- err("device with the specified bus ID does not exist");
- return -1;
- }
- udev_unref(udev);
-
- rc = unbind_other(busid);
- if (rc == UNBIND_ST_FAILED) {
- err("could not unbind driver from device on busid %s", busid);
- return -1;
- } else if (rc == UNBIND_ST_USBIP_HOST) {
- err("device on busid %s is already bound to %s", busid,
- USBIP_HOST_DRV_NAME);
- return -1;
- }
-
- rc = modify_match_busid(busid, 1);
- if (rc < 0) {
- err("unable to bind device on %s", busid);
- return -1;
- }
-
- rc = bind_usbip(busid);
- if (rc < 0) {
- err("could not bind device to %s", USBIP_HOST_DRV_NAME);
- modify_match_busid(busid, 0);
- return -1;
- }
-
- info("bind device on busid %s: complete", busid);
-
- return 0;
-}
-
-int usbip_bind(int argc, char *argv[])
-{
- static const struct option opts[] = {
- { "busid", required_argument, NULL, 'b' },
- { NULL, 0, NULL, 0 }
- };
-
- int opt;
- int ret = -1;
-
- for (;;) {
- opt = getopt_long(argc, argv, "b:", opts, NULL);
-
- if (opt == -1)
- break;
-
- switch (opt) {
- case 'b':
- ret = bind_device(optarg);
- goto out;
- default:
- goto err_out;
- }
- }
-
-err_out:
- usbip_bind_usage();
-out:
- return ret;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
- * 2005-2007 Takahiro Hirofuchi
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-#include <ctype.h>
-#include <limits.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <getopt.h>
-#include <unistd.h>
-
-#include "vhci_driver.h"
-#include "usbip_common.h"
-#include "usbip_network.h"
-#include "usbip.h"
-
-static const char usbip_detach_usage_string[] =
- "usbip detach <args>\n"
- " -p, --port=<port> " USBIP_VHCI_DRV_NAME
- " port the device is on\n";
-
-void usbip_detach_usage(void)
-{
- printf("usage: %s", usbip_detach_usage_string);
-}
-
-static int detach_port(char *port)
-{
- int ret;
- uint8_t portnum;
- char path[PATH_MAX+1];
-
- for (unsigned int i = 0; i < strlen(port); i++)
- if (!isdigit(port[i])) {
- err("invalid port %s", port);
- return -1;
- }
-
- /* check max port */
-
- portnum = atoi(port);
-
- /* remove the port state file */
-
- snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", portnum);
-
- remove(path);
- rmdir(VHCI_STATE_PATH);
-
- ret = usbip_vhci_driver_open();
- if (ret < 0) {
- err("open vhci_driver");
- return -1;
- }
-
- ret = usbip_vhci_detach_device(portnum);
- if (ret < 0)
- return -1;
-
- usbip_vhci_driver_close();
-
- return ret;
-}
-
-int usbip_detach(int argc, char *argv[])
-{
- static const struct option opts[] = {
- { "port", required_argument, NULL, 'p' },
- { NULL, 0, NULL, 0 }
- };
- int opt;
- int ret = -1;
-
- for (;;) {
- opt = getopt_long(argc, argv, "p:", opts, NULL);
-
- if (opt == -1)
- break;
-
- switch (opt) {
- case 'p':
- ret = detach_port(optarg);
- goto out;
- default:
- goto err_out;
- }
- }
-
-err_out:
- usbip_detach_usage();
-out:
- return ret;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
- * 2005-2007 Takahiro Hirofuchi
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-#include <sys/types.h>
-#include <libudev.h>
-
-#include <errno.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <getopt.h>
-#include <netdb.h>
-#include <unistd.h>
-
-#include "usbip_common.h"
-#include "usbip_network.h"
-#include "usbip.h"
-
-static const char usbip_list_usage_string[] =
- "usbip list [-p|--parsable] <args>\n"
- " -p, --parsable Parsable list format\n"
- " -r, --remote=<host> List the exportable USB devices on <host>\n"
- " -l, --local List the local USB devices\n";
-
-void usbip_list_usage(void)
-{
- printf("usage: %s", usbip_list_usage_string);
-}
-
-static int get_exported_devices(char *host, int sockfd)
-{
- char product_name[100];
- char class_name[100];
- struct op_devlist_reply reply;
- uint16_t code = OP_REP_DEVLIST;
- struct usbip_usb_device udev;
- struct usbip_usb_interface uintf;
- unsigned int i;
- int rc, j;
-
- rc = usbip_net_send_op_common(sockfd, OP_REQ_DEVLIST, 0);
- if (rc < 0) {
- dbg("usbip_net_send_op_common failed");
- return -1;
- }
-
- rc = usbip_net_recv_op_common(sockfd, &code);
- if (rc < 0) {
- dbg("usbip_net_recv_op_common failed");
- return -1;
- }
-
- memset(&reply, 0, sizeof(reply));
- rc = usbip_net_recv(sockfd, &reply, sizeof(reply));
- if (rc < 0) {
- dbg("usbip_net_recv_op_devlist failed");
- return -1;
- }
- PACK_OP_DEVLIST_REPLY(0, &reply);
- dbg("exportable devices: %d\n", reply.ndev);
-
- if (reply.ndev == 0) {
- info("no exportable devices found on %s", host);
- return 0;
- }
-
- printf("Exportable USB devices\n");
- printf("======================\n");
- printf(" - %s\n", host);
-
- for (i = 0; i < reply.ndev; i++) {
- memset(&udev, 0, sizeof(udev));
- rc = usbip_net_recv(sockfd, &udev, sizeof(udev));
- if (rc < 0) {
- dbg("usbip_net_recv failed: usbip_usb_device[%d]", i);
- return -1;
- }
- usbip_net_pack_usb_device(0, &udev);
-
- usbip_names_get_product(product_name, sizeof(product_name),
- udev.idVendor, udev.idProduct);
- usbip_names_get_class(class_name, sizeof(class_name),
- udev.bDeviceClass, udev.bDeviceSubClass,
- udev.bDeviceProtocol);
- printf("%11s: %s\n", udev.busid, product_name);
- printf("%11s: %s\n", "", udev.path);
- printf("%11s: %s\n", "", class_name);
-
- for (j = 0; j < udev.bNumInterfaces; j++) {
- rc = usbip_net_recv(sockfd, &uintf, sizeof(uintf));
- if (rc < 0) {
- err("usbip_net_recv failed: usbip_usb_intf[%d]",
- j);
-
- return -1;
- }
- usbip_net_pack_usb_interface(0, &uintf);
-
- usbip_names_get_class(class_name, sizeof(class_name),
- uintf.bInterfaceClass,
- uintf.bInterfaceSubClass,
- uintf.bInterfaceProtocol);
- printf("%11s: %2d - %s\n", "", j, class_name);
- }
-
- printf("\n");
- }
-
- return 0;
-}
-
-static int list_exported_devices(char *host)
-{
- int rc;
- int sockfd;
-
- sockfd = usbip_net_tcp_connect(host, usbip_port_string);
- if (sockfd < 0) {
- err("could not connect to %s:%s: %s", host,
- usbip_port_string, gai_strerror(sockfd));
- return -1;
- }
- dbg("connected to %s:%s", host, usbip_port_string);
-
- rc = get_exported_devices(host, sockfd);
- if (rc < 0) {
- err("failed to get device list from %s", host);
- return -1;
- }
-
- close(sockfd);
-
- return 0;
-}
-
-static void print_device(const char *busid, const char *vendor,
- const char *product, bool parsable)
-{
- if (parsable)
- printf("busid=%s#usbid=%.4s:%.4s#", busid, vendor, product);
- else
- printf(" - busid %s (%.4s:%.4s)\n", busid, vendor, product);
-}
-
-static void print_product_name(char *product_name, bool parsable)
-{
- if (!parsable)
- printf(" %s\n", product_name);
-}
-
-static int list_devices(bool parsable)
-{
- struct udev *udev;
- struct udev_enumerate *enumerate;
- struct udev_list_entry *devices, *dev_list_entry;
- struct udev_device *dev;
- const char *path;
- const char *idVendor;
- const char *idProduct;
- const char *bConfValue;
- const char *bNumIntfs;
- const char *busid;
- char product_name[128];
- int ret = -1;
-
- /* Create libudev context. */
- udev = udev_new();
-
- /* Create libudev device enumeration. */
- enumerate = udev_enumerate_new(udev);
-
- /* Take only USB devices that are not hubs and do not have
- * the bInterfaceNumber attribute, i.e. are not interfaces.
- */
- udev_enumerate_add_match_subsystem(enumerate, "usb");
- udev_enumerate_add_nomatch_sysattr(enumerate, "bDeviceClass", "09");
- udev_enumerate_add_nomatch_sysattr(enumerate, "bInterfaceNumber", NULL);
- udev_enumerate_scan_devices(enumerate);
-
- devices = udev_enumerate_get_list_entry(enumerate);
-
- /* Show information about each device. */
- udev_list_entry_foreach(dev_list_entry, devices) {
- path = udev_list_entry_get_name(dev_list_entry);
- dev = udev_device_new_from_syspath(udev, path);
-
- /* Get device information. */
- idVendor = udev_device_get_sysattr_value(dev, "idVendor");
- idProduct = udev_device_get_sysattr_value(dev, "idProduct");
- bConfValue = udev_device_get_sysattr_value(dev, "bConfigurationValue");
- bNumIntfs = udev_device_get_sysattr_value(dev, "bNumInterfaces");
- busid = udev_device_get_sysname(dev);
- if (!idVendor || !idProduct || !bConfValue || !bNumIntfs) {
- err("problem getting device attributes: %s",
- strerror(errno));
- goto err_out;
- }
-
- /* Get product name. */
- usbip_names_get_product(product_name, sizeof(product_name),
- strtol(idVendor, NULL, 16),
- strtol(idProduct, NULL, 16));
-
- /* Print information. */
- print_device(busid, idVendor, idProduct, parsable);
- print_product_name(product_name, parsable);
-
- printf("\n");
-
- udev_device_unref(dev);
- }
-
- ret = 0;
-
-err_out:
- udev_enumerate_unref(enumerate);
- udev_unref(udev);
-
- return ret;
-}
-
-int usbip_list(int argc, char *argv[])
-{
- static const struct option opts[] = {
- { "parsable", no_argument, NULL, 'p' },
- { "remote", required_argument, NULL, 'r' },
- { "local", no_argument, NULL, 'l' },
- { NULL, 0, NULL, 0 }
- };
-
- bool parsable = false;
- int opt;
- int ret = -1;
-
- if (usbip_names_init(USBIDS_FILE))
- err("failed to open %s", USBIDS_FILE);
-
- for (;;) {
- opt = getopt_long(argc, argv, "pr:l", opts, NULL);
-
- if (opt == -1)
- break;
-
- switch (opt) {
- case 'p':
- parsable = true;
- break;
- case 'r':
- ret = list_exported_devices(optarg);
- goto out;
- case 'l':
- ret = list_devices(parsable);
- goto out;
- default:
- goto err_out;
- }
- }
-
-err_out:
- usbip_list_usage();
-out:
- usbip_names_free();
-
- return ret;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
- * 2005-2007 Takahiro Hirofuchi
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-#include <sys/socket.h>
-
-#include <string.h>
-
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <netinet/tcp.h>
-#include <unistd.h>
-
-#ifdef HAVE_LIBWRAP
-#include <tcpd.h>
-#endif
-
-#include "usbip_common.h"
-#include "usbip_network.h"
-
-int usbip_port = 3240;
-char *usbip_port_string = "3240";
-
-void usbip_setup_port_number(char *arg)
-{
- dbg("parsing port arg '%s'", arg);
- char *end;
- unsigned long int port = strtoul(arg, &end, 10);
-
- if (end == arg) {
- err("port: could not parse '%s' as a decimal integer", arg);
- return;
- }
-
- if (*end != '\0') {
- err("port: garbage at end of '%s'", arg);
- return;
- }
-
- if (port > UINT16_MAX) {
- err("port: %s too high (max=%d)",
- arg, UINT16_MAX);
- return;
- }
-
- usbip_port = port;
- usbip_port_string = arg;
- info("using port %d (\"%s\")", usbip_port, usbip_port_string);
-}
-
-void usbip_net_pack_uint32_t(int pack, uint32_t *num)
-{
- uint32_t i;
-
- if (pack)
- i = htonl(*num);
- else
- i = ntohl(*num);
-
- *num = i;
-}
-
-void usbip_net_pack_uint16_t(int pack, uint16_t *num)
-{
- uint16_t i;
-
- if (pack)
- i = htons(*num);
- else
- i = ntohs(*num);
-
- *num = i;
-}
-
-void usbip_net_pack_usb_device(int pack, struct usbip_usb_device *udev)
-{
- usbip_net_pack_uint32_t(pack, &udev->busnum);
- usbip_net_pack_uint32_t(pack, &udev->devnum);
- usbip_net_pack_uint32_t(pack, &udev->speed);
-
- usbip_net_pack_uint16_t(pack, &udev->idVendor);
- usbip_net_pack_uint16_t(pack, &udev->idProduct);
- usbip_net_pack_uint16_t(pack, &udev->bcdDevice);
-}
-
-void usbip_net_pack_usb_interface(int pack __attribute__((unused)),
- struct usbip_usb_interface *udev
- __attribute__((unused)))
-{
- /* uint8_t members need nothing */
-}
-
-static ssize_t usbip_net_xmit(int sockfd, void *buff, size_t bufflen,
- int sending)
-{
- ssize_t nbytes;
- ssize_t total = 0;
-
- if (!bufflen)
- return 0;
-
- do {
- if (sending)
- nbytes = send(sockfd, buff, bufflen, 0);
- else
- nbytes = recv(sockfd, buff, bufflen, MSG_WAITALL);
-
- if (nbytes <= 0)
- return -1;
-
- buff = (void *)((intptr_t) buff + nbytes);
- bufflen -= nbytes;
- total += nbytes;
-
- } while (bufflen > 0);
-
- return total;
-}
-
-ssize_t usbip_net_recv(int sockfd, void *buff, size_t bufflen)
-{
- return usbip_net_xmit(sockfd, buff, bufflen, 0);
-}
-
-ssize_t usbip_net_send(int sockfd, void *buff, size_t bufflen)
-{
- return usbip_net_xmit(sockfd, buff, bufflen, 1);
-}
-
-int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status)
-{
- struct op_common op_common;
- int rc;
-
- memset(&op_common, 0, sizeof(op_common));
-
- op_common.version = USBIP_VERSION;
- op_common.code = code;
- op_common.status = status;
-
- PACK_OP_COMMON(1, &op_common);
-
- rc = usbip_net_send(sockfd, &op_common, sizeof(op_common));
- if (rc < 0) {
- dbg("usbip_net_send failed: %d", rc);
- return -1;
- }
-
- return 0;
-}
-
-int usbip_net_recv_op_common(int sockfd, uint16_t *code)
-{
- struct op_common op_common;
- int rc;
-
- memset(&op_common, 0, sizeof(op_common));
-
- rc = usbip_net_recv(sockfd, &op_common, sizeof(op_common));
- if (rc < 0) {
- dbg("usbip_net_recv failed: %d", rc);
- goto err;
- }
-
- PACK_OP_COMMON(0, &op_common);
-
- if (op_common.version != USBIP_VERSION) {
- dbg("version mismatch: %d %d", op_common.version,
- USBIP_VERSION);
- goto err;
- }
-
- switch (*code) {
- case OP_UNSPEC:
- break;
- default:
- if (op_common.code != *code) {
- dbg("unexpected pdu %#0x for %#0x", op_common.code,
- *code);
- goto err;
- }
- }
-
- if (op_common.status != ST_OK) {
- dbg("request failed at peer: %d", op_common.status);
- goto err;
- }
-
- *code = op_common.code;
-
- return 0;
-err:
- return -1;
-}
-
-int usbip_net_set_reuseaddr(int sockfd)
-{
- const int val = 1;
- int ret;
-
- ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
- if (ret < 0)
- dbg("setsockopt: SO_REUSEADDR");
-
- return ret;
-}
-
-int usbip_net_set_nodelay(int sockfd)
-{
- const int val = 1;
- int ret;
-
- ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
- if (ret < 0)
- dbg("setsockopt: TCP_NODELAY");
-
- return ret;
-}
-
-int usbip_net_set_keepalive(int sockfd)
-{
- const int val = 1;
- int ret;
-
- ret = setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val));
- if (ret < 0)
- dbg("setsockopt: SO_KEEPALIVE");
-
- return ret;
-}
-
-int usbip_net_set_v6only(int sockfd)
-{
- const int val = 1;
- int ret;
-
- ret = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
- if (ret < 0)
- dbg("setsockopt: IPV6_V6ONLY");
-
- return ret;
-}
-
-/*
- * IPv6 Ready
- */
-int usbip_net_tcp_connect(char *hostname, char *service)
-{
- struct addrinfo hints, *res, *rp;
- int sockfd;
- int ret;
-
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
-
- /* get all possible addresses */
- ret = getaddrinfo(hostname, service, &hints, &res);
- if (ret < 0) {
- dbg("getaddrinfo: %s service %s: %s", hostname, service,
- gai_strerror(ret));
- return ret;
- }
-
- /* try the addresses */
- for (rp = res; rp; rp = rp->ai_next) {
- sockfd = socket(rp->ai_family, rp->ai_socktype,
- rp->ai_protocol);
- if (sockfd < 0)
- continue;
-
- /* should set TCP_NODELAY for usbip */
- usbip_net_set_nodelay(sockfd);
- /* TODO: write code for heartbeat */
- usbip_net_set_keepalive(sockfd);
-
- if (connect(sockfd, rp->ai_addr, rp->ai_addrlen) == 0)
- break;
-
- close(sockfd);
- }
-
- freeaddrinfo(res);
-
- if (!rp)
- return EAI_SYSTEM;
-
- return sockfd;
-}
+++ /dev/null
-/*
- * Copyright (C) 2005-2007 Takahiro Hirofuchi
- */
-
-#ifndef __USBIP_NETWORK_H
-#define __USBIP_NETWORK_H
-
-#ifdef HAVE_CONFIG_H
-#include "../config.h"
-#endif
-
-#include <sys/types.h>
-
-#include <stdint.h>
-
-extern int usbip_port;
-extern char *usbip_port_string;
-void usbip_setup_port_number(char *arg);
-
-/* ---------------------------------------------------------------------- */
-/* Common header for all the kinds of PDUs. */
-struct op_common {
- uint16_t version;
-
-#define OP_REQUEST (0x80 << 8)
-#define OP_REPLY (0x00 << 8)
- uint16_t code;
-
- /* add more error code */
-#define ST_OK 0x00
-#define ST_NA 0x01
- uint32_t status; /* op_code status (for reply) */
-
-} __attribute__((packed));
-
-#define PACK_OP_COMMON(pack, op_common) do {\
- usbip_net_pack_uint16_t(pack, &(op_common)->version);\
- usbip_net_pack_uint16_t(pack, &(op_common)->code);\
- usbip_net_pack_uint32_t(pack, &(op_common)->status);\
-} while (0)
-
-/* ---------------------------------------------------------------------- */
-/* Dummy Code */
-#define OP_UNSPEC 0x00
-#define OP_REQ_UNSPEC OP_UNSPEC
-#define OP_REP_UNSPEC OP_UNSPEC
-
-/* ---------------------------------------------------------------------- */
-/* Retrieve USB device information. (still not used) */
-#define OP_DEVINFO 0x02
-#define OP_REQ_DEVINFO (OP_REQUEST | OP_DEVINFO)
-#define OP_REP_DEVINFO (OP_REPLY | OP_DEVINFO)
-
-struct op_devinfo_request {
- char busid[SYSFS_BUS_ID_SIZE];
-} __attribute__((packed));
-
-struct op_devinfo_reply {
- struct usbip_usb_device udev;
- struct usbip_usb_interface uinf[];
-} __attribute__((packed));
-
-/* ---------------------------------------------------------------------- */
-/* Import a remote USB device. */
-#define OP_IMPORT 0x03
-#define OP_REQ_IMPORT (OP_REQUEST | OP_IMPORT)
-#define OP_REP_IMPORT (OP_REPLY | OP_IMPORT)
-
-struct op_import_request {
- char busid[SYSFS_BUS_ID_SIZE];
-} __attribute__((packed));
-
-struct op_import_reply {
- struct usbip_usb_device udev;
-// struct usbip_usb_interface uinf[];
-} __attribute__((packed));
-
-#define PACK_OP_IMPORT_REQUEST(pack, request) do {\
-} while (0)
-
-#define PACK_OP_IMPORT_REPLY(pack, reply) do {\
- usbip_net_pack_usb_device(pack, &(reply)->udev);\
-} while (0)
-
-/* ---------------------------------------------------------------------- */
-/* Export a USB device to a remote host. */
-#define OP_EXPORT 0x06
-#define OP_REQ_EXPORT (OP_REQUEST | OP_EXPORT)
-#define OP_REP_EXPORT (OP_REPLY | OP_EXPORT)
-
-struct op_export_request {
- struct usbip_usb_device udev;
-} __attribute__((packed));
-
-struct op_export_reply {
- int returncode;
-} __attribute__((packed));
-
-
-#define PACK_OP_EXPORT_REQUEST(pack, request) do {\
- usbip_net_pack_usb_device(pack, &(request)->udev);\
-} while (0)
-
-#define PACK_OP_EXPORT_REPLY(pack, reply) do {\
-} while (0)
-
-/* ---------------------------------------------------------------------- */
-/* un-Export a USB device from a remote host. */
-#define OP_UNEXPORT 0x07
-#define OP_REQ_UNEXPORT (OP_REQUEST | OP_UNEXPORT)
-#define OP_REP_UNEXPORT (OP_REPLY | OP_UNEXPORT)
-
-struct op_unexport_request {
- struct usbip_usb_device udev;
-} __attribute__((packed));
-
-struct op_unexport_reply {
- int returncode;
-} __attribute__((packed));
-
-#define PACK_OP_UNEXPORT_REQUEST(pack, request) do {\
- usbip_net_pack_usb_device(pack, &(request)->udev);\
-} while (0)
-
-#define PACK_OP_UNEXPORT_REPLY(pack, reply) do {\
-} while (0)
-
-/* ---------------------------------------------------------------------- */
-/* Negotiate IPSec encryption key. (still not used) */
-#define OP_CRYPKEY 0x04
-#define OP_REQ_CRYPKEY (OP_REQUEST | OP_CRYPKEY)
-#define OP_REP_CRYPKEY (OP_REPLY | OP_CRYPKEY)
-
-struct op_crypkey_request {
- /* 128bit key */
- uint32_t key[4];
-} __attribute__((packed));
-
-struct op_crypkey_reply {
- uint32_t __reserved;
-} __attribute__((packed));
-
-
-/* ---------------------------------------------------------------------- */
-/* Retrieve the list of exported USB devices. */
-#define OP_DEVLIST 0x05
-#define OP_REQ_DEVLIST (OP_REQUEST | OP_DEVLIST)
-#define OP_REP_DEVLIST (OP_REPLY | OP_DEVLIST)
-
-struct op_devlist_request {
-} __attribute__((packed));
-
-struct op_devlist_reply {
- uint32_t ndev;
- /* followed by reply_extra[] */
-} __attribute__((packed));
-
-struct op_devlist_reply_extra {
- struct usbip_usb_device udev;
- struct usbip_usb_interface uinf[];
-} __attribute__((packed));
-
-#define PACK_OP_DEVLIST_REQUEST(pack, request) do {\
-} while (0)
-
-#define PACK_OP_DEVLIST_REPLY(pack, reply) do {\
- usbip_net_pack_uint32_t(pack, &(reply)->ndev);\
-} while (0)
-
-void usbip_net_pack_uint32_t(int pack, uint32_t *num);
-void usbip_net_pack_uint16_t(int pack, uint16_t *num);
-void usbip_net_pack_usb_device(int pack, struct usbip_usb_device *udev);
-void usbip_net_pack_usb_interface(int pack, struct usbip_usb_interface *uinf);
-
-ssize_t usbip_net_recv(int sockfd, void *buff, size_t bufflen);
-ssize_t usbip_net_send(int sockfd, void *buff, size_t bufflen);
-int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status);
-int usbip_net_recv_op_common(int sockfd, uint16_t *code);
-int usbip_net_set_reuseaddr(int sockfd);
-int usbip_net_set_nodelay(int sockfd);
-int usbip_net_set_keepalive(int sockfd);
-int usbip_net_set_v6only(int sockfd);
-int usbip_net_tcp_connect(char *hostname, char *port);
-
-#endif /* __USBIP_NETWORK_H */
+++ /dev/null
-/*
- * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
- * 2005-2007 Takahiro Hirofuchi
- *
- * 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.
- */
-
-#include "vhci_driver.h"
-#include "usbip_common.h"
-
-static int list_imported_devices(void)
-{
- int i;
- struct usbip_imported_device *idev;
- int ret;
-
- ret = usbip_vhci_driver_open();
- if (ret < 0) {
- err("open vhci_driver");
- return -1;
- }
-
- printf("Imported USB devices\n");
- printf("====================\n");
-
- for (i = 0; i < vhci_driver->nports; i++) {
- idev = &vhci_driver->idev[i];
-
- if (usbip_vhci_imported_device_dump(idev) < 0)
- ret = -1;
- }
-
- usbip_vhci_driver_close();
-
- return ret;
-
-}
-
-int usbip_port_show(__attribute__((unused)) int argc,
- __attribute__((unused)) char *argv[])
-{
- int ret;
-
- ret = list_imported_devices();
- if (ret < 0)
- err("list imported devices");
-
- return ret;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
- * 2005-2007 Takahiro Hirofuchi
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-#include <libudev.h>
-
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <getopt.h>
-
-#include "usbip_common.h"
-#include "utils.h"
-#include "usbip.h"
-#include "sysfs_utils.h"
-
-static const char usbip_unbind_usage_string[] =
- "usbip unbind <args>\n"
- " -b, --busid=<busid> Unbind " USBIP_HOST_DRV_NAME ".ko from "
- "device on <busid>\n";
-
-void usbip_unbind_usage(void)
-{
- printf("usage: %s", usbip_unbind_usage_string);
-}
-
-static int unbind_device(char *busid)
-{
- char bus_type[] = "usb";
- int rc, ret = -1;
-
- char unbind_attr_name[] = "unbind";
- char unbind_attr_path[SYSFS_PATH_MAX];
- char rebind_attr_name[] = "rebind";
- char rebind_attr_path[SYSFS_PATH_MAX];
-
- struct udev *udev;
- struct udev_device *dev;
- const char *driver;
-
- /* Create libudev context. */
- udev = udev_new();
-
- /* Check whether the device with this bus ID exists. */
- dev = udev_device_new_from_subsystem_sysname(udev, "usb", busid);
- if (!dev) {
- err("device with the specified bus ID does not exist");
- goto err_close_udev;
- }
-
- /* Check whether the device is using usbip-host driver. */
- driver = udev_device_get_driver(dev);
- if (!driver || strcmp(driver, "usbip-host")) {
- err("device is not bound to usbip-host driver");
- goto err_close_udev;
- }
-
- /* Unbind device from driver. */
- snprintf(unbind_attr_path, sizeof(unbind_attr_path), "%s/%s/%s/%s/%s/%s",
- SYSFS_MNT_PATH, SYSFS_BUS_NAME, bus_type, SYSFS_DRIVERS_NAME,
- USBIP_HOST_DRV_NAME, unbind_attr_name);
-
- rc = write_sysfs_attribute(unbind_attr_path, busid, strlen(busid));
- if (rc < 0) {
- err("error unbinding device %s from driver", busid);
- goto err_close_udev;
- }
-
- /* Notify driver of unbind. */
- rc = modify_match_busid(busid, 0);
- if (rc < 0) {
- err("unable to unbind device on %s", busid);
- goto err_close_udev;
- }
-
- /* Trigger new probing. */
- snprintf(rebind_attr_path, sizeof(unbind_attr_path), "%s/%s/%s/%s/%s/%s",
- SYSFS_MNT_PATH, SYSFS_BUS_NAME, bus_type, SYSFS_DRIVERS_NAME,
- USBIP_HOST_DRV_NAME, rebind_attr_name);
-
- rc = write_sysfs_attribute(rebind_attr_path, busid, strlen(busid));
- if (rc < 0) {
- err("error rebinding");
- goto err_close_udev;
- }
-
- ret = 0;
- info("unbind device on busid %s: complete", busid);
-
-err_close_udev:
- udev_device_unref(dev);
- udev_unref(udev);
-
- return ret;
-}
-
-int usbip_unbind(int argc, char *argv[])
-{
- static const struct option opts[] = {
- { "busid", required_argument, NULL, 'b' },
- { NULL, 0, NULL, 0 }
- };
-
- int opt;
- int ret = -1;
-
- for (;;) {
- opt = getopt_long(argc, argv, "b:", opts, NULL);
-
- if (opt == -1)
- break;
-
- switch (opt) {
- case 'b':
- ret = unbind_device(optarg);
- goto out;
- default:
- goto err_out;
- }
- }
-
-err_out:
- usbip_unbind_usage();
-out:
- return ret;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
- * 2005-2007 Takahiro Hirofuchi
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-#ifdef HAVE_CONFIG_H
-#include "../config.h"
-#endif
-
-#define _GNU_SOURCE
-#include <errno.h>
-#include <unistd.h>
-#include <netdb.h>
-#include <string.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <arpa/inet.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-
-#ifdef HAVE_LIBWRAP
-#include <tcpd.h>
-#endif
-
-#include <getopt.h>
-#include <signal.h>
-#include <poll.h>
-
-#include "usbip_host_driver.h"
-#include "usbip_common.h"
-#include "usbip_network.h"
-#include "list.h"
-
-#undef PROGNAME
-#define PROGNAME "usbipd"
-#define MAXSOCKFD 20
-
-#define MAIN_LOOP_TIMEOUT 10
-
-#define DEFAULT_PID_FILE "/var/run/" PROGNAME ".pid"
-
-static const char usbip_version_string[] = PACKAGE_STRING;
-
-static const char usbipd_help_string[] =
- "usage: usbipd [options]\n"
- "\n"
- " -4, --ipv4\n"
- " Bind to IPv4. Default is both.\n"
- "\n"
- " -6, --ipv6\n"
- " Bind to IPv6. Default is both.\n"
- "\n"
- " -D, --daemon\n"
- " Run as a daemon process.\n"
- "\n"
- " -d, --debug\n"
- " Print debugging information.\n"
- "\n"
- " -PFILE, --pid FILE\n"
- " Write process id to FILE.\n"
- " If no FILE specified, use " DEFAULT_PID_FILE "\n"
- "\n"
- " -tPORT, --tcp-port PORT\n"
- " Listen on TCP/IP port PORT.\n"
- "\n"
- " -h, --help\n"
- " Print this help.\n"
- "\n"
- " -v, --version\n"
- " Show version.\n";
-
-static void usbipd_help(void)
-{
- printf("%s\n", usbipd_help_string);
-}
-
-static int recv_request_import(int sockfd)
-{
- struct op_import_request req;
- struct op_common reply;
- struct usbip_exported_device *edev;
- struct usbip_usb_device pdu_udev;
- struct list_head *i;
- int found = 0;
- int error = 0;
- int rc;
-
- memset(&req, 0, sizeof(req));
- memset(&reply, 0, sizeof(reply));
-
- rc = usbip_net_recv(sockfd, &req, sizeof(req));
- if (rc < 0) {
- dbg("usbip_net_recv failed: import request");
- return -1;
- }
- PACK_OP_IMPORT_REQUEST(0, &req);
-
- list_for_each(i, &host_driver->edev_list) {
- edev = list_entry(i, struct usbip_exported_device, node);
- if (!strncmp(req.busid, edev->udev.busid, SYSFS_BUS_ID_SIZE)) {
- info("found requested device: %s", req.busid);
- found = 1;
- break;
- }
- }
-
- if (found) {
- /* should set TCP_NODELAY for usbip */
- usbip_net_set_nodelay(sockfd);
-
- /* export device needs a TCP/IP socket descriptor */
- rc = usbip_host_export_device(edev, sockfd);
- if (rc < 0)
- error = 1;
- } else {
- info("requested device not found: %s", req.busid);
- error = 1;
- }
-
- rc = usbip_net_send_op_common(sockfd, OP_REP_IMPORT,
- (!error ? ST_OK : ST_NA));
- if (rc < 0) {
- dbg("usbip_net_send_op_common failed: %#0x", OP_REP_IMPORT);
- return -1;
- }
-
- if (error) {
- dbg("import request busid %s: failed", req.busid);
- return -1;
- }
-
- memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev));
- usbip_net_pack_usb_device(1, &pdu_udev);
-
- rc = usbip_net_send(sockfd, &pdu_udev, sizeof(pdu_udev));
- if (rc < 0) {
- dbg("usbip_net_send failed: devinfo");
- return -1;
- }
-
- dbg("import request busid %s: complete", req.busid);
-
- return 0;
-}
-
-static int send_reply_devlist(int connfd)
-{
- struct usbip_exported_device *edev;
- struct usbip_usb_device pdu_udev;
- struct usbip_usb_interface pdu_uinf;
- struct op_devlist_reply reply;
- struct list_head *j;
- int rc, i;
-
- reply.ndev = 0;
- /* number of exported devices */
- list_for_each(j, &host_driver->edev_list) {
- reply.ndev += 1;
- }
- info("exportable devices: %d", reply.ndev);
-
- rc = usbip_net_send_op_common(connfd, OP_REP_DEVLIST, ST_OK);
- if (rc < 0) {
- dbg("usbip_net_send_op_common failed: %#0x", OP_REP_DEVLIST);
- return -1;
- }
- PACK_OP_DEVLIST_REPLY(1, &reply);
-
- rc = usbip_net_send(connfd, &reply, sizeof(reply));
- if (rc < 0) {
- dbg("usbip_net_send failed: %#0x", OP_REP_DEVLIST);
- return -1;
- }
-
- list_for_each(j, &host_driver->edev_list) {
- edev = list_entry(j, struct usbip_exported_device, node);
- dump_usb_device(&edev->udev);
- memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev));
- usbip_net_pack_usb_device(1, &pdu_udev);
-
- rc = usbip_net_send(connfd, &pdu_udev, sizeof(pdu_udev));
- if (rc < 0) {
- dbg("usbip_net_send failed: pdu_udev");
- return -1;
- }
-
- for (i = 0; i < edev->udev.bNumInterfaces; i++) {
- dump_usb_interface(&edev->uinf[i]);
- memcpy(&pdu_uinf, &edev->uinf[i], sizeof(pdu_uinf));
- usbip_net_pack_usb_interface(1, &pdu_uinf);
-
- rc = usbip_net_send(connfd, &pdu_uinf,
- sizeof(pdu_uinf));
- if (rc < 0) {
- err("usbip_net_send failed: pdu_uinf");
- return -1;
- }
- }
- }
-
- return 0;
-}
-
-static int recv_request_devlist(int connfd)
-{
- struct op_devlist_request req;
- int rc;
-
- memset(&req, 0, sizeof(req));
-
- rc = usbip_net_recv(connfd, &req, sizeof(req));
- if (rc < 0) {
- dbg("usbip_net_recv failed: devlist request");
- return -1;
- }
-
- rc = send_reply_devlist(connfd);
- if (rc < 0) {
- dbg("send_reply_devlist failed");
- return -1;
- }
-
- return 0;
-}
-
-static int recv_pdu(int connfd)
-{
- uint16_t code = OP_UNSPEC;
- int ret;
-
- ret = usbip_net_recv_op_common(connfd, &code);
- if (ret < 0) {
- dbg("could not receive opcode: %#0x", code);
- return -1;
- }
-
- ret = usbip_host_refresh_device_list();
- if (ret < 0) {
- dbg("could not refresh device list: %d", ret);
- return -1;
- }
-
- info("received request: %#0x(%d)", code, connfd);
- switch (code) {
- case OP_REQ_DEVLIST:
- ret = recv_request_devlist(connfd);
- break;
- case OP_REQ_IMPORT:
- ret = recv_request_import(connfd);
- break;
- case OP_REQ_DEVINFO:
- case OP_REQ_CRYPKEY:
- default:
- err("received an unknown opcode: %#0x", code);
- ret = -1;
- }
-
- if (ret == 0)
- info("request %#0x(%d): complete", code, connfd);
- else
- info("request %#0x(%d): failed", code, connfd);
-
- return ret;
-}
-
-#ifdef HAVE_LIBWRAP
-static int tcpd_auth(int connfd)
-{
- struct request_info request;
- int rc;
-
- request_init(&request, RQ_DAEMON, PROGNAME, RQ_FILE, connfd, 0);
- fromhost(&request);
- rc = hosts_access(&request);
- if (rc == 0)
- return -1;
-
- return 0;
-}
-#endif
-
-static int do_accept(int listenfd)
-{
- int connfd;
- struct sockaddr_storage ss;
- socklen_t len = sizeof(ss);
- char host[NI_MAXHOST], port[NI_MAXSERV];
- int rc;
-
- memset(&ss, 0, sizeof(ss));
-
- connfd = accept(listenfd, (struct sockaddr *)&ss, &len);
- if (connfd < 0) {
- err("failed to accept connection");
- return -1;
- }
-
- rc = getnameinfo((struct sockaddr *)&ss, len, host, sizeof(host),
- port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV);
- if (rc)
- err("getnameinfo: %s", gai_strerror(rc));
-
-#ifdef HAVE_LIBWRAP
- rc = tcpd_auth(connfd);
- if (rc < 0) {
- info("denied access from %s", host);
- close(connfd);
- return -1;
- }
-#endif
- info("connection from %s:%s", host, port);
-
- return connfd;
-}
-
-int process_request(int listenfd)
-{
- pid_t childpid;
- int connfd;
-
- connfd = do_accept(listenfd);
- if (connfd < 0)
- return -1;
- childpid = fork();
- if (childpid == 0) {
- close(listenfd);
- recv_pdu(connfd);
- exit(0);
- }
- close(connfd);
- return 0;
-}
-
-static void addrinfo_to_text(struct addrinfo *ai, char buf[],
- const size_t buf_size)
-{
- char hbuf[NI_MAXHOST];
- char sbuf[NI_MAXSERV];
- int rc;
-
- buf[0] = '\0';
-
- rc = getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf, sizeof(hbuf),
- sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV);
- if (rc)
- err("getnameinfo: %s", gai_strerror(rc));
-
- snprintf(buf, buf_size, "%s:%s", hbuf, sbuf);
-}
-
-static int listen_all_addrinfo(struct addrinfo *ai_head, int sockfdlist[],
- int maxsockfd)
-{
- struct addrinfo *ai;
- int ret, nsockfd = 0;
- const size_t ai_buf_size = NI_MAXHOST + NI_MAXSERV + 2;
- char ai_buf[ai_buf_size];
-
- for (ai = ai_head; ai && nsockfd < maxsockfd; ai = ai->ai_next) {
- int sock;
-
- addrinfo_to_text(ai, ai_buf, ai_buf_size);
- dbg("opening %s", ai_buf);
- sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
- if (sock < 0) {
- err("socket: %s: %d (%s)",
- ai_buf, errno, strerror(errno));
- continue;
- }
-
- usbip_net_set_reuseaddr(sock);
- usbip_net_set_nodelay(sock);
- /* We use seperate sockets for IPv4 and IPv6
- * (see do_standalone_mode()) */
- usbip_net_set_v6only(sock);
-
- if (sock >= FD_SETSIZE) {
- err("FD_SETSIZE: %s: sock=%d, max=%d",
- ai_buf, sock, FD_SETSIZE);
- close(sock);
- continue;
- }
-
- ret = bind(sock, ai->ai_addr, ai->ai_addrlen);
- if (ret < 0) {
- err("bind: %s: %d (%s)",
- ai_buf, errno, strerror(errno));
- close(sock);
- continue;
- }
-
- ret = listen(sock, SOMAXCONN);
- if (ret < 0) {
- err("listen: %s: %d (%s)",
- ai_buf, errno, strerror(errno));
- close(sock);
- continue;
- }
-
- info("listening on %s", ai_buf);
- sockfdlist[nsockfd++] = sock;
- }
-
- return nsockfd;
-}
-
-static struct addrinfo *do_getaddrinfo(char *host, int ai_family)
-{
- struct addrinfo hints, *ai_head;
- int rc;
-
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = ai_family;
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_flags = AI_PASSIVE;
-
- rc = getaddrinfo(host, usbip_port_string, &hints, &ai_head);
- if (rc) {
- err("failed to get a network address %s: %s", usbip_port_string,
- gai_strerror(rc));
- return NULL;
- }
-
- return ai_head;
-}
-
-static void signal_handler(int i)
-{
- dbg("received '%s' signal", strsignal(i));
-}
-
-static void set_signal(void)
-{
- struct sigaction act;
-
- memset(&act, 0, sizeof(act));
- act.sa_handler = signal_handler;
- sigemptyset(&act.sa_mask);
- sigaction(SIGTERM, &act, NULL);
- sigaction(SIGINT, &act, NULL);
- act.sa_handler = SIG_IGN;
- sigaction(SIGCLD, &act, NULL);
-}
-
-static const char *pid_file;
-
-static void write_pid_file(void)
-{
- if (pid_file) {
- dbg("creating pid file %s", pid_file);
- FILE *fp;
-
- fp = fopen(pid_file, "w");
- if (!fp) {
- err("pid_file: %s: %d (%s)",
- pid_file, errno, strerror(errno));
- return;
- }
- fprintf(fp, "%d\n", getpid());
- fclose(fp);
- }
-}
-
-static void remove_pid_file(void)
-{
- if (pid_file) {
- dbg("removing pid file %s", pid_file);
- unlink(pid_file);
- }
-}
-
-static int do_standalone_mode(int daemonize, int ipv4, int ipv6)
-{
- struct addrinfo *ai_head;
- int sockfdlist[MAXSOCKFD];
- int nsockfd, family;
- int i, terminate;
- struct pollfd *fds;
- struct timespec timeout;
- sigset_t sigmask;
-
- if (usbip_host_driver_open()) {
- err("please load " USBIP_CORE_MOD_NAME ".ko and "
- USBIP_HOST_DRV_NAME ".ko!");
- return -1;
- }
-
- if (daemonize) {
- if (daemon(0, 0) < 0) {
- err("daemonizing failed: %s", strerror(errno));
- usbip_host_driver_close();
- return -1;
- }
- umask(0);
- usbip_use_syslog = 1;
- }
- set_signal();
- write_pid_file();
-
- info("starting " PROGNAME " (%s)", usbip_version_string);
-
- /*
- * To suppress warnings on systems with bindv6only disabled
- * (default), we use seperate sockets for IPv6 and IPv4 and set
- * IPV6_V6ONLY on the IPv6 sockets.
- */
- if (ipv4 && ipv6)
- family = AF_UNSPEC;
- else if (ipv4)
- family = AF_INET;
- else
- family = AF_INET6;
-
- ai_head = do_getaddrinfo(NULL, family);
- if (!ai_head) {
- usbip_host_driver_close();
- return -1;
- }
- nsockfd = listen_all_addrinfo(ai_head, sockfdlist,
- sizeof(sockfdlist) / sizeof(*sockfdlist));
- freeaddrinfo(ai_head);
- if (nsockfd <= 0) {
- err("failed to open a listening socket");
- usbip_host_driver_close();
- return -1;
- }
-
- dbg("listening on %d address%s", nsockfd, (nsockfd == 1) ? "" : "es");
-
- fds = calloc(nsockfd, sizeof(struct pollfd));
- for (i = 0; i < nsockfd; i++) {
- fds[i].fd = sockfdlist[i];
- fds[i].events = POLLIN;
- }
- timeout.tv_sec = MAIN_LOOP_TIMEOUT;
- timeout.tv_nsec = 0;
-
- sigfillset(&sigmask);
- sigdelset(&sigmask, SIGTERM);
- sigdelset(&sigmask, SIGINT);
-
- terminate = 0;
- while (!terminate) {
- int r;
-
- r = ppoll(fds, nsockfd, &timeout, &sigmask);
- if (r < 0) {
- dbg("%s", strerror(errno));
- terminate = 1;
- } else if (r) {
- for (i = 0; i < nsockfd; i++) {
- if (fds[i].revents & POLLIN) {
- dbg("read event on fd[%d]=%d",
- i, sockfdlist[i]);
- process_request(sockfdlist[i]);
- }
- }
- } else {
- dbg("heartbeat timeout on ppoll()");
- }
- }
-
- info("shutting down " PROGNAME);
- free(fds);
- usbip_host_driver_close();
-
- return 0;
-}
-
-int main(int argc, char *argv[])
-{
- static const struct option longopts[] = {
- { "ipv4", no_argument, NULL, '4' },
- { "ipv6", no_argument, NULL, '6' },
- { "daemon", no_argument, NULL, 'D' },
- { "daemon", no_argument, NULL, 'D' },
- { "debug", no_argument, NULL, 'd' },
- { "pid", optional_argument, NULL, 'P' },
- { "tcp-port", required_argument, NULL, 't' },
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, 'v' },
- { NULL, 0, NULL, 0 }
- };
-
- enum {
- cmd_standalone_mode = 1,
- cmd_help,
- cmd_version
- } cmd;
-
- int daemonize = 0;
- int ipv4 = 0, ipv6 = 0;
- int opt, rc = -1;
-
- pid_file = NULL;
-
- usbip_use_stderr = 1;
- usbip_use_syslog = 0;
-
- if (geteuid() != 0)
- err("not running as root?");
-
- cmd = cmd_standalone_mode;
- for (;;) {
- opt = getopt_long(argc, argv, "46DdP::t:hv", longopts, NULL);
-
- if (opt == -1)
- break;
-
- switch (opt) {
- case '4':
- ipv4 = 1;
- break;
- case '6':
- ipv6 = 1;
- break;
- case 'D':
- daemonize = 1;
- break;
- case 'd':
- usbip_use_debug = 1;
- break;
- case 'h':
- cmd = cmd_help;
- break;
- case 'P':
- pid_file = optarg ? optarg : DEFAULT_PID_FILE;
- break;
- case 't':
- usbip_setup_port_number(optarg);
- break;
- case 'v':
- cmd = cmd_version;
- break;
- case '?':
- usbipd_help();
- default:
- goto err_out;
- }
- }
-
- if (!ipv4 && !ipv6)
- ipv4 = ipv6 = 1;
-
- switch (cmd) {
- case cmd_standalone_mode:
- rc = do_standalone_mode(daemonize, ipv4, ipv6);
- remove_pid_file();
- break;
- case cmd_version:
- printf(PROGNAME " (%s)\n", usbip_version_string);
- rc = 0;
- break;
- case cmd_help:
- usbipd_help();
- rc = 0;
- break;
- default:
- usbipd_help();
- goto err_out;
- }
-
-err_out:
- return (rc > -1 ? EXIT_SUCCESS : EXIT_FAILURE);
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
- * 2005-2007 Takahiro Hirofuchi
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "usbip_common.h"
-#include "utils.h"
-#include "sysfs_utils.h"
-
-int modify_match_busid(char *busid, int add)
-{
- char attr_name[] = "match_busid";
- char command[SYSFS_BUS_ID_SIZE + 4];
- char match_busid_attr_path[SYSFS_PATH_MAX];
- int rc;
-
- snprintf(match_busid_attr_path, sizeof(match_busid_attr_path),
- "%s/%s/%s/%s/%s/%s", SYSFS_MNT_PATH, SYSFS_BUS_NAME,
- SYSFS_BUS_TYPE, SYSFS_DRIVERS_NAME, USBIP_HOST_DRV_NAME,
- attr_name);
-
- if (add)
- snprintf(command, SYSFS_BUS_ID_SIZE + 4, "add %s", busid);
- else
- snprintf(command, SYSFS_BUS_ID_SIZE + 4, "del %s", busid);
-
- rc = write_sysfs_attribute(match_busid_attr_path, command,
- sizeof(command));
- if (rc < 0) {
- dbg("failed to write match_busid: %s", strerror(errno));
- return -1;
- }
-
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
- * 2005-2007 Takahiro Hirofuchi
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef __UTILS_H
-#define __UTILS_H
-
-int modify_match_busid(char *busid, int add);
-
-#endif /* __UTILS_H */
-
+++ /dev/null
-/*
- * Copyright (C) 2003-2008 Takahiro Hirofuchi
- *
- * This 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.
- *
- */
-
-#ifndef __USBIP_VHCI_H
-#define __USBIP_VHCI_H
-
-#include <linux/device.h>
-#include <linux/list.h>
-#include <linux/spinlock.h>
-#include <linux/sysfs.h>
-#include <linux/types.h>
-#include <linux/usb.h>
-#include <linux/usb/hcd.h>
-#include <linux/wait.h>
-
-struct vhci_device {
- struct usb_device *udev;
-
- /*
- * devid specifies a remote usb device uniquely instead
- * of combination of busnum and devnum.
- */
- __u32 devid;
-
- /* speed of a remote device */
- enum usb_device_speed speed;
-
- /* vhci root-hub port to which this device is attached */
- __u32 rhport;
-
- struct usbip_device ud;
-
- /* lock for the below link lists */
- spinlock_t priv_lock;
-
- /* vhci_priv is linked to one of them. */
- struct list_head priv_tx;
- struct list_head priv_rx;
-
- /* vhci_unlink is linked to one of them */
- struct list_head unlink_tx;
- struct list_head unlink_rx;
-
- /* vhci_tx thread sleeps for this queue */
- wait_queue_head_t waitq_tx;
-};
-
-/* urb->hcpriv, use container_of() */
-struct vhci_priv {
- unsigned long seqnum;
- struct list_head list;
-
- struct vhci_device *vdev;
- struct urb *urb;
-};
-
-struct vhci_unlink {
- /* seqnum of this request */
- unsigned long seqnum;
-
- struct list_head list;
-
- /* seqnum of the unlink target */
- unsigned long unlink_seqnum;
-};
-
-/* Number of supported ports. Value has an upperbound of USB_MAXCHILDREN */
-#define VHCI_NPORTS 8
-
-/* for usb_bus.hcpriv */
-struct vhci_hcd {
- spinlock_t lock;
-
- u32 port_status[VHCI_NPORTS];
-
- unsigned resuming:1;
- unsigned long re_timeout;
-
- atomic_t seqnum;
-
- /*
- * NOTE:
- * wIndex shows the port number and begins from 1.
- * But, the index of this array begins from 0.
- */
- struct vhci_device vdev[VHCI_NPORTS];
-};
-
-extern struct vhci_hcd *the_controller;
-extern const struct attribute_group dev_attr_group;
-
-/* vhci_hcd.c */
-void rh_port_connect(int rhport, enum usb_device_speed speed);
-
-/* vhci_rx.c */
-struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, __u32 seqnum);
-int vhci_rx_loop(void *data);
-
-/* vhci_tx.c */
-int vhci_tx_loop(void *data);
-
-static inline struct vhci_device *port_to_vdev(__u32 port)
-{
- return &the_controller->vdev[port];
-}
-
-static inline struct vhci_hcd *hcd_to_vhci(struct usb_hcd *hcd)
-{
- return (struct vhci_hcd *) (hcd->hcd_priv);
-}
-
-static inline struct usb_hcd *vhci_to_hcd(struct vhci_hcd *vhci)
-{
- return container_of((void *) vhci, struct usb_hcd, hcd_priv);
-}
-
-static inline struct device *vhci_dev(struct vhci_hcd *vhci)
-{
- return vhci_to_hcd(vhci)->self.controller;
-}
-
-#endif /* __USBIP_VHCI_H */
+++ /dev/null
-/*
- * Copyright (C) 2003-2008 Takahiro Hirofuchi
- *
- * This 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 is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
- */
-
-#include <linux/init.h>
-#include <linux/file.h>
-#include <linux/kernel.h>
-#include <linux/kthread.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-
-#include "usbip_common.h"
-#include "vhci.h"
-
-#define DRIVER_AUTHOR "Takahiro Hirofuchi"
-#define DRIVER_DESC "USB/IP 'Virtual' Host Controller (VHCI) Driver"
-
-/*
- * TODO
- * - update root hub emulation
- * - move the emulation code to userland ?
- * porting to other operating systems
- * minimize kernel code
- * - add suspend/resume code
- * - clean up everything
- */
-
-/* See usb gadget dummy hcd */
-
-static int vhci_hub_status(struct usb_hcd *hcd, char *buff);
-static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
- u16 wIndex, char *buff, u16 wLength);
-static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
- gfp_t mem_flags);
-static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status);
-static int vhci_start(struct usb_hcd *vhci_hcd);
-static void vhci_stop(struct usb_hcd *hcd);
-static int vhci_get_frame_number(struct usb_hcd *hcd);
-
-static const char driver_name[] = "vhci_hcd";
-static const char driver_desc[] = "USB/IP Virtual Host Controller";
-
-struct vhci_hcd *the_controller;
-
-static const char * const bit_desc[] = {
- "CONNECTION", /*0*/
- "ENABLE", /*1*/
- "SUSPEND", /*2*/
- "OVER_CURRENT", /*3*/
- "RESET", /*4*/
- "R5", /*5*/
- "R6", /*6*/
- "R7", /*7*/
- "POWER", /*8*/
- "LOWSPEED", /*9*/
- "HIGHSPEED", /*10*/
- "PORT_TEST", /*11*/
- "INDICATOR", /*12*/
- "R13", /*13*/
- "R14", /*14*/
- "R15", /*15*/
- "C_CONNECTION", /*16*/
- "C_ENABLE", /*17*/
- "C_SUSPEND", /*18*/
- "C_OVER_CURRENT", /*19*/
- "C_RESET", /*20*/
- "R21", /*21*/
- "R22", /*22*/
- "R23", /*23*/
- "R24", /*24*/
- "R25", /*25*/
- "R26", /*26*/
- "R27", /*27*/
- "R28", /*28*/
- "R29", /*29*/
- "R30", /*30*/
- "R31", /*31*/
-};
-
-static void dump_port_status_diff(u32 prev_status, u32 new_status)
-{
- int i = 0;
- u32 bit = 1;
-
- pr_debug("status prev -> new: %08x -> %08x\n", prev_status, new_status);
- while (bit) {
- u32 prev = prev_status & bit;
- u32 new = new_status & bit;
- char change;
-
- if (!prev && new)
- change = '+';
- else if (prev && !new)
- change = '-';
- else
- change = ' ';
-
- if (prev || new)
- pr_debug(" %c%s\n", change, bit_desc[i]);
- bit <<= 1;
- i++;
- }
- pr_debug("\n");
-}
-
-void rh_port_connect(int rhport, enum usb_device_speed speed)
-{
- usbip_dbg_vhci_rh("rh_port_connect %d\n", rhport);
-
- spin_lock(&the_controller->lock);
-
- the_controller->port_status[rhport] |= USB_PORT_STAT_CONNECTION
- | (1 << USB_PORT_FEAT_C_CONNECTION);
-
- switch (speed) {
- case USB_SPEED_HIGH:
- the_controller->port_status[rhport] |= USB_PORT_STAT_HIGH_SPEED;
- break;
- case USB_SPEED_LOW:
- the_controller->port_status[rhport] |= USB_PORT_STAT_LOW_SPEED;
- break;
- default:
- break;
- }
-
- spin_unlock(&the_controller->lock);
-
- usb_hcd_poll_rh_status(vhci_to_hcd(the_controller));
-}
-
-static void rh_port_disconnect(int rhport)
-{
- usbip_dbg_vhci_rh("rh_port_disconnect %d\n", rhport);
-
- spin_lock(&the_controller->lock);
-
- the_controller->port_status[rhport] &= ~USB_PORT_STAT_CONNECTION;
- the_controller->port_status[rhport] |=
- (1 << USB_PORT_FEAT_C_CONNECTION);
-
- spin_unlock(&the_controller->lock);
- usb_hcd_poll_rh_status(vhci_to_hcd(the_controller));
-}
-
-#define PORT_C_MASK \
- ((USB_PORT_STAT_C_CONNECTION \
- | USB_PORT_STAT_C_ENABLE \
- | USB_PORT_STAT_C_SUSPEND \
- | USB_PORT_STAT_C_OVERCURRENT \
- | USB_PORT_STAT_C_RESET) << 16)
-
-/*
- * Returns 0 if the status hasn't changed, or the number of bytes in buf.
- * Ports are 0-indexed from the HCD point of view,
- * and 1-indexed from the USB core pointer of view.
- *
- * @buf: a bitmap to show which port status has been changed.
- * bit 0: reserved
- * bit 1: the status of port 0 has been changed.
- * bit 2: the status of port 1 has been changed.
- * ...
- */
-static int vhci_hub_status(struct usb_hcd *hcd, char *buf)
-{
- struct vhci_hcd *vhci;
- int retval;
- int rhport;
- int changed = 0;
-
- retval = DIV_ROUND_UP(VHCI_NPORTS + 1, 8);
- memset(buf, 0, retval);
-
- vhci = hcd_to_vhci(hcd);
-
- spin_lock(&vhci->lock);
- if (!HCD_HW_ACCESSIBLE(hcd)) {
- usbip_dbg_vhci_rh("hw accessible flag not on?\n");
- goto done;
- }
-
- /* check pseudo status register for each port */
- for (rhport = 0; rhport < VHCI_NPORTS; rhport++) {
- if ((vhci->port_status[rhport] & PORT_C_MASK)) {
- /* The status of a port has been changed, */
- usbip_dbg_vhci_rh("port %d status changed\n", rhport);
-
- buf[(rhport + 1) / 8] |= 1 << (rhport + 1) % 8;
- changed = 1;
- }
- }
-
- if ((hcd->state == HC_STATE_SUSPENDED) && (changed == 1))
- usb_hcd_resume_root_hub(hcd);
-
-done:
- spin_unlock(&vhci->lock);
- return changed ? retval : 0;
-}
-
-static inline void hub_descriptor(struct usb_hub_descriptor *desc)
-{
- memset(desc, 0, sizeof(*desc));
- desc->bDescriptorType = 0x29;
- desc->bDescLength = 9;
- desc->wHubCharacteristics = (__constant_cpu_to_le16(0x0001));
- desc->bNbrPorts = VHCI_NPORTS;
- desc->u.hs.DeviceRemovable[0] = 0xff;
- desc->u.hs.DeviceRemovable[1] = 0xff;
-}
-
-static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
- u16 wIndex, char *buf, u16 wLength)
-{
- struct vhci_hcd *dum;
- int retval = 0;
- int rhport;
-
- u32 prev_port_status[VHCI_NPORTS];
-
- if (!HCD_HW_ACCESSIBLE(hcd))
- return -ETIMEDOUT;
-
- /*
- * NOTE:
- * wIndex shows the port number and begins from 1.
- */
- usbip_dbg_vhci_rh("typeReq %x wValue %x wIndex %x\n", typeReq, wValue,
- wIndex);
- if (wIndex > VHCI_NPORTS)
- pr_err("invalid port number %d\n", wIndex);
- rhport = ((__u8)(wIndex & 0x00ff)) - 1;
-
- dum = hcd_to_vhci(hcd);
-
- spin_lock(&dum->lock);
-
- /* store old status and compare now and old later */
- if (usbip_dbg_flag_vhci_rh) {
- memcpy(prev_port_status, dum->port_status,
- sizeof(prev_port_status));
- }
-
- switch (typeReq) {
- case ClearHubFeature:
- usbip_dbg_vhci_rh(" ClearHubFeature\n");
- break;
- case ClearPortFeature:
- switch (wValue) {
- case USB_PORT_FEAT_SUSPEND:
- if (dum->port_status[rhport] & USB_PORT_STAT_SUSPEND) {
- /* 20msec signaling */
- dum->resuming = 1;
- dum->re_timeout =
- jiffies + msecs_to_jiffies(20);
- }
- break;
- case USB_PORT_FEAT_POWER:
- usbip_dbg_vhci_rh(
- " ClearPortFeature: USB_PORT_FEAT_POWER\n");
- dum->port_status[rhport] = 0;
- dum->resuming = 0;
- break;
- case USB_PORT_FEAT_C_RESET:
- usbip_dbg_vhci_rh(
- " ClearPortFeature: USB_PORT_FEAT_C_RESET\n");
- switch (dum->vdev[rhport].speed) {
- case USB_SPEED_HIGH:
- dum->port_status[rhport] |=
- USB_PORT_STAT_HIGH_SPEED;
- break;
- case USB_SPEED_LOW:
- dum->port_status[rhport] |=
- USB_PORT_STAT_LOW_SPEED;
- break;
- default:
- break;
- }
- default:
- usbip_dbg_vhci_rh(" ClearPortFeature: default %x\n",
- wValue);
- dum->port_status[rhport] &= ~(1 << wValue);
- break;
- }
- break;
- case GetHubDescriptor:
- usbip_dbg_vhci_rh(" GetHubDescriptor\n");
- hub_descriptor((struct usb_hub_descriptor *) buf);
- break;
- case GetHubStatus:
- usbip_dbg_vhci_rh(" GetHubStatus\n");
- *(__le32 *) buf = cpu_to_le32(0);
- break;
- case GetPortStatus:
- usbip_dbg_vhci_rh(" GetPortStatus port %x\n", wIndex);
- if (wIndex > VHCI_NPORTS || wIndex < 1) {
- pr_err("invalid port number %d\n", wIndex);
- retval = -EPIPE;
- }
-
- /* we do not care about resume. */
-
- /* whoever resets or resumes must GetPortStatus to
- * complete it!!
- */
- if (dum->resuming && time_after(jiffies, dum->re_timeout)) {
- dum->port_status[rhport] |=
- (1 << USB_PORT_FEAT_C_SUSPEND);
- dum->port_status[rhport] &=
- ~(1 << USB_PORT_FEAT_SUSPEND);
- dum->resuming = 0;
- dum->re_timeout = 0;
- }
-
- if ((dum->port_status[rhport] & (1 << USB_PORT_FEAT_RESET)) !=
- 0 && time_after(jiffies, dum->re_timeout)) {
- dum->port_status[rhport] |=
- (1 << USB_PORT_FEAT_C_RESET);
- dum->port_status[rhport] &=
- ~(1 << USB_PORT_FEAT_RESET);
- dum->re_timeout = 0;
-
- if (dum->vdev[rhport].ud.status ==
- VDEV_ST_NOTASSIGNED) {
- usbip_dbg_vhci_rh(
- " enable rhport %d (status %u)\n",
- rhport,
- dum->vdev[rhport].ud.status);
- dum->port_status[rhport] |=
- USB_PORT_STAT_ENABLE;
- }
- }
- ((__le16 *) buf)[0] = cpu_to_le16(dum->port_status[rhport]);
- ((__le16 *) buf)[1] =
- cpu_to_le16(dum->port_status[rhport] >> 16);
-
- usbip_dbg_vhci_rh(" GetPortStatus bye %x %x\n", ((u16 *)buf)[0],
- ((u16 *)buf)[1]);
- break;
- case SetHubFeature:
- usbip_dbg_vhci_rh(" SetHubFeature\n");
- retval = -EPIPE;
- break;
- case SetPortFeature:
- switch (wValue) {
- case USB_PORT_FEAT_SUSPEND:
- usbip_dbg_vhci_rh(
- " SetPortFeature: USB_PORT_FEAT_SUSPEND\n");
- break;
- case USB_PORT_FEAT_RESET:
- usbip_dbg_vhci_rh(
- " SetPortFeature: USB_PORT_FEAT_RESET\n");
- /* if it's already running, disconnect first */
- if (dum->port_status[rhport] & USB_PORT_STAT_ENABLE) {
- dum->port_status[rhport] &=
- ~(USB_PORT_STAT_ENABLE |
- USB_PORT_STAT_LOW_SPEED |
- USB_PORT_STAT_HIGH_SPEED);
- /* FIXME test that code path! */
- }
- /* 50msec reset signaling */
- dum->re_timeout = jiffies + msecs_to_jiffies(50);
-
- /* FALLTHROUGH */
- default:
- usbip_dbg_vhci_rh(" SetPortFeature: default %d\n",
- wValue);
- dum->port_status[rhport] |= (1 << wValue);
- break;
- }
- break;
-
- default:
- pr_err("default: no such request\n");
-
- /* "protocol stall" on error */
- retval = -EPIPE;
- }
-
- if (usbip_dbg_flag_vhci_rh) {
- pr_debug("port %d\n", rhport);
- /* Only dump valid port status */
- if (rhport >= 0) {
- dump_port_status_diff(prev_port_status[rhport],
- dum->port_status[rhport]);
- }
- }
- usbip_dbg_vhci_rh(" bye\n");
-
- spin_unlock(&dum->lock);
-
- return retval;
-}
-
-static struct vhci_device *get_vdev(struct usb_device *udev)
-{
- int i;
-
- if (!udev)
- return NULL;
-
- for (i = 0; i < VHCI_NPORTS; i++)
- if (the_controller->vdev[i].udev == udev)
- return port_to_vdev(i);
-
- return NULL;
-}
-
-static void vhci_tx_urb(struct urb *urb)
-{
- struct vhci_device *vdev = get_vdev(urb->dev);
- struct vhci_priv *priv;
-
- if (!vdev) {
- pr_err("could not get virtual device");
- return;
- }
-
- priv = kzalloc(sizeof(struct vhci_priv), GFP_ATOMIC);
- if (!priv) {
- usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC);
- return;
- }
-
- spin_lock(&vdev->priv_lock);
-
- priv->seqnum = atomic_inc_return(&the_controller->seqnum);
- if (priv->seqnum == 0xffff)
- dev_info(&urb->dev->dev, "seqnum max\n");
-
- priv->vdev = vdev;
- priv->urb = urb;
-
- urb->hcpriv = (void *) priv;
-
- list_add_tail(&priv->list, &vdev->priv_tx);
-
- wake_up(&vdev->waitq_tx);
- spin_unlock(&vdev->priv_lock);
-}
-
-static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
- gfp_t mem_flags)
-{
- struct device *dev = &urb->dev->dev;
- int ret = 0;
- struct vhci_device *vdev;
-
- usbip_dbg_vhci_hc("enter, usb_hcd %p urb %p mem_flags %d\n",
- hcd, urb, mem_flags);
-
- /* patch to usb_sg_init() is in 2.5.60 */
- BUG_ON(!urb->transfer_buffer && urb->transfer_buffer_length);
-
- spin_lock(&the_controller->lock);
-
- if (urb->status != -EINPROGRESS) {
- dev_err(dev, "URB already unlinked!, status %d\n", urb->status);
- spin_unlock(&the_controller->lock);
- return urb->status;
- }
-
- vdev = port_to_vdev(urb->dev->portnum-1);
-
- /* refuse enqueue for dead connection */
- spin_lock(&vdev->ud.lock);
- if (vdev->ud.status == VDEV_ST_NULL ||
- vdev->ud.status == VDEV_ST_ERROR) {
- dev_err(dev, "enqueue for inactive port %d\n", vdev->rhport);
- spin_unlock(&vdev->ud.lock);
- spin_unlock(&the_controller->lock);
- return -ENODEV;
- }
- spin_unlock(&vdev->ud.lock);
-
- ret = usb_hcd_link_urb_to_ep(hcd, urb);
- if (ret)
- goto no_need_unlink;
-
- /*
- * The enumeration process is as follows;
- *
- * 1. Get_Descriptor request to DevAddrs(0) EndPoint(0)
- * to get max packet length of default pipe
- *
- * 2. Set_Address request to DevAddr(0) EndPoint(0)
- *
- */
- if (usb_pipedevice(urb->pipe) == 0) {
- __u8 type = usb_pipetype(urb->pipe);
- struct usb_ctrlrequest *ctrlreq =
- (struct usb_ctrlrequest *) urb->setup_packet;
-
- if (type != PIPE_CONTROL || !ctrlreq) {
- dev_err(dev, "invalid request to devnum 0\n");
- ret = -EINVAL;
- goto no_need_xmit;
- }
-
- switch (ctrlreq->bRequest) {
- case USB_REQ_SET_ADDRESS:
- /* set_address may come when a device is reset */
- dev_info(dev, "SetAddress Request (%d) to port %d\n",
- ctrlreq->wValue, vdev->rhport);
-
- if (vdev->udev)
- usb_put_dev(vdev->udev);
- vdev->udev = usb_get_dev(urb->dev);
-
- spin_lock(&vdev->ud.lock);
- vdev->ud.status = VDEV_ST_USED;
- spin_unlock(&vdev->ud.lock);
-
- if (urb->status == -EINPROGRESS) {
- /* This request is successfully completed. */
- /* If not -EINPROGRESS, possibly unlinked. */
- urb->status = 0;
- }
-
- goto no_need_xmit;
-
- case USB_REQ_GET_DESCRIPTOR:
- if (ctrlreq->wValue == cpu_to_le16(USB_DT_DEVICE << 8))
- usbip_dbg_vhci_hc(
- "Not yet?:Get_Descriptor to device 0 (get max pipe size)\n");
-
- if (vdev->udev)
- usb_put_dev(vdev->udev);
- vdev->udev = usb_get_dev(urb->dev);
- goto out;
-
- default:
- /* NOT REACHED */
- dev_err(dev,
- "invalid request to devnum 0 bRequest %u, wValue %u\n",
- ctrlreq->bRequest,
- ctrlreq->wValue);
- ret = -EINVAL;
- goto no_need_xmit;
- }
-
- }
-
-out:
- vhci_tx_urb(urb);
- spin_unlock(&the_controller->lock);
-
- return 0;
-
-no_need_xmit:
- usb_hcd_unlink_urb_from_ep(hcd, urb);
-no_need_unlink:
- spin_unlock(&the_controller->lock);
- usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status);
- return ret;
-}
-
-/*
- * vhci_rx gives back the urb after receiving the reply of the urb. If an
- * unlink pdu is sent or not, vhci_rx receives a normal return pdu and gives
- * back its urb. For the driver unlinking the urb, the content of the urb is
- * not important, but the calling to its completion handler is important; the
- * completion of unlinking is notified by the completion handler.
- *
- *
- * CLIENT SIDE
- *
- * - When vhci_hcd receives RET_SUBMIT,
- *
- * - case 1a). the urb of the pdu is not unlinking.
- * - normal case
- * => just give back the urb
- *
- * - case 1b). the urb of the pdu is unlinking.
- * - usbip.ko will return a reply of the unlinking request.
- * => give back the urb now and go to case 2b).
- *
- * - When vhci_hcd receives RET_UNLINK,
- *
- * - case 2a). a submit request is still pending in vhci_hcd.
- * - urb was really pending in usbip.ko and urb_unlink_urb() was
- * completed there.
- * => free a pending submit request
- * => notify unlink completeness by giving back the urb
- *
- * - case 2b). a submit request is *not* pending in vhci_hcd.
- * - urb was already given back to the core driver.
- * => do not give back the urb
- *
- *
- * SERVER SIDE
- *
- * - When usbip receives CMD_UNLINK,
- *
- * - case 3a). the urb of the unlink request is now in submission.
- * => do usb_unlink_urb().
- * => after the unlink is completed, send RET_UNLINK.
- *
- * - case 3b). the urb of the unlink request is not in submission.
- * - may be already completed or never be received
- * => send RET_UNLINK
- *
- */
-static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
-{
- struct vhci_priv *priv;
- struct vhci_device *vdev;
-
- pr_info("dequeue a urb %p\n", urb);
-
- spin_lock(&the_controller->lock);
-
- priv = urb->hcpriv;
- if (!priv) {
- /* URB was never linked! or will be soon given back by
- * vhci_rx. */
- spin_unlock(&the_controller->lock);
- return 0;
- }
-
- {
- int ret = 0;
-
- ret = usb_hcd_check_unlink_urb(hcd, urb, status);
- if (ret) {
- spin_unlock(&the_controller->lock);
- return ret;
- }
- }
-
- /* send unlink request here? */
- vdev = priv->vdev;
-
- if (!vdev->ud.tcp_socket) {
- /* tcp connection is closed */
- spin_lock(&vdev->priv_lock);
-
- pr_info("device %p seems to be disconnected\n", vdev);
- list_del(&priv->list);
- kfree(priv);
- urb->hcpriv = NULL;
-
- spin_unlock(&vdev->priv_lock);
-
- /*
- * If tcp connection is alive, we have sent CMD_UNLINK.
- * vhci_rx will receive RET_UNLINK and give back the URB.
- * Otherwise, we give back it here.
- */
- pr_info("gives back urb %p\n", urb);
-
- usb_hcd_unlink_urb_from_ep(hcd, urb);
-
- spin_unlock(&the_controller->lock);
- usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb,
- urb->status);
- spin_lock(&the_controller->lock);
-
- } else {
- /* tcp connection is alive */
- struct vhci_unlink *unlink;
-
- spin_lock(&vdev->priv_lock);
-
- /* setup CMD_UNLINK pdu */
- unlink = kzalloc(sizeof(struct vhci_unlink), GFP_ATOMIC);
- if (!unlink) {
- spin_unlock(&vdev->priv_lock);
- spin_unlock(&the_controller->lock);
- usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC);
- return -ENOMEM;
- }
-
- unlink->seqnum = atomic_inc_return(&the_controller->seqnum);
- if (unlink->seqnum == 0xffff)
- pr_info("seqnum max\n");
-
- unlink->unlink_seqnum = priv->seqnum;
-
- pr_info("device %p seems to be still connected\n", vdev);
-
- /* send cmd_unlink and try to cancel the pending URB in the
- * peer */
- list_add_tail(&unlink->list, &vdev->unlink_tx);
- wake_up(&vdev->waitq_tx);
-
- spin_unlock(&vdev->priv_lock);
- }
-
- spin_unlock(&the_controller->lock);
-
- usbip_dbg_vhci_hc("leave\n");
- return 0;
-}
-
-static void vhci_device_unlink_cleanup(struct vhci_device *vdev)
-{
- struct vhci_unlink *unlink, *tmp;
-
- spin_lock(&the_controller->lock);
- spin_lock(&vdev->priv_lock);
-
- list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) {
- pr_info("unlink cleanup tx %lu\n", unlink->unlink_seqnum);
- list_del(&unlink->list);
- kfree(unlink);
- }
-
- while (!list_empty(&vdev->unlink_rx)) {
- struct urb *urb;
-
- unlink = list_first_entry(&vdev->unlink_rx, struct vhci_unlink,
- list);
-
- /* give back URB of unanswered unlink request */
- pr_info("unlink cleanup rx %lu\n", unlink->unlink_seqnum);
-
- urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum);
- if (!urb) {
- pr_info("the urb (seqnum %lu) was already given back\n",
- unlink->unlink_seqnum);
- list_del(&unlink->list);
- kfree(unlink);
- continue;
- }
-
- urb->status = -ENODEV;
-
- usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb);
-
- list_del(&unlink->list);
-
- spin_unlock(&vdev->priv_lock);
- spin_unlock(&the_controller->lock);
-
- usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb,
- urb->status);
-
- spin_lock(&the_controller->lock);
- spin_lock(&vdev->priv_lock);
-
- kfree(unlink);
- }
-
- spin_unlock(&vdev->priv_lock);
- spin_unlock(&the_controller->lock);
-}
-
-/*
- * The important thing is that only one context begins cleanup.
- * This is why error handling and cleanup become simple.
- * We do not want to consider race condition as possible.
- */
-static void vhci_shutdown_connection(struct usbip_device *ud)
-{
- struct vhci_device *vdev = container_of(ud, struct vhci_device, ud);
-
- /* need this? see stub_dev.c */
- if (ud->tcp_socket) {
- pr_debug("shutdown tcp_socket %p\n", ud->tcp_socket);
- kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR);
- }
-
- /* kill threads related to this sdev */
- if (vdev->ud.tcp_rx) {
- kthread_stop_put(vdev->ud.tcp_rx);
- vdev->ud.tcp_rx = NULL;
- }
- if (vdev->ud.tcp_tx) {
- kthread_stop_put(vdev->ud.tcp_tx);
- vdev->ud.tcp_tx = NULL;
- }
- pr_info("stop threads\n");
-
- /* active connection is closed */
- if (vdev->ud.tcp_socket) {
- sockfd_put(vdev->ud.tcp_socket);
- vdev->ud.tcp_socket = NULL;
- }
- pr_info("release socket\n");
-
- vhci_device_unlink_cleanup(vdev);
-
- /*
- * rh_port_disconnect() is a trigger of ...
- * usb_disable_device():
- * disable all the endpoints for a USB device.
- * usb_disable_endpoint():
- * disable endpoints. pending urbs are unlinked(dequeued).
- *
- * NOTE: After calling rh_port_disconnect(), the USB device drivers of a
- * detached device should release used urbs in a cleanup function (i.e.
- * xxx_disconnect()). Therefore, vhci_hcd does not need to release
- * pushed urbs and their private data in this function.
- *
- * NOTE: vhci_dequeue() must be considered carefully. When shutting down
- * a connection, vhci_shutdown_connection() expects vhci_dequeue()
- * gives back pushed urbs and frees their private data by request of
- * the cleanup function of a USB driver. When unlinking a urb with an
- * active connection, vhci_dequeue() does not give back the urb which
- * is actually given back by vhci_rx after receiving its return pdu.
- *
- */
- rh_port_disconnect(vdev->rhport);
-
- pr_info("disconnect device\n");
-}
-
-
-static void vhci_device_reset(struct usbip_device *ud)
-{
- struct vhci_device *vdev = container_of(ud, struct vhci_device, ud);
-
- spin_lock(&ud->lock);
-
- vdev->speed = 0;
- vdev->devid = 0;
-
- if (vdev->udev)
- usb_put_dev(vdev->udev);
- vdev->udev = NULL;
-
- if (ud->tcp_socket) {
- sockfd_put(ud->tcp_socket);
- ud->tcp_socket = NULL;
- }
- ud->status = VDEV_ST_NULL;
-
- spin_unlock(&ud->lock);
-}
-
-static void vhci_device_unusable(struct usbip_device *ud)
-{
- spin_lock(&ud->lock);
- ud->status = VDEV_ST_ERROR;
- spin_unlock(&ud->lock);
-}
-
-static void vhci_device_init(struct vhci_device *vdev)
-{
- memset(vdev, 0, sizeof(*vdev));
-
- vdev->ud.side = USBIP_VHCI;
- vdev->ud.status = VDEV_ST_NULL;
- spin_lock_init(&vdev->ud.lock);
-
- INIT_LIST_HEAD(&vdev->priv_rx);
- INIT_LIST_HEAD(&vdev->priv_tx);
- INIT_LIST_HEAD(&vdev->unlink_tx);
- INIT_LIST_HEAD(&vdev->unlink_rx);
- spin_lock_init(&vdev->priv_lock);
-
- init_waitqueue_head(&vdev->waitq_tx);
-
- vdev->ud.eh_ops.shutdown = vhci_shutdown_connection;
- vdev->ud.eh_ops.reset = vhci_device_reset;
- vdev->ud.eh_ops.unusable = vhci_device_unusable;
-
- usbip_start_eh(&vdev->ud);
-}
-
-static int vhci_start(struct usb_hcd *hcd)
-{
- struct vhci_hcd *vhci = hcd_to_vhci(hcd);
- int rhport;
- int err = 0;
-
- usbip_dbg_vhci_hc("enter vhci_start\n");
-
- /* initialize private data of usb_hcd */
-
- for (rhport = 0; rhport < VHCI_NPORTS; rhport++) {
- struct vhci_device *vdev = &vhci->vdev[rhport];
-
- vhci_device_init(vdev);
- vdev->rhport = rhport;
- }
-
- atomic_set(&vhci->seqnum, 0);
- spin_lock_init(&vhci->lock);
-
- hcd->power_budget = 0; /* no limit */
- hcd->uses_new_polling = 1;
-
- /* vhci_hcd is now ready to be controlled through sysfs */
- err = sysfs_create_group(&vhci_dev(vhci)->kobj, &dev_attr_group);
- if (err) {
- pr_err("create sysfs files\n");
- return err;
- }
-
- return 0;
-}
-
-static void vhci_stop(struct usb_hcd *hcd)
-{
- struct vhci_hcd *vhci = hcd_to_vhci(hcd);
- int rhport = 0;
-
- usbip_dbg_vhci_hc("stop VHCI controller\n");
-
- /* 1. remove the userland interface of vhci_hcd */
- sysfs_remove_group(&vhci_dev(vhci)->kobj, &dev_attr_group);
-
- /* 2. shutdown all the ports of vhci_hcd */
- for (rhport = 0; rhport < VHCI_NPORTS; rhport++) {
- struct vhci_device *vdev = &vhci->vdev[rhport];
-
- usbip_event_add(&vdev->ud, VDEV_EVENT_REMOVED);
- usbip_stop_eh(&vdev->ud);
- }
-}
-
-static int vhci_get_frame_number(struct usb_hcd *hcd)
-{
- pr_err("Not yet implemented\n");
- return 0;
-}
-
-#ifdef CONFIG_PM
-
-/* FIXME: suspend/resume */
-static int vhci_bus_suspend(struct usb_hcd *hcd)
-{
- struct vhci_hcd *vhci = hcd_to_vhci(hcd);
-
- dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__);
-
- spin_lock(&vhci->lock);
- hcd->state = HC_STATE_SUSPENDED;
- spin_unlock(&vhci->lock);
-
- return 0;
-}
-
-static int vhci_bus_resume(struct usb_hcd *hcd)
-{
- struct vhci_hcd *vhci = hcd_to_vhci(hcd);
- int rc = 0;
-
- dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__);
-
- spin_lock(&vhci->lock);
- if (!HCD_HW_ACCESSIBLE(hcd))
- rc = -ESHUTDOWN;
- else
- hcd->state = HC_STATE_RUNNING;
- spin_unlock(&vhci->lock);
-
- return rc;
-}
-
-#else
-
-#define vhci_bus_suspend NULL
-#define vhci_bus_resume NULL
-#endif
-
-static struct hc_driver vhci_hc_driver = {
- .description = driver_name,
- .product_desc = driver_desc,
- .hcd_priv_size = sizeof(struct vhci_hcd),
-
- .flags = HCD_USB2,
-
- .start = vhci_start,
- .stop = vhci_stop,
-
- .urb_enqueue = vhci_urb_enqueue,
- .urb_dequeue = vhci_urb_dequeue,
-
- .get_frame_number = vhci_get_frame_number,
-
- .hub_status_data = vhci_hub_status,
- .hub_control = vhci_hub_control,
- .bus_suspend = vhci_bus_suspend,
- .bus_resume = vhci_bus_resume,
-};
-
-static int vhci_hcd_probe(struct platform_device *pdev)
-{
- struct usb_hcd *hcd;
- int ret;
-
- usbip_dbg_vhci_hc("name %s id %d\n", pdev->name, pdev->id);
-
- /*
- * Allocate and initialize hcd.
- * Our private data is also allocated automatically.
- */
- hcd = usb_create_hcd(&vhci_hc_driver, &pdev->dev, dev_name(&pdev->dev));
- if (!hcd) {
- pr_err("create hcd failed\n");
- return -ENOMEM;
- }
- hcd->has_tt = 1;
-
- /* this is private data for vhci_hcd */
- the_controller = hcd_to_vhci(hcd);
-
- /*
- * Finish generic HCD structure initialization and register.
- * Call the driver's reset() and start() routines.
- */
- ret = usb_add_hcd(hcd, 0, 0);
- if (ret != 0) {
- pr_err("usb_add_hcd failed %d\n", ret);
- usb_put_hcd(hcd);
- the_controller = NULL;
- return ret;
- }
-
- usbip_dbg_vhci_hc("bye\n");
- return 0;
-}
-
-static int vhci_hcd_remove(struct platform_device *pdev)
-{
- struct usb_hcd *hcd;
-
- hcd = platform_get_drvdata(pdev);
- if (!hcd)
- return 0;
-
- /*
- * Disconnects the root hub,
- * then reverses the effects of usb_add_hcd(),
- * invoking the HCD's stop() methods.
- */
- usb_remove_hcd(hcd);
- usb_put_hcd(hcd);
- the_controller = NULL;
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-
-/* what should happen for USB/IP under suspend/resume? */
-static int vhci_hcd_suspend(struct platform_device *pdev, pm_message_t state)
-{
- struct usb_hcd *hcd;
- int rhport = 0;
- int connected = 0;
- int ret = 0;
-
- hcd = platform_get_drvdata(pdev);
-
- spin_lock(&the_controller->lock);
-
- for (rhport = 0; rhport < VHCI_NPORTS; rhport++)
- if (the_controller->port_status[rhport] &
- USB_PORT_STAT_CONNECTION)
- connected += 1;
-
- spin_unlock(&the_controller->lock);
-
- if (connected > 0) {
- dev_info(&pdev->dev,
- "We have %d active connection%s. Do not suspend.\n",
- connected, (connected == 1 ? "" : "s"));
- ret = -EBUSY;
- } else {
- dev_info(&pdev->dev, "suspend vhci_hcd");
- clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
- }
-
- return ret;
-}
-
-static int vhci_hcd_resume(struct platform_device *pdev)
-{
- struct usb_hcd *hcd;
-
- dev_dbg(&pdev->dev, "%s\n", __func__);
-
- hcd = platform_get_drvdata(pdev);
- set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
- usb_hcd_poll_rh_status(hcd);
-
- return 0;
-}
-
-#else
-
-#define vhci_hcd_suspend NULL
-#define vhci_hcd_resume NULL
-
-#endif
-
-static struct platform_driver vhci_driver = {
- .probe = vhci_hcd_probe,
- .remove = vhci_hcd_remove,
- .suspend = vhci_hcd_suspend,
- .resume = vhci_hcd_resume,
- .driver = {
- .name = driver_name,
- .owner = THIS_MODULE,
- },
-};
-
-/*
- * The VHCI 'device' is 'virtual'; not a real plug&play hardware.
- * We need to add this virtual device as a platform device arbitrarily:
- * 1. platform_device_register()
- */
-static void the_pdev_release(struct device *dev)
-{
-}
-
-static struct platform_device the_pdev = {
- /* should be the same name as driver_name */
- .name = driver_name,
- .id = -1,
- .dev = {
- .release = the_pdev_release,
- },
-};
-
-static int __init vhci_hcd_init(void)
-{
- int ret;
-
- if (usb_disabled())
- return -ENODEV;
-
- ret = platform_driver_register(&vhci_driver);
- if (ret)
- goto err_driver_register;
-
- ret = platform_device_register(&the_pdev);
- if (ret)
- goto err_platform_device_register;
-
- pr_info(DRIVER_DESC " v" USBIP_VERSION "\n");
- return ret;
-
-err_platform_device_register:
- platform_driver_unregister(&vhci_driver);
-err_driver_register:
- return ret;
-}
-
-static void __exit vhci_hcd_exit(void)
-{
- platform_device_unregister(&the_pdev);
- platform_driver_unregister(&vhci_driver);
-}
-
-module_init(vhci_hcd_init);
-module_exit(vhci_hcd_exit);
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
-MODULE_VERSION(USBIP_VERSION);
+++ /dev/null
-/*
- * Copyright (C) 2003-2008 Takahiro Hirofuchi
- *
- * This 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 is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
- */
-
-#include <linux/kthread.h>
-#include <linux/slab.h>
-
-#include "usbip_common.h"
-#include "vhci.h"
-
-/* get URB from transmitted urb queue. caller must hold vdev->priv_lock */
-struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, __u32 seqnum)
-{
- struct vhci_priv *priv, *tmp;
- struct urb *urb = NULL;
- int status;
-
- list_for_each_entry_safe(priv, tmp, &vdev->priv_rx, list) {
- if (priv->seqnum != seqnum)
- continue;
-
- urb = priv->urb;
- status = urb->status;
-
- usbip_dbg_vhci_rx("find urb %p vurb %p seqnum %u\n",
- urb, priv, seqnum);
-
- switch (status) {
- case -ENOENT:
- /* fall through */
- case -ECONNRESET:
- dev_info(&urb->dev->dev,
- "urb %p was unlinked %ssynchronuously.\n", urb,
- status == -ENOENT ? "" : "a");
- break;
- case -EINPROGRESS:
- /* no info output */
- break;
- default:
- dev_info(&urb->dev->dev,
- "urb %p may be in a error, status %d\n", urb,
- status);
- }
-
- list_del(&priv->list);
- kfree(priv);
- urb->hcpriv = NULL;
-
- break;
- }
-
- return urb;
-}
-
-static void vhci_recv_ret_submit(struct vhci_device *vdev,
- struct usbip_header *pdu)
-{
- struct usbip_device *ud = &vdev->ud;
- struct urb *urb;
-
- spin_lock(&vdev->priv_lock);
- urb = pickup_urb_and_free_priv(vdev, pdu->base.seqnum);
- spin_unlock(&vdev->priv_lock);
-
- if (!urb) {
- pr_err("cannot find a urb of seqnum %u\n", pdu->base.seqnum);
- pr_info("max seqnum %d\n",
- atomic_read(&the_controller->seqnum));
- usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
- return;
- }
-
- /* unpack the pdu to a urb */
- usbip_pack_pdu(pdu, urb, USBIP_RET_SUBMIT, 0);
-
- /* recv transfer buffer */
- if (usbip_recv_xbuff(ud, urb) < 0)
- return;
-
- /* recv iso_packet_descriptor */
- if (usbip_recv_iso(ud, urb) < 0)
- return;
-
- /* restore the padding in iso packets */
- usbip_pad_iso(ud, urb);
-
- if (usbip_dbg_flag_vhci_rx)
- usbip_dump_urb(urb);
-
- usbip_dbg_vhci_rx("now giveback urb %p\n", urb);
-
- spin_lock(&the_controller->lock);
- usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb);
- spin_unlock(&the_controller->lock);
-
- usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status);
-
- usbip_dbg_vhci_rx("Leave\n");
-}
-
-static struct vhci_unlink *dequeue_pending_unlink(struct vhci_device *vdev,
- struct usbip_header *pdu)
-{
- struct vhci_unlink *unlink, *tmp;
-
- spin_lock(&vdev->priv_lock);
-
- list_for_each_entry_safe(unlink, tmp, &vdev->unlink_rx, list) {
- pr_info("unlink->seqnum %lu\n", unlink->seqnum);
- if (unlink->seqnum == pdu->base.seqnum) {
- usbip_dbg_vhci_rx("found pending unlink, %lu\n",
- unlink->seqnum);
- list_del(&unlink->list);
-
- spin_unlock(&vdev->priv_lock);
- return unlink;
- }
- }
-
- spin_unlock(&vdev->priv_lock);
-
- return NULL;
-}
-
-static void vhci_recv_ret_unlink(struct vhci_device *vdev,
- struct usbip_header *pdu)
-{
- struct vhci_unlink *unlink;
- struct urb *urb;
-
- usbip_dump_header(pdu);
-
- unlink = dequeue_pending_unlink(vdev, pdu);
- if (!unlink) {
- pr_info("cannot find the pending unlink %u\n",
- pdu->base.seqnum);
- return;
- }
-
- spin_lock(&vdev->priv_lock);
- urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum);
- spin_unlock(&vdev->priv_lock);
-
- if (!urb) {
- /*
- * I get the result of a unlink request. But, it seems that I
- * already received the result of its submit result and gave
- * back the URB.
- */
- pr_info("the urb (seqnum %d) was already given back\n",
- pdu->base.seqnum);
- } else {
- usbip_dbg_vhci_rx("now giveback urb %p\n", urb);
-
- /* If unlink is successful, status is -ECONNRESET */
- urb->status = pdu->u.ret_unlink.status;
- pr_info("urb->status %d\n", urb->status);
-
- spin_lock(&the_controller->lock);
- usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb);
- spin_unlock(&the_controller->lock);
-
- usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb,
- urb->status);
- }
-
- kfree(unlink);
-}
-
-static int vhci_priv_tx_empty(struct vhci_device *vdev)
-{
- int empty = 0;
-
- spin_lock(&vdev->priv_lock);
- empty = list_empty(&vdev->priv_rx);
- spin_unlock(&vdev->priv_lock);
-
- return empty;
-}
-
-/* recv a pdu */
-static void vhci_rx_pdu(struct usbip_device *ud)
-{
- int ret;
- struct usbip_header pdu;
- struct vhci_device *vdev = container_of(ud, struct vhci_device, ud);
-
- usbip_dbg_vhci_rx("Enter\n");
-
- memset(&pdu, 0, sizeof(pdu));
-
- /* receive a pdu header */
- ret = usbip_recv(ud->tcp_socket, &pdu, sizeof(pdu));
- if (ret < 0) {
- if (ret == -ECONNRESET)
- pr_info("connection reset by peer\n");
- else if (ret == -EAGAIN) {
- /* ignore if connection was idle */
- if (vhci_priv_tx_empty(vdev))
- return;
- pr_info("connection timed out with pending urbs\n");
- } else if (ret != -ERESTARTSYS)
- pr_info("xmit failed %d\n", ret);
-
- usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
- return;
- }
- if (ret == 0) {
- pr_info("connection closed");
- usbip_event_add(ud, VDEV_EVENT_DOWN);
- return;
- }
- if (ret != sizeof(pdu)) {
- pr_err("received pdu size is %d, should be %d\n", ret,
- (unsigned int)sizeof(pdu));
- usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
- return;
- }
-
- usbip_header_correct_endian(&pdu, 0);
-
- if (usbip_dbg_flag_vhci_rx)
- usbip_dump_header(&pdu);
-
- switch (pdu.base.command) {
- case USBIP_RET_SUBMIT:
- vhci_recv_ret_submit(vdev, &pdu);
- break;
- case USBIP_RET_UNLINK:
- vhci_recv_ret_unlink(vdev, &pdu);
- break;
- default:
- /* NOT REACHED */
- pr_err("unknown pdu %u\n", pdu.base.command);
- usbip_dump_header(&pdu);
- usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
- break;
- }
-}
-
-int vhci_rx_loop(void *data)
-{
- struct usbip_device *ud = data;
-
- while (!kthread_should_stop()) {
- if (usbip_event_happened(ud))
- break;
-
- vhci_rx_pdu(ud);
- }
-
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) 2003-2008 Takahiro Hirofuchi
- *
- * This 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 is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
- */
-
-#include <linux/kthread.h>
-#include <linux/file.h>
-#include <linux/net.h>
-
-#include "usbip_common.h"
-#include "vhci.h"
-
-/* TODO: refine locking ?*/
-
-/* Sysfs entry to show port status */
-static ssize_t status_show(struct device *dev, struct device_attribute *attr,
- char *out)
-{
- char *s = out;
- int i = 0;
-
- BUG_ON(!the_controller || !out);
-
- spin_lock(&the_controller->lock);
-
- /*
- * output example:
- * prt sta spd dev socket local_busid
- * 000 004 000 000 c5a7bb80 1-2.3
- * 001 004 000 000 d8cee980 2-3.4
- *
- * IP address can be retrieved from a socket pointer address by looking
- * up /proc/net/{tcp,tcp6}. Also, a userland program may remember a
- * port number and its peer IP address.
- */
- out += sprintf(out,
- "prt sta spd bus dev socket local_busid\n");
-
- for (i = 0; i < VHCI_NPORTS; i++) {
- struct vhci_device *vdev = port_to_vdev(i);
-
- spin_lock(&vdev->ud.lock);
- out += sprintf(out, "%03u %03u ", i, vdev->ud.status);
-
- if (vdev->ud.status == VDEV_ST_USED) {
- out += sprintf(out, "%03u %08x ",
- vdev->speed, vdev->devid);
- out += sprintf(out, "%16p ", vdev->ud.tcp_socket);
- out += sprintf(out, "%s", dev_name(&vdev->udev->dev));
-
- } else {
- out += sprintf(out, "000 000 000 0000000000000000 0-0");
- }
-
- out += sprintf(out, "\n");
- spin_unlock(&vdev->ud.lock);
- }
-
- spin_unlock(&the_controller->lock);
-
- return out - s;
-}
-static DEVICE_ATTR_RO(status);
-
-/* Sysfs entry to shutdown a virtual connection */
-static int vhci_port_disconnect(__u32 rhport)
-{
- struct vhci_device *vdev;
-
- usbip_dbg_vhci_sysfs("enter\n");
-
- /* lock */
- spin_lock(&the_controller->lock);
-
- vdev = port_to_vdev(rhport);
-
- spin_lock(&vdev->ud.lock);
- if (vdev->ud.status == VDEV_ST_NULL) {
- pr_err("not connected %d\n", vdev->ud.status);
-
- /* unlock */
- spin_unlock(&vdev->ud.lock);
- spin_unlock(&the_controller->lock);
-
- return -EINVAL;
- }
-
- /* unlock */
- spin_unlock(&vdev->ud.lock);
- spin_unlock(&the_controller->lock);
-
- usbip_event_add(&vdev->ud, VDEV_EVENT_DOWN);
-
- return 0;
-}
-
-static ssize_t store_detach(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- int err;
- __u32 rhport = 0;
-
- if (sscanf(buf, "%u", &rhport) != 1)
- return -EINVAL;
-
- /* check rhport */
- if (rhport >= VHCI_NPORTS) {
- dev_err(dev, "invalid port %u\n", rhport);
- return -EINVAL;
- }
-
- err = vhci_port_disconnect(rhport);
- if (err < 0)
- return -EINVAL;
-
- usbip_dbg_vhci_sysfs("Leave\n");
-
- return count;
-}
-static DEVICE_ATTR(detach, S_IWUSR, NULL, store_detach);
-
-/* Sysfs entry to establish a virtual connection */
-static int valid_args(__u32 rhport, enum usb_device_speed speed)
-{
- /* check rhport */
- if (rhport >= VHCI_NPORTS) {
- pr_err("port %u\n", rhport);
- return -EINVAL;
- }
-
- /* check speed */
- switch (speed) {
- case USB_SPEED_LOW:
- case USB_SPEED_FULL:
- case USB_SPEED_HIGH:
- case USB_SPEED_WIRELESS:
- break;
- default:
- pr_err("Failed attach request for unsupported USB speed: %s\n",
- usb_speed_string(speed));
- return -EINVAL;
- }
-
- return 0;
-}
-
-/*
- * To start a new USB/IP attachment, a userland program needs to setup a TCP
- * connection and then write its socket descriptor with remote device
- * information into this sysfs file.
- *
- * A remote device is virtually attached to the root-hub port of @rhport with
- * @speed. @devid is embedded into a request to specify the remote device in a
- * server host.
- *
- * write() returns 0 on success, else negative errno.
- */
-static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct vhci_device *vdev;
- struct socket *socket;
- int sockfd = 0;
- __u32 rhport = 0, devid = 0, speed = 0;
- int err;
-
- /*
- * @rhport: port number of vhci_hcd
- * @sockfd: socket descriptor of an established TCP connection
- * @devid: unique device identifier in a remote host
- * @speed: usb device speed in a remote host
- */
- if (sscanf(buf, "%u %u %u %u", &rhport, &sockfd, &devid, &speed) != 4)
- return -EINVAL;
-
- usbip_dbg_vhci_sysfs("rhport(%u) sockfd(%u) devid(%u) speed(%u)\n",
- rhport, sockfd, devid, speed);
-
- /* check received parameters */
- if (valid_args(rhport, speed) < 0)
- return -EINVAL;
-
- /* Extract socket from fd. */
- socket = sockfd_lookup(sockfd, &err);
- if (!socket)
- return -EINVAL;
-
- /* now need lock until setting vdev status as used */
-
- /* begin a lock */
- spin_lock(&the_controller->lock);
- vdev = port_to_vdev(rhport);
- spin_lock(&vdev->ud.lock);
-
- if (vdev->ud.status != VDEV_ST_NULL) {
- /* end of the lock */
- spin_unlock(&vdev->ud.lock);
- spin_unlock(&the_controller->lock);
-
- sockfd_put(socket);
-
- dev_err(dev, "port %d already used\n", rhport);
- return -EINVAL;
- }
-
- dev_info(dev,
- "rhport(%u) sockfd(%d) devid(%u) speed(%u) speed_str(%s)\n",
- rhport, sockfd, devid, speed, usb_speed_string(speed));
-
- vdev->devid = devid;
- vdev->speed = speed;
- vdev->ud.tcp_socket = socket;
- vdev->ud.status = VDEV_ST_NOTASSIGNED;
-
- spin_unlock(&vdev->ud.lock);
- spin_unlock(&the_controller->lock);
- /* end the lock */
-
- vdev->ud.tcp_rx = kthread_get_run(vhci_rx_loop, &vdev->ud, "vhci_rx");
- vdev->ud.tcp_tx = kthread_get_run(vhci_tx_loop, &vdev->ud, "vhci_tx");
-
- rh_port_connect(rhport, speed);
-
- return count;
-}
-static DEVICE_ATTR(attach, S_IWUSR, NULL, store_attach);
-
-static struct attribute *dev_attrs[] = {
- &dev_attr_status.attr,
- &dev_attr_detach.attr,
- &dev_attr_attach.attr,
- &dev_attr_usbip_debug.attr,
- NULL,
-};
-
-const struct attribute_group dev_attr_group = {
- .attrs = dev_attrs,
-};
+++ /dev/null
-/*
- * Copyright (C) 2003-2008 Takahiro Hirofuchi
- *
- * This 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 is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
- */
-
-#include <linux/kthread.h>
-#include <linux/slab.h>
-
-#include "usbip_common.h"
-#include "vhci.h"
-
-static void setup_cmd_submit_pdu(struct usbip_header *pdup, struct urb *urb)
-{
- struct vhci_priv *priv = ((struct vhci_priv *)urb->hcpriv);
- struct vhci_device *vdev = priv->vdev;
-
- usbip_dbg_vhci_tx("URB, local devnum %u, remote devid %u\n",
- usb_pipedevice(urb->pipe), vdev->devid);
-
- pdup->base.command = USBIP_CMD_SUBMIT;
- pdup->base.seqnum = priv->seqnum;
- pdup->base.devid = vdev->devid;
- pdup->base.direction = usb_pipein(urb->pipe) ?
- USBIP_DIR_IN : USBIP_DIR_OUT;
- pdup->base.ep = usb_pipeendpoint(urb->pipe);
-
- usbip_pack_pdu(pdup, urb, USBIP_CMD_SUBMIT, 1);
-
- if (urb->setup_packet)
- memcpy(pdup->u.cmd_submit.setup, urb->setup_packet, 8);
-}
-
-static struct vhci_priv *dequeue_from_priv_tx(struct vhci_device *vdev)
-{
- struct vhci_priv *priv, *tmp;
-
- spin_lock(&vdev->priv_lock);
-
- list_for_each_entry_safe(priv, tmp, &vdev->priv_tx, list) {
- list_move_tail(&priv->list, &vdev->priv_rx);
- spin_unlock(&vdev->priv_lock);
- return priv;
- }
-
- spin_unlock(&vdev->priv_lock);
-
- return NULL;
-}
-
-static int vhci_send_cmd_submit(struct vhci_device *vdev)
-{
- struct vhci_priv *priv = NULL;
-
- struct msghdr msg;
- struct kvec iov[3];
- size_t txsize;
-
- size_t total_size = 0;
-
- while ((priv = dequeue_from_priv_tx(vdev)) != NULL) {
- int ret;
- struct urb *urb = priv->urb;
- struct usbip_header pdu_header;
- struct usbip_iso_packet_descriptor *iso_buffer = NULL;
-
- txsize = 0;
- memset(&pdu_header, 0, sizeof(pdu_header));
- memset(&msg, 0, sizeof(msg));
- memset(&iov, 0, sizeof(iov));
-
- usbip_dbg_vhci_tx("setup txdata urb %p\n", urb);
-
- /* 1. setup usbip_header */
- setup_cmd_submit_pdu(&pdu_header, urb);
- usbip_header_correct_endian(&pdu_header, 1);
-
- iov[0].iov_base = &pdu_header;
- iov[0].iov_len = sizeof(pdu_header);
- txsize += sizeof(pdu_header);
-
- /* 2. setup transfer buffer */
- if (!usb_pipein(urb->pipe) && urb->transfer_buffer_length > 0) {
- iov[1].iov_base = urb->transfer_buffer;
- iov[1].iov_len = urb->transfer_buffer_length;
- txsize += urb->transfer_buffer_length;
- }
-
- /* 3. setup iso_packet_descriptor */
- if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
- ssize_t len = 0;
-
- iso_buffer = usbip_alloc_iso_desc_pdu(urb, &len);
- if (!iso_buffer) {
- usbip_event_add(&vdev->ud,
- SDEV_EVENT_ERROR_MALLOC);
- return -1;
- }
-
- iov[2].iov_base = iso_buffer;
- iov[2].iov_len = len;
- txsize += len;
- }
-
- ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 3, txsize);
- if (ret != txsize) {
- pr_err("sendmsg failed!, ret=%d for %zd\n", ret,
- txsize);
- kfree(iso_buffer);
- usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_TCP);
- return -1;
- }
-
- kfree(iso_buffer);
- usbip_dbg_vhci_tx("send txdata\n");
-
- total_size += txsize;
- }
-
- return total_size;
-}
-
-static struct vhci_unlink *dequeue_from_unlink_tx(struct vhci_device *vdev)
-{
- struct vhci_unlink *unlink, *tmp;
-
- spin_lock(&vdev->priv_lock);
-
- list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) {
- list_move_tail(&unlink->list, &vdev->unlink_rx);
- spin_unlock(&vdev->priv_lock);
- return unlink;
- }
-
- spin_unlock(&vdev->priv_lock);
-
- return NULL;
-}
-
-static int vhci_send_cmd_unlink(struct vhci_device *vdev)
-{
- struct vhci_unlink *unlink = NULL;
-
- struct msghdr msg;
- struct kvec iov[3];
- size_t txsize;
-
- size_t total_size = 0;
-
- while ((unlink = dequeue_from_unlink_tx(vdev)) != NULL) {
- int ret;
- struct usbip_header pdu_header;
-
- txsize = 0;
- memset(&pdu_header, 0, sizeof(pdu_header));
- memset(&msg, 0, sizeof(msg));
- memset(&iov, 0, sizeof(iov));
-
- usbip_dbg_vhci_tx("setup cmd unlink, %lu\n", unlink->seqnum);
-
- /* 1. setup usbip_header */
- pdu_header.base.command = USBIP_CMD_UNLINK;
- pdu_header.base.seqnum = unlink->seqnum;
- pdu_header.base.devid = vdev->devid;
- pdu_header.base.ep = 0;
- pdu_header.u.cmd_unlink.seqnum = unlink->unlink_seqnum;
-
- usbip_header_correct_endian(&pdu_header, 1);
-
- iov[0].iov_base = &pdu_header;
- iov[0].iov_len = sizeof(pdu_header);
- txsize += sizeof(pdu_header);
-
- ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 1, txsize);
- if (ret != txsize) {
- pr_err("sendmsg failed!, ret=%d for %zd\n", ret,
- txsize);
- usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_TCP);
- return -1;
- }
-
- usbip_dbg_vhci_tx("send txdata\n");
-
- total_size += txsize;
- }
-
- return total_size;
-}
-
-int vhci_tx_loop(void *data)
-{
- struct usbip_device *ud = data;
- struct vhci_device *vdev = container_of(ud, struct vhci_device, ud);
-
- while (!kthread_should_stop()) {
- if (vhci_send_cmd_submit(vdev) < 0)
- break;
-
- if (vhci_send_cmd_unlink(vdev) < 0)
- break;
-
- wait_event_interruptible(vdev->waitq_tx,
- (!list_empty(&vdev->priv_tx) ||
- !list_empty(&vdev->unlink_tx) ||
- kthread_should_stop()));
-
- usbip_dbg_vhci_tx("pending urbs ?, now wake up\n");
- }
-
- return 0;
-}
/* Activate hops. */
for (i = path->path_length - 1; i >= 0; i--) {
- struct tb_regs_hop hop;
+ struct tb_regs_hop hop = { 0 };
+
+ /*
+ * We do (currently) not tear down paths setup by the firmeware.
+ * If a firmware device is unplugged and plugged in again then
+ * it can happen that we reuse some of the hops from the (now
+ * defunct) firmeware path. This causes the hotplug operation to
+ * fail (the pci device does not show up). Clearing the hop
+ * before overwriting it fixes the problem.
+ *
+ * Should be removed once we discover and tear down firmeware
+ * paths.
+ */
+ res = tb_port_write(path->hops[i].in_port, &hop, TB_CFG_HOPS,
+ 2 * path->hops[i].in_hop_index, 2);
+ if (res) {
+ __tb_path_deactivate_hops(path, i);
+ __tb_path_deallocate_nfc(path, 0);
+ goto err;
+ }
/* dword 0 */
hop.next_hop = path->hops[i].next_hop_index;
source "drivers/usb/image/Kconfig"
+source "drivers/usb/usbip/Kconfig"
+
endif
source "drivers/usb/musb/Kconfig"
obj-$(CONFIG_USB_GADGET) += gadget/
obj-$(CONFIG_USB_COMMON) += common/
+
+obj-$(CONFIG_USBIP_CORE) += usbip/
* - Change autosuspend delay of hub can avoid unnecessary auto
* suspend timer for hub, also may decrease power consumption
* of USB bus.
+ *
+ * - If user has indicated to prevent autosuspend by passing
+ * usbcore.autosuspend = -1 then keep autosuspend disabled.
*/
- pm_runtime_set_autosuspend_delay(&hdev->dev, 0);
+#ifdef CONFIG_PM_RUNTIME
+ if (hdev->dev.power.autosuspend_delay >= 0)
+ pm_runtime_set_autosuspend_delay(&hdev->dev, 0);
+#endif
/*
* Hubs have proper suspend/resume support, except for root hubs
{
struct usb_port *port_dev = NULL;
struct usb_device *udev = *pdev;
- struct usb_hub *hub;
- int port1;
+ struct usb_hub *hub = NULL;
+ int port1 = 1;
/* mark the device as inactive, so any further urb submissions for
* this device (and any of its children) will fail immediately.
if (status != -ENODEV &&
port1 != unreliable_port &&
printk_ratelimit())
- dev_err(&udev->dev, "connect-debounce failed, port %d disabled\n",
- port1);
-
+ dev_err(&port_dev->dev, "connect-debounce failed\n");
portstatus &= ~USB_PORT_STAT_CONNECTION;
unreliable_port = port1;
} else {
static void s3c_hsotg_irq_enumdone(struct s3c_hsotg *hsotg)
{
u32 dsts = readl(hsotg->regs + DSTS);
- int ep0_mps = 0, ep_mps;
+ int ep0_mps = 0, ep_mps = 8;
/*
* This should signal the finish of the enumeration phase
static int dwc3_omap_extcon_register(struct dwc3_omap *omap)
{
- u32 ret;
+ int ret;
struct device_node *node = omap->dev->of_node;
struct extcon_dev *edev;
#
subdir-ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG
subdir-ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG
-ccflags-y += -I$(PWD)/drivers/usb/gadget/udc
+ccflags-y += -Idrivers/usb/gadget/udc
obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o
libcomposite-y := usbstring.o config.o epautoconf.o
# USB peripheral controller drivers
#
-ccflags-y := -I$(PWD)/drivers/usb/gadget/
-ccflags-y += -I$(PWD)/drivers/usb/gadget/udc/
+ccflags-y := -Idrivers/usb/gadget/
+ccflags-y += -Idrivers/usb/gadget/udc/
# USB Functions
usb_f_acm-y := f_acm.o
DBG(dev, "%s\n", __func__);
- netif_tx_lock(dev->net);
netif_stop_queue(dev->net);
- netif_tx_unlock(dev->net);
-
netif_carrier_off(dev->net);
/* disable endpoints, forcing (synchronous) completion
printk(KERN_INFO "Failed to queue request (%d).\n", ret);
usb_ep_set_halt(ep);
spin_unlock_irqrestore(&video->queue.irqlock, flags);
+ uvc_queue_cancel(queue, 0);
goto requeue;
}
spin_unlock_irqrestore(&video->queue.irqlock, flags);
static int
uvc_video_pump(struct uvc_video *video)
{
+ struct uvc_video_queue *queue = &video->queue;
struct usb_request *req;
struct uvc_buffer *buf;
unsigned long flags;
printk(KERN_INFO "Failed to queue request (%d)\n", ret);
usb_ep_set_halt(video->ep);
spin_unlock_irqrestore(&video->queue.irqlock, flags);
+ uvc_queue_cancel(queue, 0);
break;
}
spin_unlock_irqrestore(&video->queue.irqlock, flags);
# USB gadget drivers
#
-ccflags-y := -I$(PWD)/drivers/usb/gadget/
-ccflags-y += -I$(PWD)/drivers/usb/gadget/udc/
-ccflags-y += -I$(PWD)/drivers/usb/gadget/function/
+ccflags-y := -Idrivers/usb/gadget/
+ccflags-y += -Idrivers/usb/gadget/udc/
+ccflags-y += -Idrivers/usb/gadget/function/
g_zero-y := zero.o
g_audio-y := audio.o
{
#ifdef CONFIG_USB_G_DBGP_SERIAL
kfree(dbgp.serial);
+ dbgp.serial = NULL;
#endif
if (dbgp.req) {
kfree(dbgp.req->buf);
usb_ep_free_request(gadget->ep0, dbgp.req);
+ dbgp.req = NULL;
}
gadget->ep0->driver_data = NULL;
value = -ENOMEM;
kbuf = memdup_user(buf, len);
- if (!kbuf) {
+ if (IS_ERR(kbuf)) {
value = PTR_ERR(kbuf);
goto free1;
}
gadget drivers to also be dynamically linked.
config USB_EG20T
- tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC"
+ tristate "Intel QUARK X1000/EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC"
depends on PCI
help
This is a USB device driver for EG20T PCH.
ML7213/ML7831 is companion chip for Intel Atom E6xx series.
ML7213/ML7831 is completely compatible for Intel EG20T PCH.
+ This driver can be used with Intel's Quark X1000 SOC platform
#
# LAST -- dummy/emulated controller
#
if (dma_status) {
int i;
- for (i = 1; i < USBA_NR_DMAS; i++)
+ for (i = 1; i <= USBA_NR_DMAS; i++)
if (dma_status & (1 << i))
usba_dma_irq(udc, &udc->usba_ep[i]);
}
/* initialize udc */
fusb300 = kzalloc(sizeof(struct fusb300), GFP_KERNEL);
- if (fusb300 == NULL)
+ if (fusb300 == NULL) {
+ ret = -ENOMEM;
goto clean_up;
+ }
for (i = 0; i < FUSB300_MAX_NUM_EP; i++) {
_ep[i] = kzalloc(sizeof(struct fusb300_ep), GFP_KERNEL);
- if (_ep[i] == NULL)
+ if (_ep[i] == NULL) {
+ ret = -ENOMEM;
goto clean_up;
+ }
fusb300->ep[i] = _ep[i];
}
* @setup_data: Received setup data
* @phys_addr: of device memory
* @base_addr: for mapped device memory
+ * @bar: Indicates which PCI BAR for USB regs
* @irq: IRQ line for the device
* @cfg_data: current cfg, intf, and alt in use
* @vbus_gpio: GPIO informaton for detecting VBUS
struct usb_ctrlrequest setup_data;
unsigned long phys_addr;
void __iomem *base_addr;
+ unsigned bar;
unsigned irq;
struct pch_udc_cfg_data cfg_data;
struct pch_vbus_gpio_data vbus_gpio;
};
#define to_pch_udc(g) (container_of((g), struct pch_udc_dev, gadget))
+#define PCH_UDC_PCI_BAR_QUARK_X1000 0
#define PCH_UDC_PCI_BAR 1
#define PCI_DEVICE_ID_INTEL_EG20T_UDC 0x8808
+#define PCI_DEVICE_ID_INTEL_QUARK_X1000_UDC 0x0939
#define PCI_VENDOR_ID_ROHM 0x10DB
#define PCI_DEVICE_ID_ML7213_IOH_UDC 0x801D
#define PCI_DEVICE_ID_ML7831_IOH_UDC 0x8808
iounmap(dev->base_addr);
if (dev->mem_region)
release_mem_region(dev->phys_addr,
- pci_resource_len(pdev, PCH_UDC_PCI_BAR));
+ pci_resource_len(pdev, dev->bar));
if (dev->active)
pci_disable_device(pdev);
kfree(dev);
dev->active = 1;
pci_set_drvdata(pdev, dev);
+ /* Determine BAR based on PCI ID */
+ if (id->device == PCI_DEVICE_ID_INTEL_QUARK_X1000_UDC)
+ dev->bar = PCH_UDC_PCI_BAR_QUARK_X1000;
+ else
+ dev->bar = PCH_UDC_PCI_BAR;
+
/* PCI resource allocation */
- resource = pci_resource_start(pdev, 1);
- len = pci_resource_len(pdev, 1);
+ resource = pci_resource_start(pdev, dev->bar);
+ len = pci_resource_len(pdev, dev->bar);
if (!request_mem_region(resource, len, KBUILD_MODNAME)) {
dev_err(&pdev->dev, "%s: pci device used already\n", __func__);
}
static const struct pci_device_id pch_udc_pcidev_id[] = {
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_QUARK_X1000_UDC),
+ .class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe,
+ .class_mask = 0xffffffff,
+ },
{
PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EG20T_UDC),
.class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe,
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
reg = devm_ioremap_resource(&pdev->dev, res);
- if (!reg)
- return -ENODEV;
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
irq = ires->start;
if (selector == EHSET_TEST_SINGLE_STEP_SET_FEATURE) {
spin_unlock_irqrestore(&ehci->lock, flags);
retval = ehset_single_step_set_feature(hcd,
- wIndex);
+ wIndex + 1);
spin_lock_irqsave(&ehci->lock, flags);
break;
}
/* AMD PLL quirk */
if (pdev->vendor == PCI_VENDOR_ID_AMD && usb_amd_find_chipset_info())
xhci->quirks |= XHCI_AMD_PLL_FIX;
+
+ if (pdev->vendor == PCI_VENDOR_ID_AMD)
+ xhci->quirks |= XHCI_TRUST_TX_LENGTH;
+
if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
xhci->quirks |= XHCI_LPM_SUPPORT;
xhci->quirks |= XHCI_INTEL_HOST;
if (pdev->vendor == PCI_VENDOR_ID_VIA)
xhci->quirks |= XHCI_RESET_ON_RESUME;
+ /* See https://bugzilla.kernel.org/show_bug.cgi?id=79511 */
+ if (pdev->vendor == PCI_VENDOR_ID_VIA &&
+ pdev->device == 0x3432)
+ xhci->quirks |= XHCI_BROKEN_STREAMS;
+
if (xhci->quirks & XHCI_RESET_ON_RESUME)
xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
"QUIRK: Resetting on resume");
}
}
-/*
- * Find the segment that trb is in. Start searching in start_seg.
- * If we must move past a segment that has a link TRB with a toggle cycle state
- * bit set, then we will toggle the value pointed at by cycle_state.
- */
-static struct xhci_segment *find_trb_seg(
- struct xhci_segment *start_seg,
- union xhci_trb *trb, int *cycle_state)
-{
- struct xhci_segment *cur_seg = start_seg;
- struct xhci_generic_trb *generic_trb;
-
- while (cur_seg->trbs > trb ||
- &cur_seg->trbs[TRBS_PER_SEGMENT - 1] < trb) {
- generic_trb = &cur_seg->trbs[TRBS_PER_SEGMENT - 1].generic;
- if (generic_trb->field[3] & cpu_to_le32(LINK_TOGGLE))
- *cycle_state ^= 0x1;
- cur_seg = cur_seg->next;
- if (cur_seg == start_seg)
- /* Looped over the entire list. Oops! */
- return NULL;
- }
- return cur_seg;
-}
-
-
static struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci,
unsigned int slot_id, unsigned int ep_index,
unsigned int stream_id)
struct xhci_virt_device *dev = xhci->devs[slot_id];
struct xhci_virt_ep *ep = &dev->eps[ep_index];
struct xhci_ring *ep_ring;
- struct xhci_generic_trb *trb;
+ struct xhci_segment *new_seg;
+ union xhci_trb *new_deq;
dma_addr_t addr;
u64 hw_dequeue;
+ bool cycle_found = false;
+ bool td_last_trb_found = false;
ep_ring = xhci_triad_to_transfer_ring(xhci, slot_id,
ep_index, stream_id);
hw_dequeue = le64_to_cpu(ep_ctx->deq);
}
- /* Find virtual address and segment of hardware dequeue pointer */
- state->new_deq_seg = ep_ring->deq_seg;
- state->new_deq_ptr = ep_ring->dequeue;
- while (xhci_trb_virt_to_dma(state->new_deq_seg, state->new_deq_ptr)
- != (dma_addr_t)(hw_dequeue & ~0xf)) {
- next_trb(xhci, ep_ring, &state->new_deq_seg,
- &state->new_deq_ptr);
- if (state->new_deq_ptr == ep_ring->dequeue) {
- WARN_ON(1);
- return;
- }
- }
+ new_seg = ep_ring->deq_seg;
+ new_deq = ep_ring->dequeue;
+ state->new_cycle_state = hw_dequeue & 0x1;
+
/*
- * Find cycle state for last_trb, starting at old cycle state of
- * hw_dequeue. If there is only one segment ring, find_trb_seg() will
- * return immediately and cannot toggle the cycle state if this search
- * wraps around, so add one more toggle manually in that case.
+ * We want to find the pointer, segment and cycle state of the new trb
+ * (the one after current TD's last_trb). We know the cycle state at
+ * hw_dequeue, so walk the ring until both hw_dequeue and last_trb are
+ * found.
*/
- state->new_cycle_state = hw_dequeue & 0x1;
- if (ep_ring->first_seg == ep_ring->first_seg->next &&
- cur_td->last_trb < state->new_deq_ptr)
- state->new_cycle_state ^= 0x1;
+ do {
+ if (!cycle_found && xhci_trb_virt_to_dma(new_seg, new_deq)
+ == (dma_addr_t)(hw_dequeue & ~0xf)) {
+ cycle_found = true;
+ if (td_last_trb_found)
+ break;
+ }
+ if (new_deq == cur_td->last_trb)
+ td_last_trb_found = true;
- state->new_deq_ptr = cur_td->last_trb;
- xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
- "Finding segment containing last TRB in TD.");
- state->new_deq_seg = find_trb_seg(state->new_deq_seg,
- state->new_deq_ptr, &state->new_cycle_state);
- if (!state->new_deq_seg) {
- WARN_ON(1);
- return;
- }
+ if (cycle_found &&
+ TRB_TYPE_LINK_LE32(new_deq->generic.field[3]) &&
+ new_deq->generic.field[3] & cpu_to_le32(LINK_TOGGLE))
+ state->new_cycle_state ^= 0x1;
+
+ next_trb(xhci, ep_ring, &new_seg, &new_deq);
+
+ /* Search wrapped around, bail out */
+ if (new_deq == ep->ring->dequeue) {
+ xhci_err(xhci, "Error: Failed finding new dequeue state\n");
+ state->new_deq_seg = NULL;
+ state->new_deq_ptr = NULL;
+ return;
+ }
+
+ } while (!cycle_found || !td_last_trb_found);
- /* Increment to find next TRB after last_trb. Cycle if appropriate. */
- trb = &state->new_deq_ptr->generic;
- if (TRB_TYPE_LINK_LE32(trb->field[3]) &&
- (trb->field[3] & cpu_to_le32(LINK_TOGGLE)))
- state->new_cycle_state ^= 0x1;
- next_trb(xhci, ep_ring, &state->new_deq_seg, &state->new_deq_ptr);
+ state->new_deq_seg = new_seg;
+ state->new_deq_ptr = new_deq;
/* Don't update the ring cycle state for the producer (us). */
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
* last TRB of the previous TD. The command completion handle
* will take care the rest.
*/
- if (!event_seg && trb_comp_code == COMP_STOP_INVAL) {
+ if (!event_seg && (trb_comp_code == COMP_STOP ||
+ trb_comp_code == COMP_STOP_INVAL)) {
ret = 0;
goto cleanup;
}
ep_index, ep->stopped_stream, ep->stopped_td,
&deq_state);
+ if (!deq_state.new_deq_ptr || !deq_state.new_deq_seg)
+ return;
+
/* HW with the reset endpoint quirk will use the saved dequeue state to
* issue a configure endpoint command later.
*/
{ USB_DEVICE(0x0711, 0x0918) },
{ USB_DEVICE(0x0711, 0x0920) },
{ USB_DEVICE(0x0711, 0x0950) },
+ { USB_DEVICE(0x0711, 0x5200) },
{ USB_DEVICE(0x182d, 0x021c) },
{ USB_DEVICE(0x182d, 0x0269) },
{ }
struct musb *musb = ux500_channel->controller->private_data;
dev_dbg(musb->controller,
- "packet_sz=%d, mode=%d, dma_addr=0x%llu, len=%d is_tx=%d\n",
+ "packet_sz=%d, mode=%d, dma_addr=0x%llx, len=%d is_tx=%d\n",
packet_sz, mode, (unsigned long long) dma_addr,
len, ux500_channel->is_tx);
gpio_vbus->phy.otg = devm_kzalloc(&pdev->dev, sizeof(struct usb_otg),
GFP_KERNEL);
- if (!gpio_vbus->phy.otg) {
- kfree(gpio_vbus);
+ if (!gpio_vbus->phy.otg)
return -ENOMEM;
- }
platform_set_drvdata(pdev, gpio_vbus);
gpio_vbus->dev = &pdev->dev;
*/
if (motg->phy_number) {
phy_select = devm_ioremap_nocache(&pdev->dev, USB2_PHY_SEL, 4);
- if (IS_ERR(phy_select))
- return PTR_ERR(phy_select);
+ if (!phy_select)
+ return -ENOMEM;
/* Enable second PHY with the OTG port */
writel(0x1, phy_select);
}
#define EXYNOS5_DRD_PHYPARAM1 (0x20)
-#define PHYPARAM1_PCS_TXDEEMPH_MASK (0x1f << 0)
+#define PHYPARAM1_PCS_TXDEEMPH_MASK (0x3f << 0)
#define PHYPARAM1_PCS_TXDEEMPH (0x1c)
#define EXYNOS5_DRD_PHYTERM (0x24)
phy = __usb_find_phy_dev(dev, &phy_bind_list, index);
if (IS_ERR(phy) || !try_module_get(phy->dev->driver->owner)) {
dev_dbg(dev, "unable to find transceiver\n");
+ if (!IS_ERR(phy))
+ phy = ERR_PTR(-ENODEV);
+
goto err0;
}
{ USB_DEVICE(FTDI_VID, FTDI_AMC232_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_CANUSB_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_CANDAPTER_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_BM_ATOM_NANO_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_NXTCAM_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_EV3CON_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_0_PID) },
{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_842_2_PID) },
{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_842_3_PID) },
{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_842_4_PID) },
+ /* ekey Devices */
+ { USB_DEVICE(FTDI_VID, FTDI_EKEY_CONV_USB_PID) },
/* Infineon Devices */
{ USB_DEVICE_INTERFACE_NUMBER(INFINEON_VID, INFINEON_TRIBOARD_PID, 1) },
{ } /* Terminating entry */
/* www.candapter.com Ewert Energy Systems CANdapter device */
#define FTDI_CANDAPTER_PID 0x9F80 /* Product Id */
+#define FTDI_BM_ATOM_NANO_PID 0xa559 /* Basic Micro ATOM Nano USB2Serial */
+
/*
* Texas Instruments XDS100v2 JTAG / BeagleBone A3
* http://processors.wiki.ti.com/index.php/XDS100
#define BRAINBOXES_US_160_6_PID 0x9006 /* US-160 16xRS232 1Mbaud Port 11 and 12 */
#define BRAINBOXES_US_160_7_PID 0x9007 /* US-160 16xRS232 1Mbaud Port 13 and 14 */
#define BRAINBOXES_US_160_8_PID 0x9008 /* US-160 16xRS232 1Mbaud Port 15 and 16 */
+
+/*
+ * ekey biometric systems GmbH (http://ekey.net/)
+ */
+#define FTDI_EKEY_CONV_USB_PID 0xCB08 /* Converter USB */
#define ZTE_PRODUCT_MF622 0x0001
#define ZTE_PRODUCT_MF628 0x0015
#define ZTE_PRODUCT_MF626 0x0031
-#define ZTE_PRODUCT_MC2718 0xffe8
#define ZTE_PRODUCT_AC2726 0xfff1
+#define ZTE_PRODUCT_CDMA_TECH 0xfffe
+#define ZTE_PRODUCT_AC8710T 0xffff
+#define ZTE_PRODUCT_MC2718 0xffe8
+#define ZTE_PRODUCT_AD3812 0xffeb
+#define ZTE_PRODUCT_MC2716 0xffed
#define BENQ_VENDOR_ID 0x04a5
#define BENQ_PRODUCT_H10 0x4068
#define INOVIA_VENDOR_ID 0x20a6
#define INOVIA_SEW858 0x1105
+/* VIA Telecom */
+#define VIATELECOM_VENDOR_ID 0x15eb
+#define VIATELECOM_PRODUCT_CDS7 0x0001
+
/* some devices interfaces need special handling due to a number of reasons */
enum option_blacklist_reason {
OPTION_BLACKLIST_NONE = 0,
.reserved = BIT(4),
};
+static const struct option_blacklist_info zte_ad3812_z_blacklist = {
+ .sendsetup = BIT(0) | BIT(1) | BIT(2),
+};
+
static const struct option_blacklist_info zte_mc2718_z_blacklist = {
.sendsetup = BIT(1) | BIT(2) | BIT(3) | BIT(4),
};
+static const struct option_blacklist_info zte_mc2716_z_blacklist = {
+ .sendsetup = BIT(1) | BIT(2) | BIT(3),
+};
+
static const struct option_blacklist_info huawei_cdc12_blacklist = {
.reserved = BIT(1) | BIT(2),
};
{ USB_DEVICE_INTERFACE_CLASS(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1012, 0xff) },
{ USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_PRODUCT_KPC650) },
{ USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_PRODUCT_KPC680) },
+ { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6000)}, /* ZTE AC8700 */
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x0023)}, /* ONYX 3G device */
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x9000)}, /* SIMCom SIM5218 */
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff93, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff94, 0xff, 0xff, 0xff) },
- /* NOTE: most ZTE CDMA devices should be driven by zte_ev, not option */
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_CDMA_TECH, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC2726, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC8710T, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MC2718, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t)&zte_mc2718_z_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AD3812, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&zte_ad3812_z_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MC2716, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&zte_mc2716_z_blacklist },
{ USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x01) },
{ USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x05) },
{ USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x86, 0x10) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC2726, 0xff, 0xff, 0xff) },
{ USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_H10) },
{ USB_DEVICE(DLINK_VENDOR_ID, DLINK_PRODUCT_DWM_652) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) }, /* D-Link DWM-152/C1 */
{ USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e02, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/C1 */
{ USB_DEVICE(INOVIA_VENDOR_ID, INOVIA_SEW858) },
+ { USB_DEVICE(VIATELECOM_VENDOR_ID, VIATELECOM_PRODUCT_CDS7) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, option_ids);
dev_dbg(dev, "%s: type %x req %x\n", __func__,
req_pkt->bRequestType, req_pkt->bRequest);
}
+ } else if (status == -ENOENT || status == -ESHUTDOWN) {
+ dev_dbg(dev, "%s: urb stopped: %d\n", __func__, status);
} else
dev_err(dev, "%s: error %d\n", __func__, status);
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GPRS) },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_HCR331) },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MOTOROLA) },
+ { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_ZTEK) },
{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) },
{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) },
{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) },
#define PL2303_PRODUCT_ID_GPRS 0x0609
#define PL2303_PRODUCT_ID_HCR331 0x331a
#define PL2303_PRODUCT_ID_MOTOROLA 0x0307
+#define PL2303_PRODUCT_ID_ZTEK 0xe1f1
#define ATEN_VENDOR_ID 0x0557
#define ATEN_VENDOR_ID2 0x0547
if (usb_endpoint_is_bulk_in(endpoint)) {
/* we found a bulk in endpoint */
dev_dbg(ddev, "found bulk in on endpoint %d\n", i);
- bulk_in_endpoint[num_bulk_in] = endpoint;
- ++num_bulk_in;
+ if (num_bulk_in < MAX_NUM_PORTS) {
+ bulk_in_endpoint[num_bulk_in] = endpoint;
+ ++num_bulk_in;
+ }
}
if (usb_endpoint_is_bulk_out(endpoint)) {
/* we found a bulk out endpoint */
dev_dbg(ddev, "found bulk out on endpoint %d\n", i);
- bulk_out_endpoint[num_bulk_out] = endpoint;
- ++num_bulk_out;
+ if (num_bulk_out < MAX_NUM_PORTS) {
+ bulk_out_endpoint[num_bulk_out] = endpoint;
+ ++num_bulk_out;
+ }
}
if (usb_endpoint_is_int_in(endpoint)) {
/* we found a interrupt in endpoint */
dev_dbg(ddev, "found interrupt in on endpoint %d\n", i);
- interrupt_in_endpoint[num_interrupt_in] = endpoint;
- ++num_interrupt_in;
+ if (num_interrupt_in < MAX_NUM_PORTS) {
+ interrupt_in_endpoint[num_interrupt_in] =
+ endpoint;
+ ++num_interrupt_in;
+ }
}
if (usb_endpoint_is_int_out(endpoint)) {
/* we found an interrupt out endpoint */
dev_dbg(ddev, "found interrupt out on endpoint %d\n", i);
- interrupt_out_endpoint[num_interrupt_out] = endpoint;
- ++num_interrupt_out;
+ if (num_interrupt_out < MAX_NUM_PORTS) {
+ interrupt_out_endpoint[num_interrupt_out] =
+ endpoint;
+ ++num_interrupt_out;
+ }
}
}
if (usb_endpoint_is_int_in(endpoint)) {
/* we found a interrupt in endpoint */
dev_dbg(ddev, "found interrupt in for Prolific device on separate interface\n");
- interrupt_in_endpoint[num_interrupt_in] = endpoint;
- ++num_interrupt_in;
+ if (num_interrupt_in < MAX_NUM_PORTS) {
+ interrupt_in_endpoint[num_interrupt_in] = endpoint;
+ ++num_interrupt_in;
+ }
}
}
}
num_ports = type->num_ports;
}
+ if (num_ports > MAX_NUM_PORTS) {
+ dev_warn(ddev, "too many ports requested: %d\n", num_ports);
+ num_ports = MAX_NUM_PORTS;
+ }
+
serial->num_ports = num_ports;
serial->num_bulk_in = num_bulk_in;
serial->num_bulk_out = num_bulk_out;
dev_dbg(&urb->dev->dev, "%s - command_info is NULL, exiting.\n", __func__);
return;
}
+ if (!urb->actual_length) {
+ dev_dbg(&urb->dev->dev, "%s - empty response, exiting.\n", __func__);
+ return;
+ }
if (status) {
dev_dbg(&urb->dev->dev, "%s - nonzero urb status: %d\n", __func__, status);
if (status != -ENOENT)
/* These are unsolicited reports from the firmware, hence no
waiting command to wakeup */
dev_dbg(&urb->dev->dev, "%s - event received\n", __func__);
- } else if (data[0] == WHITEHEAT_GET_DTR_RTS) {
+ } else if ((data[0] == WHITEHEAT_GET_DTR_RTS) &&
+ (urb->actual_length - 1 <= sizeof(command_info->result_buffer))) {
memcpy(command_info->result_buffer, &data[1],
urb->actual_length - 1);
command_info->command_finished = WHITEHEAT_CMD_COMPLETE;
}
static const struct usb_device_id id_table[] = {
- /* AC8710, AC8710T */
- { USB_DEVICE_AND_INTERFACE_INFO(0x19d2, 0xffff, 0xff, 0xff, 0xff) },
- /* AC8700 */
- { USB_DEVICE_AND_INTERFACE_INFO(0x19d2, 0xfffe, 0xff, 0xff, 0xff) },
/* MG880 */
{ USB_DEVICE(0x19d2, 0xfffd) },
- { USB_DEVICE(0x19d2, 0xfffc) },
- { USB_DEVICE(0x19d2, 0xfffb) },
- /* AC8710_V3 */
- { USB_DEVICE(0x19d2, 0xfff6) },
- { USB_DEVICE(0x19d2, 0xfff7) },
- { USB_DEVICE(0x19d2, 0xfff8) },
- { USB_DEVICE(0x19d2, 0xfff9) },
- { USB_DEVICE(0x19d2, 0xffee) },
- /* AC2716, MC2716 */
- { USB_DEVICE_AND_INTERFACE_INFO(0x19d2, 0xffed, 0xff, 0xff, 0xff) },
- /* AD3812 */
- { USB_DEVICE_AND_INTERFACE_INFO(0x19d2, 0xffeb, 0xff, 0xff, 0xff) },
- { USB_DEVICE(0x19d2, 0xffec) },
- { USB_DEVICE(0x05C6, 0x3197) },
- { USB_DEVICE(0x05C6, 0x6000) },
- { USB_DEVICE(0x05C6, 0x9008) },
{ },
};
MODULE_DEVICE_TABLE(usb, id_table);
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_FIX_CAPACITY ),
+UNUSUAL_DEV( 0x06ca, 0x2003, 0x0100, 0x0100,
+ "Newer Technology",
+ "uSCSI",
+ USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_euscsi_init,
+ US_FL_SCM_MULT_TARG ),
+
/* Reported by Adrian Pilchowiec <adi1981@epf.pl> */
UNUSUAL_DEV( 0x071b, 0x3203, 0x0000, 0x0000,
"RockChip",
--- /dev/null
+config USBIP_CORE
+ tristate "USB/IP support"
+ depends on USB && NET
+ ---help---
+ This enables pushing USB packets over IP to allow remote
+ machines direct access to USB devices. It provides the
+ USB/IP core that is required by both drivers.
+
+ For more details, and to get the userspace utility
+ programs, please see <http://usbip.sourceforge.net/>.
+
+ To compile this as a module, choose M here: the module will
+ be called usbip-core.
+
+ If unsure, say N.
+
+config USBIP_VHCI_HCD
+ tristate "VHCI hcd"
+ depends on USBIP_CORE
+ ---help---
+ This enables the USB/IP virtual host controller driver,
+ which is run on the remote machine.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vhci-hcd.
+
+config USBIP_HOST
+ tristate "Host driver"
+ depends on USBIP_CORE
+ ---help---
+ This enables the USB/IP host driver, which is run on the
+ machine that is sharing the USB devices.
+
+ To compile this driver as a module, choose M here: the
+ module will be called usbip-host.
+
+config USBIP_DEBUG
+ bool "Debug messages for USB/IP"
+ depends on USBIP_CORE
+ ---help---
+ This enables the debug messages from the USB/IP drivers.
--- /dev/null
+ccflags-$(CONFIG_USBIP_DEBUG) := -DDEBUG
+
+obj-$(CONFIG_USBIP_CORE) += usbip-core.o
+usbip-core-y := usbip_common.o usbip_event.o
+
+obj-$(CONFIG_USBIP_VHCI_HCD) += vhci-hcd.o
+vhci-hcd-y := vhci_sysfs.o vhci_tx.o vhci_rx.o vhci_hcd.o
+
+obj-$(CONFIG_USBIP_HOST) += usbip-host.o
+usbip-host-y := stub_dev.o stub_main.o stub_rx.o stub_tx.o
--- /dev/null
+TODO:
+ - more discussion about the protocol
+ - testing
+ - review of the userspace interface
+ - document the protocol
+
+Please send patches for this code to Greg Kroah-Hartman <greg@kroah.com>
--- /dev/null
+/*
+ * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ *
+ * This 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 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 __USBIP_STUB_H
+#define __USBIP_STUB_H
+
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/usb.h>
+#include <linux/wait.h>
+
+#define STUB_BUSID_OTHER 0
+#define STUB_BUSID_REMOV 1
+#define STUB_BUSID_ADDED 2
+#define STUB_BUSID_ALLOC 3
+
+struct stub_device {
+ struct usb_interface *interface;
+ struct usb_device *udev;
+
+ struct usbip_device ud;
+ __u32 devid;
+
+ /*
+ * stub_priv preserves private data of each urb.
+ * It is allocated as stub_priv_cache and assigned to urb->context.
+ *
+ * stub_priv is always linked to any one of 3 lists;
+ * priv_init: linked to this until the comletion of a urb.
+ * priv_tx : linked to this after the completion of a urb.
+ * priv_free: linked to this after the sending of the result.
+ *
+ * Any of these list operations should be locked by priv_lock.
+ */
+ spinlock_t priv_lock;
+ struct list_head priv_init;
+ struct list_head priv_tx;
+ struct list_head priv_free;
+
+ /* see comments for unlinking in stub_rx.c */
+ struct list_head unlink_tx;
+ struct list_head unlink_free;
+
+ wait_queue_head_t tx_waitq;
+};
+
+/* private data into urb->priv */
+struct stub_priv {
+ unsigned long seqnum;
+ struct list_head list;
+ struct stub_device *sdev;
+ struct urb *urb;
+
+ int unlinking;
+};
+
+struct stub_unlink {
+ unsigned long seqnum;
+ struct list_head list;
+ __u32 status;
+};
+
+/* same as SYSFS_BUS_ID_SIZE */
+#define BUSID_SIZE 32
+
+struct bus_id_priv {
+ char name[BUSID_SIZE];
+ char status;
+ int interf_count;
+ struct stub_device *sdev;
+ struct usb_device *udev;
+ char shutdown_busid;
+};
+
+/* stub_priv is allocated from stub_priv_cache */
+extern struct kmem_cache *stub_priv_cache;
+
+/* stub_dev.c */
+extern struct usb_device_driver stub_driver;
+
+/* stub_main.c */
+struct bus_id_priv *get_busid_priv(const char *busid);
+int del_match_busid(char *busid);
+void stub_device_cleanup_urbs(struct stub_device *sdev);
+
+/* stub_rx.c */
+int stub_rx_loop(void *data);
+
+/* stub_tx.c */
+void stub_enqueue_ret_unlink(struct stub_device *sdev, __u32 seqnum,
+ __u32 status);
+void stub_complete(struct urb *urb);
+int stub_tx_loop(void *data);
+
+#endif /* __USBIP_STUB_H */
--- /dev/null
+/*
+ * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ *
+ * This 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 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <linux/device.h>
+#include <linux/file.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+
+#include "usbip_common.h"
+#include "stub.h"
+
+/*
+ * usbip_status shows the status of usbip-host as long as this driver is bound
+ * to the target device.
+ */
+static ssize_t usbip_status_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct stub_device *sdev = dev_get_drvdata(dev);
+ int status;
+
+ if (!sdev) {
+ dev_err(dev, "sdev is null\n");
+ return -ENODEV;
+ }
+
+ spin_lock_irq(&sdev->ud.lock);
+ status = sdev->ud.status;
+ spin_unlock_irq(&sdev->ud.lock);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", status);
+}
+static DEVICE_ATTR_RO(usbip_status);
+
+/*
+ * usbip_sockfd gets a socket descriptor of an established TCP connection that
+ * is used to transfer usbip requests by kernel threads. -1 is a magic number
+ * by which usbip connection is finished.
+ */
+static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct stub_device *sdev = dev_get_drvdata(dev);
+ int sockfd = 0;
+ struct socket *socket;
+ int rv;
+
+ if (!sdev) {
+ dev_err(dev, "sdev is null\n");
+ return -ENODEV;
+ }
+
+ rv = sscanf(buf, "%d", &sockfd);
+ if (rv != 1)
+ return -EINVAL;
+
+ if (sockfd != -1) {
+ int err;
+
+ dev_info(dev, "stub up\n");
+
+ spin_lock_irq(&sdev->ud.lock);
+
+ if (sdev->ud.status != SDEV_ST_AVAILABLE) {
+ dev_err(dev, "not ready\n");
+ goto err;
+ }
+
+ socket = sockfd_lookup(sockfd, &err);
+ if (!socket)
+ goto err;
+
+ sdev->ud.tcp_socket = socket;
+
+ spin_unlock_irq(&sdev->ud.lock);
+
+ sdev->ud.tcp_rx = kthread_get_run(stub_rx_loop, &sdev->ud,
+ "stub_rx");
+ sdev->ud.tcp_tx = kthread_get_run(stub_tx_loop, &sdev->ud,
+ "stub_tx");
+
+ spin_lock_irq(&sdev->ud.lock);
+ sdev->ud.status = SDEV_ST_USED;
+ spin_unlock_irq(&sdev->ud.lock);
+
+ } else {
+ dev_info(dev, "stub down\n");
+
+ spin_lock_irq(&sdev->ud.lock);
+ if (sdev->ud.status != SDEV_ST_USED)
+ goto err;
+
+ spin_unlock_irq(&sdev->ud.lock);
+
+ usbip_event_add(&sdev->ud, SDEV_EVENT_DOWN);
+ }
+
+ return count;
+
+err:
+ spin_unlock_irq(&sdev->ud.lock);
+ return -EINVAL;
+}
+static DEVICE_ATTR(usbip_sockfd, S_IWUSR, NULL, store_sockfd);
+
+static int stub_add_files(struct device *dev)
+{
+ int err = 0;
+
+ err = device_create_file(dev, &dev_attr_usbip_status);
+ if (err)
+ goto err_status;
+
+ err = device_create_file(dev, &dev_attr_usbip_sockfd);
+ if (err)
+ goto err_sockfd;
+
+ err = device_create_file(dev, &dev_attr_usbip_debug);
+ if (err)
+ goto err_debug;
+
+ return 0;
+
+err_debug:
+ device_remove_file(dev, &dev_attr_usbip_sockfd);
+err_sockfd:
+ device_remove_file(dev, &dev_attr_usbip_status);
+err_status:
+ return err;
+}
+
+static void stub_remove_files(struct device *dev)
+{
+ device_remove_file(dev, &dev_attr_usbip_status);
+ device_remove_file(dev, &dev_attr_usbip_sockfd);
+ device_remove_file(dev, &dev_attr_usbip_debug);
+}
+
+static void stub_shutdown_connection(struct usbip_device *ud)
+{
+ struct stub_device *sdev = container_of(ud, struct stub_device, ud);
+
+ /*
+ * When removing an exported device, kernel panic sometimes occurred
+ * and then EIP was sk_wait_data of stub_rx thread. Is this because
+ * sk_wait_data returned though stub_rx thread was already finished by
+ * step 1?
+ */
+ if (ud->tcp_socket) {
+ dev_dbg(&sdev->udev->dev, "shutdown tcp_socket %p\n",
+ ud->tcp_socket);
+ kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR);
+ }
+
+ /* 1. stop threads */
+ if (ud->tcp_rx) {
+ kthread_stop_put(ud->tcp_rx);
+ ud->tcp_rx = NULL;
+ }
+ if (ud->tcp_tx) {
+ kthread_stop_put(ud->tcp_tx);
+ ud->tcp_tx = NULL;
+ }
+
+ /*
+ * 2. close the socket
+ *
+ * tcp_socket is freed after threads are killed so that usbip_xmit does
+ * not touch NULL socket.
+ */
+ if (ud->tcp_socket) {
+ sockfd_put(ud->tcp_socket);
+ ud->tcp_socket = NULL;
+ }
+
+ /* 3. free used data */
+ stub_device_cleanup_urbs(sdev);
+
+ /* 4. free stub_unlink */
+ {
+ unsigned long flags;
+ struct stub_unlink *unlink, *tmp;
+
+ spin_lock_irqsave(&sdev->priv_lock, flags);
+ list_for_each_entry_safe(unlink, tmp, &sdev->unlink_tx, list) {
+ list_del(&unlink->list);
+ kfree(unlink);
+ }
+ list_for_each_entry_safe(unlink, tmp, &sdev->unlink_free,
+ list) {
+ list_del(&unlink->list);
+ kfree(unlink);
+ }
+ spin_unlock_irqrestore(&sdev->priv_lock, flags);
+ }
+}
+
+static void stub_device_reset(struct usbip_device *ud)
+{
+ struct stub_device *sdev = container_of(ud, struct stub_device, ud);
+ struct usb_device *udev = sdev->udev;
+ int ret;
+
+ dev_dbg(&udev->dev, "device reset");
+
+ ret = usb_lock_device_for_reset(udev, sdev->interface);
+ if (ret < 0) {
+ dev_err(&udev->dev, "lock for reset\n");
+ spin_lock_irq(&ud->lock);
+ ud->status = SDEV_ST_ERROR;
+ spin_unlock_irq(&ud->lock);
+ return;
+ }
+
+ /* try to reset the device */
+ ret = usb_reset_device(udev);
+ usb_unlock_device(udev);
+
+ spin_lock_irq(&ud->lock);
+ if (ret) {
+ dev_err(&udev->dev, "device reset\n");
+ ud->status = SDEV_ST_ERROR;
+ } else {
+ dev_info(&udev->dev, "device reset\n");
+ ud->status = SDEV_ST_AVAILABLE;
+ }
+ spin_unlock_irq(&ud->lock);
+}
+
+static void stub_device_unusable(struct usbip_device *ud)
+{
+ spin_lock_irq(&ud->lock);
+ ud->status = SDEV_ST_ERROR;
+ spin_unlock_irq(&ud->lock);
+}
+
+/**
+ * stub_device_alloc - allocate a new stub_device struct
+ * @interface: usb_interface of a new device
+ *
+ * Allocates and initializes a new stub_device struct.
+ */
+static struct stub_device *stub_device_alloc(struct usb_device *udev)
+{
+ struct stub_device *sdev;
+ int busnum = udev->bus->busnum;
+ int devnum = udev->devnum;
+
+ dev_dbg(&udev->dev, "allocating stub device");
+
+ /* yes, it's a new device */
+ sdev = kzalloc(sizeof(struct stub_device), GFP_KERNEL);
+ if (!sdev)
+ return NULL;
+
+ sdev->udev = usb_get_dev(udev);
+
+ /*
+ * devid is defined with devnum when this driver is first allocated.
+ * devnum may change later if a device is reset. However, devid never
+ * changes during a usbip connection.
+ */
+ sdev->devid = (busnum << 16) | devnum;
+ sdev->ud.side = USBIP_STUB;
+ sdev->ud.status = SDEV_ST_AVAILABLE;
+ spin_lock_init(&sdev->ud.lock);
+ sdev->ud.tcp_socket = NULL;
+
+ INIT_LIST_HEAD(&sdev->priv_init);
+ INIT_LIST_HEAD(&sdev->priv_tx);
+ INIT_LIST_HEAD(&sdev->priv_free);
+ INIT_LIST_HEAD(&sdev->unlink_free);
+ INIT_LIST_HEAD(&sdev->unlink_tx);
+ spin_lock_init(&sdev->priv_lock);
+
+ init_waitqueue_head(&sdev->tx_waitq);
+
+ sdev->ud.eh_ops.shutdown = stub_shutdown_connection;
+ sdev->ud.eh_ops.reset = stub_device_reset;
+ sdev->ud.eh_ops.unusable = stub_device_unusable;
+
+ usbip_start_eh(&sdev->ud);
+
+ dev_dbg(&udev->dev, "register new device\n");
+
+ return sdev;
+}
+
+static void stub_device_free(struct stub_device *sdev)
+{
+ kfree(sdev);
+}
+
+static int stub_probe(struct usb_device *udev)
+{
+ struct stub_device *sdev = NULL;
+ const char *udev_busid = dev_name(&udev->dev);
+ int err = 0;
+ struct bus_id_priv *busid_priv;
+ int rc;
+
+ dev_dbg(&udev->dev, "Enter\n");
+
+ /* check we should claim or not by busid_table */
+ busid_priv = get_busid_priv(udev_busid);
+ if (!busid_priv || (busid_priv->status == STUB_BUSID_REMOV) ||
+ (busid_priv->status == STUB_BUSID_OTHER)) {
+ dev_info(&udev->dev,
+ "%s is not in match_busid table... skip!\n",
+ udev_busid);
+
+ /*
+ * Return value should be ENODEV or ENOXIO to continue trying
+ * other matched drivers by the driver core.
+ * See driver_probe_device() in driver/base/dd.c
+ */
+ return -ENODEV;
+ }
+
+ if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) {
+ dev_dbg(&udev->dev, "%s is a usb hub device... skip!\n",
+ udev_busid);
+ return -ENODEV;
+ }
+
+ if (!strcmp(udev->bus->bus_name, "vhci_hcd")) {
+ dev_dbg(&udev->dev,
+ "%s is attached on vhci_hcd... skip!\n",
+ udev_busid);
+
+ return -ENODEV;
+ }
+
+ /* ok, this is my device */
+ sdev = stub_device_alloc(udev);
+ if (!sdev)
+ return -ENOMEM;
+
+ dev_info(&udev->dev,
+ "usbip-host: register new device (bus %u dev %u)\n",
+ udev->bus->busnum, udev->devnum);
+
+ busid_priv->shutdown_busid = 0;
+
+ /* set private data to usb_device */
+ dev_set_drvdata(&udev->dev, sdev);
+ busid_priv->sdev = sdev;
+ busid_priv->udev = udev;
+
+ /*
+ * Claim this hub port.
+ * It doesn't matter what value we pass as owner
+ * (struct dev_state) as long as it is unique.
+ */
+ rc = usb_hub_claim_port(udev->parent, udev->portnum,
+ (struct usb_dev_state *) udev);
+ if (rc) {
+ dev_dbg(&udev->dev, "unable to claim port\n");
+ return rc;
+ }
+
+ err = stub_add_files(&udev->dev);
+ if (err) {
+ dev_err(&udev->dev, "stub_add_files for %s\n", udev_busid);
+ dev_set_drvdata(&udev->dev, NULL);
+ usb_put_dev(udev);
+ kthread_stop_put(sdev->ud.eh);
+
+ busid_priv->sdev = NULL;
+ stub_device_free(sdev);
+ return err;
+ }
+ busid_priv->status = STUB_BUSID_ALLOC;
+
+ return 0;
+}
+
+static void shutdown_busid(struct bus_id_priv *busid_priv)
+{
+ if (busid_priv->sdev && !busid_priv->shutdown_busid) {
+ busid_priv->shutdown_busid = 1;
+ usbip_event_add(&busid_priv->sdev->ud, SDEV_EVENT_REMOVED);
+
+ /* wait for the stop of the event handler */
+ usbip_stop_eh(&busid_priv->sdev->ud);
+ }
+}
+
+/*
+ * called in usb_disconnect() or usb_deregister()
+ * but only if actconfig(active configuration) exists
+ */
+static void stub_disconnect(struct usb_device *udev)
+{
+ struct stub_device *sdev;
+ const char *udev_busid = dev_name(&udev->dev);
+ struct bus_id_priv *busid_priv;
+ int rc;
+
+ dev_dbg(&udev->dev, "Enter\n");
+
+ busid_priv = get_busid_priv(udev_busid);
+ if (!busid_priv) {
+ BUG();
+ return;
+ }
+
+ sdev = dev_get_drvdata(&udev->dev);
+
+ /* get stub_device */
+ if (!sdev) {
+ dev_err(&udev->dev, "could not get device");
+ return;
+ }
+
+ dev_set_drvdata(&udev->dev, NULL);
+
+ /*
+ * NOTE: rx/tx threads are invoked for each usb_device.
+ */
+ stub_remove_files(&udev->dev);
+
+ /* release port */
+ rc = usb_hub_release_port(udev->parent, udev->portnum,
+ (struct usb_dev_state *) udev);
+ if (rc) {
+ dev_dbg(&udev->dev, "unable to release port\n");
+ return;
+ }
+
+ /* If usb reset is called from event handler */
+ if (busid_priv->sdev->ud.eh == current)
+ return;
+
+ /* shutdown the current connection */
+ shutdown_busid(busid_priv);
+
+ usb_put_dev(sdev->udev);
+
+ /* free sdev */
+ busid_priv->sdev = NULL;
+ stub_device_free(sdev);
+
+ if (busid_priv->status == STUB_BUSID_ALLOC) {
+ busid_priv->status = STUB_BUSID_ADDED;
+ } else {
+ busid_priv->status = STUB_BUSID_OTHER;
+ del_match_busid((char *)udev_busid);
+ }
+}
+
+#ifdef CONFIG_PM
+
+/* These functions need usb_port_suspend and usb_port_resume,
+ * which reside in drivers/usb/core/usb.h. Skip for now. */
+
+static int stub_suspend(struct usb_device *udev, pm_message_t message)
+{
+ dev_dbg(&udev->dev, "stub_suspend\n");
+
+ return 0;
+}
+
+static int stub_resume(struct usb_device *udev, pm_message_t message)
+{
+ dev_dbg(&udev->dev, "stub_resume\n");
+
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
+struct usb_device_driver stub_driver = {
+ .name = "usbip-host",
+ .probe = stub_probe,
+ .disconnect = stub_disconnect,
+#ifdef CONFIG_PM
+ .suspend = stub_suspend,
+ .resume = stub_resume,
+#endif
+ .supports_autosuspend = 0,
+};
--- /dev/null
+/*
+ * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ *
+ * This 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 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/device.h>
+
+#include "usbip_common.h"
+#include "stub.h"
+
+#define DRIVER_AUTHOR "Takahiro Hirofuchi"
+#define DRIVER_DESC "USB/IP Host Driver"
+
+struct kmem_cache *stub_priv_cache;
+/*
+ * busid_tables defines matching busids that usbip can grab. A user can change
+ * dynamically what device is locally used and what device is exported to a
+ * remote host.
+ */
+#define MAX_BUSID 16
+static struct bus_id_priv busid_table[MAX_BUSID];
+static spinlock_t busid_table_lock;
+
+static void init_busid_table(void)
+{
+ /*
+ * This also sets the bus_table[i].status to
+ * STUB_BUSID_OTHER, which is 0.
+ */
+ memset(busid_table, 0, sizeof(busid_table));
+
+ spin_lock_init(&busid_table_lock);
+}
+
+/*
+ * Find the index of the busid by name.
+ * Must be called with busid_table_lock held.
+ */
+static int get_busid_idx(const char *busid)
+{
+ int i;
+ int idx = -1;
+
+ for (i = 0; i < MAX_BUSID; i++)
+ if (busid_table[i].name[0])
+ if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) {
+ idx = i;
+ break;
+ }
+ return idx;
+}
+
+struct bus_id_priv *get_busid_priv(const char *busid)
+{
+ int idx;
+ struct bus_id_priv *bid = NULL;
+
+ spin_lock(&busid_table_lock);
+ idx = get_busid_idx(busid);
+ if (idx >= 0)
+ bid = &(busid_table[idx]);
+ spin_unlock(&busid_table_lock);
+
+ return bid;
+}
+
+static int add_match_busid(char *busid)
+{
+ int i;
+ int ret = -1;
+
+ spin_lock(&busid_table_lock);
+ /* already registered? */
+ if (get_busid_idx(busid) >= 0) {
+ ret = 0;
+ goto out;
+ }
+
+ for (i = 0; i < MAX_BUSID; i++)
+ if (!busid_table[i].name[0]) {
+ strlcpy(busid_table[i].name, busid, BUSID_SIZE);
+ if ((busid_table[i].status != STUB_BUSID_ALLOC) &&
+ (busid_table[i].status != STUB_BUSID_REMOV))
+ busid_table[i].status = STUB_BUSID_ADDED;
+ ret = 0;
+ break;
+ }
+
+out:
+ spin_unlock(&busid_table_lock);
+
+ return ret;
+}
+
+int del_match_busid(char *busid)
+{
+ int idx;
+ int ret = -1;
+
+ spin_lock(&busid_table_lock);
+ idx = get_busid_idx(busid);
+ if (idx < 0)
+ goto out;
+
+ /* found */
+ ret = 0;
+
+ if (busid_table[idx].status == STUB_BUSID_OTHER)
+ memset(busid_table[idx].name, 0, BUSID_SIZE);
+
+ if ((busid_table[idx].status != STUB_BUSID_OTHER) &&
+ (busid_table[idx].status != STUB_BUSID_ADDED))
+ busid_table[idx].status = STUB_BUSID_REMOV;
+
+out:
+ spin_unlock(&busid_table_lock);
+
+ return ret;
+}
+
+static ssize_t show_match_busid(struct device_driver *drv, char *buf)
+{
+ int i;
+ char *out = buf;
+
+ spin_lock(&busid_table_lock);
+ for (i = 0; i < MAX_BUSID; i++)
+ if (busid_table[i].name[0])
+ out += sprintf(out, "%s ", busid_table[i].name);
+ spin_unlock(&busid_table_lock);
+ out += sprintf(out, "\n");
+
+ return out - buf;
+}
+
+static ssize_t store_match_busid(struct device_driver *dev, const char *buf,
+ size_t count)
+{
+ int len;
+ char busid[BUSID_SIZE];
+
+ if (count < 5)
+ return -EINVAL;
+
+ /* busid needs to include \0 termination */
+ len = strlcpy(busid, buf + 4, BUSID_SIZE);
+ if (sizeof(busid) <= len)
+ return -EINVAL;
+
+ if (!strncmp(buf, "add ", 4)) {
+ if (add_match_busid(busid) < 0)
+ return -ENOMEM;
+
+ pr_debug("add busid %s\n", busid);
+ return count;
+ }
+
+ if (!strncmp(buf, "del ", 4)) {
+ if (del_match_busid(busid) < 0)
+ return -ENODEV;
+
+ pr_debug("del busid %s\n", busid);
+ return count;
+ }
+
+ return -EINVAL;
+}
+static DRIVER_ATTR(match_busid, S_IRUSR | S_IWUSR, show_match_busid,
+ store_match_busid);
+
+static ssize_t rebind_store(struct device_driver *dev, const char *buf,
+ size_t count)
+{
+ int ret;
+ int len;
+ struct bus_id_priv *bid;
+
+ /* buf length should be less that BUSID_SIZE */
+ len = strnlen(buf, BUSID_SIZE);
+
+ if (!(len < BUSID_SIZE))
+ return -EINVAL;
+
+ bid = get_busid_priv(buf);
+ if (!bid)
+ return -ENODEV;
+
+ ret = device_attach(&bid->udev->dev);
+ if (ret < 0) {
+ dev_err(&bid->udev->dev, "rebind failed\n");
+ return ret;
+ }
+
+ return count;
+}
+
+static DRIVER_ATTR_WO(rebind);
+
+static struct stub_priv *stub_priv_pop_from_listhead(struct list_head *listhead)
+{
+ struct stub_priv *priv, *tmp;
+
+ list_for_each_entry_safe(priv, tmp, listhead, list) {
+ list_del(&priv->list);
+ return priv;
+ }
+
+ return NULL;
+}
+
+static struct stub_priv *stub_priv_pop(struct stub_device *sdev)
+{
+ unsigned long flags;
+ struct stub_priv *priv;
+
+ spin_lock_irqsave(&sdev->priv_lock, flags);
+
+ priv = stub_priv_pop_from_listhead(&sdev->priv_init);
+ if (priv)
+ goto done;
+
+ priv = stub_priv_pop_from_listhead(&sdev->priv_tx);
+ if (priv)
+ goto done;
+
+ priv = stub_priv_pop_from_listhead(&sdev->priv_free);
+
+done:
+ spin_unlock_irqrestore(&sdev->priv_lock, flags);
+
+ return priv;
+}
+
+void stub_device_cleanup_urbs(struct stub_device *sdev)
+{
+ struct stub_priv *priv;
+ struct urb *urb;
+
+ dev_dbg(&sdev->udev->dev, "free sdev %p\n", sdev);
+
+ while ((priv = stub_priv_pop(sdev))) {
+ urb = priv->urb;
+ dev_dbg(&sdev->udev->dev, "free urb %p\n", urb);
+ usb_kill_urb(urb);
+
+ kmem_cache_free(stub_priv_cache, priv);
+
+ kfree(urb->transfer_buffer);
+ kfree(urb->setup_packet);
+ usb_free_urb(urb);
+ }
+}
+
+static int __init usbip_host_init(void)
+{
+ int ret;
+
+ init_busid_table();
+
+ stub_priv_cache = KMEM_CACHE(stub_priv, SLAB_HWCACHE_ALIGN);
+ if (!stub_priv_cache) {
+ pr_err("kmem_cache_create failed\n");
+ return -ENOMEM;
+ }
+
+ ret = usb_register_device_driver(&stub_driver, THIS_MODULE);
+ if (ret) {
+ pr_err("usb_register failed %d\n", ret);
+ goto err_usb_register;
+ }
+
+ ret = driver_create_file(&stub_driver.drvwrap.driver,
+ &driver_attr_match_busid);
+ if (ret) {
+ pr_err("driver_create_file failed\n");
+ goto err_create_file;
+ }
+
+ ret = driver_create_file(&stub_driver.drvwrap.driver,
+ &driver_attr_rebind);
+ if (ret) {
+ pr_err("driver_create_file failed\n");
+ goto err_create_file;
+ }
+
+ pr_info(DRIVER_DESC " v" USBIP_VERSION "\n");
+ return ret;
+
+err_create_file:
+ usb_deregister_device_driver(&stub_driver);
+err_usb_register:
+ kmem_cache_destroy(stub_priv_cache);
+ return ret;
+}
+
+static void __exit usbip_host_exit(void)
+{
+ driver_remove_file(&stub_driver.drvwrap.driver,
+ &driver_attr_match_busid);
+
+ driver_remove_file(&stub_driver.drvwrap.driver,
+ &driver_attr_rebind);
+
+ /*
+ * deregister() calls stub_disconnect() for all devices. Device
+ * specific data is cleared in stub_disconnect().
+ */
+ usb_deregister_device_driver(&stub_driver);
+
+ kmem_cache_destroy(stub_priv_cache);
+}
+
+module_init(usbip_host_init);
+module_exit(usbip_host_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(USBIP_VERSION);
--- /dev/null
+/*
+ * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ *
+ * This 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 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <asm/byteorder.h>
+#include <linux/kthread.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "usbip_common.h"
+#include "stub.h"
+
+static int is_clear_halt_cmd(struct urb *urb)
+{
+ struct usb_ctrlrequest *req;
+
+ req = (struct usb_ctrlrequest *) urb->setup_packet;
+
+ return (req->bRequest == USB_REQ_CLEAR_FEATURE) &&
+ (req->bRequestType == USB_RECIP_ENDPOINT) &&
+ (req->wValue == USB_ENDPOINT_HALT);
+}
+
+static int is_set_interface_cmd(struct urb *urb)
+{
+ struct usb_ctrlrequest *req;
+
+ req = (struct usb_ctrlrequest *) urb->setup_packet;
+
+ return (req->bRequest == USB_REQ_SET_INTERFACE) &&
+ (req->bRequestType == USB_RECIP_INTERFACE);
+}
+
+static int is_set_configuration_cmd(struct urb *urb)
+{
+ struct usb_ctrlrequest *req;
+
+ req = (struct usb_ctrlrequest *) urb->setup_packet;
+
+ return (req->bRequest == USB_REQ_SET_CONFIGURATION) &&
+ (req->bRequestType == USB_RECIP_DEVICE);
+}
+
+static int is_reset_device_cmd(struct urb *urb)
+{
+ struct usb_ctrlrequest *req;
+ __u16 value;
+ __u16 index;
+
+ req = (struct usb_ctrlrequest *) urb->setup_packet;
+ value = le16_to_cpu(req->wValue);
+ index = le16_to_cpu(req->wIndex);
+
+ if ((req->bRequest == USB_REQ_SET_FEATURE) &&
+ (req->bRequestType == USB_RT_PORT) &&
+ (value == USB_PORT_FEAT_RESET)) {
+ usbip_dbg_stub_rx("reset_device_cmd, port %u\n", index);
+ return 1;
+ } else
+ return 0;
+}
+
+static int tweak_clear_halt_cmd(struct urb *urb)
+{
+ struct usb_ctrlrequest *req;
+ int target_endp;
+ int target_dir;
+ int target_pipe;
+ int ret;
+
+ req = (struct usb_ctrlrequest *) urb->setup_packet;
+
+ /*
+ * The stalled endpoint is specified in the wIndex value. The endpoint
+ * of the urb is the target of this clear_halt request (i.e., control
+ * endpoint).
+ */
+ target_endp = le16_to_cpu(req->wIndex) & 0x000f;
+
+ /* the stalled endpoint direction is IN or OUT?. USB_DIR_IN is 0x80. */
+ target_dir = le16_to_cpu(req->wIndex) & 0x0080;
+
+ if (target_dir)
+ target_pipe = usb_rcvctrlpipe(urb->dev, target_endp);
+ else
+ target_pipe = usb_sndctrlpipe(urb->dev, target_endp);
+
+ ret = usb_clear_halt(urb->dev, target_pipe);
+ if (ret < 0)
+ dev_err(&urb->dev->dev,
+ "usb_clear_halt error: devnum %d endp %d ret %d\n",
+ urb->dev->devnum, target_endp, ret);
+ else
+ dev_info(&urb->dev->dev,
+ "usb_clear_halt done: devnum %d endp %d\n",
+ urb->dev->devnum, target_endp);
+
+ return ret;
+}
+
+static int tweak_set_interface_cmd(struct urb *urb)
+{
+ struct usb_ctrlrequest *req;
+ __u16 alternate;
+ __u16 interface;
+ int ret;
+
+ req = (struct usb_ctrlrequest *) urb->setup_packet;
+ alternate = le16_to_cpu(req->wValue);
+ interface = le16_to_cpu(req->wIndex);
+
+ usbip_dbg_stub_rx("set_interface: inf %u alt %u\n",
+ interface, alternate);
+
+ ret = usb_set_interface(urb->dev, interface, alternate);
+ if (ret < 0)
+ dev_err(&urb->dev->dev,
+ "usb_set_interface error: inf %u alt %u ret %d\n",
+ interface, alternate, ret);
+ else
+ dev_info(&urb->dev->dev,
+ "usb_set_interface done: inf %u alt %u\n",
+ interface, alternate);
+
+ return ret;
+}
+
+static int tweak_set_configuration_cmd(struct urb *urb)
+{
+ struct stub_priv *priv = (struct stub_priv *) urb->context;
+ struct stub_device *sdev = priv->sdev;
+ struct usb_ctrlrequest *req;
+ __u16 config;
+ int err;
+
+ req = (struct usb_ctrlrequest *) urb->setup_packet;
+ config = le16_to_cpu(req->wValue);
+
+ err = usb_set_configuration(sdev->udev, config);
+ if (err && err != -ENODEV)
+ dev_err(&sdev->udev->dev, "can't set config #%d, error %d\n",
+ config, err);
+ return 0;
+}
+
+static int tweak_reset_device_cmd(struct urb *urb)
+{
+ struct stub_priv *priv = (struct stub_priv *) urb->context;
+ struct stub_device *sdev = priv->sdev;
+
+ dev_info(&urb->dev->dev, "usb_queue_reset_device\n");
+
+ /*
+ * With the implementation of pre_reset and post_reset the driver no
+ * longer unbinds. This allows the use of synchronous reset.
+ */
+
+ if (usb_lock_device_for_reset(sdev->udev, sdev->interface) < 0) {
+ dev_err(&urb->dev->dev, "could not obtain lock to reset device\n");
+ return 0;
+ }
+ usb_reset_device(sdev->udev);
+ usb_unlock_device(sdev->udev);
+
+ return 0;
+}
+
+/*
+ * clear_halt, set_interface, and set_configuration require special tricks.
+ */
+static void tweak_special_requests(struct urb *urb)
+{
+ if (!urb || !urb->setup_packet)
+ return;
+
+ if (usb_pipetype(urb->pipe) != PIPE_CONTROL)
+ return;
+
+ if (is_clear_halt_cmd(urb))
+ /* tweak clear_halt */
+ tweak_clear_halt_cmd(urb);
+
+ else if (is_set_interface_cmd(urb))
+ /* tweak set_interface */
+ tweak_set_interface_cmd(urb);
+
+ else if (is_set_configuration_cmd(urb))
+ /* tweak set_configuration */
+ tweak_set_configuration_cmd(urb);
+
+ else if (is_reset_device_cmd(urb))
+ tweak_reset_device_cmd(urb);
+ else
+ usbip_dbg_stub_rx("no need to tweak\n");
+}
+
+/*
+ * stub_recv_unlink() unlinks the URB by a call to usb_unlink_urb().
+ * By unlinking the urb asynchronously, stub_rx can continuously
+ * process coming urbs. Even if the urb is unlinked, its completion
+ * handler will be called and stub_tx will send a return pdu.
+ *
+ * See also comments about unlinking strategy in vhci_hcd.c.
+ */
+static int stub_recv_cmd_unlink(struct stub_device *sdev,
+ struct usbip_header *pdu)
+{
+ int ret;
+ unsigned long flags;
+ struct stub_priv *priv;
+
+ spin_lock_irqsave(&sdev->priv_lock, flags);
+
+ list_for_each_entry(priv, &sdev->priv_init, list) {
+ if (priv->seqnum != pdu->u.cmd_unlink.seqnum)
+ continue;
+
+ dev_info(&priv->urb->dev->dev, "unlink urb %p\n",
+ priv->urb);
+
+ /*
+ * This matched urb is not completed yet (i.e., be in
+ * flight in usb hcd hardware/driver). Now we are
+ * cancelling it. The unlinking flag means that we are
+ * now not going to return the normal result pdu of a
+ * submission request, but going to return a result pdu
+ * of the unlink request.
+ */
+ priv->unlinking = 1;
+
+ /*
+ * In the case that unlinking flag is on, prev->seqnum
+ * is changed from the seqnum of the cancelling urb to
+ * the seqnum of the unlink request. This will be used
+ * to make the result pdu of the unlink request.
+ */
+ priv->seqnum = pdu->base.seqnum;
+
+ spin_unlock_irqrestore(&sdev->priv_lock, flags);
+
+ /*
+ * usb_unlink_urb() is now out of spinlocking to avoid
+ * spinlock recursion since stub_complete() is
+ * sometimes called in this context but not in the
+ * interrupt context. If stub_complete() is executed
+ * before we call usb_unlink_urb(), usb_unlink_urb()
+ * will return an error value. In this case, stub_tx
+ * will return the result pdu of this unlink request
+ * though submission is completed and actual unlinking
+ * is not executed. OK?
+ */
+ /* In the above case, urb->status is not -ECONNRESET,
+ * so a driver in a client host will know the failure
+ * of the unlink request ?
+ */
+ ret = usb_unlink_urb(priv->urb);
+ if (ret != -EINPROGRESS)
+ dev_err(&priv->urb->dev->dev,
+ "failed to unlink a urb %p, ret %d\n",
+ priv->urb, ret);
+
+ return 0;
+ }
+
+ usbip_dbg_stub_rx("seqnum %d is not pending\n",
+ pdu->u.cmd_unlink.seqnum);
+
+ /*
+ * The urb of the unlink target is not found in priv_init queue. It was
+ * already completed and its results is/was going to be sent by a
+ * CMD_RET pdu. In this case, usb_unlink_urb() is not needed. We only
+ * return the completeness of this unlink request to vhci_hcd.
+ */
+ stub_enqueue_ret_unlink(sdev, pdu->base.seqnum, 0);
+
+ spin_unlock_irqrestore(&sdev->priv_lock, flags);
+
+ return 0;
+}
+
+static int valid_request(struct stub_device *sdev, struct usbip_header *pdu)
+{
+ struct usbip_device *ud = &sdev->ud;
+ int valid = 0;
+
+ if (pdu->base.devid == sdev->devid) {
+ spin_lock_irq(&ud->lock);
+ if (ud->status == SDEV_ST_USED) {
+ /* A request is valid. */
+ valid = 1;
+ }
+ spin_unlock_irq(&ud->lock);
+ }
+
+ return valid;
+}
+
+static struct stub_priv *stub_priv_alloc(struct stub_device *sdev,
+ struct usbip_header *pdu)
+{
+ struct stub_priv *priv;
+ struct usbip_device *ud = &sdev->ud;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sdev->priv_lock, flags);
+
+ priv = kmem_cache_zalloc(stub_priv_cache, GFP_ATOMIC);
+ if (!priv) {
+ dev_err(&sdev->interface->dev, "alloc stub_priv\n");
+ spin_unlock_irqrestore(&sdev->priv_lock, flags);
+ usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
+ return NULL;
+ }
+
+ priv->seqnum = pdu->base.seqnum;
+ priv->sdev = sdev;
+
+ /*
+ * After a stub_priv is linked to a list_head,
+ * our error handler can free allocated data.
+ */
+ list_add_tail(&priv->list, &sdev->priv_init);
+
+ spin_unlock_irqrestore(&sdev->priv_lock, flags);
+
+ return priv;
+}
+
+static int get_pipe(struct stub_device *sdev, int epnum, int dir)
+{
+ struct usb_device *udev = sdev->udev;
+ struct usb_host_endpoint *ep;
+ struct usb_endpoint_descriptor *epd = NULL;
+
+ if (dir == USBIP_DIR_IN)
+ ep = udev->ep_in[epnum & 0x7f];
+ else
+ ep = udev->ep_out[epnum & 0x7f];
+ if (!ep) {
+ dev_err(&sdev->interface->dev, "no such endpoint?, %d\n",
+ epnum);
+ BUG();
+ }
+
+ epd = &ep->desc;
+ if (usb_endpoint_xfer_control(epd)) {
+ if (dir == USBIP_DIR_OUT)
+ return usb_sndctrlpipe(udev, epnum);
+ else
+ return usb_rcvctrlpipe(udev, epnum);
+ }
+
+ if (usb_endpoint_xfer_bulk(epd)) {
+ if (dir == USBIP_DIR_OUT)
+ return usb_sndbulkpipe(udev, epnum);
+ else
+ return usb_rcvbulkpipe(udev, epnum);
+ }
+
+ if (usb_endpoint_xfer_int(epd)) {
+ if (dir == USBIP_DIR_OUT)
+ return usb_sndintpipe(udev, epnum);
+ else
+ return usb_rcvintpipe(udev, epnum);
+ }
+
+ if (usb_endpoint_xfer_isoc(epd)) {
+ if (dir == USBIP_DIR_OUT)
+ return usb_sndisocpipe(udev, epnum);
+ else
+ return usb_rcvisocpipe(udev, epnum);
+ }
+
+ /* NOT REACHED */
+ dev_err(&sdev->interface->dev, "get pipe, epnum %d\n", epnum);
+ return 0;
+}
+
+static void masking_bogus_flags(struct urb *urb)
+{
+ int xfertype;
+ struct usb_device *dev;
+ struct usb_host_endpoint *ep;
+ int is_out;
+ unsigned int allowed;
+
+ if (!urb || urb->hcpriv || !urb->complete)
+ return;
+ dev = urb->dev;
+ if ((!dev) || (dev->state < USB_STATE_UNAUTHENTICATED))
+ return;
+
+ ep = (usb_pipein(urb->pipe) ? dev->ep_in : dev->ep_out)
+ [usb_pipeendpoint(urb->pipe)];
+ if (!ep)
+ return;
+
+ xfertype = usb_endpoint_type(&ep->desc);
+ if (xfertype == USB_ENDPOINT_XFER_CONTROL) {
+ struct usb_ctrlrequest *setup =
+ (struct usb_ctrlrequest *) urb->setup_packet;
+
+ if (!setup)
+ return;
+ is_out = !(setup->bRequestType & USB_DIR_IN) ||
+ !setup->wLength;
+ } else {
+ is_out = usb_endpoint_dir_out(&ep->desc);
+ }
+
+ /* enforce simple/standard policy */
+ allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT |
+ URB_DIR_MASK | URB_FREE_BUFFER);
+ switch (xfertype) {
+ case USB_ENDPOINT_XFER_BULK:
+ if (is_out)
+ allowed |= URB_ZERO_PACKET;
+ /* FALLTHROUGH */
+ case USB_ENDPOINT_XFER_CONTROL:
+ allowed |= URB_NO_FSBR; /* only affects UHCI */
+ /* FALLTHROUGH */
+ default: /* all non-iso endpoints */
+ if (!is_out)
+ allowed |= URB_SHORT_NOT_OK;
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ allowed |= URB_ISO_ASAP;
+ break;
+ }
+ urb->transfer_flags &= allowed;
+}
+
+static void stub_recv_cmd_submit(struct stub_device *sdev,
+ struct usbip_header *pdu)
+{
+ int ret;
+ struct stub_priv *priv;
+ struct usbip_device *ud = &sdev->ud;
+ struct usb_device *udev = sdev->udev;
+ int pipe = get_pipe(sdev, pdu->base.ep, pdu->base.direction);
+
+ priv = stub_priv_alloc(sdev, pdu);
+ if (!priv)
+ return;
+
+ /* setup a urb */
+ if (usb_pipeisoc(pipe))
+ priv->urb = usb_alloc_urb(pdu->u.cmd_submit.number_of_packets,
+ GFP_KERNEL);
+ else
+ priv->urb = usb_alloc_urb(0, GFP_KERNEL);
+
+ if (!priv->urb) {
+ dev_err(&sdev->interface->dev, "malloc urb\n");
+ usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
+ return;
+ }
+
+ /* allocate urb transfer buffer, if needed */
+ if (pdu->u.cmd_submit.transfer_buffer_length > 0) {
+ priv->urb->transfer_buffer =
+ kzalloc(pdu->u.cmd_submit.transfer_buffer_length,
+ GFP_KERNEL);
+ if (!priv->urb->transfer_buffer) {
+ usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
+ return;
+ }
+ }
+
+ /* copy urb setup packet */
+ priv->urb->setup_packet = kmemdup(&pdu->u.cmd_submit.setup, 8,
+ GFP_KERNEL);
+ if (!priv->urb->setup_packet) {
+ dev_err(&sdev->interface->dev, "allocate setup_packet\n");
+ usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
+ return;
+ }
+
+ /* set other members from the base header of pdu */
+ priv->urb->context = (void *) priv;
+ priv->urb->dev = udev;
+ priv->urb->pipe = pipe;
+ priv->urb->complete = stub_complete;
+
+ usbip_pack_pdu(pdu, priv->urb, USBIP_CMD_SUBMIT, 0);
+
+
+ if (usbip_recv_xbuff(ud, priv->urb) < 0)
+ return;
+
+ if (usbip_recv_iso(ud, priv->urb) < 0)
+ return;
+
+ /* no need to submit an intercepted request, but harmless? */
+ tweak_special_requests(priv->urb);
+
+ masking_bogus_flags(priv->urb);
+ /* urb is now ready to submit */
+ ret = usb_submit_urb(priv->urb, GFP_KERNEL);
+
+ if (ret == 0)
+ usbip_dbg_stub_rx("submit urb ok, seqnum %u\n",
+ pdu->base.seqnum);
+ else {
+ dev_err(&sdev->interface->dev, "submit_urb error, %d\n", ret);
+ usbip_dump_header(pdu);
+ usbip_dump_urb(priv->urb);
+
+ /*
+ * Pessimistic.
+ * This connection will be discarded.
+ */
+ usbip_event_add(ud, SDEV_EVENT_ERROR_SUBMIT);
+ }
+
+ usbip_dbg_stub_rx("Leave\n");
+}
+
+/* recv a pdu */
+static void stub_rx_pdu(struct usbip_device *ud)
+{
+ int ret;
+ struct usbip_header pdu;
+ struct stub_device *sdev = container_of(ud, struct stub_device, ud);
+ struct device *dev = &sdev->udev->dev;
+
+ usbip_dbg_stub_rx("Enter\n");
+
+ memset(&pdu, 0, sizeof(pdu));
+
+ /* receive a pdu header */
+ ret = usbip_recv(ud->tcp_socket, &pdu, sizeof(pdu));
+ if (ret != sizeof(pdu)) {
+ dev_err(dev, "recv a header, %d\n", ret);
+ usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
+ return;
+ }
+
+ usbip_header_correct_endian(&pdu, 0);
+
+ if (usbip_dbg_flag_stub_rx)
+ usbip_dump_header(&pdu);
+
+ if (!valid_request(sdev, &pdu)) {
+ dev_err(dev, "recv invalid request\n");
+ usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
+ return;
+ }
+
+ switch (pdu.base.command) {
+ case USBIP_CMD_UNLINK:
+ stub_recv_cmd_unlink(sdev, &pdu);
+ break;
+
+ case USBIP_CMD_SUBMIT:
+ stub_recv_cmd_submit(sdev, &pdu);
+ break;
+
+ default:
+ /* NOTREACHED */
+ dev_err(dev, "unknown pdu\n");
+ usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
+ break;
+ }
+}
+
+int stub_rx_loop(void *data)
+{
+ struct usbip_device *ud = data;
+
+ while (!kthread_should_stop()) {
+ if (usbip_event_happened(ud))
+ break;
+
+ stub_rx_pdu(ud);
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ *
+ * This 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 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <linux/kthread.h>
+#include <linux/socket.h>
+
+#include "usbip_common.h"
+#include "stub.h"
+
+static void stub_free_priv_and_urb(struct stub_priv *priv)
+{
+ struct urb *urb = priv->urb;
+
+ kfree(urb->setup_packet);
+ kfree(urb->transfer_buffer);
+ list_del(&priv->list);
+ kmem_cache_free(stub_priv_cache, priv);
+ usb_free_urb(urb);
+}
+
+/* be in spin_lock_irqsave(&sdev->priv_lock, flags) */
+void stub_enqueue_ret_unlink(struct stub_device *sdev, __u32 seqnum,
+ __u32 status)
+{
+ struct stub_unlink *unlink;
+
+ unlink = kzalloc(sizeof(struct stub_unlink), GFP_ATOMIC);
+ if (!unlink) {
+ usbip_event_add(&sdev->ud, VDEV_EVENT_ERROR_MALLOC);
+ return;
+ }
+
+ unlink->seqnum = seqnum;
+ unlink->status = status;
+
+ list_add_tail(&unlink->list, &sdev->unlink_tx);
+}
+
+/**
+ * stub_complete - completion handler of a usbip urb
+ * @urb: pointer to the urb completed
+ *
+ * When a urb has completed, the USB core driver calls this function mostly in
+ * the interrupt context. To return the result of a urb, the completed urb is
+ * linked to the pending list of returning.
+ *
+ */
+void stub_complete(struct urb *urb)
+{
+ struct stub_priv *priv = (struct stub_priv *) urb->context;
+ struct stub_device *sdev = priv->sdev;
+ unsigned long flags;
+
+ usbip_dbg_stub_tx("complete! status %d\n", urb->status);
+
+ switch (urb->status) {
+ case 0:
+ /* OK */
+ break;
+ case -ENOENT:
+ dev_info(&urb->dev->dev,
+ "stopped by a call to usb_kill_urb() because of cleaning up a virtual connection\n");
+ return;
+ case -ECONNRESET:
+ dev_info(&urb->dev->dev,
+ "unlinked by a call to usb_unlink_urb()\n");
+ break;
+ case -EPIPE:
+ dev_info(&urb->dev->dev, "endpoint %d is stalled\n",
+ usb_pipeendpoint(urb->pipe));
+ break;
+ case -ESHUTDOWN:
+ dev_info(&urb->dev->dev, "device removed?\n");
+ break;
+ default:
+ dev_info(&urb->dev->dev,
+ "urb completion with non-zero status %d\n",
+ urb->status);
+ break;
+ }
+
+ /* link a urb to the queue of tx. */
+ spin_lock_irqsave(&sdev->priv_lock, flags);
+ if (priv->unlinking) {
+ stub_enqueue_ret_unlink(sdev, priv->seqnum, urb->status);
+ stub_free_priv_and_urb(priv);
+ } else {
+ list_move_tail(&priv->list, &sdev->priv_tx);
+ }
+ spin_unlock_irqrestore(&sdev->priv_lock, flags);
+
+ /* wake up tx_thread */
+ wake_up(&sdev->tx_waitq);
+}
+
+static inline void setup_base_pdu(struct usbip_header_basic *base,
+ __u32 command, __u32 seqnum)
+{
+ base->command = command;
+ base->seqnum = seqnum;
+ base->devid = 0;
+ base->ep = 0;
+ base->direction = 0;
+}
+
+static void setup_ret_submit_pdu(struct usbip_header *rpdu, struct urb *urb)
+{
+ struct stub_priv *priv = (struct stub_priv *) urb->context;
+
+ setup_base_pdu(&rpdu->base, USBIP_RET_SUBMIT, priv->seqnum);
+ usbip_pack_pdu(rpdu, urb, USBIP_RET_SUBMIT, 1);
+}
+
+static void setup_ret_unlink_pdu(struct usbip_header *rpdu,
+ struct stub_unlink *unlink)
+{
+ setup_base_pdu(&rpdu->base, USBIP_RET_UNLINK, unlink->seqnum);
+ rpdu->u.ret_unlink.status = unlink->status;
+}
+
+static struct stub_priv *dequeue_from_priv_tx(struct stub_device *sdev)
+{
+ unsigned long flags;
+ struct stub_priv *priv, *tmp;
+
+ spin_lock_irqsave(&sdev->priv_lock, flags);
+
+ list_for_each_entry_safe(priv, tmp, &sdev->priv_tx, list) {
+ list_move_tail(&priv->list, &sdev->priv_free);
+ spin_unlock_irqrestore(&sdev->priv_lock, flags);
+ return priv;
+ }
+
+ spin_unlock_irqrestore(&sdev->priv_lock, flags);
+
+ return NULL;
+}
+
+static int stub_send_ret_submit(struct stub_device *sdev)
+{
+ unsigned long flags;
+ struct stub_priv *priv, *tmp;
+
+ struct msghdr msg;
+ size_t txsize;
+
+ size_t total_size = 0;
+
+ while ((priv = dequeue_from_priv_tx(sdev)) != NULL) {
+ int ret;
+ struct urb *urb = priv->urb;
+ struct usbip_header pdu_header;
+ struct usbip_iso_packet_descriptor *iso_buffer = NULL;
+ struct kvec *iov = NULL;
+ int iovnum = 0;
+
+ txsize = 0;
+ memset(&pdu_header, 0, sizeof(pdu_header));
+ memset(&msg, 0, sizeof(msg));
+
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)
+ iovnum = 2 + urb->number_of_packets;
+ else
+ iovnum = 2;
+
+ iov = kcalloc(iovnum, sizeof(struct kvec), GFP_KERNEL);
+
+ if (!iov) {
+ usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_MALLOC);
+ return -1;
+ }
+
+ iovnum = 0;
+
+ /* 1. setup usbip_header */
+ setup_ret_submit_pdu(&pdu_header, urb);
+ usbip_dbg_stub_tx("setup txdata seqnum: %d urb: %p\n",
+ pdu_header.base.seqnum, urb);
+ usbip_header_correct_endian(&pdu_header, 1);
+
+ iov[iovnum].iov_base = &pdu_header;
+ iov[iovnum].iov_len = sizeof(pdu_header);
+ iovnum++;
+ txsize += sizeof(pdu_header);
+
+ /* 2. setup transfer buffer */
+ if (usb_pipein(urb->pipe) &&
+ usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS &&
+ urb->actual_length > 0) {
+ iov[iovnum].iov_base = urb->transfer_buffer;
+ iov[iovnum].iov_len = urb->actual_length;
+ iovnum++;
+ txsize += urb->actual_length;
+ } else if (usb_pipein(urb->pipe) &&
+ usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ /*
+ * For isochronous packets: actual length is the sum of
+ * the actual length of the individual, packets, but as
+ * the packet offsets are not changed there will be
+ * padding between the packets. To optimally use the
+ * bandwidth the padding is not transmitted.
+ */
+
+ int i;
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ iov[iovnum].iov_base = urb->transfer_buffer +
+ urb->iso_frame_desc[i].offset;
+ iov[iovnum].iov_len =
+ urb->iso_frame_desc[i].actual_length;
+ iovnum++;
+ txsize += urb->iso_frame_desc[i].actual_length;
+ }
+
+ if (txsize != sizeof(pdu_header) + urb->actual_length) {
+ dev_err(&sdev->interface->dev,
+ "actual length of urb %d does not match iso packet sizes %zu\n",
+ urb->actual_length,
+ txsize-sizeof(pdu_header));
+ kfree(iov);
+ usbip_event_add(&sdev->ud,
+ SDEV_EVENT_ERROR_TCP);
+ return -1;
+ }
+ }
+
+ /* 3. setup iso_packet_descriptor */
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ ssize_t len = 0;
+
+ iso_buffer = usbip_alloc_iso_desc_pdu(urb, &len);
+ if (!iso_buffer) {
+ usbip_event_add(&sdev->ud,
+ SDEV_EVENT_ERROR_MALLOC);
+ kfree(iov);
+ return -1;
+ }
+
+ iov[iovnum].iov_base = iso_buffer;
+ iov[iovnum].iov_len = len;
+ txsize += len;
+ iovnum++;
+ }
+
+ ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg,
+ iov, iovnum, txsize);
+ if (ret != txsize) {
+ dev_err(&sdev->interface->dev,
+ "sendmsg failed!, retval %d for %zd\n",
+ ret, txsize);
+ kfree(iov);
+ kfree(iso_buffer);
+ usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP);
+ return -1;
+ }
+
+ kfree(iov);
+ kfree(iso_buffer);
+
+ total_size += txsize;
+ }
+
+ spin_lock_irqsave(&sdev->priv_lock, flags);
+ list_for_each_entry_safe(priv, tmp, &sdev->priv_free, list) {
+ stub_free_priv_and_urb(priv);
+ }
+ spin_unlock_irqrestore(&sdev->priv_lock, flags);
+
+ return total_size;
+}
+
+static struct stub_unlink *dequeue_from_unlink_tx(struct stub_device *sdev)
+{
+ unsigned long flags;
+ struct stub_unlink *unlink, *tmp;
+
+ spin_lock_irqsave(&sdev->priv_lock, flags);
+
+ list_for_each_entry_safe(unlink, tmp, &sdev->unlink_tx, list) {
+ list_move_tail(&unlink->list, &sdev->unlink_free);
+ spin_unlock_irqrestore(&sdev->priv_lock, flags);
+ return unlink;
+ }
+
+ spin_unlock_irqrestore(&sdev->priv_lock, flags);
+
+ return NULL;
+}
+
+static int stub_send_ret_unlink(struct stub_device *sdev)
+{
+ unsigned long flags;
+ struct stub_unlink *unlink, *tmp;
+
+ struct msghdr msg;
+ struct kvec iov[1];
+ size_t txsize;
+
+ size_t total_size = 0;
+
+ while ((unlink = dequeue_from_unlink_tx(sdev)) != NULL) {
+ int ret;
+ struct usbip_header pdu_header;
+
+ txsize = 0;
+ memset(&pdu_header, 0, sizeof(pdu_header));
+ memset(&msg, 0, sizeof(msg));
+ memset(&iov, 0, sizeof(iov));
+
+ usbip_dbg_stub_tx("setup ret unlink %lu\n", unlink->seqnum);
+
+ /* 1. setup usbip_header */
+ setup_ret_unlink_pdu(&pdu_header, unlink);
+ usbip_header_correct_endian(&pdu_header, 1);
+
+ iov[0].iov_base = &pdu_header;
+ iov[0].iov_len = sizeof(pdu_header);
+ txsize += sizeof(pdu_header);
+
+ ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg, iov,
+ 1, txsize);
+ if (ret != txsize) {
+ dev_err(&sdev->interface->dev,
+ "sendmsg failed!, retval %d for %zd\n",
+ ret, txsize);
+ usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP);
+ return -1;
+ }
+
+ usbip_dbg_stub_tx("send txdata\n");
+ total_size += txsize;
+ }
+
+ spin_lock_irqsave(&sdev->priv_lock, flags);
+
+ list_for_each_entry_safe(unlink, tmp, &sdev->unlink_free, list) {
+ list_del(&unlink->list);
+ kfree(unlink);
+ }
+
+ spin_unlock_irqrestore(&sdev->priv_lock, flags);
+
+ return total_size;
+}
+
+int stub_tx_loop(void *data)
+{
+ struct usbip_device *ud = data;
+ struct stub_device *sdev = container_of(ud, struct stub_device, ud);
+
+ while (!kthread_should_stop()) {
+ if (usbip_event_happened(ud))
+ break;
+
+ /*
+ * send_ret_submit comes earlier than send_ret_unlink. stub_rx
+ * looks at only priv_init queue. If the completion of a URB is
+ * earlier than the receive of CMD_UNLINK, priv is moved to
+ * priv_tx queue and stub_rx does not find the target priv. In
+ * this case, vhci_rx receives the result of the submit request
+ * and then receives the result of the unlink request. The
+ * result of the submit is given back to the usbcore as the
+ * completion of the unlink request. The request of the
+ * unlink is ignored. This is ok because a driver who calls
+ * usb_unlink_urb() understands the unlink was too late by
+ * getting the status of the given-backed URB which has the
+ * status of usb_submit_urb().
+ */
+ if (stub_send_ret_submit(sdev) < 0)
+ break;
+
+ if (stub_send_ret_unlink(sdev) < 0)
+ break;
+
+ wait_event_interruptible(sdev->tx_waitq,
+ (!list_empty(&sdev->priv_tx) ||
+ !list_empty(&sdev->unlink_tx) ||
+ kthread_should_stop()));
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ *
+ * This 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 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <asm/byteorder.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <net/sock.h>
+
+#include "usbip_common.h"
+
+#define DRIVER_AUTHOR "Takahiro Hirofuchi <hirofuchi@users.sourceforge.net>"
+#define DRIVER_DESC "USB/IP Core"
+
+#ifdef CONFIG_USBIP_DEBUG
+unsigned long usbip_debug_flag = 0xffffffff;
+#else
+unsigned long usbip_debug_flag;
+#endif
+EXPORT_SYMBOL_GPL(usbip_debug_flag);
+module_param(usbip_debug_flag, ulong, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(usbip_debug_flag, "debug flags (defined in usbip_common.h)");
+
+/* FIXME */
+struct device_attribute dev_attr_usbip_debug;
+EXPORT_SYMBOL_GPL(dev_attr_usbip_debug);
+
+static ssize_t usbip_debug_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%lx\n", usbip_debug_flag);
+}
+
+static ssize_t usbip_debug_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ if (sscanf(buf, "%lx", &usbip_debug_flag) != 1)
+ return -EINVAL;
+ return count;
+}
+DEVICE_ATTR_RW(usbip_debug);
+
+static void usbip_dump_buffer(char *buff, int bufflen)
+{
+ print_hex_dump(KERN_DEBUG, "usbip-core", DUMP_PREFIX_OFFSET, 16, 4,
+ buff, bufflen, false);
+}
+
+static void usbip_dump_pipe(unsigned int p)
+{
+ unsigned char type = usb_pipetype(p);
+ unsigned char ep = usb_pipeendpoint(p);
+ unsigned char dev = usb_pipedevice(p);
+ unsigned char dir = usb_pipein(p);
+
+ pr_debug("dev(%d) ep(%d) [%s] ", dev, ep, dir ? "IN" : "OUT");
+
+ switch (type) {
+ case PIPE_ISOCHRONOUS:
+ pr_debug("ISO\n");
+ break;
+ case PIPE_INTERRUPT:
+ pr_debug("INT\n");
+ break;
+ case PIPE_CONTROL:
+ pr_debug("CTRL\n");
+ break;
+ case PIPE_BULK:
+ pr_debug("BULK\n");
+ break;
+ default:
+ pr_debug("ERR\n");
+ break;
+ }
+}
+
+static void usbip_dump_usb_device(struct usb_device *udev)
+{
+ struct device *dev = &udev->dev;
+ int i;
+
+ dev_dbg(dev, " devnum(%d) devpath(%s) usb speed(%s)",
+ udev->devnum, udev->devpath, usb_speed_string(udev->speed));
+
+ pr_debug("tt %p, ttport %d\n", udev->tt, udev->ttport);
+
+ dev_dbg(dev, " ");
+ for (i = 0; i < 16; i++)
+ pr_debug(" %2u", i);
+ pr_debug("\n");
+
+ dev_dbg(dev, " toggle0(IN) :");
+ for (i = 0; i < 16; i++)
+ pr_debug(" %2u", (udev->toggle[0] & (1 << i)) ? 1 : 0);
+ pr_debug("\n");
+
+ dev_dbg(dev, " toggle1(OUT):");
+ for (i = 0; i < 16; i++)
+ pr_debug(" %2u", (udev->toggle[1] & (1 << i)) ? 1 : 0);
+ pr_debug("\n");
+
+ dev_dbg(dev, " epmaxp_in :");
+ for (i = 0; i < 16; i++) {
+ if (udev->ep_in[i])
+ pr_debug(" %2u",
+ le16_to_cpu(udev->ep_in[i]->desc.wMaxPacketSize));
+ }
+ pr_debug("\n");
+
+ dev_dbg(dev, " epmaxp_out :");
+ for (i = 0; i < 16; i++) {
+ if (udev->ep_out[i])
+ pr_debug(" %2u",
+ le16_to_cpu(udev->ep_out[i]->desc.wMaxPacketSize));
+ }
+ pr_debug("\n");
+
+ dev_dbg(dev, "parent %p, bus %p\n", udev->parent, udev->bus);
+
+ dev_dbg(dev,
+ "descriptor %p, config %p, actconfig %p, rawdescriptors %p\n",
+ &udev->descriptor, udev->config,
+ udev->actconfig, udev->rawdescriptors);
+
+ dev_dbg(dev, "have_langid %d, string_langid %d\n",
+ udev->have_langid, udev->string_langid);
+
+ dev_dbg(dev, "maxchild %d\n", udev->maxchild);
+}
+
+static void usbip_dump_request_type(__u8 rt)
+{
+ switch (rt & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ pr_debug("DEVICE");
+ break;
+ case USB_RECIP_INTERFACE:
+ pr_debug("INTERF");
+ break;
+ case USB_RECIP_ENDPOINT:
+ pr_debug("ENDPOI");
+ break;
+ case USB_RECIP_OTHER:
+ pr_debug("OTHER ");
+ break;
+ default:
+ pr_debug("------");
+ break;
+ }
+}
+
+static void usbip_dump_usb_ctrlrequest(struct usb_ctrlrequest *cmd)
+{
+ if (!cmd) {
+ pr_debug(" : null pointer\n");
+ return;
+ }
+
+ pr_debug(" ");
+ pr_debug("bRequestType(%02X) bRequest(%02X) wValue(%04X) wIndex(%04X) wLength(%04X) ",
+ cmd->bRequestType, cmd->bRequest,
+ cmd->wValue, cmd->wIndex, cmd->wLength);
+ pr_debug("\n ");
+
+ if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+ pr_debug("STANDARD ");
+ switch (cmd->bRequest) {
+ case USB_REQ_GET_STATUS:
+ pr_debug("GET_STATUS\n");
+ break;
+ case USB_REQ_CLEAR_FEATURE:
+ pr_debug("CLEAR_FEAT\n");
+ break;
+ case USB_REQ_SET_FEATURE:
+ pr_debug("SET_FEAT\n");
+ break;
+ case USB_REQ_SET_ADDRESS:
+ pr_debug("SET_ADDRRS\n");
+ break;
+ case USB_REQ_GET_DESCRIPTOR:
+ pr_debug("GET_DESCRI\n");
+ break;
+ case USB_REQ_SET_DESCRIPTOR:
+ pr_debug("SET_DESCRI\n");
+ break;
+ case USB_REQ_GET_CONFIGURATION:
+ pr_debug("GET_CONFIG\n");
+ break;
+ case USB_REQ_SET_CONFIGURATION:
+ pr_debug("SET_CONFIG\n");
+ break;
+ case USB_REQ_GET_INTERFACE:
+ pr_debug("GET_INTERF\n");
+ break;
+ case USB_REQ_SET_INTERFACE:
+ pr_debug("SET_INTERF\n");
+ break;
+ case USB_REQ_SYNCH_FRAME:
+ pr_debug("SYNC_FRAME\n");
+ break;
+ default:
+ pr_debug("REQ(%02X)\n", cmd->bRequest);
+ break;
+ }
+ usbip_dump_request_type(cmd->bRequestType);
+ } else if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) {
+ pr_debug("CLASS\n");
+ } else if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) {
+ pr_debug("VENDOR\n");
+ } else if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_RESERVED) {
+ pr_debug("RESERVED\n");
+ }
+}
+
+void usbip_dump_urb(struct urb *urb)
+{
+ struct device *dev;
+
+ if (!urb) {
+ pr_debug("urb: null pointer!!\n");
+ return;
+ }
+
+ if (!urb->dev) {
+ pr_debug("urb->dev: null pointer!!\n");
+ return;
+ }
+
+ dev = &urb->dev->dev;
+
+ dev_dbg(dev, " urb :%p\n", urb);
+ dev_dbg(dev, " dev :%p\n", urb->dev);
+
+ usbip_dump_usb_device(urb->dev);
+
+ dev_dbg(dev, " pipe :%08x ", urb->pipe);
+
+ usbip_dump_pipe(urb->pipe);
+
+ dev_dbg(dev, " status :%d\n", urb->status);
+ dev_dbg(dev, " transfer_flags :%08X\n", urb->transfer_flags);
+ dev_dbg(dev, " transfer_buffer :%p\n", urb->transfer_buffer);
+ dev_dbg(dev, " transfer_buffer_length:%d\n",
+ urb->transfer_buffer_length);
+ dev_dbg(dev, " actual_length :%d\n", urb->actual_length);
+ dev_dbg(dev, " setup_packet :%p\n", urb->setup_packet);
+
+ if (urb->setup_packet && usb_pipetype(urb->pipe) == PIPE_CONTROL)
+ usbip_dump_usb_ctrlrequest(
+ (struct usb_ctrlrequest *)urb->setup_packet);
+
+ dev_dbg(dev, " start_frame :%d\n", urb->start_frame);
+ dev_dbg(dev, " number_of_packets :%d\n", urb->number_of_packets);
+ dev_dbg(dev, " interval :%d\n", urb->interval);
+ dev_dbg(dev, " error_count :%d\n", urb->error_count);
+ dev_dbg(dev, " context :%p\n", urb->context);
+ dev_dbg(dev, " complete :%p\n", urb->complete);
+}
+EXPORT_SYMBOL_GPL(usbip_dump_urb);
+
+void usbip_dump_header(struct usbip_header *pdu)
+{
+ pr_debug("BASE: cmd %u seq %u devid %u dir %u ep %u\n",
+ pdu->base.command,
+ pdu->base.seqnum,
+ pdu->base.devid,
+ pdu->base.direction,
+ pdu->base.ep);
+
+ switch (pdu->base.command) {
+ case USBIP_CMD_SUBMIT:
+ pr_debug("USBIP_CMD_SUBMIT: x_flags %u x_len %u sf %u #p %d iv %d\n",
+ pdu->u.cmd_submit.transfer_flags,
+ pdu->u.cmd_submit.transfer_buffer_length,
+ pdu->u.cmd_submit.start_frame,
+ pdu->u.cmd_submit.number_of_packets,
+ pdu->u.cmd_submit.interval);
+ break;
+ case USBIP_CMD_UNLINK:
+ pr_debug("USBIP_CMD_UNLINK: seq %u\n",
+ pdu->u.cmd_unlink.seqnum);
+ break;
+ case USBIP_RET_SUBMIT:
+ pr_debug("USBIP_RET_SUBMIT: st %d al %u sf %d #p %d ec %d\n",
+ pdu->u.ret_submit.status,
+ pdu->u.ret_submit.actual_length,
+ pdu->u.ret_submit.start_frame,
+ pdu->u.ret_submit.number_of_packets,
+ pdu->u.ret_submit.error_count);
+ break;
+ case USBIP_RET_UNLINK:
+ pr_debug("USBIP_RET_UNLINK: status %d\n",
+ pdu->u.ret_unlink.status);
+ break;
+ default:
+ /* NOT REACHED */
+ pr_err("unknown command\n");
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(usbip_dump_header);
+
+/* Receive data over TCP/IP. */
+int usbip_recv(struct socket *sock, void *buf, int size)
+{
+ int result;
+ struct msghdr msg;
+ struct kvec iov;
+ int total = 0;
+
+ /* for blocks of if (usbip_dbg_flag_xmit) */
+ char *bp = buf;
+ int osize = size;
+
+ usbip_dbg_xmit("enter\n");
+
+ if (!sock || !buf || !size) {
+ pr_err("invalid arg, sock %p buff %p size %d\n", sock, buf,
+ size);
+ return -EINVAL;
+ }
+
+ do {
+ sock->sk->sk_allocation = GFP_NOIO;
+ iov.iov_base = buf;
+ iov.iov_len = size;
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = MSG_NOSIGNAL;
+
+ result = kernel_recvmsg(sock, &msg, &iov, 1, size, MSG_WAITALL);
+ if (result <= 0) {
+ pr_debug("receive sock %p buf %p size %u ret %d total %d\n",
+ sock, buf, size, result, total);
+ goto err;
+ }
+
+ size -= result;
+ buf += result;
+ total += result;
+ } while (size > 0);
+
+ if (usbip_dbg_flag_xmit) {
+ if (!in_interrupt())
+ pr_debug("%-10s:", current->comm);
+ else
+ pr_debug("interrupt :");
+
+ pr_debug("receiving....\n");
+ usbip_dump_buffer(bp, osize);
+ pr_debug("received, osize %d ret %d size %d total %d\n",
+ osize, result, size, total);
+ }
+
+ return total;
+
+err:
+ return result;
+}
+EXPORT_SYMBOL_GPL(usbip_recv);
+
+/* there may be more cases to tweak the flags. */
+static unsigned int tweak_transfer_flags(unsigned int flags)
+{
+ flags &= ~URB_NO_TRANSFER_DMA_MAP;
+ return flags;
+}
+
+static void usbip_pack_cmd_submit(struct usbip_header *pdu, struct urb *urb,
+ int pack)
+{
+ struct usbip_header_cmd_submit *spdu = &pdu->u.cmd_submit;
+
+ /*
+ * Some members are not still implemented in usbip. I hope this issue
+ * will be discussed when usbip is ported to other operating systems.
+ */
+ if (pack) {
+ spdu->transfer_flags =
+ tweak_transfer_flags(urb->transfer_flags);
+ spdu->transfer_buffer_length = urb->transfer_buffer_length;
+ spdu->start_frame = urb->start_frame;
+ spdu->number_of_packets = urb->number_of_packets;
+ spdu->interval = urb->interval;
+ } else {
+ urb->transfer_flags = spdu->transfer_flags;
+ urb->transfer_buffer_length = spdu->transfer_buffer_length;
+ urb->start_frame = spdu->start_frame;
+ urb->number_of_packets = spdu->number_of_packets;
+ urb->interval = spdu->interval;
+ }
+}
+
+static void usbip_pack_ret_submit(struct usbip_header *pdu, struct urb *urb,
+ int pack)
+{
+ struct usbip_header_ret_submit *rpdu = &pdu->u.ret_submit;
+
+ if (pack) {
+ rpdu->status = urb->status;
+ rpdu->actual_length = urb->actual_length;
+ rpdu->start_frame = urb->start_frame;
+ rpdu->number_of_packets = urb->number_of_packets;
+ rpdu->error_count = urb->error_count;
+ } else {
+ urb->status = rpdu->status;
+ urb->actual_length = rpdu->actual_length;
+ urb->start_frame = rpdu->start_frame;
+ urb->number_of_packets = rpdu->number_of_packets;
+ urb->error_count = rpdu->error_count;
+ }
+}
+
+void usbip_pack_pdu(struct usbip_header *pdu, struct urb *urb, int cmd,
+ int pack)
+{
+ switch (cmd) {
+ case USBIP_CMD_SUBMIT:
+ usbip_pack_cmd_submit(pdu, urb, pack);
+ break;
+ case USBIP_RET_SUBMIT:
+ usbip_pack_ret_submit(pdu, urb, pack);
+ break;
+ default:
+ /* NOT REACHED */
+ pr_err("unknown command\n");
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(usbip_pack_pdu);
+
+static void correct_endian_basic(struct usbip_header_basic *base, int send)
+{
+ if (send) {
+ base->command = cpu_to_be32(base->command);
+ base->seqnum = cpu_to_be32(base->seqnum);
+ base->devid = cpu_to_be32(base->devid);
+ base->direction = cpu_to_be32(base->direction);
+ base->ep = cpu_to_be32(base->ep);
+ } else {
+ base->command = be32_to_cpu(base->command);
+ base->seqnum = be32_to_cpu(base->seqnum);
+ base->devid = be32_to_cpu(base->devid);
+ base->direction = be32_to_cpu(base->direction);
+ base->ep = be32_to_cpu(base->ep);
+ }
+}
+
+static void correct_endian_cmd_submit(struct usbip_header_cmd_submit *pdu,
+ int send)
+{
+ if (send) {
+ pdu->transfer_flags = cpu_to_be32(pdu->transfer_flags);
+
+ cpu_to_be32s(&pdu->transfer_buffer_length);
+ cpu_to_be32s(&pdu->start_frame);
+ cpu_to_be32s(&pdu->number_of_packets);
+ cpu_to_be32s(&pdu->interval);
+ } else {
+ pdu->transfer_flags = be32_to_cpu(pdu->transfer_flags);
+
+ be32_to_cpus(&pdu->transfer_buffer_length);
+ be32_to_cpus(&pdu->start_frame);
+ be32_to_cpus(&pdu->number_of_packets);
+ be32_to_cpus(&pdu->interval);
+ }
+}
+
+static void correct_endian_ret_submit(struct usbip_header_ret_submit *pdu,
+ int send)
+{
+ if (send) {
+ cpu_to_be32s(&pdu->status);
+ cpu_to_be32s(&pdu->actual_length);
+ cpu_to_be32s(&pdu->start_frame);
+ cpu_to_be32s(&pdu->number_of_packets);
+ cpu_to_be32s(&pdu->error_count);
+ } else {
+ be32_to_cpus(&pdu->status);
+ be32_to_cpus(&pdu->actual_length);
+ be32_to_cpus(&pdu->start_frame);
+ be32_to_cpus(&pdu->number_of_packets);
+ be32_to_cpus(&pdu->error_count);
+ }
+}
+
+static void correct_endian_cmd_unlink(struct usbip_header_cmd_unlink *pdu,
+ int send)
+{
+ if (send)
+ pdu->seqnum = cpu_to_be32(pdu->seqnum);
+ else
+ pdu->seqnum = be32_to_cpu(pdu->seqnum);
+}
+
+static void correct_endian_ret_unlink(struct usbip_header_ret_unlink *pdu,
+ int send)
+{
+ if (send)
+ cpu_to_be32s(&pdu->status);
+ else
+ be32_to_cpus(&pdu->status);
+}
+
+void usbip_header_correct_endian(struct usbip_header *pdu, int send)
+{
+ __u32 cmd = 0;
+
+ if (send)
+ cmd = pdu->base.command;
+
+ correct_endian_basic(&pdu->base, send);
+
+ if (!send)
+ cmd = pdu->base.command;
+
+ switch (cmd) {
+ case USBIP_CMD_SUBMIT:
+ correct_endian_cmd_submit(&pdu->u.cmd_submit, send);
+ break;
+ case USBIP_RET_SUBMIT:
+ correct_endian_ret_submit(&pdu->u.ret_submit, send);
+ break;
+ case USBIP_CMD_UNLINK:
+ correct_endian_cmd_unlink(&pdu->u.cmd_unlink, send);
+ break;
+ case USBIP_RET_UNLINK:
+ correct_endian_ret_unlink(&pdu->u.ret_unlink, send);
+ break;
+ default:
+ /* NOT REACHED */
+ pr_err("unknown command\n");
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(usbip_header_correct_endian);
+
+static void usbip_iso_packet_correct_endian(
+ struct usbip_iso_packet_descriptor *iso, int send)
+{
+ /* does not need all members. but copy all simply. */
+ if (send) {
+ iso->offset = cpu_to_be32(iso->offset);
+ iso->length = cpu_to_be32(iso->length);
+ iso->status = cpu_to_be32(iso->status);
+ iso->actual_length = cpu_to_be32(iso->actual_length);
+ } else {
+ iso->offset = be32_to_cpu(iso->offset);
+ iso->length = be32_to_cpu(iso->length);
+ iso->status = be32_to_cpu(iso->status);
+ iso->actual_length = be32_to_cpu(iso->actual_length);
+ }
+}
+
+static void usbip_pack_iso(struct usbip_iso_packet_descriptor *iso,
+ struct usb_iso_packet_descriptor *uiso, int pack)
+{
+ if (pack) {
+ iso->offset = uiso->offset;
+ iso->length = uiso->length;
+ iso->status = uiso->status;
+ iso->actual_length = uiso->actual_length;
+ } else {
+ uiso->offset = iso->offset;
+ uiso->length = iso->length;
+ uiso->status = iso->status;
+ uiso->actual_length = iso->actual_length;
+ }
+}
+
+/* must free buffer */
+struct usbip_iso_packet_descriptor*
+usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen)
+{
+ struct usbip_iso_packet_descriptor *iso;
+ int np = urb->number_of_packets;
+ ssize_t size = np * sizeof(*iso);
+ int i;
+
+ iso = kzalloc(size, GFP_KERNEL);
+ if (!iso)
+ return NULL;
+
+ for (i = 0; i < np; i++) {
+ usbip_pack_iso(&iso[i], &urb->iso_frame_desc[i], 1);
+ usbip_iso_packet_correct_endian(&iso[i], 1);
+ }
+
+ *bufflen = size;
+
+ return iso;
+}
+EXPORT_SYMBOL_GPL(usbip_alloc_iso_desc_pdu);
+
+/* some members of urb must be substituted before. */
+int usbip_recv_iso(struct usbip_device *ud, struct urb *urb)
+{
+ void *buff;
+ struct usbip_iso_packet_descriptor *iso;
+ int np = urb->number_of_packets;
+ int size = np * sizeof(*iso);
+ int i;
+ int ret;
+ int total_length = 0;
+
+ if (!usb_pipeisoc(urb->pipe))
+ return 0;
+
+ /* my Bluetooth dongle gets ISO URBs which are np = 0 */
+ if (np == 0)
+ return 0;
+
+ buff = kzalloc(size, GFP_KERNEL);
+ if (!buff)
+ return -ENOMEM;
+
+ ret = usbip_recv(ud->tcp_socket, buff, size);
+ if (ret != size) {
+ dev_err(&urb->dev->dev, "recv iso_frame_descriptor, %d\n",
+ ret);
+ kfree(buff);
+
+ if (ud->side == USBIP_STUB)
+ usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
+ else
+ usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
+
+ return -EPIPE;
+ }
+
+ iso = (struct usbip_iso_packet_descriptor *) buff;
+ for (i = 0; i < np; i++) {
+ usbip_iso_packet_correct_endian(&iso[i], 0);
+ usbip_pack_iso(&iso[i], &urb->iso_frame_desc[i], 0);
+ total_length += urb->iso_frame_desc[i].actual_length;
+ }
+
+ kfree(buff);
+
+ if (total_length != urb->actual_length) {
+ dev_err(&urb->dev->dev,
+ "total length of iso packets %d not equal to actual length of buffer %d\n",
+ total_length, urb->actual_length);
+
+ if (ud->side == USBIP_STUB)
+ usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
+ else
+ usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
+
+ return -EPIPE;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usbip_recv_iso);
+
+/*
+ * This functions restores the padding which was removed for optimizing
+ * the bandwidth during transfer over tcp/ip
+ *
+ * buffer and iso packets need to be stored and be in propeper endian in urb
+ * before calling this function
+ */
+void usbip_pad_iso(struct usbip_device *ud, struct urb *urb)
+{
+ int np = urb->number_of_packets;
+ int i;
+ int actualoffset = urb->actual_length;
+
+ if (!usb_pipeisoc(urb->pipe))
+ return;
+
+ /* if no packets or length of data is 0, then nothing to unpack */
+ if (np == 0 || urb->actual_length == 0)
+ return;
+
+ /*
+ * if actual_length is transfer_buffer_length then no padding is
+ * present.
+ */
+ if (urb->actual_length == urb->transfer_buffer_length)
+ return;
+
+ /*
+ * loop over all packets from last to first (to prevent overwritting
+ * memory when padding) and move them into the proper place
+ */
+ for (i = np-1; i > 0; i--) {
+ actualoffset -= urb->iso_frame_desc[i].actual_length;
+ memmove(urb->transfer_buffer + urb->iso_frame_desc[i].offset,
+ urb->transfer_buffer + actualoffset,
+ urb->iso_frame_desc[i].actual_length);
+ }
+}
+EXPORT_SYMBOL_GPL(usbip_pad_iso);
+
+/* some members of urb must be substituted before. */
+int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb)
+{
+ int ret;
+ int size;
+
+ if (ud->side == USBIP_STUB) {
+ /* the direction of urb must be OUT. */
+ if (usb_pipein(urb->pipe))
+ return 0;
+
+ size = urb->transfer_buffer_length;
+ } else {
+ /* the direction of urb must be IN. */
+ if (usb_pipeout(urb->pipe))
+ return 0;
+
+ size = urb->actual_length;
+ }
+
+ /* no need to recv xbuff */
+ if (!(size > 0))
+ return 0;
+
+ ret = usbip_recv(ud->tcp_socket, urb->transfer_buffer, size);
+ if (ret != size) {
+ dev_err(&urb->dev->dev, "recv xbuf, %d\n", ret);
+ if (ud->side == USBIP_STUB) {
+ usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
+ } else {
+ usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
+ return -EPIPE;
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usbip_recv_xbuff);
+
+static int __init usbip_core_init(void)
+{
+ pr_info(DRIVER_DESC " v" USBIP_VERSION "\n");
+ return 0;
+}
+
+static void __exit usbip_core_exit(void)
+{
+ return;
+}
+
+module_init(usbip_core_init);
+module_exit(usbip_core_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(USBIP_VERSION);
--- /dev/null
+/*
+ * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ *
+ * This 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 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 __USBIP_COMMON_H
+#define __USBIP_COMMON_H
+
+#include <linux/compiler.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/net.h>
+#include <linux/printk.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/usb.h>
+#include <linux/wait.h>
+#include <uapi/linux/usbip.h>
+
+#define USBIP_VERSION "1.0.0"
+
+#undef pr_fmt
+
+#ifdef DEBUG
+#define pr_fmt(fmt) KBUILD_MODNAME ": %s:%d: " fmt, __func__, __LINE__
+#else
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#endif
+
+enum {
+ usbip_debug_xmit = (1 << 0),
+ usbip_debug_sysfs = (1 << 1),
+ usbip_debug_urb = (1 << 2),
+ usbip_debug_eh = (1 << 3),
+
+ usbip_debug_stub_cmp = (1 << 8),
+ usbip_debug_stub_dev = (1 << 9),
+ usbip_debug_stub_rx = (1 << 10),
+ usbip_debug_stub_tx = (1 << 11),
+
+ usbip_debug_vhci_rh = (1 << 8),
+ usbip_debug_vhci_hc = (1 << 9),
+ usbip_debug_vhci_rx = (1 << 10),
+ usbip_debug_vhci_tx = (1 << 11),
+ usbip_debug_vhci_sysfs = (1 << 12)
+};
+
+#define usbip_dbg_flag_xmit (usbip_debug_flag & usbip_debug_xmit)
+#define usbip_dbg_flag_vhci_rh (usbip_debug_flag & usbip_debug_vhci_rh)
+#define usbip_dbg_flag_vhci_hc (usbip_debug_flag & usbip_debug_vhci_hc)
+#define usbip_dbg_flag_vhci_rx (usbip_debug_flag & usbip_debug_vhci_rx)
+#define usbip_dbg_flag_vhci_tx (usbip_debug_flag & usbip_debug_vhci_tx)
+#define usbip_dbg_flag_stub_rx (usbip_debug_flag & usbip_debug_stub_rx)
+#define usbip_dbg_flag_stub_tx (usbip_debug_flag & usbip_debug_stub_tx)
+#define usbip_dbg_flag_vhci_sysfs (usbip_debug_flag & usbip_debug_vhci_sysfs)
+
+extern unsigned long usbip_debug_flag;
+extern struct device_attribute dev_attr_usbip_debug;
+
+#define usbip_dbg_with_flag(flag, fmt, args...) \
+ do { \
+ if (flag & usbip_debug_flag) \
+ pr_debug(fmt, ##args); \
+ } while (0)
+
+#define usbip_dbg_sysfs(fmt, args...) \
+ usbip_dbg_with_flag(usbip_debug_sysfs, fmt , ##args)
+#define usbip_dbg_xmit(fmt, args...) \
+ usbip_dbg_with_flag(usbip_debug_xmit, fmt , ##args)
+#define usbip_dbg_urb(fmt, args...) \
+ usbip_dbg_with_flag(usbip_debug_urb, fmt , ##args)
+#define usbip_dbg_eh(fmt, args...) \
+ usbip_dbg_with_flag(usbip_debug_eh, fmt , ##args)
+
+#define usbip_dbg_vhci_rh(fmt, args...) \
+ usbip_dbg_with_flag(usbip_debug_vhci_rh, fmt , ##args)
+#define usbip_dbg_vhci_hc(fmt, args...) \
+ usbip_dbg_with_flag(usbip_debug_vhci_hc, fmt , ##args)
+#define usbip_dbg_vhci_rx(fmt, args...) \
+ usbip_dbg_with_flag(usbip_debug_vhci_rx, fmt , ##args)
+#define usbip_dbg_vhci_tx(fmt, args...) \
+ usbip_dbg_with_flag(usbip_debug_vhci_tx, fmt , ##args)
+#define usbip_dbg_vhci_sysfs(fmt, args...) \
+ usbip_dbg_with_flag(usbip_debug_vhci_sysfs, fmt , ##args)
+
+#define usbip_dbg_stub_cmp(fmt, args...) \
+ usbip_dbg_with_flag(usbip_debug_stub_cmp, fmt , ##args)
+#define usbip_dbg_stub_rx(fmt, args...) \
+ usbip_dbg_with_flag(usbip_debug_stub_rx, fmt , ##args)
+#define usbip_dbg_stub_tx(fmt, args...) \
+ usbip_dbg_with_flag(usbip_debug_stub_tx, fmt , ##args)
+
+/*
+ * USB/IP request headers
+ *
+ * Each request is transferred across the network to its counterpart, which
+ * facilitates the normal USB communication. The values contained in the headers
+ * are basically the same as in a URB. Currently, four request types are
+ * defined:
+ *
+ * - USBIP_CMD_SUBMIT: a USB request block, corresponds to usb_submit_urb()
+ * (client to server)
+ *
+ * - USBIP_RET_SUBMIT: the result of USBIP_CMD_SUBMIT
+ * (server to client)
+ *
+ * - USBIP_CMD_UNLINK: an unlink request of a pending USBIP_CMD_SUBMIT,
+ * corresponds to usb_unlink_urb()
+ * (client to server)
+ *
+ * - USBIP_RET_UNLINK: the result of USBIP_CMD_UNLINK
+ * (server to client)
+ *
+ */
+#define USBIP_CMD_SUBMIT 0x0001
+#define USBIP_CMD_UNLINK 0x0002
+#define USBIP_RET_SUBMIT 0x0003
+#define USBIP_RET_UNLINK 0x0004
+
+#define USBIP_DIR_OUT 0x00
+#define USBIP_DIR_IN 0x01
+
+/**
+ * struct usbip_header_basic - data pertinent to every request
+ * @command: the usbip request type
+ * @seqnum: sequential number that identifies requests; incremented per
+ * connection
+ * @devid: specifies a remote USB device uniquely instead of busnum and devnum;
+ * in the stub driver, this value is ((busnum << 16) | devnum)
+ * @direction: direction of the transfer
+ * @ep: endpoint number
+ */
+struct usbip_header_basic {
+ __u32 command;
+ __u32 seqnum;
+ __u32 devid;
+ __u32 direction;
+ __u32 ep;
+} __packed;
+
+/**
+ * struct usbip_header_cmd_submit - USBIP_CMD_SUBMIT packet header
+ * @transfer_flags: URB flags
+ * @transfer_buffer_length: the data size for (in) or (out) transfer
+ * @start_frame: initial frame for isochronous or interrupt transfers
+ * @number_of_packets: number of isochronous packets
+ * @interval: maximum time for the request on the server-side host controller
+ * @setup: setup data for a control request
+ */
+struct usbip_header_cmd_submit {
+ __u32 transfer_flags;
+ __s32 transfer_buffer_length;
+
+ /* it is difficult for usbip to sync frames (reserved only?) */
+ __s32 start_frame;
+ __s32 number_of_packets;
+ __s32 interval;
+
+ unsigned char setup[8];
+} __packed;
+
+/**
+ * struct usbip_header_ret_submit - USBIP_RET_SUBMIT packet header
+ * @status: return status of a non-iso request
+ * @actual_length: number of bytes transferred
+ * @start_frame: initial frame for isochronous or interrupt transfers
+ * @number_of_packets: number of isochronous packets
+ * @error_count: number of errors for isochronous transfers
+ */
+struct usbip_header_ret_submit {
+ __s32 status;
+ __s32 actual_length;
+ __s32 start_frame;
+ __s32 number_of_packets;
+ __s32 error_count;
+} __packed;
+
+/**
+ * struct usbip_header_cmd_unlink - USBIP_CMD_UNLINK packet header
+ * @seqnum: the URB seqnum to unlink
+ */
+struct usbip_header_cmd_unlink {
+ __u32 seqnum;
+} __packed;
+
+/**
+ * struct usbip_header_ret_unlink - USBIP_RET_UNLINK packet header
+ * @status: return status of the request
+ */
+struct usbip_header_ret_unlink {
+ __s32 status;
+} __packed;
+
+/**
+ * struct usbip_header - common header for all usbip packets
+ * @base: the basic header
+ * @u: packet type dependent header
+ */
+struct usbip_header {
+ struct usbip_header_basic base;
+
+ union {
+ struct usbip_header_cmd_submit cmd_submit;
+ struct usbip_header_ret_submit ret_submit;
+ struct usbip_header_cmd_unlink cmd_unlink;
+ struct usbip_header_ret_unlink ret_unlink;
+ } u;
+} __packed;
+
+/*
+ * This is the same as usb_iso_packet_descriptor but packed for pdu.
+ */
+struct usbip_iso_packet_descriptor {
+ __u32 offset;
+ __u32 length; /* expected length */
+ __u32 actual_length;
+ __u32 status;
+} __packed;
+
+enum usbip_side {
+ USBIP_VHCI,
+ USBIP_STUB,
+};
+
+/* event handler */
+#define USBIP_EH_SHUTDOWN (1 << 0)
+#define USBIP_EH_BYE (1 << 1)
+#define USBIP_EH_RESET (1 << 2)
+#define USBIP_EH_UNUSABLE (1 << 3)
+
+#define SDEV_EVENT_REMOVED (USBIP_EH_SHUTDOWN | USBIP_EH_RESET | USBIP_EH_BYE)
+#define SDEV_EVENT_DOWN (USBIP_EH_SHUTDOWN | USBIP_EH_RESET)
+#define SDEV_EVENT_ERROR_TCP (USBIP_EH_SHUTDOWN | USBIP_EH_RESET)
+#define SDEV_EVENT_ERROR_SUBMIT (USBIP_EH_SHUTDOWN | USBIP_EH_RESET)
+#define SDEV_EVENT_ERROR_MALLOC (USBIP_EH_SHUTDOWN | USBIP_EH_UNUSABLE)
+
+#define VDEV_EVENT_REMOVED (USBIP_EH_SHUTDOWN | USBIP_EH_BYE)
+#define VDEV_EVENT_DOWN (USBIP_EH_SHUTDOWN | USBIP_EH_RESET)
+#define VDEV_EVENT_ERROR_TCP (USBIP_EH_SHUTDOWN | USBIP_EH_RESET)
+#define VDEV_EVENT_ERROR_MALLOC (USBIP_EH_SHUTDOWN | USBIP_EH_UNUSABLE)
+
+/* a common structure for stub_device and vhci_device */
+struct usbip_device {
+ enum usbip_side side;
+ enum usbip_device_status status;
+
+ /* lock for status */
+ spinlock_t lock;
+
+ struct socket *tcp_socket;
+
+ struct task_struct *tcp_rx;
+ struct task_struct *tcp_tx;
+
+ unsigned long event;
+ struct task_struct *eh;
+ wait_queue_head_t eh_waitq;
+
+ struct eh_ops {
+ void (*shutdown)(struct usbip_device *);
+ void (*reset)(struct usbip_device *);
+ void (*unusable)(struct usbip_device *);
+ } eh_ops;
+};
+
+#define kthread_get_run(threadfn, data, namefmt, ...) \
+({ \
+ struct task_struct *__k \
+ = kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \
+ if (!IS_ERR(__k)) { \
+ get_task_struct(__k); \
+ wake_up_process(__k); \
+ } \
+ __k; \
+})
+
+#define kthread_stop_put(k) \
+ do { \
+ kthread_stop(k); \
+ put_task_struct(k); \
+ } while (0)
+
+/* usbip_common.c */
+void usbip_dump_urb(struct urb *purb);
+void usbip_dump_header(struct usbip_header *pdu);
+
+int usbip_recv(struct socket *sock, void *buf, int size);
+
+void usbip_pack_pdu(struct usbip_header *pdu, struct urb *urb, int cmd,
+ int pack);
+void usbip_header_correct_endian(struct usbip_header *pdu, int send);
+
+struct usbip_iso_packet_descriptor*
+usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen);
+
+/* some members of urb must be substituted before. */
+int usbip_recv_iso(struct usbip_device *ud, struct urb *urb);
+void usbip_pad_iso(struct usbip_device *ud, struct urb *urb);
+int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb);
+
+/* usbip_event.c */
+int usbip_start_eh(struct usbip_device *ud);
+void usbip_stop_eh(struct usbip_device *ud);
+void usbip_event_add(struct usbip_device *ud, unsigned long event);
+int usbip_event_happened(struct usbip_device *ud);
+
+static inline int interface_to_busnum(struct usb_interface *interface)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+
+ return udev->bus->busnum;
+}
+
+static inline int interface_to_devnum(struct usb_interface *interface)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+
+ return udev->devnum;
+}
+
+#endif /* __USBIP_COMMON_H */
--- /dev/null
+/*
+ * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ *
+ * This 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 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <linux/kthread.h>
+#include <linux/export.h>
+
+#include "usbip_common.h"
+
+static int event_handler(struct usbip_device *ud)
+{
+ usbip_dbg_eh("enter\n");
+
+ /*
+ * Events are handled by only this thread.
+ */
+ while (usbip_event_happened(ud)) {
+ usbip_dbg_eh("pending event %lx\n", ud->event);
+
+ /*
+ * NOTE: shutdown must come first.
+ * Shutdown the device.
+ */
+ if (ud->event & USBIP_EH_SHUTDOWN) {
+ ud->eh_ops.shutdown(ud);
+ ud->event &= ~USBIP_EH_SHUTDOWN;
+ }
+
+ /* Reset the device. */
+ if (ud->event & USBIP_EH_RESET) {
+ ud->eh_ops.reset(ud);
+ ud->event &= ~USBIP_EH_RESET;
+ }
+
+ /* Mark the device as unusable. */
+ if (ud->event & USBIP_EH_UNUSABLE) {
+ ud->eh_ops.unusable(ud);
+ ud->event &= ~USBIP_EH_UNUSABLE;
+ }
+
+ /* Stop the error handler. */
+ if (ud->event & USBIP_EH_BYE)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int event_handler_loop(void *data)
+{
+ struct usbip_device *ud = data;
+
+ while (!kthread_should_stop()) {
+ wait_event_interruptible(ud->eh_waitq,
+ usbip_event_happened(ud) ||
+ kthread_should_stop());
+ usbip_dbg_eh("wakeup\n");
+
+ if (event_handler(ud) < 0)
+ break;
+ }
+
+ return 0;
+}
+
+int usbip_start_eh(struct usbip_device *ud)
+{
+ init_waitqueue_head(&ud->eh_waitq);
+ ud->event = 0;
+
+ ud->eh = kthread_run(event_handler_loop, ud, "usbip_eh");
+ if (IS_ERR(ud->eh)) {
+ pr_warn("Unable to start control thread\n");
+ return PTR_ERR(ud->eh);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usbip_start_eh);
+
+void usbip_stop_eh(struct usbip_device *ud)
+{
+ if (ud->eh == current)
+ return; /* do not wait for myself */
+
+ kthread_stop(ud->eh);
+ usbip_dbg_eh("usbip_eh has finished\n");
+}
+EXPORT_SYMBOL_GPL(usbip_stop_eh);
+
+void usbip_event_add(struct usbip_device *ud, unsigned long event)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ud->lock, flags);
+ ud->event |= event;
+ wake_up(&ud->eh_waitq);
+ spin_unlock_irqrestore(&ud->lock, flags);
+}
+EXPORT_SYMBOL_GPL(usbip_event_add);
+
+int usbip_event_happened(struct usbip_device *ud)
+{
+ int happened = 0;
+
+ spin_lock(&ud->lock);
+ if (ud->event != 0)
+ happened = 1;
+ spin_unlock(&ud->lock);
+
+ return happened;
+}
+EXPORT_SYMBOL_GPL(usbip_event_happened);
--- /dev/null
+PRELIMINARY DRAFT, MAY CONTAIN MISTAKES!
+28 Jun 2011
+
+The USB/IP protocol follows a server/client architecture. The server exports the
+USB devices and the clients imports them. The device driver for the exported
+USB device runs on the client machine.
+
+The client may ask for the list of the exported USB devices. To get the list the
+client opens a TCP/IP connection towards the server, and sends an OP_REQ_DEVLIST
+packet on top of the TCP/IP connection (so the actual OP_REQ_DEVLIST may be sent
+in one or more pieces at the low level transport layer). The server sends back
+the OP_REP_DEVLIST packet which lists the exported USB devices. Finally the
+TCP/IP connection is closed.
+
+ virtual host controller usb host
+ "client" "server"
+ (imports USB devices) (exports USB devices)
+ | |
+ | OP_REQ_DEVLIST |
+ | ----------------------------------------------> |
+ | |
+ | OP_REP_DEVLIST |
+ | <---------------------------------------------- |
+ | |
+
+Once the client knows the list of exported USB devices it may decide to use one
+of them. First the client opens a TCP/IP connection towards the server and
+sends an OP_REQ_IMPORT packet. The server replies with OP_REP_IMPORT. If the
+import was successful the TCP/IP connection remains open and will be used
+to transfer the URB traffic between the client and the server. The client may
+send two types of packets: the USBIP_CMD_SUBMIT to submit an URB, and
+USBIP_CMD_UNLINK to unlink a previously submitted URB. The answers of the
+server may be USBIP_RET_SUBMIT and USBIP_RET_UNLINK respectively.
+
+ virtual host controller usb host
+ "client" "server"
+ (imports USB devices) (exports USB devices)
+ | |
+ | OP_REQ_IMPORT |
+ | ----------------------------------------------> |
+ | |
+ | OP_REP_IMPORT |
+ | <---------------------------------------------- |
+ | |
+ | |
+ | USBIP_CMD_SUBMIT(seqnum = n) |
+ | ----------------------------------------------> |
+ | |
+ | USBIP_RET_SUBMIT(seqnum = n) |
+ | <---------------------------------------------- |
+ | . |
+ | : |
+ | |
+ | USBIP_CMD_SUBMIT(seqnum = m) |
+ | ----------------------------------------------> |
+ | |
+ | USBIP_CMD_SUBMIT(seqnum = m+1) |
+ | ----------------------------------------------> |
+ | |
+ | USBIP_CMD_SUBMIT(seqnum = m+2) |
+ | ----------------------------------------------> |
+ | |
+ | USBIP_RET_SUBMIT(seqnum = m) |
+ | <---------------------------------------------- |
+ | |
+ | USBIP_CMD_SUBMIT(seqnum = m+3) |
+ | ----------------------------------------------> |
+ | |
+ | USBIP_RET_SUBMIT(seqnum = m+1) |
+ | <---------------------------------------------- |
+ | |
+ | USBIP_CMD_SUBMIT(seqnum = m+4) |
+ | ----------------------------------------------> |
+ | |
+ | USBIP_RET_SUBMIT(seqnum = m+2) |
+ | <---------------------------------------------- |
+ | . |
+ | : |
+ | |
+ | USBIP_CMD_UNLINK |
+ | ----------------------------------------------> |
+ | |
+ | USBIP_RET_UNLINK |
+ | <---------------------------------------------- |
+ | |
+
+The fields are in network (big endian) byte order meaning that the most significant
+byte (MSB) is stored at the lowest address.
+
+
+OP_REQ_DEVLIST: Retrieve the list of exported USB devices.
+
+ Offset | Length | Value | Description
+-----------+--------+------------+---------------------------------------------------
+ 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0
+-----------+--------+------------+---------------------------------------------------
+ 2 | 2 | 0x8005 | Command code: Retrieve the list of exported USB
+ | | | devices.
+-----------+--------+------------+---------------------------------------------------
+ 4 | 4 | 0x00000000 | Status: unused, shall be set to 0
+
+OP_REP_DEVLIST: Reply with the list of exported USB devices.
+
+ Offset | Length | Value | Description
+-----------+--------+------------+---------------------------------------------------
+ 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0.
+-----------+--------+------------+---------------------------------------------------
+ 2 | 2 | 0x0005 | Reply code: The list of exported USB devices.
+-----------+--------+------------+---------------------------------------------------
+ 4 | 4 | 0x00000000 | Status: 0 for OK
+-----------+--------+------------+---------------------------------------------------
+ 8 | 4 | n | Number of exported devices: 0 means no exported
+ | | | devices.
+-----------+--------+------------+---------------------------------------------------
+ 0x0C | | | From now on the exported n devices are described,
+ | | | if any. If no devices are exported the message
+ | | | ends with the previous "number of exported
+ | | | devices" field.
+-----------+--------+------------+---------------------------------------------------
+ | 256 | | path: Path of the device on the host exporting the
+ | | | USB device, string closed with zero byte, e.g.
+ | | | "/sys/devices/pci0000:00/0000:00:1d.1/usb3/3-2"
+ | | | The unused bytes shall be filled with zero
+ | | | bytes.
+-----------+--------+------------+---------------------------------------------------
+ 0x10C | 32 | | busid: Bus ID of the exported device, string
+ | | | closed with zero byte, e.g. "3-2". The unused
+ | | | bytes shall be filled with zero bytes.
+-----------+--------+------------+---------------------------------------------------
+ 0x12C | 4 | | busnum
+-----------+--------+------------+---------------------------------------------------
+ 0x130 | 4 | | devnum
+-----------+--------+------------+---------------------------------------------------
+ 0x134 | 4 | | speed
+-----------+--------+------------+---------------------------------------------------
+ 0x138 | 2 | | idVendor
+-----------+--------+------------+---------------------------------------------------
+ 0x13A | 2 | | idProduct
+-----------+--------+------------+---------------------------------------------------
+ 0x13C | 2 | | bcdDevice
+-----------+--------+------------+---------------------------------------------------
+ 0x13E | 1 | | bDeviceClass
+-----------+--------+------------+---------------------------------------------------
+ 0x13F | 1 | | bDeviceSubClass
+-----------+--------+------------+---------------------------------------------------
+ 0x140 | 1 | | bDeviceProtocol
+-----------+--------+------------+---------------------------------------------------
+ 0x141 | 1 | | bConfigurationValue
+-----------+--------+------------+---------------------------------------------------
+ 0x142 | 1 | | bNumConfigurations
+-----------+--------+------------+---------------------------------------------------
+ 0x143 | 1 | | bNumInterfaces
+-----------+--------+------------+---------------------------------------------------
+ 0x144 | | m_0 | From now on each interface is described, all
+ | | | together bNumInterfaces times, with the
+ | | | the following 4 fields:
+-----------+--------+------------+---------------------------------------------------
+ | 1 | | bInterfaceClass
+-----------+--------+------------+---------------------------------------------------
+ 0x145 | 1 | | bInterfaceSubClass
+-----------+--------+------------+---------------------------------------------------
+ 0x146 | 1 | | bInterfaceProtocol
+-----------+--------+------------+---------------------------------------------------
+ 0x147 | 1 | | padding byte for alignment, shall be set to zero
+-----------+--------+------------+---------------------------------------------------
+ 0xC + | | | The second exported USB device starts at i=1
+ i*0x138 + | | | with the busid field.
+ m_(i-1)*4 | | |
+
+OP_REQ_IMPORT: Request to import (attach) a remote USB device.
+
+ Offset | Length | Value | Description
+-----------+--------+------------+---------------------------------------------------
+ 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0
+-----------+--------+------------+---------------------------------------------------
+ 2 | 2 | 0x8003 | Command code: import a remote USB device.
+-----------+--------+------------+---------------------------------------------------
+ 4 | 4 | 0x00000000 | Status: unused, shall be set to 0
+-----------+--------+------------+---------------------------------------------------
+ 8 | 32 | | busid: the busid of the exported device on the
+ | | | remote host. The possible values are taken
+ | | | from the message field OP_REP_DEVLIST.busid.
+ | | | A string closed with zero, the unused bytes
+ | | | shall be filled with zeros.
+-----------+--------+------------+---------------------------------------------------
+
+OP_REP_IMPORT: Reply to import (attach) a remote USB device.
+
+ Offset | Length | Value | Description
+-----------+--------+------------+---------------------------------------------------
+ 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0
+-----------+--------+------------+---------------------------------------------------
+ 2 | 2 | 0x0003 | Reply code: Reply to import.
+-----------+--------+------------+---------------------------------------------------
+ 4 | 4 | 0x00000000 | Status: 0 for OK
+ | | | 1 for error
+-----------+--------+------------+---------------------------------------------------
+ 8 | | | From now on comes the details of the imported
+ | | | device, if the previous status field was OK (0),
+ | | | otherwise the reply ends with the status field.
+-----------+--------+------------+---------------------------------------------------
+ | 256 | | path: Path of the device on the host exporting the
+ | | | USB device, string closed with zero byte, e.g.
+ | | | "/sys/devices/pci0000:00/0000:00:1d.1/usb3/3-2"
+ | | | The unused bytes shall be filled with zero
+ | | | bytes.
+-----------+--------+------------+---------------------------------------------------
+ 0x108 | 32 | | busid: Bus ID of the exported device, string
+ | | | closed with zero byte, e.g. "3-2". The unused
+ | | | bytes shall be filled with zero bytes.
+-----------+--------+------------+---------------------------------------------------
+ 0x128 | 4 | | busnum
+-----------+--------+------------+---------------------------------------------------
+ 0x12C | 4 | | devnum
+-----------+--------+------------+---------------------------------------------------
+ 0x130 | 4 | | speed
+-----------+--------+------------+---------------------------------------------------
+ 0x134 | 2 | | idVendor
+-----------+--------+------------+---------------------------------------------------
+ 0x136 | 2 | | idProduct
+-----------+--------+------------+---------------------------------------------------
+ 0x138 | 2 | | bcdDevice
+-----------+--------+------------+---------------------------------------------------
+ 0x139 | 1 | | bDeviceClass
+-----------+--------+------------+---------------------------------------------------
+ 0x13A | 1 | | bDeviceSubClass
+-----------+--------+------------+---------------------------------------------------
+ 0x13B | 1 | | bDeviceProtocol
+-----------+--------+------------+---------------------------------------------------
+ 0x13C | 1 | | bConfigurationValue
+-----------+--------+------------+---------------------------------------------------
+ 0x13D | 1 | | bNumConfigurations
+-----------+--------+------------+---------------------------------------------------
+ 0x13E | 1 | | bNumInterfaces
+
+USBIP_CMD_SUBMIT: Submit an URB
+
+ Offset | Length | Value | Description
+-----------+--------+------------+---------------------------------------------------
+ 0 | 4 | 0x00000001 | command: Submit an URB
+-----------+--------+------------+---------------------------------------------------
+ 4 | 4 | | seqnum: the sequence number of the URB to submit
+-----------+--------+------------+---------------------------------------------------
+ 8 | 4 | | devid
+-----------+--------+------------+---------------------------------------------------
+ 0xC | 4 | | direction: 0: USBIP_DIR_OUT
+ | | | 1: USBIP_DIR_IN
+-----------+--------+------------+---------------------------------------------------
+ 0x10 | 4 | | ep: endpoint number, possible values are: 0...15
+-----------+--------+------------+---------------------------------------------------
+ 0x14 | 4 | | transfer_flags: possible values depend on the
+ | | | URB transfer type, see below
+-----------+--------+------------+---------------------------------------------------
+ 0x18 | 4 | | transfer_buffer_length
+-----------+--------+------------+---------------------------------------------------
+ 0x1C | 4 | | start_frame: specify the selected frame to
+ | | | transmit an ISO frame, ignored if URB_ISO_ASAP
+ | | | is specified at transfer_flags
+-----------+--------+------------+---------------------------------------------------
+ 0x20 | 4 | | number_of_packets: number of ISO packets
+-----------+--------+------------+---------------------------------------------------
+ 0x24 | 4 | | interval: maximum time for the request on the
+ | | | server-side host controller
+-----------+--------+------------+---------------------------------------------------
+ 0x28 | 8 | | setup: data bytes for USB setup, filled with
+ | | | zeros if not used
+-----------+--------+------------+---------------------------------------------------
+ 0x30 | | | URB data. For ISO transfers the padding between
+ | | | each ISO packets is not transmitted.
+
+
+ Allowed transfer_flags | value | control | interrupt | bulk | isochronous
+ -------------------------+------------+---------+-----------+----------+-------------
+ URB_SHORT_NOT_OK | 0x00000001 | only in | only in | only in | no
+ URB_ISO_ASAP | 0x00000002 | no | no | no | yes
+ URB_NO_TRANSFER_DMA_MAP | 0x00000004 | yes | yes | yes | yes
+ URB_NO_FSBR | 0x00000020 | yes | no | no | no
+ URB_ZERO_PACKET | 0x00000040 | no | no | only out | no
+ URB_NO_INTERRUPT | 0x00000080 | yes | yes | yes | yes
+ URB_FREE_BUFFER | 0x00000100 | yes | yes | yes | yes
+ URB_DIR_MASK | 0x00000200 | yes | yes | yes | yes
+
+
+USBIP_RET_SUBMIT: Reply for submitting an URB
+
+ Offset | Length | Value | Description
+-----------+--------+------------+---------------------------------------------------
+ 0 | 4 | 0x00000003 | command
+-----------+--------+------------+---------------------------------------------------
+ 4 | 4 | | seqnum: URB sequence number
+-----------+--------+------------+---------------------------------------------------
+ 8 | 4 | | devid
+-----------+--------+------------+---------------------------------------------------
+ 0xC | 4 | | direction: 0: USBIP_DIR_OUT
+ | | | 1: USBIP_DIR_IN
+-----------+--------+------------+---------------------------------------------------
+ 0x10 | 4 | | ep: endpoint number
+-----------+--------+------------+---------------------------------------------------
+ 0x14 | 4 | | status: zero for successful URB transaction,
+ | | | otherwise some kind of error happened.
+-----------+--------+------------+---------------------------------------------------
+ 0x18 | 4 | n | actual_length: number of URB data bytes
+-----------+--------+------------+---------------------------------------------------
+ 0x1C | 4 | | start_frame: for an ISO frame the actually
+ | | | selected frame for transmit.
+-----------+--------+------------+---------------------------------------------------
+ 0x20 | 4 | | number_of_packets
+-----------+--------+------------+---------------------------------------------------
+ 0x24 | 4 | | error_count
+-----------+--------+------------+---------------------------------------------------
+ 0x28 | 8 | | setup: data bytes for USB setup, filled with
+ | | | zeros if not used
+-----------+--------+------------+---------------------------------------------------
+ 0x30 | n | | URB data bytes. For ISO transfers the padding
+ | | | between each ISO packets is not transmitted.
+
+USBIP_CMD_UNLINK: Unlink an URB
+
+ Offset | Length | Value | Description
+-----------+--------+------------+---------------------------------------------------
+ 0 | 4 | 0x00000002 | command: URB unlink command
+-----------+--------+------------+---------------------------------------------------
+ 4 | 4 | | seqnum: URB sequence number to unlink: FIXME: is this so?
+-----------+--------+------------+---------------------------------------------------
+ 8 | 4 | | devid
+-----------+--------+------------+---------------------------------------------------
+ 0xC | 4 | | direction: 0: USBIP_DIR_OUT
+ | | | 1: USBIP_DIR_IN
+-----------+--------+------------+---------------------------------------------------
+ 0x10 | 4 | | ep: endpoint number: zero
+-----------+--------+------------+---------------------------------------------------
+ 0x14 | 4 | | seqnum: the URB sequence number given previously
+ | | | at USBIP_CMD_SUBMIT.seqnum field
+-----------+--------+------------+---------------------------------------------------
+ 0x30 | n | | URB data bytes. For ISO transfers the padding
+ | | | between each ISO packets is not transmitted.
+
+USBIP_RET_UNLINK: Reply for URB unlink
+
+ Offset | Length | Value | Description
+-----------+--------+------------+---------------------------------------------------
+ 0 | 4 | 0x00000004 | command: reply for the URB unlink command
+-----------+--------+------------+---------------------------------------------------
+ 4 | 4 | | seqnum: the unlinked URB sequence number
+-----------+--------+------------+---------------------------------------------------
+ 8 | 4 | | devid
+-----------+--------+------------+---------------------------------------------------
+ 0xC | 4 | | direction: 0: USBIP_DIR_OUT
+ | | | 1: USBIP_DIR_IN
+-----------+--------+------------+---------------------------------------------------
+ 0x10 | 4 | | ep: endpoint number
+-----------+--------+------------+---------------------------------------------------
+ 0x14 | 4 | | status: This is the value contained in the
+ | | | urb->status in the URB completition handler.
+ | | | FIXME: a better explanation needed.
+-----------+--------+------------+---------------------------------------------------
+ 0x30 | n | | URB data bytes. For ISO transfers the padding
+ | | | between each ISO packets is not transmitted.
--- /dev/null
+/*
+ * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ *
+ * This 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.
+ *
+ */
+
+#ifndef __USBIP_VHCI_H
+#define __USBIP_VHCI_H
+
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/wait.h>
+
+struct vhci_device {
+ struct usb_device *udev;
+
+ /*
+ * devid specifies a remote usb device uniquely instead
+ * of combination of busnum and devnum.
+ */
+ __u32 devid;
+
+ /* speed of a remote device */
+ enum usb_device_speed speed;
+
+ /* vhci root-hub port to which this device is attached */
+ __u32 rhport;
+
+ struct usbip_device ud;
+
+ /* lock for the below link lists */
+ spinlock_t priv_lock;
+
+ /* vhci_priv is linked to one of them. */
+ struct list_head priv_tx;
+ struct list_head priv_rx;
+
+ /* vhci_unlink is linked to one of them */
+ struct list_head unlink_tx;
+ struct list_head unlink_rx;
+
+ /* vhci_tx thread sleeps for this queue */
+ wait_queue_head_t waitq_tx;
+};
+
+/* urb->hcpriv, use container_of() */
+struct vhci_priv {
+ unsigned long seqnum;
+ struct list_head list;
+
+ struct vhci_device *vdev;
+ struct urb *urb;
+};
+
+struct vhci_unlink {
+ /* seqnum of this request */
+ unsigned long seqnum;
+
+ struct list_head list;
+
+ /* seqnum of the unlink target */
+ unsigned long unlink_seqnum;
+};
+
+/* Number of supported ports. Value has an upperbound of USB_MAXCHILDREN */
+#define VHCI_NPORTS 8
+
+/* for usb_bus.hcpriv */
+struct vhci_hcd {
+ spinlock_t lock;
+
+ u32 port_status[VHCI_NPORTS];
+
+ unsigned resuming:1;
+ unsigned long re_timeout;
+
+ atomic_t seqnum;
+
+ /*
+ * NOTE:
+ * wIndex shows the port number and begins from 1.
+ * But, the index of this array begins from 0.
+ */
+ struct vhci_device vdev[VHCI_NPORTS];
+};
+
+extern struct vhci_hcd *the_controller;
+extern const struct attribute_group dev_attr_group;
+
+/* vhci_hcd.c */
+void rh_port_connect(int rhport, enum usb_device_speed speed);
+
+/* vhci_rx.c */
+struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, __u32 seqnum);
+int vhci_rx_loop(void *data);
+
+/* vhci_tx.c */
+int vhci_tx_loop(void *data);
+
+static inline struct vhci_device *port_to_vdev(__u32 port)
+{
+ return &the_controller->vdev[port];
+}
+
+static inline struct vhci_hcd *hcd_to_vhci(struct usb_hcd *hcd)
+{
+ return (struct vhci_hcd *) (hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *vhci_to_hcd(struct vhci_hcd *vhci)
+{
+ return container_of((void *) vhci, struct usb_hcd, hcd_priv);
+}
+
+static inline struct device *vhci_dev(struct vhci_hcd *vhci)
+{
+ return vhci_to_hcd(vhci)->self.controller;
+}
+
+#endif /* __USBIP_VHCI_H */
--- /dev/null
+/*
+ * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ *
+ * This 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 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <linux/init.h>
+#include <linux/file.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "usbip_common.h"
+#include "vhci.h"
+
+#define DRIVER_AUTHOR "Takahiro Hirofuchi"
+#define DRIVER_DESC "USB/IP 'Virtual' Host Controller (VHCI) Driver"
+
+/*
+ * TODO
+ * - update root hub emulation
+ * - move the emulation code to userland ?
+ * porting to other operating systems
+ * minimize kernel code
+ * - add suspend/resume code
+ * - clean up everything
+ */
+
+/* See usb gadget dummy hcd */
+
+static int vhci_hub_status(struct usb_hcd *hcd, char *buff);
+static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buff, u16 wLength);
+static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
+ gfp_t mem_flags);
+static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status);
+static int vhci_start(struct usb_hcd *vhci_hcd);
+static void vhci_stop(struct usb_hcd *hcd);
+static int vhci_get_frame_number(struct usb_hcd *hcd);
+
+static const char driver_name[] = "vhci_hcd";
+static const char driver_desc[] = "USB/IP Virtual Host Controller";
+
+struct vhci_hcd *the_controller;
+
+static const char * const bit_desc[] = {
+ "CONNECTION", /*0*/
+ "ENABLE", /*1*/
+ "SUSPEND", /*2*/
+ "OVER_CURRENT", /*3*/
+ "RESET", /*4*/
+ "R5", /*5*/
+ "R6", /*6*/
+ "R7", /*7*/
+ "POWER", /*8*/
+ "LOWSPEED", /*9*/
+ "HIGHSPEED", /*10*/
+ "PORT_TEST", /*11*/
+ "INDICATOR", /*12*/
+ "R13", /*13*/
+ "R14", /*14*/
+ "R15", /*15*/
+ "C_CONNECTION", /*16*/
+ "C_ENABLE", /*17*/
+ "C_SUSPEND", /*18*/
+ "C_OVER_CURRENT", /*19*/
+ "C_RESET", /*20*/
+ "R21", /*21*/
+ "R22", /*22*/
+ "R23", /*23*/
+ "R24", /*24*/
+ "R25", /*25*/
+ "R26", /*26*/
+ "R27", /*27*/
+ "R28", /*28*/
+ "R29", /*29*/
+ "R30", /*30*/
+ "R31", /*31*/
+};
+
+static void dump_port_status_diff(u32 prev_status, u32 new_status)
+{
+ int i = 0;
+ u32 bit = 1;
+
+ pr_debug("status prev -> new: %08x -> %08x\n", prev_status, new_status);
+ while (bit) {
+ u32 prev = prev_status & bit;
+ u32 new = new_status & bit;
+ char change;
+
+ if (!prev && new)
+ change = '+';
+ else if (prev && !new)
+ change = '-';
+ else
+ change = ' ';
+
+ if (prev || new)
+ pr_debug(" %c%s\n", change, bit_desc[i]);
+ bit <<= 1;
+ i++;
+ }
+ pr_debug("\n");
+}
+
+void rh_port_connect(int rhport, enum usb_device_speed speed)
+{
+ usbip_dbg_vhci_rh("rh_port_connect %d\n", rhport);
+
+ spin_lock(&the_controller->lock);
+
+ the_controller->port_status[rhport] |= USB_PORT_STAT_CONNECTION
+ | (1 << USB_PORT_FEAT_C_CONNECTION);
+
+ switch (speed) {
+ case USB_SPEED_HIGH:
+ the_controller->port_status[rhport] |= USB_PORT_STAT_HIGH_SPEED;
+ break;
+ case USB_SPEED_LOW:
+ the_controller->port_status[rhport] |= USB_PORT_STAT_LOW_SPEED;
+ break;
+ default:
+ break;
+ }
+
+ spin_unlock(&the_controller->lock);
+
+ usb_hcd_poll_rh_status(vhci_to_hcd(the_controller));
+}
+
+static void rh_port_disconnect(int rhport)
+{
+ usbip_dbg_vhci_rh("rh_port_disconnect %d\n", rhport);
+
+ spin_lock(&the_controller->lock);
+
+ the_controller->port_status[rhport] &= ~USB_PORT_STAT_CONNECTION;
+ the_controller->port_status[rhport] |=
+ (1 << USB_PORT_FEAT_C_CONNECTION);
+
+ spin_unlock(&the_controller->lock);
+ usb_hcd_poll_rh_status(vhci_to_hcd(the_controller));
+}
+
+#define PORT_C_MASK \
+ ((USB_PORT_STAT_C_CONNECTION \
+ | USB_PORT_STAT_C_ENABLE \
+ | USB_PORT_STAT_C_SUSPEND \
+ | USB_PORT_STAT_C_OVERCURRENT \
+ | USB_PORT_STAT_C_RESET) << 16)
+
+/*
+ * Returns 0 if the status hasn't changed, or the number of bytes in buf.
+ * Ports are 0-indexed from the HCD point of view,
+ * and 1-indexed from the USB core pointer of view.
+ *
+ * @buf: a bitmap to show which port status has been changed.
+ * bit 0: reserved
+ * bit 1: the status of port 0 has been changed.
+ * bit 2: the status of port 1 has been changed.
+ * ...
+ */
+static int vhci_hub_status(struct usb_hcd *hcd, char *buf)
+{
+ struct vhci_hcd *vhci;
+ int retval;
+ int rhport;
+ int changed = 0;
+
+ retval = DIV_ROUND_UP(VHCI_NPORTS + 1, 8);
+ memset(buf, 0, retval);
+
+ vhci = hcd_to_vhci(hcd);
+
+ spin_lock(&vhci->lock);
+ if (!HCD_HW_ACCESSIBLE(hcd)) {
+ usbip_dbg_vhci_rh("hw accessible flag not on?\n");
+ goto done;
+ }
+
+ /* check pseudo status register for each port */
+ for (rhport = 0; rhport < VHCI_NPORTS; rhport++) {
+ if ((vhci->port_status[rhport] & PORT_C_MASK)) {
+ /* The status of a port has been changed, */
+ usbip_dbg_vhci_rh("port %d status changed\n", rhport);
+
+ buf[(rhport + 1) / 8] |= 1 << (rhport + 1) % 8;
+ changed = 1;
+ }
+ }
+
+ if ((hcd->state == HC_STATE_SUSPENDED) && (changed == 1))
+ usb_hcd_resume_root_hub(hcd);
+
+done:
+ spin_unlock(&vhci->lock);
+ return changed ? retval : 0;
+}
+
+static inline void hub_descriptor(struct usb_hub_descriptor *desc)
+{
+ memset(desc, 0, sizeof(*desc));
+ desc->bDescriptorType = 0x29;
+ desc->bDescLength = 9;
+ desc->wHubCharacteristics = (__constant_cpu_to_le16(0x0001));
+ desc->bNbrPorts = VHCI_NPORTS;
+ desc->u.hs.DeviceRemovable[0] = 0xff;
+ desc->u.hs.DeviceRemovable[1] = 0xff;
+}
+
+static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ struct vhci_hcd *dum;
+ int retval = 0;
+ int rhport;
+
+ u32 prev_port_status[VHCI_NPORTS];
+
+ if (!HCD_HW_ACCESSIBLE(hcd))
+ return -ETIMEDOUT;
+
+ /*
+ * NOTE:
+ * wIndex shows the port number and begins from 1.
+ */
+ usbip_dbg_vhci_rh("typeReq %x wValue %x wIndex %x\n", typeReq, wValue,
+ wIndex);
+ if (wIndex > VHCI_NPORTS)
+ pr_err("invalid port number %d\n", wIndex);
+ rhport = ((__u8)(wIndex & 0x00ff)) - 1;
+
+ dum = hcd_to_vhci(hcd);
+
+ spin_lock(&dum->lock);
+
+ /* store old status and compare now and old later */
+ if (usbip_dbg_flag_vhci_rh) {
+ memcpy(prev_port_status, dum->port_status,
+ sizeof(prev_port_status));
+ }
+
+ switch (typeReq) {
+ case ClearHubFeature:
+ usbip_dbg_vhci_rh(" ClearHubFeature\n");
+ break;
+ case ClearPortFeature:
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ if (dum->port_status[rhport] & USB_PORT_STAT_SUSPEND) {
+ /* 20msec signaling */
+ dum->resuming = 1;
+ dum->re_timeout =
+ jiffies + msecs_to_jiffies(20);
+ }
+ break;
+ case USB_PORT_FEAT_POWER:
+ usbip_dbg_vhci_rh(
+ " ClearPortFeature: USB_PORT_FEAT_POWER\n");
+ dum->port_status[rhport] = 0;
+ dum->resuming = 0;
+ break;
+ case USB_PORT_FEAT_C_RESET:
+ usbip_dbg_vhci_rh(
+ " ClearPortFeature: USB_PORT_FEAT_C_RESET\n");
+ switch (dum->vdev[rhport].speed) {
+ case USB_SPEED_HIGH:
+ dum->port_status[rhport] |=
+ USB_PORT_STAT_HIGH_SPEED;
+ break;
+ case USB_SPEED_LOW:
+ dum->port_status[rhport] |=
+ USB_PORT_STAT_LOW_SPEED;
+ break;
+ default:
+ break;
+ }
+ default:
+ usbip_dbg_vhci_rh(" ClearPortFeature: default %x\n",
+ wValue);
+ dum->port_status[rhport] &= ~(1 << wValue);
+ break;
+ }
+ break;
+ case GetHubDescriptor:
+ usbip_dbg_vhci_rh(" GetHubDescriptor\n");
+ hub_descriptor((struct usb_hub_descriptor *) buf);
+ break;
+ case GetHubStatus:
+ usbip_dbg_vhci_rh(" GetHubStatus\n");
+ *(__le32 *) buf = cpu_to_le32(0);
+ break;
+ case GetPortStatus:
+ usbip_dbg_vhci_rh(" GetPortStatus port %x\n", wIndex);
+ if (wIndex > VHCI_NPORTS || wIndex < 1) {
+ pr_err("invalid port number %d\n", wIndex);
+ retval = -EPIPE;
+ }
+
+ /* we do not care about resume. */
+
+ /* whoever resets or resumes must GetPortStatus to
+ * complete it!!
+ */
+ if (dum->resuming && time_after(jiffies, dum->re_timeout)) {
+ dum->port_status[rhport] |=
+ (1 << USB_PORT_FEAT_C_SUSPEND);
+ dum->port_status[rhport] &=
+ ~(1 << USB_PORT_FEAT_SUSPEND);
+ dum->resuming = 0;
+ dum->re_timeout = 0;
+ }
+
+ if ((dum->port_status[rhport] & (1 << USB_PORT_FEAT_RESET)) !=
+ 0 && time_after(jiffies, dum->re_timeout)) {
+ dum->port_status[rhport] |=
+ (1 << USB_PORT_FEAT_C_RESET);
+ dum->port_status[rhport] &=
+ ~(1 << USB_PORT_FEAT_RESET);
+ dum->re_timeout = 0;
+
+ if (dum->vdev[rhport].ud.status ==
+ VDEV_ST_NOTASSIGNED) {
+ usbip_dbg_vhci_rh(
+ " enable rhport %d (status %u)\n",
+ rhport,
+ dum->vdev[rhport].ud.status);
+ dum->port_status[rhport] |=
+ USB_PORT_STAT_ENABLE;
+ }
+ }
+ ((__le16 *) buf)[0] = cpu_to_le16(dum->port_status[rhport]);
+ ((__le16 *) buf)[1] =
+ cpu_to_le16(dum->port_status[rhport] >> 16);
+
+ usbip_dbg_vhci_rh(" GetPortStatus bye %x %x\n", ((u16 *)buf)[0],
+ ((u16 *)buf)[1]);
+ break;
+ case SetHubFeature:
+ usbip_dbg_vhci_rh(" SetHubFeature\n");
+ retval = -EPIPE;
+ break;
+ case SetPortFeature:
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ usbip_dbg_vhci_rh(
+ " SetPortFeature: USB_PORT_FEAT_SUSPEND\n");
+ break;
+ case USB_PORT_FEAT_RESET:
+ usbip_dbg_vhci_rh(
+ " SetPortFeature: USB_PORT_FEAT_RESET\n");
+ /* if it's already running, disconnect first */
+ if (dum->port_status[rhport] & USB_PORT_STAT_ENABLE) {
+ dum->port_status[rhport] &=
+ ~(USB_PORT_STAT_ENABLE |
+ USB_PORT_STAT_LOW_SPEED |
+ USB_PORT_STAT_HIGH_SPEED);
+ /* FIXME test that code path! */
+ }
+ /* 50msec reset signaling */
+ dum->re_timeout = jiffies + msecs_to_jiffies(50);
+
+ /* FALLTHROUGH */
+ default:
+ usbip_dbg_vhci_rh(" SetPortFeature: default %d\n",
+ wValue);
+ dum->port_status[rhport] |= (1 << wValue);
+ break;
+ }
+ break;
+
+ default:
+ pr_err("default: no such request\n");
+
+ /* "protocol stall" on error */
+ retval = -EPIPE;
+ }
+
+ if (usbip_dbg_flag_vhci_rh) {
+ pr_debug("port %d\n", rhport);
+ /* Only dump valid port status */
+ if (rhport >= 0) {
+ dump_port_status_diff(prev_port_status[rhport],
+ dum->port_status[rhport]);
+ }
+ }
+ usbip_dbg_vhci_rh(" bye\n");
+
+ spin_unlock(&dum->lock);
+
+ return retval;
+}
+
+static struct vhci_device *get_vdev(struct usb_device *udev)
+{
+ int i;
+
+ if (!udev)
+ return NULL;
+
+ for (i = 0; i < VHCI_NPORTS; i++)
+ if (the_controller->vdev[i].udev == udev)
+ return port_to_vdev(i);
+
+ return NULL;
+}
+
+static void vhci_tx_urb(struct urb *urb)
+{
+ struct vhci_device *vdev = get_vdev(urb->dev);
+ struct vhci_priv *priv;
+
+ if (!vdev) {
+ pr_err("could not get virtual device");
+ return;
+ }
+
+ priv = kzalloc(sizeof(struct vhci_priv), GFP_ATOMIC);
+ if (!priv) {
+ usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC);
+ return;
+ }
+
+ spin_lock(&vdev->priv_lock);
+
+ priv->seqnum = atomic_inc_return(&the_controller->seqnum);
+ if (priv->seqnum == 0xffff)
+ dev_info(&urb->dev->dev, "seqnum max\n");
+
+ priv->vdev = vdev;
+ priv->urb = urb;
+
+ urb->hcpriv = (void *) priv;
+
+ list_add_tail(&priv->list, &vdev->priv_tx);
+
+ wake_up(&vdev->waitq_tx);
+ spin_unlock(&vdev->priv_lock);
+}
+
+static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
+ gfp_t mem_flags)
+{
+ struct device *dev = &urb->dev->dev;
+ int ret = 0;
+ struct vhci_device *vdev;
+
+ usbip_dbg_vhci_hc("enter, usb_hcd %p urb %p mem_flags %d\n",
+ hcd, urb, mem_flags);
+
+ /* patch to usb_sg_init() is in 2.5.60 */
+ BUG_ON(!urb->transfer_buffer && urb->transfer_buffer_length);
+
+ spin_lock(&the_controller->lock);
+
+ if (urb->status != -EINPROGRESS) {
+ dev_err(dev, "URB already unlinked!, status %d\n", urb->status);
+ spin_unlock(&the_controller->lock);
+ return urb->status;
+ }
+
+ vdev = port_to_vdev(urb->dev->portnum-1);
+
+ /* refuse enqueue for dead connection */
+ spin_lock(&vdev->ud.lock);
+ if (vdev->ud.status == VDEV_ST_NULL ||
+ vdev->ud.status == VDEV_ST_ERROR) {
+ dev_err(dev, "enqueue for inactive port %d\n", vdev->rhport);
+ spin_unlock(&vdev->ud.lock);
+ spin_unlock(&the_controller->lock);
+ return -ENODEV;
+ }
+ spin_unlock(&vdev->ud.lock);
+
+ ret = usb_hcd_link_urb_to_ep(hcd, urb);
+ if (ret)
+ goto no_need_unlink;
+
+ /*
+ * The enumeration process is as follows;
+ *
+ * 1. Get_Descriptor request to DevAddrs(0) EndPoint(0)
+ * to get max packet length of default pipe
+ *
+ * 2. Set_Address request to DevAddr(0) EndPoint(0)
+ *
+ */
+ if (usb_pipedevice(urb->pipe) == 0) {
+ __u8 type = usb_pipetype(urb->pipe);
+ struct usb_ctrlrequest *ctrlreq =
+ (struct usb_ctrlrequest *) urb->setup_packet;
+
+ if (type != PIPE_CONTROL || !ctrlreq) {
+ dev_err(dev, "invalid request to devnum 0\n");
+ ret = -EINVAL;
+ goto no_need_xmit;
+ }
+
+ switch (ctrlreq->bRequest) {
+ case USB_REQ_SET_ADDRESS:
+ /* set_address may come when a device is reset */
+ dev_info(dev, "SetAddress Request (%d) to port %d\n",
+ ctrlreq->wValue, vdev->rhport);
+
+ if (vdev->udev)
+ usb_put_dev(vdev->udev);
+ vdev->udev = usb_get_dev(urb->dev);
+
+ spin_lock(&vdev->ud.lock);
+ vdev->ud.status = VDEV_ST_USED;
+ spin_unlock(&vdev->ud.lock);
+
+ if (urb->status == -EINPROGRESS) {
+ /* This request is successfully completed. */
+ /* If not -EINPROGRESS, possibly unlinked. */
+ urb->status = 0;
+ }
+
+ goto no_need_xmit;
+
+ case USB_REQ_GET_DESCRIPTOR:
+ if (ctrlreq->wValue == cpu_to_le16(USB_DT_DEVICE << 8))
+ usbip_dbg_vhci_hc(
+ "Not yet?:Get_Descriptor to device 0 (get max pipe size)\n");
+
+ if (vdev->udev)
+ usb_put_dev(vdev->udev);
+ vdev->udev = usb_get_dev(urb->dev);
+ goto out;
+
+ default:
+ /* NOT REACHED */
+ dev_err(dev,
+ "invalid request to devnum 0 bRequest %u, wValue %u\n",
+ ctrlreq->bRequest,
+ ctrlreq->wValue);
+ ret = -EINVAL;
+ goto no_need_xmit;
+ }
+
+ }
+
+out:
+ vhci_tx_urb(urb);
+ spin_unlock(&the_controller->lock);
+
+ return 0;
+
+no_need_xmit:
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+no_need_unlink:
+ spin_unlock(&the_controller->lock);
+ usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status);
+ return ret;
+}
+
+/*
+ * vhci_rx gives back the urb after receiving the reply of the urb. If an
+ * unlink pdu is sent or not, vhci_rx receives a normal return pdu and gives
+ * back its urb. For the driver unlinking the urb, the content of the urb is
+ * not important, but the calling to its completion handler is important; the
+ * completion of unlinking is notified by the completion handler.
+ *
+ *
+ * CLIENT SIDE
+ *
+ * - When vhci_hcd receives RET_SUBMIT,
+ *
+ * - case 1a). the urb of the pdu is not unlinking.
+ * - normal case
+ * => just give back the urb
+ *
+ * - case 1b). the urb of the pdu is unlinking.
+ * - usbip.ko will return a reply of the unlinking request.
+ * => give back the urb now and go to case 2b).
+ *
+ * - When vhci_hcd receives RET_UNLINK,
+ *
+ * - case 2a). a submit request is still pending in vhci_hcd.
+ * - urb was really pending in usbip.ko and urb_unlink_urb() was
+ * completed there.
+ * => free a pending submit request
+ * => notify unlink completeness by giving back the urb
+ *
+ * - case 2b). a submit request is *not* pending in vhci_hcd.
+ * - urb was already given back to the core driver.
+ * => do not give back the urb
+ *
+ *
+ * SERVER SIDE
+ *
+ * - When usbip receives CMD_UNLINK,
+ *
+ * - case 3a). the urb of the unlink request is now in submission.
+ * => do usb_unlink_urb().
+ * => after the unlink is completed, send RET_UNLINK.
+ *
+ * - case 3b). the urb of the unlink request is not in submission.
+ * - may be already completed or never be received
+ * => send RET_UNLINK
+ *
+ */
+static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+ struct vhci_priv *priv;
+ struct vhci_device *vdev;
+
+ pr_info("dequeue a urb %p\n", urb);
+
+ spin_lock(&the_controller->lock);
+
+ priv = urb->hcpriv;
+ if (!priv) {
+ /* URB was never linked! or will be soon given back by
+ * vhci_rx. */
+ spin_unlock(&the_controller->lock);
+ return 0;
+ }
+
+ {
+ int ret = 0;
+
+ ret = usb_hcd_check_unlink_urb(hcd, urb, status);
+ if (ret) {
+ spin_unlock(&the_controller->lock);
+ return ret;
+ }
+ }
+
+ /* send unlink request here? */
+ vdev = priv->vdev;
+
+ if (!vdev->ud.tcp_socket) {
+ /* tcp connection is closed */
+ spin_lock(&vdev->priv_lock);
+
+ pr_info("device %p seems to be disconnected\n", vdev);
+ list_del(&priv->list);
+ kfree(priv);
+ urb->hcpriv = NULL;
+
+ spin_unlock(&vdev->priv_lock);
+
+ /*
+ * If tcp connection is alive, we have sent CMD_UNLINK.
+ * vhci_rx will receive RET_UNLINK and give back the URB.
+ * Otherwise, we give back it here.
+ */
+ pr_info("gives back urb %p\n", urb);
+
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+
+ spin_unlock(&the_controller->lock);
+ usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb,
+ urb->status);
+ spin_lock(&the_controller->lock);
+
+ } else {
+ /* tcp connection is alive */
+ struct vhci_unlink *unlink;
+
+ spin_lock(&vdev->priv_lock);
+
+ /* setup CMD_UNLINK pdu */
+ unlink = kzalloc(sizeof(struct vhci_unlink), GFP_ATOMIC);
+ if (!unlink) {
+ spin_unlock(&vdev->priv_lock);
+ spin_unlock(&the_controller->lock);
+ usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC);
+ return -ENOMEM;
+ }
+
+ unlink->seqnum = atomic_inc_return(&the_controller->seqnum);
+ if (unlink->seqnum == 0xffff)
+ pr_info("seqnum max\n");
+
+ unlink->unlink_seqnum = priv->seqnum;
+
+ pr_info("device %p seems to be still connected\n", vdev);
+
+ /* send cmd_unlink and try to cancel the pending URB in the
+ * peer */
+ list_add_tail(&unlink->list, &vdev->unlink_tx);
+ wake_up(&vdev->waitq_tx);
+
+ spin_unlock(&vdev->priv_lock);
+ }
+
+ spin_unlock(&the_controller->lock);
+
+ usbip_dbg_vhci_hc("leave\n");
+ return 0;
+}
+
+static void vhci_device_unlink_cleanup(struct vhci_device *vdev)
+{
+ struct vhci_unlink *unlink, *tmp;
+
+ spin_lock(&the_controller->lock);
+ spin_lock(&vdev->priv_lock);
+
+ list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) {
+ pr_info("unlink cleanup tx %lu\n", unlink->unlink_seqnum);
+ list_del(&unlink->list);
+ kfree(unlink);
+ }
+
+ while (!list_empty(&vdev->unlink_rx)) {
+ struct urb *urb;
+
+ unlink = list_first_entry(&vdev->unlink_rx, struct vhci_unlink,
+ list);
+
+ /* give back URB of unanswered unlink request */
+ pr_info("unlink cleanup rx %lu\n", unlink->unlink_seqnum);
+
+ urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum);
+ if (!urb) {
+ pr_info("the urb (seqnum %lu) was already given back\n",
+ unlink->unlink_seqnum);
+ list_del(&unlink->list);
+ kfree(unlink);
+ continue;
+ }
+
+ urb->status = -ENODEV;
+
+ usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb);
+
+ list_del(&unlink->list);
+
+ spin_unlock(&vdev->priv_lock);
+ spin_unlock(&the_controller->lock);
+
+ usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb,
+ urb->status);
+
+ spin_lock(&the_controller->lock);
+ spin_lock(&vdev->priv_lock);
+
+ kfree(unlink);
+ }
+
+ spin_unlock(&vdev->priv_lock);
+ spin_unlock(&the_controller->lock);
+}
+
+/*
+ * The important thing is that only one context begins cleanup.
+ * This is why error handling and cleanup become simple.
+ * We do not want to consider race condition as possible.
+ */
+static void vhci_shutdown_connection(struct usbip_device *ud)
+{
+ struct vhci_device *vdev = container_of(ud, struct vhci_device, ud);
+
+ /* need this? see stub_dev.c */
+ if (ud->tcp_socket) {
+ pr_debug("shutdown tcp_socket %p\n", ud->tcp_socket);
+ kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR);
+ }
+
+ /* kill threads related to this sdev */
+ if (vdev->ud.tcp_rx) {
+ kthread_stop_put(vdev->ud.tcp_rx);
+ vdev->ud.tcp_rx = NULL;
+ }
+ if (vdev->ud.tcp_tx) {
+ kthread_stop_put(vdev->ud.tcp_tx);
+ vdev->ud.tcp_tx = NULL;
+ }
+ pr_info("stop threads\n");
+
+ /* active connection is closed */
+ if (vdev->ud.tcp_socket) {
+ sockfd_put(vdev->ud.tcp_socket);
+ vdev->ud.tcp_socket = NULL;
+ }
+ pr_info("release socket\n");
+
+ vhci_device_unlink_cleanup(vdev);
+
+ /*
+ * rh_port_disconnect() is a trigger of ...
+ * usb_disable_device():
+ * disable all the endpoints for a USB device.
+ * usb_disable_endpoint():
+ * disable endpoints. pending urbs are unlinked(dequeued).
+ *
+ * NOTE: After calling rh_port_disconnect(), the USB device drivers of a
+ * detached device should release used urbs in a cleanup function (i.e.
+ * xxx_disconnect()). Therefore, vhci_hcd does not need to release
+ * pushed urbs and their private data in this function.
+ *
+ * NOTE: vhci_dequeue() must be considered carefully. When shutting down
+ * a connection, vhci_shutdown_connection() expects vhci_dequeue()
+ * gives back pushed urbs and frees their private data by request of
+ * the cleanup function of a USB driver. When unlinking a urb with an
+ * active connection, vhci_dequeue() does not give back the urb which
+ * is actually given back by vhci_rx after receiving its return pdu.
+ *
+ */
+ rh_port_disconnect(vdev->rhport);
+
+ pr_info("disconnect device\n");
+}
+
+
+static void vhci_device_reset(struct usbip_device *ud)
+{
+ struct vhci_device *vdev = container_of(ud, struct vhci_device, ud);
+
+ spin_lock(&ud->lock);
+
+ vdev->speed = 0;
+ vdev->devid = 0;
+
+ if (vdev->udev)
+ usb_put_dev(vdev->udev);
+ vdev->udev = NULL;
+
+ if (ud->tcp_socket) {
+ sockfd_put(ud->tcp_socket);
+ ud->tcp_socket = NULL;
+ }
+ ud->status = VDEV_ST_NULL;
+
+ spin_unlock(&ud->lock);
+}
+
+static void vhci_device_unusable(struct usbip_device *ud)
+{
+ spin_lock(&ud->lock);
+ ud->status = VDEV_ST_ERROR;
+ spin_unlock(&ud->lock);
+}
+
+static void vhci_device_init(struct vhci_device *vdev)
+{
+ memset(vdev, 0, sizeof(*vdev));
+
+ vdev->ud.side = USBIP_VHCI;
+ vdev->ud.status = VDEV_ST_NULL;
+ spin_lock_init(&vdev->ud.lock);
+
+ INIT_LIST_HEAD(&vdev->priv_rx);
+ INIT_LIST_HEAD(&vdev->priv_tx);
+ INIT_LIST_HEAD(&vdev->unlink_tx);
+ INIT_LIST_HEAD(&vdev->unlink_rx);
+ spin_lock_init(&vdev->priv_lock);
+
+ init_waitqueue_head(&vdev->waitq_tx);
+
+ vdev->ud.eh_ops.shutdown = vhci_shutdown_connection;
+ vdev->ud.eh_ops.reset = vhci_device_reset;
+ vdev->ud.eh_ops.unusable = vhci_device_unusable;
+
+ usbip_start_eh(&vdev->ud);
+}
+
+static int vhci_start(struct usb_hcd *hcd)
+{
+ struct vhci_hcd *vhci = hcd_to_vhci(hcd);
+ int rhport;
+ int err = 0;
+
+ usbip_dbg_vhci_hc("enter vhci_start\n");
+
+ /* initialize private data of usb_hcd */
+
+ for (rhport = 0; rhport < VHCI_NPORTS; rhport++) {
+ struct vhci_device *vdev = &vhci->vdev[rhport];
+
+ vhci_device_init(vdev);
+ vdev->rhport = rhport;
+ }
+
+ atomic_set(&vhci->seqnum, 0);
+ spin_lock_init(&vhci->lock);
+
+ hcd->power_budget = 0; /* no limit */
+ hcd->uses_new_polling = 1;
+
+ /* vhci_hcd is now ready to be controlled through sysfs */
+ err = sysfs_create_group(&vhci_dev(vhci)->kobj, &dev_attr_group);
+ if (err) {
+ pr_err("create sysfs files\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static void vhci_stop(struct usb_hcd *hcd)
+{
+ struct vhci_hcd *vhci = hcd_to_vhci(hcd);
+ int rhport = 0;
+
+ usbip_dbg_vhci_hc("stop VHCI controller\n");
+
+ /* 1. remove the userland interface of vhci_hcd */
+ sysfs_remove_group(&vhci_dev(vhci)->kobj, &dev_attr_group);
+
+ /* 2. shutdown all the ports of vhci_hcd */
+ for (rhport = 0; rhport < VHCI_NPORTS; rhport++) {
+ struct vhci_device *vdev = &vhci->vdev[rhport];
+
+ usbip_event_add(&vdev->ud, VDEV_EVENT_REMOVED);
+ usbip_stop_eh(&vdev->ud);
+ }
+}
+
+static int vhci_get_frame_number(struct usb_hcd *hcd)
+{
+ pr_err("Not yet implemented\n");
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+/* FIXME: suspend/resume */
+static int vhci_bus_suspend(struct usb_hcd *hcd)
+{
+ struct vhci_hcd *vhci = hcd_to_vhci(hcd);
+
+ dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__);
+
+ spin_lock(&vhci->lock);
+ hcd->state = HC_STATE_SUSPENDED;
+ spin_unlock(&vhci->lock);
+
+ return 0;
+}
+
+static int vhci_bus_resume(struct usb_hcd *hcd)
+{
+ struct vhci_hcd *vhci = hcd_to_vhci(hcd);
+ int rc = 0;
+
+ dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__);
+
+ spin_lock(&vhci->lock);
+ if (!HCD_HW_ACCESSIBLE(hcd))
+ rc = -ESHUTDOWN;
+ else
+ hcd->state = HC_STATE_RUNNING;
+ spin_unlock(&vhci->lock);
+
+ return rc;
+}
+
+#else
+
+#define vhci_bus_suspend NULL
+#define vhci_bus_resume NULL
+#endif
+
+static struct hc_driver vhci_hc_driver = {
+ .description = driver_name,
+ .product_desc = driver_desc,
+ .hcd_priv_size = sizeof(struct vhci_hcd),
+
+ .flags = HCD_USB2,
+
+ .start = vhci_start,
+ .stop = vhci_stop,
+
+ .urb_enqueue = vhci_urb_enqueue,
+ .urb_dequeue = vhci_urb_dequeue,
+
+ .get_frame_number = vhci_get_frame_number,
+
+ .hub_status_data = vhci_hub_status,
+ .hub_control = vhci_hub_control,
+ .bus_suspend = vhci_bus_suspend,
+ .bus_resume = vhci_bus_resume,
+};
+
+static int vhci_hcd_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+ int ret;
+
+ usbip_dbg_vhci_hc("name %s id %d\n", pdev->name, pdev->id);
+
+ /*
+ * Allocate and initialize hcd.
+ * Our private data is also allocated automatically.
+ */
+ hcd = usb_create_hcd(&vhci_hc_driver, &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd) {
+ pr_err("create hcd failed\n");
+ return -ENOMEM;
+ }
+ hcd->has_tt = 1;
+
+ /* this is private data for vhci_hcd */
+ the_controller = hcd_to_vhci(hcd);
+
+ /*
+ * Finish generic HCD structure initialization and register.
+ * Call the driver's reset() and start() routines.
+ */
+ ret = usb_add_hcd(hcd, 0, 0);
+ if (ret != 0) {
+ pr_err("usb_add_hcd failed %d\n", ret);
+ usb_put_hcd(hcd);
+ the_controller = NULL;
+ return ret;
+ }
+
+ usbip_dbg_vhci_hc("bye\n");
+ return 0;
+}
+
+static int vhci_hcd_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+
+ hcd = platform_get_drvdata(pdev);
+ if (!hcd)
+ return 0;
+
+ /*
+ * Disconnects the root hub,
+ * then reverses the effects of usb_add_hcd(),
+ * invoking the HCD's stop() methods.
+ */
+ usb_remove_hcd(hcd);
+ usb_put_hcd(hcd);
+ the_controller = NULL;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+/* what should happen for USB/IP under suspend/resume? */
+static int vhci_hcd_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct usb_hcd *hcd;
+ int rhport = 0;
+ int connected = 0;
+ int ret = 0;
+
+ hcd = platform_get_drvdata(pdev);
+
+ spin_lock(&the_controller->lock);
+
+ for (rhport = 0; rhport < VHCI_NPORTS; rhport++)
+ if (the_controller->port_status[rhport] &
+ USB_PORT_STAT_CONNECTION)
+ connected += 1;
+
+ spin_unlock(&the_controller->lock);
+
+ if (connected > 0) {
+ dev_info(&pdev->dev,
+ "We have %d active connection%s. Do not suspend.\n",
+ connected, (connected == 1 ? "" : "s"));
+ ret = -EBUSY;
+ } else {
+ dev_info(&pdev->dev, "suspend vhci_hcd");
+ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ }
+
+ return ret;
+}
+
+static int vhci_hcd_resume(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ hcd = platform_get_drvdata(pdev);
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ usb_hcd_poll_rh_status(hcd);
+
+ return 0;
+}
+
+#else
+
+#define vhci_hcd_suspend NULL
+#define vhci_hcd_resume NULL
+
+#endif
+
+static struct platform_driver vhci_driver = {
+ .probe = vhci_hcd_probe,
+ .remove = vhci_hcd_remove,
+ .suspend = vhci_hcd_suspend,
+ .resume = vhci_hcd_resume,
+ .driver = {
+ .name = driver_name,
+ .owner = THIS_MODULE,
+ },
+};
+
+/*
+ * The VHCI 'device' is 'virtual'; not a real plug&play hardware.
+ * We need to add this virtual device as a platform device arbitrarily:
+ * 1. platform_device_register()
+ */
+static void the_pdev_release(struct device *dev)
+{
+}
+
+static struct platform_device the_pdev = {
+ /* should be the same name as driver_name */
+ .name = driver_name,
+ .id = -1,
+ .dev = {
+ .release = the_pdev_release,
+ },
+};
+
+static int __init vhci_hcd_init(void)
+{
+ int ret;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ ret = platform_driver_register(&vhci_driver);
+ if (ret)
+ goto err_driver_register;
+
+ ret = platform_device_register(&the_pdev);
+ if (ret)
+ goto err_platform_device_register;
+
+ pr_info(DRIVER_DESC " v" USBIP_VERSION "\n");
+ return ret;
+
+err_platform_device_register:
+ platform_driver_unregister(&vhci_driver);
+err_driver_register:
+ return ret;
+}
+
+static void __exit vhci_hcd_exit(void)
+{
+ platform_device_unregister(&the_pdev);
+ platform_driver_unregister(&vhci_driver);
+}
+
+module_init(vhci_hcd_init);
+module_exit(vhci_hcd_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(USBIP_VERSION);
--- /dev/null
+/*
+ * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ *
+ * This 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 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <linux/kthread.h>
+#include <linux/slab.h>
+
+#include "usbip_common.h"
+#include "vhci.h"
+
+/* get URB from transmitted urb queue. caller must hold vdev->priv_lock */
+struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, __u32 seqnum)
+{
+ struct vhci_priv *priv, *tmp;
+ struct urb *urb = NULL;
+ int status;
+
+ list_for_each_entry_safe(priv, tmp, &vdev->priv_rx, list) {
+ if (priv->seqnum != seqnum)
+ continue;
+
+ urb = priv->urb;
+ status = urb->status;
+
+ usbip_dbg_vhci_rx("find urb %p vurb %p seqnum %u\n",
+ urb, priv, seqnum);
+
+ switch (status) {
+ case -ENOENT:
+ /* fall through */
+ case -ECONNRESET:
+ dev_info(&urb->dev->dev,
+ "urb %p was unlinked %ssynchronuously.\n", urb,
+ status == -ENOENT ? "" : "a");
+ break;
+ case -EINPROGRESS:
+ /* no info output */
+ break;
+ default:
+ dev_info(&urb->dev->dev,
+ "urb %p may be in a error, status %d\n", urb,
+ status);
+ }
+
+ list_del(&priv->list);
+ kfree(priv);
+ urb->hcpriv = NULL;
+
+ break;
+ }
+
+ return urb;
+}
+
+static void vhci_recv_ret_submit(struct vhci_device *vdev,
+ struct usbip_header *pdu)
+{
+ struct usbip_device *ud = &vdev->ud;
+ struct urb *urb;
+
+ spin_lock(&vdev->priv_lock);
+ urb = pickup_urb_and_free_priv(vdev, pdu->base.seqnum);
+ spin_unlock(&vdev->priv_lock);
+
+ if (!urb) {
+ pr_err("cannot find a urb of seqnum %u\n", pdu->base.seqnum);
+ pr_info("max seqnum %d\n",
+ atomic_read(&the_controller->seqnum));
+ usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
+ return;
+ }
+
+ /* unpack the pdu to a urb */
+ usbip_pack_pdu(pdu, urb, USBIP_RET_SUBMIT, 0);
+
+ /* recv transfer buffer */
+ if (usbip_recv_xbuff(ud, urb) < 0)
+ return;
+
+ /* recv iso_packet_descriptor */
+ if (usbip_recv_iso(ud, urb) < 0)
+ return;
+
+ /* restore the padding in iso packets */
+ usbip_pad_iso(ud, urb);
+
+ if (usbip_dbg_flag_vhci_rx)
+ usbip_dump_urb(urb);
+
+ usbip_dbg_vhci_rx("now giveback urb %p\n", urb);
+
+ spin_lock(&the_controller->lock);
+ usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb);
+ spin_unlock(&the_controller->lock);
+
+ usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status);
+
+ usbip_dbg_vhci_rx("Leave\n");
+}
+
+static struct vhci_unlink *dequeue_pending_unlink(struct vhci_device *vdev,
+ struct usbip_header *pdu)
+{
+ struct vhci_unlink *unlink, *tmp;
+
+ spin_lock(&vdev->priv_lock);
+
+ list_for_each_entry_safe(unlink, tmp, &vdev->unlink_rx, list) {
+ pr_info("unlink->seqnum %lu\n", unlink->seqnum);
+ if (unlink->seqnum == pdu->base.seqnum) {
+ usbip_dbg_vhci_rx("found pending unlink, %lu\n",
+ unlink->seqnum);
+ list_del(&unlink->list);
+
+ spin_unlock(&vdev->priv_lock);
+ return unlink;
+ }
+ }
+
+ spin_unlock(&vdev->priv_lock);
+
+ return NULL;
+}
+
+static void vhci_recv_ret_unlink(struct vhci_device *vdev,
+ struct usbip_header *pdu)
+{
+ struct vhci_unlink *unlink;
+ struct urb *urb;
+
+ usbip_dump_header(pdu);
+
+ unlink = dequeue_pending_unlink(vdev, pdu);
+ if (!unlink) {
+ pr_info("cannot find the pending unlink %u\n",
+ pdu->base.seqnum);
+ return;
+ }
+
+ spin_lock(&vdev->priv_lock);
+ urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum);
+ spin_unlock(&vdev->priv_lock);
+
+ if (!urb) {
+ /*
+ * I get the result of a unlink request. But, it seems that I
+ * already received the result of its submit result and gave
+ * back the URB.
+ */
+ pr_info("the urb (seqnum %d) was already given back\n",
+ pdu->base.seqnum);
+ } else {
+ usbip_dbg_vhci_rx("now giveback urb %p\n", urb);
+
+ /* If unlink is successful, status is -ECONNRESET */
+ urb->status = pdu->u.ret_unlink.status;
+ pr_info("urb->status %d\n", urb->status);
+
+ spin_lock(&the_controller->lock);
+ usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb);
+ spin_unlock(&the_controller->lock);
+
+ usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb,
+ urb->status);
+ }
+
+ kfree(unlink);
+}
+
+static int vhci_priv_tx_empty(struct vhci_device *vdev)
+{
+ int empty = 0;
+
+ spin_lock(&vdev->priv_lock);
+ empty = list_empty(&vdev->priv_rx);
+ spin_unlock(&vdev->priv_lock);
+
+ return empty;
+}
+
+/* recv a pdu */
+static void vhci_rx_pdu(struct usbip_device *ud)
+{
+ int ret;
+ struct usbip_header pdu;
+ struct vhci_device *vdev = container_of(ud, struct vhci_device, ud);
+
+ usbip_dbg_vhci_rx("Enter\n");
+
+ memset(&pdu, 0, sizeof(pdu));
+
+ /* receive a pdu header */
+ ret = usbip_recv(ud->tcp_socket, &pdu, sizeof(pdu));
+ if (ret < 0) {
+ if (ret == -ECONNRESET)
+ pr_info("connection reset by peer\n");
+ else if (ret == -EAGAIN) {
+ /* ignore if connection was idle */
+ if (vhci_priv_tx_empty(vdev))
+ return;
+ pr_info("connection timed out with pending urbs\n");
+ } else if (ret != -ERESTARTSYS)
+ pr_info("xmit failed %d\n", ret);
+
+ usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
+ return;
+ }
+ if (ret == 0) {
+ pr_info("connection closed");
+ usbip_event_add(ud, VDEV_EVENT_DOWN);
+ return;
+ }
+ if (ret != sizeof(pdu)) {
+ pr_err("received pdu size is %d, should be %d\n", ret,
+ (unsigned int)sizeof(pdu));
+ usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
+ return;
+ }
+
+ usbip_header_correct_endian(&pdu, 0);
+
+ if (usbip_dbg_flag_vhci_rx)
+ usbip_dump_header(&pdu);
+
+ switch (pdu.base.command) {
+ case USBIP_RET_SUBMIT:
+ vhci_recv_ret_submit(vdev, &pdu);
+ break;
+ case USBIP_RET_UNLINK:
+ vhci_recv_ret_unlink(vdev, &pdu);
+ break;
+ default:
+ /* NOT REACHED */
+ pr_err("unknown pdu %u\n", pdu.base.command);
+ usbip_dump_header(&pdu);
+ usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
+ break;
+ }
+}
+
+int vhci_rx_loop(void *data)
+{
+ struct usbip_device *ud = data;
+
+ while (!kthread_should_stop()) {
+ if (usbip_event_happened(ud))
+ break;
+
+ vhci_rx_pdu(ud);
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ *
+ * This 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 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <linux/kthread.h>
+#include <linux/file.h>
+#include <linux/net.h>
+
+#include "usbip_common.h"
+#include "vhci.h"
+
+/* TODO: refine locking ?*/
+
+/* Sysfs entry to show port status */
+static ssize_t status_show(struct device *dev, struct device_attribute *attr,
+ char *out)
+{
+ char *s = out;
+ int i = 0;
+
+ BUG_ON(!the_controller || !out);
+
+ spin_lock(&the_controller->lock);
+
+ /*
+ * output example:
+ * prt sta spd dev socket local_busid
+ * 000 004 000 000 c5a7bb80 1-2.3
+ * 001 004 000 000 d8cee980 2-3.4
+ *
+ * IP address can be retrieved from a socket pointer address by looking
+ * up /proc/net/{tcp,tcp6}. Also, a userland program may remember a
+ * port number and its peer IP address.
+ */
+ out += sprintf(out,
+ "prt sta spd bus dev socket local_busid\n");
+
+ for (i = 0; i < VHCI_NPORTS; i++) {
+ struct vhci_device *vdev = port_to_vdev(i);
+
+ spin_lock(&vdev->ud.lock);
+ out += sprintf(out, "%03u %03u ", i, vdev->ud.status);
+
+ if (vdev->ud.status == VDEV_ST_USED) {
+ out += sprintf(out, "%03u %08x ",
+ vdev->speed, vdev->devid);
+ out += sprintf(out, "%16p ", vdev->ud.tcp_socket);
+ out += sprintf(out, "%s", dev_name(&vdev->udev->dev));
+
+ } else {
+ out += sprintf(out, "000 000 000 0000000000000000 0-0");
+ }
+
+ out += sprintf(out, "\n");
+ spin_unlock(&vdev->ud.lock);
+ }
+
+ spin_unlock(&the_controller->lock);
+
+ return out - s;
+}
+static DEVICE_ATTR_RO(status);
+
+/* Sysfs entry to shutdown a virtual connection */
+static int vhci_port_disconnect(__u32 rhport)
+{
+ struct vhci_device *vdev;
+
+ usbip_dbg_vhci_sysfs("enter\n");
+
+ /* lock */
+ spin_lock(&the_controller->lock);
+
+ vdev = port_to_vdev(rhport);
+
+ spin_lock(&vdev->ud.lock);
+ if (vdev->ud.status == VDEV_ST_NULL) {
+ pr_err("not connected %d\n", vdev->ud.status);
+
+ /* unlock */
+ spin_unlock(&vdev->ud.lock);
+ spin_unlock(&the_controller->lock);
+
+ return -EINVAL;
+ }
+
+ /* unlock */
+ spin_unlock(&vdev->ud.lock);
+ spin_unlock(&the_controller->lock);
+
+ usbip_event_add(&vdev->ud, VDEV_EVENT_DOWN);
+
+ return 0;
+}
+
+static ssize_t store_detach(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int err;
+ __u32 rhport = 0;
+
+ if (sscanf(buf, "%u", &rhport) != 1)
+ return -EINVAL;
+
+ /* check rhport */
+ if (rhport >= VHCI_NPORTS) {
+ dev_err(dev, "invalid port %u\n", rhport);
+ return -EINVAL;
+ }
+
+ err = vhci_port_disconnect(rhport);
+ if (err < 0)
+ return -EINVAL;
+
+ usbip_dbg_vhci_sysfs("Leave\n");
+
+ return count;
+}
+static DEVICE_ATTR(detach, S_IWUSR, NULL, store_detach);
+
+/* Sysfs entry to establish a virtual connection */
+static int valid_args(__u32 rhport, enum usb_device_speed speed)
+{
+ /* check rhport */
+ if (rhport >= VHCI_NPORTS) {
+ pr_err("port %u\n", rhport);
+ return -EINVAL;
+ }
+
+ /* check speed */
+ switch (speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ case USB_SPEED_HIGH:
+ case USB_SPEED_WIRELESS:
+ break;
+ default:
+ pr_err("Failed attach request for unsupported USB speed: %s\n",
+ usb_speed_string(speed));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * To start a new USB/IP attachment, a userland program needs to setup a TCP
+ * connection and then write its socket descriptor with remote device
+ * information into this sysfs file.
+ *
+ * A remote device is virtually attached to the root-hub port of @rhport with
+ * @speed. @devid is embedded into a request to specify the remote device in a
+ * server host.
+ *
+ * write() returns 0 on success, else negative errno.
+ */
+static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vhci_device *vdev;
+ struct socket *socket;
+ int sockfd = 0;
+ __u32 rhport = 0, devid = 0, speed = 0;
+ int err;
+
+ /*
+ * @rhport: port number of vhci_hcd
+ * @sockfd: socket descriptor of an established TCP connection
+ * @devid: unique device identifier in a remote host
+ * @speed: usb device speed in a remote host
+ */
+ if (sscanf(buf, "%u %u %u %u", &rhport, &sockfd, &devid, &speed) != 4)
+ return -EINVAL;
+
+ usbip_dbg_vhci_sysfs("rhport(%u) sockfd(%u) devid(%u) speed(%u)\n",
+ rhport, sockfd, devid, speed);
+
+ /* check received parameters */
+ if (valid_args(rhport, speed) < 0)
+ return -EINVAL;
+
+ /* Extract socket from fd. */
+ socket = sockfd_lookup(sockfd, &err);
+ if (!socket)
+ return -EINVAL;
+
+ /* now need lock until setting vdev status as used */
+
+ /* begin a lock */
+ spin_lock(&the_controller->lock);
+ vdev = port_to_vdev(rhport);
+ spin_lock(&vdev->ud.lock);
+
+ if (vdev->ud.status != VDEV_ST_NULL) {
+ /* end of the lock */
+ spin_unlock(&vdev->ud.lock);
+ spin_unlock(&the_controller->lock);
+
+ sockfd_put(socket);
+
+ dev_err(dev, "port %d already used\n", rhport);
+ return -EINVAL;
+ }
+
+ dev_info(dev,
+ "rhport(%u) sockfd(%d) devid(%u) speed(%u) speed_str(%s)\n",
+ rhport, sockfd, devid, speed, usb_speed_string(speed));
+
+ vdev->devid = devid;
+ vdev->speed = speed;
+ vdev->ud.tcp_socket = socket;
+ vdev->ud.status = VDEV_ST_NOTASSIGNED;
+
+ spin_unlock(&vdev->ud.lock);
+ spin_unlock(&the_controller->lock);
+ /* end the lock */
+
+ vdev->ud.tcp_rx = kthread_get_run(vhci_rx_loop, &vdev->ud, "vhci_rx");
+ vdev->ud.tcp_tx = kthread_get_run(vhci_tx_loop, &vdev->ud, "vhci_tx");
+
+ rh_port_connect(rhport, speed);
+
+ return count;
+}
+static DEVICE_ATTR(attach, S_IWUSR, NULL, store_attach);
+
+static struct attribute *dev_attrs[] = {
+ &dev_attr_status.attr,
+ &dev_attr_detach.attr,
+ &dev_attr_attach.attr,
+ &dev_attr_usbip_debug.attr,
+ NULL,
+};
+
+const struct attribute_group dev_attr_group = {
+ .attrs = dev_attrs,
+};
--- /dev/null
+/*
+ * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ *
+ * This 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 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <linux/kthread.h>
+#include <linux/slab.h>
+
+#include "usbip_common.h"
+#include "vhci.h"
+
+static void setup_cmd_submit_pdu(struct usbip_header *pdup, struct urb *urb)
+{
+ struct vhci_priv *priv = ((struct vhci_priv *)urb->hcpriv);
+ struct vhci_device *vdev = priv->vdev;
+
+ usbip_dbg_vhci_tx("URB, local devnum %u, remote devid %u\n",
+ usb_pipedevice(urb->pipe), vdev->devid);
+
+ pdup->base.command = USBIP_CMD_SUBMIT;
+ pdup->base.seqnum = priv->seqnum;
+ pdup->base.devid = vdev->devid;
+ pdup->base.direction = usb_pipein(urb->pipe) ?
+ USBIP_DIR_IN : USBIP_DIR_OUT;
+ pdup->base.ep = usb_pipeendpoint(urb->pipe);
+
+ usbip_pack_pdu(pdup, urb, USBIP_CMD_SUBMIT, 1);
+
+ if (urb->setup_packet)
+ memcpy(pdup->u.cmd_submit.setup, urb->setup_packet, 8);
+}
+
+static struct vhci_priv *dequeue_from_priv_tx(struct vhci_device *vdev)
+{
+ struct vhci_priv *priv, *tmp;
+
+ spin_lock(&vdev->priv_lock);
+
+ list_for_each_entry_safe(priv, tmp, &vdev->priv_tx, list) {
+ list_move_tail(&priv->list, &vdev->priv_rx);
+ spin_unlock(&vdev->priv_lock);
+ return priv;
+ }
+
+ spin_unlock(&vdev->priv_lock);
+
+ return NULL;
+}
+
+static int vhci_send_cmd_submit(struct vhci_device *vdev)
+{
+ struct vhci_priv *priv = NULL;
+
+ struct msghdr msg;
+ struct kvec iov[3];
+ size_t txsize;
+
+ size_t total_size = 0;
+
+ while ((priv = dequeue_from_priv_tx(vdev)) != NULL) {
+ int ret;
+ struct urb *urb = priv->urb;
+ struct usbip_header pdu_header;
+ struct usbip_iso_packet_descriptor *iso_buffer = NULL;
+
+ txsize = 0;
+ memset(&pdu_header, 0, sizeof(pdu_header));
+ memset(&msg, 0, sizeof(msg));
+ memset(&iov, 0, sizeof(iov));
+
+ usbip_dbg_vhci_tx("setup txdata urb %p\n", urb);
+
+ /* 1. setup usbip_header */
+ setup_cmd_submit_pdu(&pdu_header, urb);
+ usbip_header_correct_endian(&pdu_header, 1);
+
+ iov[0].iov_base = &pdu_header;
+ iov[0].iov_len = sizeof(pdu_header);
+ txsize += sizeof(pdu_header);
+
+ /* 2. setup transfer buffer */
+ if (!usb_pipein(urb->pipe) && urb->transfer_buffer_length > 0) {
+ iov[1].iov_base = urb->transfer_buffer;
+ iov[1].iov_len = urb->transfer_buffer_length;
+ txsize += urb->transfer_buffer_length;
+ }
+
+ /* 3. setup iso_packet_descriptor */
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ ssize_t len = 0;
+
+ iso_buffer = usbip_alloc_iso_desc_pdu(urb, &len);
+ if (!iso_buffer) {
+ usbip_event_add(&vdev->ud,
+ SDEV_EVENT_ERROR_MALLOC);
+ return -1;
+ }
+
+ iov[2].iov_base = iso_buffer;
+ iov[2].iov_len = len;
+ txsize += len;
+ }
+
+ ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 3, txsize);
+ if (ret != txsize) {
+ pr_err("sendmsg failed!, ret=%d for %zd\n", ret,
+ txsize);
+ kfree(iso_buffer);
+ usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_TCP);
+ return -1;
+ }
+
+ kfree(iso_buffer);
+ usbip_dbg_vhci_tx("send txdata\n");
+
+ total_size += txsize;
+ }
+
+ return total_size;
+}
+
+static struct vhci_unlink *dequeue_from_unlink_tx(struct vhci_device *vdev)
+{
+ struct vhci_unlink *unlink, *tmp;
+
+ spin_lock(&vdev->priv_lock);
+
+ list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) {
+ list_move_tail(&unlink->list, &vdev->unlink_rx);
+ spin_unlock(&vdev->priv_lock);
+ return unlink;
+ }
+
+ spin_unlock(&vdev->priv_lock);
+
+ return NULL;
+}
+
+static int vhci_send_cmd_unlink(struct vhci_device *vdev)
+{
+ struct vhci_unlink *unlink = NULL;
+
+ struct msghdr msg;
+ struct kvec iov[3];
+ size_t txsize;
+
+ size_t total_size = 0;
+
+ while ((unlink = dequeue_from_unlink_tx(vdev)) != NULL) {
+ int ret;
+ struct usbip_header pdu_header;
+
+ txsize = 0;
+ memset(&pdu_header, 0, sizeof(pdu_header));
+ memset(&msg, 0, sizeof(msg));
+ memset(&iov, 0, sizeof(iov));
+
+ usbip_dbg_vhci_tx("setup cmd unlink, %lu\n", unlink->seqnum);
+
+ /* 1. setup usbip_header */
+ pdu_header.base.command = USBIP_CMD_UNLINK;
+ pdu_header.base.seqnum = unlink->seqnum;
+ pdu_header.base.devid = vdev->devid;
+ pdu_header.base.ep = 0;
+ pdu_header.u.cmd_unlink.seqnum = unlink->unlink_seqnum;
+
+ usbip_header_correct_endian(&pdu_header, 1);
+
+ iov[0].iov_base = &pdu_header;
+ iov[0].iov_len = sizeof(pdu_header);
+ txsize += sizeof(pdu_header);
+
+ ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 1, txsize);
+ if (ret != txsize) {
+ pr_err("sendmsg failed!, ret=%d for %zd\n", ret,
+ txsize);
+ usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_TCP);
+ return -1;
+ }
+
+ usbip_dbg_vhci_tx("send txdata\n");
+
+ total_size += txsize;
+ }
+
+ return total_size;
+}
+
+int vhci_tx_loop(void *data)
+{
+ struct usbip_device *ud = data;
+ struct vhci_device *vdev = container_of(ud, struct vhci_device, ud);
+
+ while (!kthread_should_stop()) {
+ if (vhci_send_cmd_submit(vdev) < 0)
+ break;
+
+ if (vhci_send_cmd_unlink(vdev) < 0)
+ break;
+
+ wait_event_interruptible(vdev->waitq_tx,
+ (!list_empty(&vdev->priv_tx) ||
+ !list_empty(&vdev->unlink_tx) ||
+ kthread_should_stop()));
+
+ usbip_dbg_vhci_tx("pending urbs ?, now wake up\n");
+ }
+
+ return 0;
+}
dev = &wa->usb_iface->dev;
--(wa->active_buf_in_urbs);
active_buf_in_urbs = wa->active_buf_in_urbs;
+ rpipe = xfer->ep->hcpriv;
if (usb_pipeisoc(xfer->urb->pipe)) {
struct usb_iso_packet_descriptor *iso_frame_desc =
resubmit_dti = (isoc_data_frame_count ==
urb_frame_count);
} else if (active_buf_in_urbs == 0) {
- rpipe = xfer->ep->hcpriv;
dev_dbg(dev,
"xfer %p 0x%08X#%u: data in done (%zu bytes)\n",
xfer, wa_xfer_id(xfer), seg->index,
*/
resubmit_dti = wa->dti_state != WA_DTI_TRANSFER_RESULT_PENDING;
spin_lock_irqsave(&xfer->lock, flags);
- rpipe = xfer->ep->hcpriv;
if (printk_ratelimit())
dev_err(dev, "xfer %p 0x%08X#%u: data in error %d\n",
xfer, wa_xfer_id(xfer), seg->index,
data->max_brightness--;
}
+ data->enable_gpio = -EINVAL;
return 0;
}
#include <linux/list.h>
#include <linux/amba/bus.h>
#include <linux/amba/clcd.h>
+#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/hardirq.h>
#include <linux/dma-mapping.h>
{
struct device_node *endpoint;
int err;
+ unsigned int bpp;
u32 max_bandwidth;
u32 tft_r0b0g0[3];
err = of_property_read_u32(fb->dev->dev.of_node, "max-memory-bandwidth",
&max_bandwidth);
- if (!err)
- fb->panel->bpp = 8 * max_bandwidth / (fb->panel->mode.xres *
- fb->panel->mode.yres * fb->panel->mode.refresh);
- else
- fb->panel->bpp = 32;
+ if (!err) {
+ /*
+ * max_bandwidth is in bytes per second and pixclock in
+ * pico-seconds, so the maximum allowed bits per pixel is
+ * 8 * max_bandwidth / (PICOS2KHZ(pixclock) * 1000)
+ * Rearrange this calculation to avoid overflow and then ensure
+ * result is a valid format.
+ */
+ bpp = max_bandwidth / (1000 / 8)
+ / PICOS2KHZ(fb->panel->mode.pixclock);
+ bpp = rounddown_pow_of_two(bpp);
+ if (bpp > 32)
+ bpp = 32;
+ } else
+ bpp = 32;
+ fb->panel->bpp = bpp;
#ifdef CONFIG_CPU_BIG_ENDIAN
fb->panel->cntl |= CNTL_BEBO;
timings = of_get_display_timings(display_np);
if (!timings) {
dev_err(dev, "failed to get display timings\n");
+ ret = -EINVAL;
goto put_display_node;
}
timings_np = of_find_node_by_name(display_np, "display-timings");
if (!timings_np) {
dev_err(dev, "failed to find display-timings node\n");
+ ret = -ENODEV;
goto put_display_node;
}
{ 0xa8, 0x00 }
};
-static void __init chips_hw_init(void)
+static void chips_hw_init(void)
{
int i;
{
u32 reg;
- reg = lcdc_read(LCD_RASTER_TIMING_0_REG) & 0xf;
+ reg = lcdc_read(LCD_RASTER_TIMING_0_REG) & 0x3ff;
reg |= (((back_porch-1) & 0xff) << 24)
| (((front_porch-1) & 0xff) << 16)
| (((pulse_width-1) & 0x3f) << 10);
if (native_mode)
of_node_put(native_mode);
display_timings_release(disp);
+ disp = NULL;
entryfail:
kfree(disp);
dispfail:
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/freezer.h>
-#include <linux/workqueue.h>
#include "async-thread.h"
#include "ctree.h"
struct __btrfs_workqueue *high;
};
-static inline struct __btrfs_workqueue
-*__btrfs_alloc_workqueue(const char *name, int flags, int max_active,
+static void normal_work_helper(struct btrfs_work *work);
+
+#define BTRFS_WORK_HELPER(name) \
+void btrfs_##name(struct work_struct *arg) \
+{ \
+ struct btrfs_work *work = container_of(arg, struct btrfs_work, \
+ normal_work); \
+ normal_work_helper(work); \
+}
+
+BTRFS_WORK_HELPER(worker_helper);
+BTRFS_WORK_HELPER(delalloc_helper);
+BTRFS_WORK_HELPER(flush_delalloc_helper);
+BTRFS_WORK_HELPER(cache_helper);
+BTRFS_WORK_HELPER(submit_helper);
+BTRFS_WORK_HELPER(fixup_helper);
+BTRFS_WORK_HELPER(endio_helper);
+BTRFS_WORK_HELPER(endio_meta_helper);
+BTRFS_WORK_HELPER(endio_meta_write_helper);
+BTRFS_WORK_HELPER(endio_raid56_helper);
+BTRFS_WORK_HELPER(rmw_helper);
+BTRFS_WORK_HELPER(endio_write_helper);
+BTRFS_WORK_HELPER(freespace_write_helper);
+BTRFS_WORK_HELPER(delayed_meta_helper);
+BTRFS_WORK_HELPER(readahead_helper);
+BTRFS_WORK_HELPER(qgroup_rescan_helper);
+BTRFS_WORK_HELPER(extent_refs_helper);
+BTRFS_WORK_HELPER(scrub_helper);
+BTRFS_WORK_HELPER(scrubwrc_helper);
+BTRFS_WORK_HELPER(scrubnc_helper);
+
+static struct __btrfs_workqueue *
+__btrfs_alloc_workqueue(const char *name, int flags, int max_active,
int thresh)
{
struct __btrfs_workqueue *ret = kzalloc(sizeof(*ret), GFP_NOFS);
spin_unlock_irqrestore(lock, flags);
}
-static void normal_work_helper(struct work_struct *arg)
+static void normal_work_helper(struct btrfs_work *work)
{
- struct btrfs_work *work;
struct __btrfs_workqueue *wq;
int need_order = 0;
- work = container_of(arg, struct btrfs_work, normal_work);
/*
* We should not touch things inside work in the following cases:
* 1) after work->func() if it has no ordered_free
trace_btrfs_all_work_done(work);
}
-void btrfs_init_work(struct btrfs_work *work,
+void btrfs_init_work(struct btrfs_work *work, btrfs_work_func_t uniq_func,
btrfs_func_t func,
btrfs_func_t ordered_func,
btrfs_func_t ordered_free)
work->func = func;
work->ordered_func = ordered_func;
work->ordered_free = ordered_free;
- INIT_WORK(&work->normal_work, normal_work_helper);
+ INIT_WORK(&work->normal_work, uniq_func);
INIT_LIST_HEAD(&work->ordered_list);
work->flags = 0;
}
#ifndef __BTRFS_ASYNC_THREAD_
#define __BTRFS_ASYNC_THREAD_
+#include <linux/workqueue.h>
struct btrfs_workqueue;
/* Internal use only */
struct __btrfs_workqueue;
struct btrfs_work;
typedef void (*btrfs_func_t)(struct btrfs_work *arg);
+typedef void (*btrfs_work_func_t)(struct work_struct *arg);
struct btrfs_work {
btrfs_func_t func;
unsigned long flags;
};
+#define BTRFS_WORK_HELPER_PROTO(name) \
+void btrfs_##name(struct work_struct *arg)
+
+BTRFS_WORK_HELPER_PROTO(worker_helper);
+BTRFS_WORK_HELPER_PROTO(delalloc_helper);
+BTRFS_WORK_HELPER_PROTO(flush_delalloc_helper);
+BTRFS_WORK_HELPER_PROTO(cache_helper);
+BTRFS_WORK_HELPER_PROTO(submit_helper);
+BTRFS_WORK_HELPER_PROTO(fixup_helper);
+BTRFS_WORK_HELPER_PROTO(endio_helper);
+BTRFS_WORK_HELPER_PROTO(endio_meta_helper);
+BTRFS_WORK_HELPER_PROTO(endio_meta_write_helper);
+BTRFS_WORK_HELPER_PROTO(endio_raid56_helper);
+BTRFS_WORK_HELPER_PROTO(rmw_helper);
+BTRFS_WORK_HELPER_PROTO(endio_write_helper);
+BTRFS_WORK_HELPER_PROTO(freespace_write_helper);
+BTRFS_WORK_HELPER_PROTO(delayed_meta_helper);
+BTRFS_WORK_HELPER_PROTO(readahead_helper);
+BTRFS_WORK_HELPER_PROTO(qgroup_rescan_helper);
+BTRFS_WORK_HELPER_PROTO(extent_refs_helper);
+BTRFS_WORK_HELPER_PROTO(scrub_helper);
+BTRFS_WORK_HELPER_PROTO(scrubwrc_helper);
+BTRFS_WORK_HELPER_PROTO(scrubnc_helper);
+
struct btrfs_workqueue *btrfs_alloc_workqueue(const char *name,
int flags,
int max_active,
int thresh);
-void btrfs_init_work(struct btrfs_work *work,
+void btrfs_init_work(struct btrfs_work *work, btrfs_work_func_t helper,
btrfs_func_t func,
btrfs_func_t ordered_func,
btrfs_func_t ordered_free);
return -ENOMEM;
async_work->delayed_root = delayed_root;
- btrfs_init_work(&async_work->work, btrfs_async_run_delayed_root,
- NULL, NULL);
+ btrfs_init_work(&async_work->work, btrfs_delayed_meta_helper,
+ btrfs_async_run_delayed_root, NULL, NULL);
async_work->nr = nr;
btrfs_queue_work(root->fs_info->delayed_workers, &async_work->work);
#include "btrfs_inode.h"
#include "volumes.h"
#include "print-tree.h"
-#include "async-thread.h"
#include "locking.h"
#include "tree-log.h"
#include "free-space-cache.h"
{
struct end_io_wq *end_io_wq = bio->bi_private;
struct btrfs_fs_info *fs_info;
+ struct btrfs_workqueue *wq;
+ btrfs_work_func_t func;
fs_info = end_io_wq->info;
end_io_wq->error = err;
- btrfs_init_work(&end_io_wq->work, end_workqueue_fn, NULL, NULL);
if (bio->bi_rw & REQ_WRITE) {
- if (end_io_wq->metadata == BTRFS_WQ_ENDIO_METADATA)
- btrfs_queue_work(fs_info->endio_meta_write_workers,
- &end_io_wq->work);
- else if (end_io_wq->metadata == BTRFS_WQ_ENDIO_FREE_SPACE)
- btrfs_queue_work(fs_info->endio_freespace_worker,
- &end_io_wq->work);
- else if (end_io_wq->metadata == BTRFS_WQ_ENDIO_RAID56)
- btrfs_queue_work(fs_info->endio_raid56_workers,
- &end_io_wq->work);
- else
- btrfs_queue_work(fs_info->endio_write_workers,
- &end_io_wq->work);
+ if (end_io_wq->metadata == BTRFS_WQ_ENDIO_METADATA) {
+ wq = fs_info->endio_meta_write_workers;
+ func = btrfs_endio_meta_write_helper;
+ } else if (end_io_wq->metadata == BTRFS_WQ_ENDIO_FREE_SPACE) {
+ wq = fs_info->endio_freespace_worker;
+ func = btrfs_freespace_write_helper;
+ } else if (end_io_wq->metadata == BTRFS_WQ_ENDIO_RAID56) {
+ wq = fs_info->endio_raid56_workers;
+ func = btrfs_endio_raid56_helper;
+ } else {
+ wq = fs_info->endio_write_workers;
+ func = btrfs_endio_write_helper;
+ }
} else {
- if (end_io_wq->metadata == BTRFS_WQ_ENDIO_RAID56)
- btrfs_queue_work(fs_info->endio_raid56_workers,
- &end_io_wq->work);
- else if (end_io_wq->metadata)
- btrfs_queue_work(fs_info->endio_meta_workers,
- &end_io_wq->work);
- else
- btrfs_queue_work(fs_info->endio_workers,
- &end_io_wq->work);
+ if (end_io_wq->metadata == BTRFS_WQ_ENDIO_RAID56) {
+ wq = fs_info->endio_raid56_workers;
+ func = btrfs_endio_raid56_helper;
+ } else if (end_io_wq->metadata) {
+ wq = fs_info->endio_meta_workers;
+ func = btrfs_endio_meta_helper;
+ } else {
+ wq = fs_info->endio_workers;
+ func = btrfs_endio_helper;
+ }
}
+
+ btrfs_init_work(&end_io_wq->work, func, end_workqueue_fn, NULL, NULL);
+ btrfs_queue_work(wq, &end_io_wq->work);
}
/*
async->submit_bio_start = submit_bio_start;
async->submit_bio_done = submit_bio_done;
- btrfs_init_work(&async->work, run_one_async_start,
+ btrfs_init_work(&async->work, btrfs_worker_helper, run_one_async_start,
run_one_async_done, run_one_async_free);
async->bio_flags = bio_flags;
btrfs_set_stack_device_generation(dev_item, 0);
btrfs_set_stack_device_type(dev_item, dev->type);
btrfs_set_stack_device_id(dev_item, dev->devid);
- btrfs_set_stack_device_total_bytes(dev_item, dev->total_bytes);
+ btrfs_set_stack_device_total_bytes(dev_item,
+ dev->disk_total_bytes);
btrfs_set_stack_device_bytes_used(dev_item, dev->bytes_used);
btrfs_set_stack_device_io_align(dev_item, dev->io_align);
btrfs_set_stack_device_io_width(dev_item, dev->io_width);
caching_ctl->block_group = cache;
caching_ctl->progress = cache->key.objectid;
atomic_set(&caching_ctl->count, 1);
- btrfs_init_work(&caching_ctl->work, caching_thread, NULL, NULL);
+ btrfs_init_work(&caching_ctl->work, btrfs_cache_helper,
+ caching_thread, NULL, NULL);
spin_lock(&cache->lock);
/*
async->sync = 0;
init_completion(&async->wait);
- btrfs_init_work(&async->work, delayed_ref_async_start,
- NULL, NULL);
+ btrfs_init_work(&async->work, btrfs_extent_refs_helper,
+ delayed_ref_async_start, NULL, NULL);
btrfs_queue_work(root->fs_info->extent_workers, &async->work);
*/
static u64 btrfs_reduce_alloc_profile(struct btrfs_root *root, u64 flags)
{
- /*
- * we add in the count of missing devices because we want
- * to make sure that any RAID levels on a degraded FS
- * continue to be honored.
- */
- u64 num_devices = root->fs_info->fs_devices->rw_devices +
- root->fs_info->fs_devices->missing_devices;
+ u64 num_devices = root->fs_info->fs_devices->rw_devices;
u64 target;
u64 tmp;
if (stripped)
return extended_to_chunk(stripped);
- /*
- * we add in the count of missing devices because we want
- * to make sure that any RAID levels on a degraded FS
- * continue to be honored.
- */
- num_devices = root->fs_info->fs_devices->rw_devices +
- root->fs_info->fs_devices->missing_devices;
+ num_devices = root->fs_info->fs_devices->rw_devices;
stripped = BTRFS_BLOCK_GROUP_RAID0 |
BTRFS_BLOCK_GROUP_RAID5 | BTRFS_BLOCK_GROUP_RAID6 |
test_bit(BIO_UPTODATE, &bio->bi_flags);
if (err)
uptodate = 0;
+ offset += len;
continue;
}
}
return -ENOMEM;
path->leave_spinning = 1;
- start = ALIGN(start, BTRFS_I(inode)->root->sectorsize);
- len = ALIGN(len, BTRFS_I(inode)->root->sectorsize);
+ start = round_down(start, BTRFS_I(inode)->root->sectorsize);
+ len = round_up(max, BTRFS_I(inode)->root->sectorsize) - start;
/*
* lookup the last file extent. We're not using i_size here
{
if (filp->private_data)
btrfs_ioctl_trans_end(filp);
- filemap_flush(inode->i_mapping);
+ /*
+ * ordered_data_close is set by settattr when we are about to truncate
+ * a file from a non-zero size to a zero size. This tries to
+ * flush down new bytes that may have been written if the
+ * application were using truncate to replace a file in place.
+ */
+ if (test_and_clear_bit(BTRFS_INODE_ORDERED_DATA_CLOSE,
+ &BTRFS_I(inode)->runtime_flags))
+ filemap_flush(inode->i_mapping);
return 0;
}
goto out;
}
- if (hole_mergeable(inode, leaf, path->slots[0]+1, offset, end)) {
+ if (hole_mergeable(inode, leaf, path->slots[0], offset, end)) {
u64 num_bytes;
- path->slots[0]++;
key.offset = offset;
btrfs_set_item_key_safe(root, path, &key);
fi = btrfs_item_ptr(leaf, path->slots[0],
goto out_only_mutex;
}
- lockstart = round_up(offset , BTRFS_I(inode)->root->sectorsize);
+ lockstart = round_up(offset, BTRFS_I(inode)->root->sectorsize);
lockend = round_down(offset + len,
BTRFS_I(inode)->root->sectorsize) - 1;
same_page = ((offset >> PAGE_CACHE_SHIFT) ==
tail_start + tail_len, 0, 1);
if (ret)
goto out_only_mutex;
- }
+ }
}
}
async_cow->end = cur_end;
INIT_LIST_HEAD(&async_cow->extents);
- btrfs_init_work(&async_cow->work, async_cow_start,
- async_cow_submit, async_cow_free);
+ btrfs_init_work(&async_cow->work,
+ btrfs_delalloc_helper,
+ async_cow_start, async_cow_submit,
+ async_cow_free);
nr_pages = (cur_end - start + PAGE_CACHE_SIZE) >>
PAGE_CACHE_SHIFT;
SetPageChecked(page);
page_cache_get(page);
- btrfs_init_work(&fixup->work, btrfs_writepage_fixup_worker, NULL, NULL);
+ btrfs_init_work(&fixup->work, btrfs_fixup_helper,
+ btrfs_writepage_fixup_worker, NULL, NULL);
fixup->page = page;
btrfs_queue_work(root->fs_info->fixup_workers, &fixup->work);
return -EBUSY;
struct inode *inode = page->mapping->host;
struct btrfs_root *root = BTRFS_I(inode)->root;
struct btrfs_ordered_extent *ordered_extent = NULL;
- struct btrfs_workqueue *workers;
+ struct btrfs_workqueue *wq;
+ btrfs_work_func_t func;
trace_btrfs_writepage_end_io_hook(page, start, end, uptodate);
end - start + 1, uptodate))
return 0;
- btrfs_init_work(&ordered_extent->work, finish_ordered_fn, NULL, NULL);
+ if (btrfs_is_free_space_inode(inode)) {
+ wq = root->fs_info->endio_freespace_worker;
+ func = btrfs_freespace_write_helper;
+ } else {
+ wq = root->fs_info->endio_write_workers;
+ func = btrfs_endio_write_helper;
+ }
- if (btrfs_is_free_space_inode(inode))
- workers = root->fs_info->endio_freespace_worker;
- else
- workers = root->fs_info->endio_write_workers;
- btrfs_queue_work(workers, &ordered_extent->work);
+ btrfs_init_work(&ordered_extent->work, func, finish_ordered_fn, NULL,
+ NULL);
+ btrfs_queue_work(wq, &ordered_extent->work);
return 0;
}
clear_bit(EXTENT_FLAG_LOGGING, &em->flags);
remove_extent_mapping(map_tree, em);
free_extent_map(em);
+ if (need_resched()) {
+ write_unlock(&map_tree->lock);
+ cond_resched();
+ write_lock(&map_tree->lock);
+ }
}
write_unlock(&map_tree->lock);
&cached_state, GFP_NOFS);
free_extent_state(state);
+ cond_resched();
spin_lock(&io_tree->lock);
}
spin_unlock(&io_tree->lock);
iput(inode);
inode = ERR_PTR(ret);
}
+ /*
+ * If orphan cleanup did remove any orphans, it means the tree
+ * was modified and therefore the commit root is not the same as
+ * the current root anymore. This is a problem, because send
+ * uses the commit root and therefore can see inode items that
+ * don't exist in the current root anymore, and for example make
+ * calls to btrfs_iget, which will do tree lookups based on the
+ * current root and not on the commit root. Those lookups will
+ * fail, returning a -ESTALE error, and making send fail with
+ * that error. So make sure a send does not see any orphans we
+ * have just removed, and that it will see the same inodes
+ * regardless of whether a transaction commit happened before
+ * it started (meaning that the commit root will be the same as
+ * the current root) or not.
+ */
+ if (sub_root->node != sub_root->commit_root) {
+ u64 sub_flags = btrfs_root_flags(&sub_root->root_item);
+
+ if (sub_flags & BTRFS_ROOT_SUBVOL_RDONLY) {
+ struct extent_buffer *eb;
+
+ /*
+ * Assert we can't have races between dentry
+ * lookup called through the snapshot creation
+ * ioctl and the VFS.
+ */
+ ASSERT(mutex_is_locked(&dir->i_mutex));
+
+ down_write(&root->fs_info->commit_root_sem);
+ eb = sub_root->commit_root;
+ sub_root->commit_root =
+ btrfs_root_node(sub_root);
+ up_write(&root->fs_info->commit_root_sem);
+ free_extent_buffer(eb);
+ }
+ }
}
return inode;
return ERR_PTR(-ENOMEM);
}
+ /*
+ * O_TMPFILE, set link count to 0, so that after this point,
+ * we fill in an inode item with the correct link count.
+ */
+ if (!name)
+ set_nlink(inode, 0);
+
/*
* we have to initialize this early, so we can reclaim the inode
* number if we fail afterwards in this function.
static int merge_extent_mapping(struct extent_map_tree *em_tree,
struct extent_map *existing,
struct extent_map *em,
- u64 map_start, u64 map_len)
+ u64 map_start)
{
u64 start_diff;
BUG_ON(map_start < em->start || map_start >= extent_map_end(em));
start_diff = map_start - em->start;
em->start = map_start;
- em->len = map_len;
+ em->len = existing->start - em->start;
if (em->block_start < EXTENT_MAP_LAST_BYTE &&
!test_bit(EXTENT_FLAG_COMPRESSED, &em->flags)) {
em->block_start += start_diff;
goto not_found;
if (start + len <= found_key.offset)
goto not_found;
+ if (start > found_key.offset)
+ goto next;
em->start = start;
em->orig_start = start;
em->len = found_key.offset - start;
em->len);
if (existing) {
err = merge_extent_mapping(em_tree, existing,
- em, start,
- root->sectorsize);
+ em, start);
free_extent_map(existing);
if (err) {
free_extent_map(em);
if (!ret)
goto out_test;
- btrfs_init_work(&ordered->work, finish_ordered_fn, NULL, NULL);
+ btrfs_init_work(&ordered->work, btrfs_endio_write_helper,
+ finish_ordered_fn, NULL, NULL);
btrfs_queue_work(root->fs_info->endio_write_workers,
&ordered->work);
out_test:
map_length = orig_bio->bi_iter.bi_size;
ret = btrfs_map_block(root->fs_info, rw, start_sector << 9,
&map_length, NULL, 0);
- if (ret) {
- bio_put(orig_bio);
+ if (ret)
return -EIO;
- }
if (map_length >= orig_bio->bi_iter.bi_size) {
bio = orig_bio;
bio = btrfs_dio_bio_alloc(orig_bio->bi_bdev, start_sector, GFP_NOFS);
if (!bio)
return -ENOMEM;
+
bio->bi_private = dip;
bio->bi_end_io = btrfs_end_dio_bio;
atomic_inc(&dip->pending_bios);
count = iov_iter_count(iter);
if (test_bit(BTRFS_INODE_HAS_ASYNC_EXTENT,
&BTRFS_I(inode)->runtime_flags))
- filemap_fdatawrite_range(inode->i_mapping, offset, count);
+ filemap_fdatawrite_range(inode->i_mapping, offset,
+ offset + count - 1);
if (rw & WRITE) {
/*
work->inode = inode;
work->wait = wait;
work->delay_iput = delay_iput;
- btrfs_init_work(&work->work, btrfs_run_delalloc_work, NULL, NULL);
+ WARN_ON_ONCE(!inode);
+ btrfs_init_work(&work->work, btrfs_flush_delalloc_helper,
+ btrfs_run_delalloc_work, NULL, NULL);
return work;
}
if (ret)
goto out;
+ /*
+ * We set number of links to 0 in btrfs_new_inode(), and here we set
+ * it to 1 because d_tmpfile() will issue a warning if the count is 0,
+ * through:
+ *
+ * d_tmpfile() -> inode_dec_link_count() -> drop_nlink()
+ */
+ set_nlink(inode, 1);
d_tmpfile(dentry, inode);
mark_inode_dirty(inode);
if (ret)
goto fail;
- ret = btrfs_orphan_cleanup(pending_snapshot->snap);
- if (ret)
- goto fail;
-
- /*
- * If orphan cleanup did remove any orphans, it means the tree was
- * modified and therefore the commit root is not the same as the
- * current root anymore. This is a problem, because send uses the
- * commit root and therefore can see inode items that don't exist
- * in the current root anymore, and for example make calls to
- * btrfs_iget, which will do tree lookups based on the current root
- * and not on the commit root. Those lookups will fail, returning a
- * -ESTALE error, and making send fail with that error. So make sure
- * a send does not see any orphans we have just removed, and that it
- * will see the same inodes regardless of whether a transaction
- * commit happened before it started (meaning that the commit root
- * will be the same as the current root) or not.
- */
- if (readonly && pending_snapshot->snap->node !=
- pending_snapshot->snap->commit_root) {
- trans = btrfs_join_transaction(pending_snapshot->snap);
- if (IS_ERR(trans) && PTR_ERR(trans) != -ENOENT) {
- ret = PTR_ERR(trans);
- goto fail;
- }
- if (!IS_ERR(trans)) {
- ret = btrfs_commit_transaction(trans,
- pending_snapshot->snap);
- if (ret)
- goto fail;
- }
- }
-
inode = btrfs_lookup_dentry(dentry->d_parent->d_inode, dentry);
if (IS_ERR(inode)) {
ret = PTR_ERR(inode);
btrfs_mark_buffer_dirty(leaf);
btrfs_release_path(path);
- last_dest_end = new_key.offset + datal;
+ last_dest_end = ALIGN(new_key.offset + datal,
+ root->sectorsize);
ret = clone_finish_inode_update(trans, inode,
last_dest_end,
destoff, olen);
spin_unlock(&root->ordered_extent_lock);
btrfs_init_work(&ordered->flush_work,
+ btrfs_flush_delalloc_helper,
btrfs_run_ordered_extent_work, NULL, NULL);
list_add_tail(&ordered->work_list, &works);
btrfs_queue_work(root->fs_info->flush_workers,
elem.seq, &roots);
btrfs_put_tree_mod_seq(fs_info, &elem);
if (ret < 0)
- return ret;
+ goto out;
if (roots->nnodes != 1)
goto out;
memset(&fs_info->qgroup_rescan_work, 0,
sizeof(fs_info->qgroup_rescan_work));
btrfs_init_work(&fs_info->qgroup_rescan_work,
+ btrfs_qgroup_rescan_helper,
btrfs_qgroup_rescan_worker, NULL, NULL);
if (ret) {
static void async_rmw_stripe(struct btrfs_raid_bio *rbio)
{
- btrfs_init_work(&rbio->work, rmw_work, NULL, NULL);
+ btrfs_init_work(&rbio->work, btrfs_rmw_helper,
+ rmw_work, NULL, NULL);
btrfs_queue_work(rbio->fs_info->rmw_workers,
&rbio->work);
static void async_read_rebuild(struct btrfs_raid_bio *rbio)
{
- btrfs_init_work(&rbio->work, read_rebuild_work, NULL, NULL);
+ btrfs_init_work(&rbio->work, btrfs_rmw_helper,
+ read_rebuild_work, NULL, NULL);
btrfs_queue_work(rbio->fs_info->rmw_workers,
&rbio->work);
plug = container_of(cb, struct btrfs_plug_cb, cb);
if (from_schedule) {
- btrfs_init_work(&plug->work, unplug_work, NULL, NULL);
+ btrfs_init_work(&plug->work, btrfs_rmw_helper,
+ unplug_work, NULL, NULL);
btrfs_queue_work(plug->info->rmw_workers,
&plug->work);
return;
/* FIXME we cannot handle this properly right now */
BUG();
}
- btrfs_init_work(&rmw->work, reada_start_machine_worker, NULL, NULL);
+ btrfs_init_work(&rmw->work, btrfs_readahead_helper,
+ reada_start_machine_worker, NULL, NULL);
rmw->fs_info = fs_info;
btrfs_queue_work(fs_info->readahead_workers, &rmw->work);
sbio->index = i;
sbio->sctx = sctx;
sbio->page_count = 0;
- btrfs_init_work(&sbio->work, scrub_bio_end_io_worker,
- NULL, NULL);
+ btrfs_init_work(&sbio->work, btrfs_scrub_helper,
+ scrub_bio_end_io_worker, NULL, NULL);
if (i != SCRUB_BIOS_PER_SCTX - 1)
sctx->bios[i]->next_free = i + 1;
fixup_nodatasum->root = fs_info->extent_root;
fixup_nodatasum->mirror_num = failed_mirror_index + 1;
scrub_pending_trans_workers_inc(sctx);
- btrfs_init_work(&fixup_nodatasum->work, scrub_fixup_nodatasum,
- NULL, NULL);
+ btrfs_init_work(&fixup_nodatasum->work, btrfs_scrub_helper,
+ scrub_fixup_nodatasum, NULL, NULL);
btrfs_queue_work(fs_info->scrub_workers,
&fixup_nodatasum->work);
goto out;
sbio->err = err;
sbio->bio = bio;
- btrfs_init_work(&sbio->work, scrub_wr_bio_end_io_worker, NULL, NULL);
+ btrfs_init_work(&sbio->work, btrfs_scrubwrc_helper,
+ scrub_wr_bio_end_io_worker, NULL, NULL);
btrfs_queue_work(fs_info->scrub_wr_completion_workers, &sbio->work);
}
struct scrub_ctx *sctx;
int ret;
struct btrfs_device *dev;
+ struct rcu_string *name;
if (btrfs_fs_closing(fs_info))
return -EINVAL;
return -ENODEV;
}
+ if (!is_dev_replace && !readonly && !dev->writeable) {
+ mutex_unlock(&fs_info->fs_devices->device_list_mutex);
+ rcu_read_lock();
+ name = rcu_dereference(dev->name);
+ btrfs_err(fs_info, "scrub: device %s is not writable",
+ name->str);
+ rcu_read_unlock();
+ return -EROFS;
+ }
+
mutex_lock(&fs_info->scrub_lock);
if (!dev->in_fs_metadata || dev->is_tgtdev_for_dev_replace) {
mutex_unlock(&fs_info->scrub_lock);
nocow_ctx->len = len;
nocow_ctx->mirror_num = mirror_num;
nocow_ctx->physical_for_dev_replace = physical_for_dev_replace;
- btrfs_init_work(&nocow_ctx->work, copy_nocow_pages_worker, NULL, NULL);
+ btrfs_init_work(&nocow_ctx->work, btrfs_scrubnc_helper,
+ copy_nocow_pages_worker, NULL, NULL);
INIT_LIST_HEAD(&nocow_ctx->inodes);
btrfs_queue_work(fs_info->scrub_nocow_workers,
&nocow_ctx->work);
if (!fs_info->device_dir_kobj)
return -EINVAL;
- if (one_device) {
+ if (one_device && one_device->bdev) {
disk = one_device->bdev->bd_part;
disk_kobj = &part_to_dev(disk)->kobj;
struct list_head ordered_sums;
int skip_csum = BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM;
bool has_extents = false;
- bool need_find_last_extent = (*last_extent == 0);
+ bool need_find_last_extent = true;
bool done = false;
INIT_LIST_HEAD(&ordered_sums);
*/
if (ins_keys[i].type == BTRFS_EXTENT_DATA_KEY) {
has_extents = true;
- if (need_find_last_extent &&
- first_key.objectid == (u64)-1)
+ if (first_key.objectid == (u64)-1)
first_key = ins_keys[i];
} else {
need_find_last_extent = false;
if (!has_extents)
return ret;
+ if (need_find_last_extent && *last_extent == first_key.offset) {
+ /*
+ * We don't have any leafs between our current one and the one
+ * we processed before that can have file extent items for our
+ * inode (and have a generation number smaller than our current
+ * transaction id).
+ */
+ need_find_last_extent = false;
+ }
+
/*
* Because we use btrfs_search_forward we could skip leaves that were
* not modified and then assume *last_extent is valid when it really
0, 0);
if (ret)
break;
- *last_extent = offset + len;
+ *last_extent = extent_end;
}
/*
* Need to let the callers know we dropped the path so they should
ret = 1;
device->fs_devices = fs_devices;
} else if (!device->name || strcmp(device->name->str, path)) {
+ /*
+ * When FS is already mounted.
+ * 1. If you are here and if the device->name is NULL that
+ * means this device was missing at time of FS mount.
+ * 2. If you are here and if the device->name is different
+ * from 'path' that means either
+ * a. The same device disappeared and reappeared with
+ * different name. or
+ * b. The missing-disk-which-was-replaced, has
+ * reappeared now.
+ *
+ * We must allow 1 and 2a above. But 2b would be a spurious
+ * and unintentional.
+ *
+ * Further in case of 1 and 2a above, the disk at 'path'
+ * would have missed some transaction when it was away and
+ * in case of 2a the stale bdev has to be updated as well.
+ * 2b must not be allowed at all time.
+ */
+
+ /*
+ * As of now don't allow update to btrfs_fs_device through
+ * the btrfs dev scan cli, after FS has been mounted.
+ */
+ if (fs_devices->opened) {
+ return -EBUSY;
+ } else {
+ /*
+ * That is if the FS is _not_ mounted and if you
+ * are here, that means there is more than one
+ * disk with same uuid and devid.We keep the one
+ * with larger generation number or the last-in if
+ * generation are equal.
+ */
+ if (found_transid < device->generation)
+ return -EEXIST;
+ }
+
name = rcu_string_strdup(path, GFP_NOFS);
if (!name)
return -ENOMEM;
}
}
+ /*
+ * Unmount does not free the btrfs_device struct but would zero
+ * generation along with most of the other members. So just update
+ * it back. We need it to pick the disk with largest generation
+ * (as above).
+ */
+ if (!fs_devices->opened)
+ device->generation = found_transid;
+
if (found_transid > fs_devices->latest_trans) {
fs_devices->latest_devid = devid;
fs_devices->latest_trans = found_transid;
btrfs_set_device_io_align(leaf, dev_item, device->io_align);
btrfs_set_device_io_width(leaf, dev_item, device->io_width);
btrfs_set_device_sector_size(leaf, dev_item, device->sector_size);
- btrfs_set_device_total_bytes(leaf, dev_item, device->total_bytes);
+ btrfs_set_device_total_bytes(leaf, dev_item, device->disk_total_bytes);
btrfs_set_device_bytes_used(leaf, dev_item, device->bytes_used);
btrfs_set_device_group(leaf, dev_item, 0);
btrfs_set_device_seek_speed(leaf, dev_item, 0);
device->fs_devices->total_devices--;
if (device->missing)
- root->fs_info->fs_devices->missing_devices--;
+ device->fs_devices->missing_devices--;
next_device = list_entry(root->fs_info->fs_devices->devices.next,
struct btrfs_device, dev_list);
if (srcdev->bdev) {
fs_info->fs_devices->open_devices--;
- /* zero out the old super */
- btrfs_scratch_superblock(srcdev);
+ /*
+ * zero out the old super if it is not writable
+ * (e.g. seed device)
+ */
+ if (srcdev->writeable)
+ btrfs_scratch_superblock(srcdev);
}
call_rcu(&srcdev->rcu, free_device);
fs_devices->seeding = 0;
fs_devices->num_devices = 0;
fs_devices->open_devices = 0;
+ fs_devices->missing_devices = 0;
+ fs_devices->num_can_discard = 0;
+ fs_devices->rotating = 0;
fs_devices->seed = seed_devices;
generate_random_uuid(fs_devices->fsid);
else
generate_random_uuid(dev->uuid);
- btrfs_init_work(&dev->work, pending_bios_fn, NULL, NULL);
+ btrfs_init_work(&dev->work, btrfs_submit_helper,
+ pending_bios_fn, NULL, NULL);
return dev;
}
/*
* Special error return code only used by dx_probe() and its callers.
*/
-#define ERR_BAD_DX_DIR -75000
+#define ERR_BAD_DX_DIR (-(MAX_ERRNO - 1))
/*
* Timeout and state flag for lazy initialization inode thread.
up_write(&EXT4_I(inode)->i_data_sem);
}
+/* Update i_size, i_disksize. Requires i_mutex to avoid races with truncate */
+static inline int ext4_update_inode_size(struct inode *inode, loff_t newsize)
+{
+ int changed = 0;
+
+ if (newsize > inode->i_size) {
+ i_size_write(inode, newsize);
+ changed = 1;
+ }
+ if (newsize > EXT4_I(inode)->i_disksize) {
+ ext4_update_i_disksize(inode, newsize);
+ changed |= 2;
+ }
+ return changed;
+}
+
struct ext4_group_info {
unsigned long bb_state;
struct rb_root bb_free_root;
}
static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset,
- ext4_lblk_t len, int flags, int mode)
+ ext4_lblk_t len, loff_t new_size,
+ int flags, int mode)
{
struct inode *inode = file_inode(file);
handle_t *handle;
int retries = 0;
struct ext4_map_blocks map;
unsigned int credits;
+ loff_t epos;
map.m_lblk = offset;
+ map.m_len = len;
/*
* Don't normalize the request if it can fit in one extent so
* that it doesn't get unnecessarily split into multiple
credits = ext4_chunk_trans_blocks(inode, len);
retry:
- while (ret >= 0 && ret < len) {
- map.m_lblk = map.m_lblk + ret;
- map.m_len = len = len - ret;
+ while (ret >= 0 && len) {
handle = ext4_journal_start(inode, EXT4_HT_MAP_BLOCKS,
credits);
if (IS_ERR(handle)) {
ret2 = ext4_journal_stop(handle);
break;
}
+ map.m_lblk += ret;
+ map.m_len = len = len - ret;
+ epos = (loff_t)map.m_lblk << inode->i_blkbits;
+ inode->i_ctime = ext4_current_time(inode);
+ if (new_size) {
+ if (epos > new_size)
+ epos = new_size;
+ if (ext4_update_inode_size(inode, epos) & 0x1)
+ inode->i_mtime = inode->i_ctime;
+ } else {
+ if (epos > inode->i_size)
+ ext4_set_inode_flag(inode,
+ EXT4_INODE_EOFBLOCKS);
+ }
+ ext4_mark_inode_dirty(handle, inode);
ret2 = ext4_journal_stop(handle);
if (ret2)
break;
loff_t new_size = 0;
int ret = 0;
int flags;
- int partial;
+ int credits;
+ int partial_begin, partial_end;
loff_t start, end;
ext4_lblk_t lblk;
struct address_space *mapping = inode->i_mapping;
if (start < offset || end > offset + len)
return -EINVAL;
- partial = (offset + len) & ((1 << blkbits) - 1);
+ partial_begin = offset & ((1 << blkbits) - 1);
+ partial_end = (offset + len) & ((1 << blkbits) - 1);
lblk = start >> blkbits;
max_blocks = (end >> blkbits);
* If we have a partial block after EOF we have to allocate
* the entire block.
*/
- if (partial)
+ if (partial_end)
max_blocks += 1;
}
/* Now release the pages and zero block aligned part of pages*/
truncate_pagecache_range(inode, start, end - 1);
+ inode->i_mtime = inode->i_ctime = ext4_current_time(inode);
/* Wait all existing dio workers, newcomers will block on i_mutex */
ext4_inode_block_unlocked_dio(inode);
if (ret)
goto out_dio;
- ret = ext4_alloc_file_blocks(file, lblk, max_blocks, flags,
- mode);
+ ret = ext4_alloc_file_blocks(file, lblk, max_blocks, new_size,
+ flags, mode);
if (ret)
goto out_dio;
}
+ if (!partial_begin && !partial_end)
+ goto out_dio;
- handle = ext4_journal_start(inode, EXT4_HT_MISC, 4);
+ /*
+ * In worst case we have to writeout two nonadjacent unwritten
+ * blocks and update the inode
+ */
+ credits = (2 * ext4_ext_index_trans_blocks(inode, 2)) + 1;
+ if (ext4_should_journal_data(inode))
+ credits += 2;
+ handle = ext4_journal_start(inode, EXT4_HT_MISC, credits);
if (IS_ERR(handle)) {
ret = PTR_ERR(handle);
ext4_std_error(inode->i_sb, ret);
}
inode->i_mtime = inode->i_ctime = ext4_current_time(inode);
-
if (new_size) {
- if (new_size > i_size_read(inode))
- i_size_write(inode, new_size);
- if (new_size > EXT4_I(inode)->i_disksize)
- ext4_update_i_disksize(inode, new_size);
+ ext4_update_inode_size(inode, new_size);
} else {
/*
* Mark that we allocate beyond EOF so the subsequent truncate
if ((offset + len) > i_size_read(inode))
ext4_set_inode_flag(inode, EXT4_INODE_EOFBLOCKS);
}
-
ext4_mark_inode_dirty(handle, inode);
/* Zero out partial block at the edges of the range */
long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
{
struct inode *inode = file_inode(file);
- handle_t *handle;
loff_t new_size = 0;
unsigned int max_blocks;
int ret = 0;
int flags;
ext4_lblk_t lblk;
- struct timespec tv;
unsigned int blkbits = inode->i_blkbits;
/* Return error if mode is not supported */
goto out;
}
- ret = ext4_alloc_file_blocks(file, lblk, max_blocks, flags, mode);
+ ret = ext4_alloc_file_blocks(file, lblk, max_blocks, new_size,
+ flags, mode);
if (ret)
goto out;
- handle = ext4_journal_start(inode, EXT4_HT_INODE, 2);
- if (IS_ERR(handle))
- goto out;
-
- tv = inode->i_ctime = ext4_current_time(inode);
-
- if (new_size) {
- if (new_size > i_size_read(inode)) {
- i_size_write(inode, new_size);
- inode->i_mtime = tv;
- }
- if (new_size > EXT4_I(inode)->i_disksize)
- ext4_update_i_disksize(inode, new_size);
- } else {
- /*
- * Mark that we allocate beyond EOF so the subsequent truncate
- * can proceed even if the new size is the same as i_size.
- */
- if ((offset + len) > i_size_read(inode))
- ext4_set_inode_flag(inode, EXT4_INODE_EOFBLOCKS);
+ if (file->f_flags & O_SYNC && EXT4_SB(inode->i_sb)->s_journal) {
+ ret = jbd2_complete_transaction(EXT4_SB(inode->i_sb)->s_journal,
+ EXT4_I(inode)->i_sync_tid);
}
- ext4_mark_inode_dirty(handle, inode);
- if (file->f_flags & O_SYNC)
- ext4_handle_sync(handle);
-
- ext4_journal_stop(handle);
out:
mutex_unlock(&inode->i_mutex);
trace_ext4_fallocate_exit(inode, offset, max_blocks, ret);
} else
copied = block_write_end(file, mapping, pos,
len, copied, page, fsdata);
-
/*
- * No need to use i_size_read() here, the i_size
- * cannot change under us because we hole i_mutex.
- *
- * But it's important to update i_size while still holding page lock:
+ * it's important to update i_size while still holding page lock:
* page writeout could otherwise come in and zero beyond i_size.
*/
- if (pos + copied > inode->i_size) {
- i_size_write(inode, pos + copied);
- i_size_changed = 1;
- }
-
- if (pos + copied > EXT4_I(inode)->i_disksize) {
- /* We need to mark inode dirty even if
- * new_i_size is less that inode->i_size
- * but greater than i_disksize. (hint delalloc)
- */
- ext4_update_i_disksize(inode, (pos + copied));
- i_size_changed = 1;
- }
+ i_size_changed = ext4_update_inode_size(inode, pos + copied);
unlock_page(page);
page_cache_release(page);
int ret = 0, ret2;
int partial = 0;
unsigned from, to;
- loff_t new_i_size;
+ int size_changed = 0;
trace_ext4_journalled_write_end(inode, pos, len, copied);
from = pos & (PAGE_CACHE_SIZE - 1);
if (!partial)
SetPageUptodate(page);
}
- new_i_size = pos + copied;
- if (new_i_size > inode->i_size)
- i_size_write(inode, pos+copied);
+ size_changed = ext4_update_inode_size(inode, pos + copied);
ext4_set_inode_state(inode, EXT4_STATE_JDATA);
EXT4_I(inode)->i_datasync_tid = handle->h_transaction->t_tid;
- if (new_i_size > EXT4_I(inode)->i_disksize) {
- ext4_update_i_disksize(inode, new_i_size);
+ unlock_page(page);
+ page_cache_release(page);
+
+ if (size_changed) {
ret2 = ext4_mark_inode_dirty(handle, inode);
if (!ret)
ret = ret2;
}
- unlock_page(page);
- page_cache_release(page);
if (pos + len > inode->i_size && ext4_can_truncate(inode))
/* if we have allocated more blocks and copied
* less. We will have blocks allocated outside
struct ext4_map_blocks *map = &mpd->map;
int err;
loff_t disksize;
+ int progress = 0;
mpd->io_submit.io_end->offset =
((loff_t)map->m_lblk) << inode->i_blkbits;
* is non-zero, a commit should free up blocks.
*/
if ((err == -ENOMEM) ||
- (err == -ENOSPC && ext4_count_free_clusters(sb)))
+ (err == -ENOSPC && ext4_count_free_clusters(sb))) {
+ if (progress)
+ goto update_disksize;
return err;
+ }
ext4_msg(sb, KERN_CRIT,
"Delayed block allocation failed for "
"inode %lu at logical offset %llu with"
*give_up_on_write = true;
return err;
}
+ progress = 1;
/*
* Update buffer state, submit mapped pages, and get us new
* extent to map
*/
err = mpage_map_and_submit_buffers(mpd);
if (err < 0)
- return err;
+ goto update_disksize;
} while (map->m_len);
+update_disksize:
/*
* Update on-disk size after IO is submitted. Races with
* truncate are avoided by checking i_size under i_data_sem.
int last = first + count - 1;
struct super_block *sb = e4b->bd_sb;
+ if (WARN_ON(count == 0))
+ return;
BUG_ON(last >= (sb->s_blocksize << 3));
assert_spin_locked(ext4_group_lock_ptr(sb, e4b->bd_group));
/* Don't bother if the block group is corrupt. */
int err;
if (pa == NULL) {
+ if (ac->ac_f_ex.fe_len == 0)
+ return;
err = ext4_mb_load_buddy(ac->ac_sb, ac->ac_f_ex.fe_group, &e4b);
if (err) {
/*
mb_free_blocks(ac->ac_inode, &e4b, ac->ac_f_ex.fe_start,
ac->ac_f_ex.fe_len);
ext4_unlock_group(ac->ac_sb, ac->ac_f_ex.fe_group);
+ ext4_mb_unload_buddy(&e4b);
return;
}
if (pa->pa_type == MB_INODE_PA)
buffer */
int num = 0;
ext4_lblk_t nblocks;
- int i, err;
+ int i, err = 0;
int namelen;
*res_dir = NULL;
* return. Otherwise, fall back to doing a search the
* old fashioned way.
*/
- if (bh || (err != ERR_BAD_DX_DIR))
+ if (err == -ENOENT)
+ return NULL;
+ if (err && err != ERR_BAD_DX_DIR)
+ return ERR_PTR(err);
+ if (bh)
return bh;
dxtrace(printk(KERN_DEBUG "ext4_find_entry: dx failed, "
"falling back\n"));
}
num++;
bh = ext4_getblk(NULL, dir, b++, 0, &err);
+ if (unlikely(err)) {
+ if (ra_max == 0)
+ return ERR_PTR(err);
+ break;
+ }
bh_use[ra_max] = bh;
if (bh)
ll_rw_block(READ | REQ_META | REQ_PRIO,
return ERR_PTR(-ENAMETOOLONG);
bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL);
+ if (IS_ERR(bh))
+ return (struct dentry *) bh;
inode = NULL;
if (bh) {
__u32 ino = le32_to_cpu(de->inode);
struct buffer_head *bh;
bh = ext4_find_entry(child->d_inode, &dotdot, &de, NULL);
+ if (IS_ERR(bh))
+ return (struct dentry *) bh;
if (!bh)
return ERR_PTR(-ENOENT);
ino = le32_to_cpu(de->inode);
retval = -ENOENT;
bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL);
+ if (IS_ERR(bh))
+ return PTR_ERR(bh);
if (!bh)
goto end_rmdir;
retval = -ENOENT;
bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL);
+ if (IS_ERR(bh))
+ return PTR_ERR(bh);
if (!bh)
goto end_unlink;
struct ext4_dir_entry_2 *de;
bh = ext4_find_entry(dir, d_name, &de, NULL);
+ if (IS_ERR(bh))
+ return PTR_ERR(bh);
if (bh) {
retval = ext4_delete_entry(handle, dir, de, bh);
brelse(bh);
return retval;
}
-static void ext4_rename_delete(handle_t *handle, struct ext4_renament *ent)
+static void ext4_rename_delete(handle_t *handle, struct ext4_renament *ent,
+ int force_reread)
{
int retval;
/*
if (le32_to_cpu(ent->de->inode) != ent->inode->i_ino ||
ent->de->name_len != ent->dentry->d_name.len ||
strncmp(ent->de->name, ent->dentry->d_name.name,
- ent->de->name_len)) {
+ ent->de->name_len) ||
+ force_reread) {
retval = ext4_find_delete_entry(handle, ent->dir,
&ent->dentry->d_name);
} else {
.dentry = new_dentry,
.inode = new_dentry->d_inode,
};
+ int force_reread;
int retval;
dquot_initialize(old.dir);
dquot_initialize(new.inode);
old.bh = ext4_find_entry(old.dir, &old.dentry->d_name, &old.de, NULL);
+ if (IS_ERR(old.bh))
+ return PTR_ERR(old.bh);
/*
* Check for inode number is _not_ due to possible IO errors.
* We might rmdir the source, keep it as pwd of some process
new.bh = ext4_find_entry(new.dir, &new.dentry->d_name,
&new.de, &new.inlined);
+ if (IS_ERR(new.bh)) {
+ retval = PTR_ERR(new.bh);
+ goto end_rename;
+ }
if (new.bh) {
if (!new.inode) {
brelse(new.bh);
if (retval)
goto end_rename;
}
+ /*
+ * If we're renaming a file within an inline_data dir and adding or
+ * setting the new dirent causes a conversion from inline_data to
+ * extents/blockmap, we need to force the dirent delete code to
+ * re-read the directory, or else we end up trying to delete a dirent
+ * from what is now the extent tree root (or a block map).
+ */
+ force_reread = (new.dir->i_ino == old.dir->i_ino &&
+ ext4_test_inode_flag(new.dir, EXT4_INODE_INLINE_DATA));
if (!new.bh) {
retval = ext4_add_entry(handle, new.dentry, old.inode);
if (retval)
if (retval)
goto end_rename;
}
+ if (force_reread)
+ force_reread = !ext4_test_inode_flag(new.dir,
+ EXT4_INODE_INLINE_DATA);
/*
* Like most other Unix systems, set the ctime for inodes on a
/*
* ok, that's it
*/
- ext4_rename_delete(handle, &old);
+ ext4_rename_delete(handle, &old, force_reread);
if (new.inode) {
ext4_dec_count(handle, new.inode);
old.bh = ext4_find_entry(old.dir, &old.dentry->d_name,
&old.de, &old.inlined);
+ if (IS_ERR(old.bh))
+ return PTR_ERR(old.bh);
/*
* Check for inode number is _not_ due to possible IO errors.
* We might rmdir the source, keep it as pwd of some process
new.bh = ext4_find_entry(new.dir, &new.dentry->d_name,
&new.de, &new.inlined);
+ if (IS_ERR(new.bh)) {
+ retval = PTR_ERR(new.bh);
+ goto end_rename;
+ }
/* RENAME_EXCHANGE case: old *and* new must both exist */
if (!new.bh || le32_to_cpu(new.de->inode) != new.inode->i_ino)
if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
- /* journal checksum v2 */
+ /* journal checksum v3 */
compat = 0;
- incompat = JBD2_FEATURE_INCOMPAT_CSUM_V2;
+ incompat = JBD2_FEATURE_INCOMPAT_CSUM_V3;
} else {
/* journal checksum v1 */
compat = JBD2_FEATURE_COMPAT_CHECKSUM;
jbd2_journal_clear_features(sbi->s_journal,
JBD2_FEATURE_COMPAT_CHECKSUM, 0,
JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT |
+ JBD2_FEATURE_INCOMPAT_CSUM_V3 |
JBD2_FEATURE_INCOMPAT_CSUM_V2);
}
struct commit_header *h;
__u32 csum;
- if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+ if (!jbd2_journal_has_csum_v2or3(j))
return;
h = (struct commit_header *)(bh->b_data);
return checksum;
}
-static void write_tag_block(int tag_bytes, journal_block_tag_t *tag,
+static void write_tag_block(journal_t *j, journal_block_tag_t *tag,
unsigned long long block)
{
tag->t_blocknr = cpu_to_be32(block & (u32)~0);
- if (tag_bytes > JBD2_TAG_SIZE32)
+ if (JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_64BIT))
tag->t_blocknr_high = cpu_to_be32((block >> 31) >> 1);
}
struct jbd2_journal_block_tail *tail;
__u32 csum;
- if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+ if (!jbd2_journal_has_csum_v2or3(j))
return;
tail = (struct jbd2_journal_block_tail *)(bh->b_data + j->j_blocksize -
static void jbd2_block_tag_csum_set(journal_t *j, journal_block_tag_t *tag,
struct buffer_head *bh, __u32 sequence)
{
+ journal_block_tag3_t *tag3 = (journal_block_tag3_t *)tag;
struct page *page = bh->b_page;
__u8 *addr;
__u32 csum32;
__be32 seq;
- if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+ if (!jbd2_journal_has_csum_v2or3(j))
return;
seq = cpu_to_be32(sequence);
bh->b_size);
kunmap_atomic(addr);
- /* We only have space to store the lower 16 bits of the crc32c. */
- tag->t_checksum = cpu_to_be16(csum32);
+ if (JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V3))
+ tag3->t_checksum = cpu_to_be32(csum32);
+ else
+ tag->t_checksum = cpu_to_be16(csum32);
}
/*
* jbd2_journal_commit_transaction
LIST_HEAD(io_bufs);
LIST_HEAD(log_bufs);
- if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+ if (jbd2_journal_has_csum_v2or3(journal))
csum_size = sizeof(struct jbd2_journal_block_tail);
/*
tag_flag |= JBD2_FLAG_SAME_UUID;
tag = (journal_block_tag_t *) tagp;
- write_tag_block(tag_bytes, tag, jh2bh(jh)->b_blocknr);
+ write_tag_block(journal, tag, jh2bh(jh)->b_blocknr);
tag->t_flags = cpu_to_be16(tag_flag);
jbd2_block_tag_csum_set(journal, tag, wbuf[bufs],
commit_transaction->t_tid);
/* Checksumming functions */
static int jbd2_verify_csum_type(journal_t *j, journal_superblock_t *sb)
{
- if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+ if (!jbd2_journal_has_csum_v2or3(j))
return 1;
return sb->s_checksum_type == JBD2_CRC32C_CHKSUM;
static int jbd2_superblock_csum_verify(journal_t *j, journal_superblock_t *sb)
{
- if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+ if (!jbd2_journal_has_csum_v2or3(j))
return 1;
return sb->s_checksum == jbd2_superblock_csum(j, sb);
static void jbd2_superblock_csum_set(journal_t *j, journal_superblock_t *sb)
{
- if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+ if (!jbd2_journal_has_csum_v2or3(j))
return;
sb->s_checksum = jbd2_superblock_csum(j, sb);
goto out;
}
- if (JBD2_HAS_COMPAT_FEATURE(journal, JBD2_FEATURE_COMPAT_CHECKSUM) &&
- JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2)) {
+ if (jbd2_journal_has_csum_v2or3(journal) &&
+ JBD2_HAS_COMPAT_FEATURE(journal, JBD2_FEATURE_COMPAT_CHECKSUM)) {
/* Can't have checksum v1 and v2 on at the same time! */
printk(KERN_ERR "JBD2: Can't enable checksumming v1 and v2 "
"at the same time!\n");
goto out;
}
+ if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2) &&
+ JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V3)) {
+ /* Can't have checksum v2 and v3 at the same time! */
+ printk(KERN_ERR "JBD2: Can't enable checksumming v2 and v3 "
+ "at the same time!\n");
+ goto out;
+ }
+
if (!jbd2_verify_csum_type(journal, sb)) {
printk(KERN_ERR "JBD2: Unknown checksum type\n");
goto out;
}
/* Load the checksum driver */
- if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2)) {
+ if (jbd2_journal_has_csum_v2or3(journal)) {
journal->j_chksum_driver = crypto_alloc_shash("crc32c", 0, 0);
if (IS_ERR(journal->j_chksum_driver)) {
printk(KERN_ERR "JBD2: Cannot load crc32c driver.\n");
}
/* Precompute checksum seed for all metadata */
- if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+ if (jbd2_journal_has_csum_v2or3(journal))
journal->j_csum_seed = jbd2_chksum(journal, ~0, sb->s_uuid,
sizeof(sb->s_uuid));
if (!jbd2_journal_check_available_features(journal, compat, ro, incompat))
return 0;
- /* Asking for checksumming v2 and v1? Only give them v2. */
- if (incompat & JBD2_FEATURE_INCOMPAT_CSUM_V2 &&
+ /* If enabling v2 checksums, turn on v3 instead */
+ if (incompat & JBD2_FEATURE_INCOMPAT_CSUM_V2) {
+ incompat &= ~JBD2_FEATURE_INCOMPAT_CSUM_V2;
+ incompat |= JBD2_FEATURE_INCOMPAT_CSUM_V3;
+ }
+
+ /* Asking for checksumming v3 and v1? Only give them v3. */
+ if (incompat & JBD2_FEATURE_INCOMPAT_CSUM_V3 &&
compat & JBD2_FEATURE_COMPAT_CHECKSUM)
compat &= ~JBD2_FEATURE_COMPAT_CHECKSUM;
sb = journal->j_superblock;
- /* If enabling v2 checksums, update superblock */
- if (INCOMPAT_FEATURE_ON(JBD2_FEATURE_INCOMPAT_CSUM_V2)) {
+ /* If enabling v3 checksums, update superblock */
+ if (INCOMPAT_FEATURE_ON(JBD2_FEATURE_INCOMPAT_CSUM_V3)) {
sb->s_checksum_type = JBD2_CRC32C_CHKSUM;
sb->s_feature_compat &=
~cpu_to_be32(JBD2_FEATURE_COMPAT_CHECKSUM);
}
/* Precompute checksum seed for all metadata */
- if (JBD2_HAS_INCOMPAT_FEATURE(journal,
- JBD2_FEATURE_INCOMPAT_CSUM_V2))
+ if (jbd2_journal_has_csum_v2or3(journal))
journal->j_csum_seed = jbd2_chksum(journal, ~0,
sb->s_uuid,
sizeof(sb->s_uuid));
/* If enabling v1 checksums, downgrade superblock */
if (COMPAT_FEATURE_ON(JBD2_FEATURE_COMPAT_CHECKSUM))
sb->s_feature_incompat &=
- ~cpu_to_be32(JBD2_FEATURE_INCOMPAT_CSUM_V2);
+ ~cpu_to_be32(JBD2_FEATURE_INCOMPAT_CSUM_V2 |
+ JBD2_FEATURE_INCOMPAT_CSUM_V3);
sb->s_feature_compat |= cpu_to_be32(compat);
sb->s_feature_ro_compat |= cpu_to_be32(ro);
*/
size_t journal_tag_bytes(journal_t *journal)
{
- journal_block_tag_t tag;
- size_t x = 0;
+ size_t sz;
+
+ if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V3))
+ return sizeof(journal_block_tag3_t);
+
+ sz = sizeof(journal_block_tag_t);
if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2))
- x += sizeof(tag.t_checksum);
+ sz += sizeof(__u16);
if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT))
- return x + JBD2_TAG_SIZE64;
+ return sz;
else
- return x + JBD2_TAG_SIZE32;
+ return sz - sizeof(__u32);
}
/*
__be32 provided;
__u32 calculated;
- if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+ if (!jbd2_journal_has_csum_v2or3(j))
return 1;
tail = (struct jbd2_journal_block_tail *)(buf + j->j_blocksize -
int nr = 0, size = journal->j_blocksize;
int tag_bytes = journal_tag_bytes(journal);
- if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+ if (jbd2_journal_has_csum_v2or3(journal))
size -= sizeof(struct jbd2_journal_block_tail);
tagp = &bh->b_data[sizeof(journal_header_t)];
return err;
}
-static inline unsigned long long read_tag_block(int tag_bytes, journal_block_tag_t *tag)
+static inline unsigned long long read_tag_block(journal_t *journal,
+ journal_block_tag_t *tag)
{
unsigned long long block = be32_to_cpu(tag->t_blocknr);
- if (tag_bytes > JBD2_TAG_SIZE32)
+ if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT))
block |= (u64)be32_to_cpu(tag->t_blocknr_high) << 32;
return block;
}
__be32 provided;
__u32 calculated;
- if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+ if (!jbd2_journal_has_csum_v2or3(j))
return 1;
h = buf;
static int jbd2_block_tag_csum_verify(journal_t *j, journal_block_tag_t *tag,
void *buf, __u32 sequence)
{
+ journal_block_tag3_t *tag3 = (journal_block_tag3_t *)tag;
__u32 csum32;
__be32 seq;
- if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+ if (!jbd2_journal_has_csum_v2or3(j))
return 1;
seq = cpu_to_be32(sequence);
csum32 = jbd2_chksum(j, j->j_csum_seed, (__u8 *)&seq, sizeof(seq));
csum32 = jbd2_chksum(j, csum32, buf, j->j_blocksize);
- return tag->t_checksum == cpu_to_be16(csum32);
+ if (JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V3))
+ return tag3->t_checksum == cpu_to_be32(csum32);
+ else
+ return tag->t_checksum == cpu_to_be16(csum32);
}
static int do_one_pass(journal_t *journal,
int tag_bytes = journal_tag_bytes(journal);
__u32 crc32_sum = ~0; /* Transactional Checksums */
int descr_csum_size = 0;
+ int block_error = 0;
/*
* First thing is to establish what we expect to find in the log
switch(blocktype) {
case JBD2_DESCRIPTOR_BLOCK:
/* Verify checksum first */
- if (JBD2_HAS_INCOMPAT_FEATURE(journal,
- JBD2_FEATURE_INCOMPAT_CSUM_V2))
+ if (jbd2_journal_has_csum_v2or3(journal))
descr_csum_size =
sizeof(struct jbd2_journal_block_tail);
if (descr_csum_size > 0 &&
unsigned long long blocknr;
J_ASSERT(obh != NULL);
- blocknr = read_tag_block(tag_bytes,
+ blocknr = read_tag_block(journal,
tag);
/* If the block has been
"checksum recovering "
"block %llu in log\n",
blocknr);
- continue;
+ block_error = 1;
+ goto skip_write;
}
/* Find a buffer for the new
success = -EIO;
}
}
-
+ if (block_error && success == 0)
+ success = -EIO;
return success;
failed:
__be32 provided;
__u32 calculated;
- if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+ if (!jbd2_journal_has_csum_v2or3(j))
return 1;
tail = (struct jbd2_journal_revoke_tail *)(buf + j->j_blocksize -
#include <linux/list.h>
#include <linux/init.h>
#include <linux/bio.h>
-#endif
#include <linux/log2.h>
+#endif
static struct kmem_cache *jbd2_revoke_record_cache;
static struct kmem_cache *jbd2_revoke_table_cache;
offset = *offsetp;
/* Do we need to leave space at the end for a checksum? */
- if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+ if (jbd2_journal_has_csum_v2or3(journal))
csum_size = sizeof(struct jbd2_journal_revoke_tail);
/* Make sure we have a descriptor with space left for the record */
struct jbd2_journal_revoke_tail *tail;
__u32 csum;
- if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2))
+ if (!jbd2_journal_has_csum_v2or3(j))
return;
tail = (struct jbd2_journal_revoke_tail *)(bh->b_data + j->j_blocksize -
smp_mb();
error = check_conflicting_open(dentry, arg);
if (error)
- locks_unlink_lock(flp);
+ locks_unlink_lock(before);
out:
if (is_deleg)
mutex_unlock(&inode->i_mutex);
.rpc_argp = &args,
.rpc_resp = &fattr,
};
- int status;
+ int status = 0;
+
+ if (acl == NULL && (!S_ISDIR(inode->i_mode) || dfacl == NULL))
+ goto out;
status = -EOPNOTSUPP;
if (!nfs_server_capable(inode, NFS_CAP_ACLS))
struct nfs4_closedata *calldata = data;
struct nfs4_state *state = calldata->state;
struct nfs_server *server = NFS_SERVER(calldata->inode);
+ nfs4_stateid *res_stateid = NULL;
dprintk("%s: begin!\n", __func__);
if (!nfs4_sequence_done(task, &calldata->res.seq_res))
*/
switch (task->tk_status) {
case 0:
- if (calldata->roc)
+ res_stateid = &calldata->res.stateid;
+ if (calldata->arg.fmode == 0 && calldata->roc)
pnfs_roc_set_barrier(state->inode,
calldata->roc_barrier);
- nfs_clear_open_stateid(state, &calldata->res.stateid, 0);
renew_lease(server, calldata->timestamp);
- goto out_release;
+ break;
case -NFS4ERR_ADMIN_REVOKED:
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_OLD_STATEID:
goto out_release;
}
}
- nfs_clear_open_stateid(state, NULL, calldata->arg.fmode);
+ nfs_clear_open_stateid(state, res_stateid, calldata->arg.fmode);
out_release:
nfs_release_seqid(calldata->arg.seqid);
nfs_refresh_inode(calldata->inode, calldata->res.fattr);
struct nfs4_closedata *calldata = data;
struct nfs4_state *state = calldata->state;
struct inode *inode = calldata->inode;
+ bool is_rdonly, is_wronly, is_rdwr;
int call_close = 0;
dprintk("%s: begin!\n", __func__);
goto out_wait;
task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE];
- calldata->arg.fmode = FMODE_READ|FMODE_WRITE;
spin_lock(&state->owner->so_lock);
+ is_rdwr = test_bit(NFS_O_RDWR_STATE, &state->flags);
+ is_rdonly = test_bit(NFS_O_RDONLY_STATE, &state->flags);
+ is_wronly = test_bit(NFS_O_WRONLY_STATE, &state->flags);
+ /* Calculate the current open share mode */
+ calldata->arg.fmode = 0;
+ if (is_rdonly || is_rdwr)
+ calldata->arg.fmode |= FMODE_READ;
+ if (is_wronly || is_rdwr)
+ calldata->arg.fmode |= FMODE_WRITE;
/* Calculate the change in open mode */
if (state->n_rdwr == 0) {
if (state->n_rdonly == 0) {
- call_close |= test_bit(NFS_O_RDONLY_STATE, &state->flags);
- call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags);
+ call_close |= is_rdonly || is_rdwr;
calldata->arg.fmode &= ~FMODE_READ;
}
if (state->n_wronly == 0) {
- call_close |= test_bit(NFS_O_WRONLY_STATE, &state->flags);
- call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags);
+ call_close |= is_wronly || is_rdwr;
calldata->arg.fmode &= ~FMODE_WRITE;
}
}
}
out:
- spin_unlock(&qs->qs_lock);
- if (fence)
+ if (fence) {
+ spin_unlock(&qs->qs_lock);
o2quo_fence_self();
+ } else {
+ mlog(ML_NOTICE, "not fencing this node, heartbeating: %d, "
+ "connected: %d, lowest: %d (%sreachable)\n",
+ qs->qs_heartbeating, qs->qs_connected, lowest_hb,
+ lowest_reachable ? "" : "un");
+ spin_unlock(&qs->qs_lock);
+
+ }
+
}
static void o2quo_set_hold(struct o2quo_state *qs, u8 node)
return ret;
}
+static int o2net_set_usertimeout(struct socket *sock)
+{
+ int user_timeout = O2NET_TCP_USER_TIMEOUT;
+
+ return kernel_setsockopt(sock, SOL_TCP, TCP_USER_TIMEOUT,
+ (char *)&user_timeout, sizeof(user_timeout));
+}
+
static void o2net_initialize_handshake(void)
{
o2net_hand->o2hb_heartbeat_timeout_ms = cpu_to_be32(
#endif
printk(KERN_NOTICE "o2net: Connection to " SC_NODEF_FMT " has been "
- "idle for %lu.%lu secs, shutting it down.\n", SC_NODEF_ARGS(sc),
- msecs / 1000, msecs % 1000);
+ "idle for %lu.%lu secs.\n",
+ SC_NODEF_ARGS(sc), msecs / 1000, msecs % 1000);
- /*
- * Initialize the nn_timeout so that the next connection attempt
- * will continue in o2net_start_connect.
+ /* idle timerout happen, don't shutdown the connection, but
+ * make fence decision. Maybe the connection can recover before
+ * the decision is made.
*/
atomic_set(&nn->nn_timeout, 1);
+ o2quo_conn_err(o2net_num_from_nn(nn));
+ queue_delayed_work(o2net_wq, &nn->nn_still_up,
+ msecs_to_jiffies(O2NET_QUORUM_DELAY_MS));
+
+ o2net_sc_reset_idle_timer(sc);
- o2net_sc_queue_work(sc, &sc->sc_shutdown_work);
}
static void o2net_sc_reset_idle_timer(struct o2net_sock_container *sc)
static void o2net_sc_postpone_idle(struct o2net_sock_container *sc)
{
+ struct o2net_node *nn = o2net_nn_from_num(sc->sc_node->nd_num);
+
+ /* clear fence decision since the connection recover from timeout*/
+ if (atomic_read(&nn->nn_timeout)) {
+ o2quo_conn_up(o2net_num_from_nn(nn));
+ cancel_delayed_work(&nn->nn_still_up);
+ atomic_set(&nn->nn_timeout, 0);
+ }
+
/* Only push out an existing timer */
if (timer_pending(&sc->sc_idle_timeout))
o2net_sc_reset_idle_timer(sc);
goto out;
}
+ ret = o2net_set_usertimeout(sock);
+ if (ret) {
+ mlog(ML_ERROR, "set TCP_USER_TIMEOUT failed with %d\n", ret);
+ goto out;
+ }
+
o2net_register_callbacks(sc->sc_sock->sk, sc);
spin_lock(&nn->nn_lock);
goto out;
}
+ ret = o2net_set_usertimeout(new_sock);
+ if (ret) {
+ mlog(ML_ERROR, "set TCP_USER_TIMEOUT failed with %d\n", ret);
+ goto out;
+ }
+
slen = sizeof(sin);
ret = new_sock->ops->getname(new_sock, (struct sockaddr *) &sin,
&slen, 1);
#define O2NET_KEEPALIVE_DELAY_MS_DEFAULT 2000
#define O2NET_IDLE_TIMEOUT_MS_DEFAULT 30000
+#define O2NET_TCP_USER_TIMEOUT 0x7fffffff
/* TODO: figure this out.... */
static inline int o2net_link_down(int err, struct socket *sock)
copy_to_user((typeof(a) __user *)b, &(a), sizeof(a))
/*
- * This call is void because we are already reporting an error that may
- * be -EFAULT. The error will be returned from the ioctl(2) call. It's
- * just a best-effort to tell userspace that this request caused the error.
+ * This is just a best-effort to tell userspace that this request
+ * caused the error.
*/
static inline void o2info_set_request_error(struct ocfs2_info_request *kreq,
struct ocfs2_info_request __user *req)
static int ocfs2_info_handle_blocksize(struct inode *inode,
struct ocfs2_info_request __user *req)
{
- int status = -EFAULT;
struct ocfs2_info_blocksize oib;
if (o2info_from_user(oib, req))
- goto bail;
+ return -EFAULT;
oib.ib_blocksize = inode->i_sb->s_blocksize;
o2info_set_request_filled(&oib.ib_req);
if (o2info_to_user(oib, req))
- goto bail;
-
- status = 0;
-bail:
- if (status)
- o2info_set_request_error(&oib.ib_req, req);
+ return -EFAULT;
- return status;
+ return 0;
}
static int ocfs2_info_handle_clustersize(struct inode *inode,
struct ocfs2_info_request __user *req)
{
- int status = -EFAULT;
struct ocfs2_info_clustersize oic;
struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
if (o2info_from_user(oic, req))
- goto bail;
+ return -EFAULT;
oic.ic_clustersize = osb->s_clustersize;
o2info_set_request_filled(&oic.ic_req);
if (o2info_to_user(oic, req))
- goto bail;
-
- status = 0;
-bail:
- if (status)
- o2info_set_request_error(&oic.ic_req, req);
+ return -EFAULT;
- return status;
+ return 0;
}
static int ocfs2_info_handle_maxslots(struct inode *inode,
struct ocfs2_info_request __user *req)
{
- int status = -EFAULT;
struct ocfs2_info_maxslots oim;
struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
if (o2info_from_user(oim, req))
- goto bail;
+ return -EFAULT;
oim.im_max_slots = osb->max_slots;
o2info_set_request_filled(&oim.im_req);
if (o2info_to_user(oim, req))
- goto bail;
+ return -EFAULT;
- status = 0;
-bail:
- if (status)
- o2info_set_request_error(&oim.im_req, req);
-
- return status;
+ return 0;
}
static int ocfs2_info_handle_label(struct inode *inode,
struct ocfs2_info_request __user *req)
{
- int status = -EFAULT;
struct ocfs2_info_label oil;
struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
if (o2info_from_user(oil, req))
- goto bail;
+ return -EFAULT;
memcpy(oil.il_label, osb->vol_label, OCFS2_MAX_VOL_LABEL_LEN);
o2info_set_request_filled(&oil.il_req);
if (o2info_to_user(oil, req))
- goto bail;
+ return -EFAULT;
- status = 0;
-bail:
- if (status)
- o2info_set_request_error(&oil.il_req, req);
-
- return status;
+ return 0;
}
static int ocfs2_info_handle_uuid(struct inode *inode,
struct ocfs2_info_request __user *req)
{
- int status = -EFAULT;
struct ocfs2_info_uuid oiu;
struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
if (o2info_from_user(oiu, req))
- goto bail;
+ return -EFAULT;
memcpy(oiu.iu_uuid_str, osb->uuid_str, OCFS2_TEXT_UUID_LEN + 1);
o2info_set_request_filled(&oiu.iu_req);
if (o2info_to_user(oiu, req))
- goto bail;
-
- status = 0;
-bail:
- if (status)
- o2info_set_request_error(&oiu.iu_req, req);
+ return -EFAULT;
- return status;
+ return 0;
}
static int ocfs2_info_handle_fs_features(struct inode *inode,
struct ocfs2_info_request __user *req)
{
- int status = -EFAULT;
struct ocfs2_info_fs_features oif;
struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
if (o2info_from_user(oif, req))
- goto bail;
+ return -EFAULT;
oif.if_compat_features = osb->s_feature_compat;
oif.if_incompat_features = osb->s_feature_incompat;
o2info_set_request_filled(&oif.if_req);
if (o2info_to_user(oif, req))
- goto bail;
+ return -EFAULT;
- status = 0;
-bail:
- if (status)
- o2info_set_request_error(&oif.if_req, req);
-
- return status;
+ return 0;
}
static int ocfs2_info_handle_journal_size(struct inode *inode,
struct ocfs2_info_request __user *req)
{
- int status = -EFAULT;
struct ocfs2_info_journal_size oij;
struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
if (o2info_from_user(oij, req))
- goto bail;
+ return -EFAULT;
oij.ij_journal_size = i_size_read(osb->journal->j_inode);
o2info_set_request_filled(&oij.ij_req);
if (o2info_to_user(oij, req))
- goto bail;
+ return -EFAULT;
- status = 0;
-bail:
- if (status)
- o2info_set_request_error(&oij.ij_req, req);
-
- return status;
+ return 0;
}
static int ocfs2_info_scan_inode_alloc(struct ocfs2_super *osb,
u32 i;
u64 blkno = -1;
char namebuf[40];
- int status = -EFAULT, type = INODE_ALLOC_SYSTEM_INODE;
+ int status, type = INODE_ALLOC_SYSTEM_INODE;
struct ocfs2_info_freeinode *oifi = NULL;
struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
struct inode *inode_alloc = NULL;
goto out_err;
}
- if (o2info_from_user(*oifi, req))
- goto bail;
+ if (o2info_from_user(*oifi, req)) {
+ status = -EFAULT;
+ goto out_free;
+ }
oifi->ifi_slotnum = osb->max_slots;
o2info_set_request_filled(&oifi->ifi_req);
- if (o2info_to_user(*oifi, req))
- goto bail;
+ if (o2info_to_user(*oifi, req)) {
+ status = -EFAULT;
+ goto out_free;
+ }
status = 0;
bail:
if (status)
o2info_set_request_error(&oifi->ifi_req, req);
-
+out_free:
kfree(oifi);
out_err:
return status;
{
u64 blkno = -1;
char namebuf[40];
- int status = -EFAULT, type = GLOBAL_BITMAP_SYSTEM_INODE;
+ int status, type = GLOBAL_BITMAP_SYSTEM_INODE;
struct ocfs2_info_freefrag *oiff;
struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
goto out_err;
}
- if (o2info_from_user(*oiff, req))
- goto bail;
+ if (o2info_from_user(*oiff, req)) {
+ status = -EFAULT;
+ goto out_free;
+ }
/*
* chunksize from userspace should be power of 2.
*/
if (o2info_to_user(*oiff, req)) {
status = -EFAULT;
- goto bail;
+ goto out_free;
}
status = 0;
bail:
if (status)
o2info_set_request_error(&oiff->iff_req, req);
-
+out_free:
kfree(oiff);
out_err:
return status;
static int ocfs2_info_handle_unknown(struct inode *inode,
struct ocfs2_info_request __user *req)
{
- int status = -EFAULT;
struct ocfs2_info_request oir;
if (o2info_from_user(oir, req))
- goto bail;
+ return -EFAULT;
o2info_clear_request_filled(&oir);
if (o2info_to_user(oir, req))
- goto bail;
+ return -EFAULT;
- status = 0;
-bail:
- if (status)
- o2info_set_request_error(&oir, req);
-
- return status;
+ return 0;
}
/*
BLK_MQ_RQ_QUEUE_ERROR = 2, /* end IO with error */
BLK_MQ_F_SHOULD_MERGE = 1 << 0,
- BLK_MQ_F_SHOULD_SORT = 1 << 1,
- BLK_MQ_F_TAG_SHARED = 1 << 2,
- BLK_MQ_F_SG_MERGE = 1 << 3,
- BLK_MQ_F_SYSFS_UP = 1 << 4,
+ BLK_MQ_F_TAG_SHARED = 1 << 1,
+ BLK_MQ_F_SG_MERGE = 1 << 2,
+ BLK_MQ_F_SYSFS_UP = 1 << 3,
BLK_MQ_S_STOPPED = 0,
BLK_MQ_S_TAG_ACTIVE = 1,
* journal_block_tag (in the descriptor). The other h_chksum* fields are
* not used.
*
- * Checksum v1 and v2 are mutually exclusive features.
+ * If FEATURE_INCOMPAT_CSUM_V3 is set, the descriptor block uses
+ * journal_block_tag3_t to store a full 32-bit checksum. Everything else
+ * is the same as v2.
+ *
+ * Checksum v1, v2, and v3 are mutually exclusive features.
*/
struct commit_header {
__be32 h_magic;
* raw struct shouldn't be used for pointer math or sizeof() - use
* journal_tag_bytes(journal) instead to compute this.
*/
+typedef struct journal_block_tag3_s
+{
+ __be32 t_blocknr; /* The on-disk block number */
+ __be32 t_flags; /* See below */
+ __be32 t_blocknr_high; /* most-significant high 32bits. */
+ __be32 t_checksum; /* crc32c(uuid+seq+block) */
+} journal_block_tag3_t;
+
typedef struct journal_block_tag_s
{
__be32 t_blocknr; /* The on-disk block number */
__be32 t_blocknr_high; /* most-significant high 32bits. */
} journal_block_tag_t;
-#define JBD2_TAG_SIZE32 (offsetof(journal_block_tag_t, t_blocknr_high))
-#define JBD2_TAG_SIZE64 (sizeof(journal_block_tag_t))
-
/* Tail of descriptor block, for checksumming */
struct jbd2_journal_block_tail {
__be32 t_checksum; /* crc32c(uuid+descr_block) */
#define JBD2_FEATURE_INCOMPAT_64BIT 0x00000002
#define JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT 0x00000004
#define JBD2_FEATURE_INCOMPAT_CSUM_V2 0x00000008
+#define JBD2_FEATURE_INCOMPAT_CSUM_V3 0x00000010
/* Features known to this kernel version: */
#define JBD2_KNOWN_COMPAT_FEATURES JBD2_FEATURE_COMPAT_CHECKSUM
#define JBD2_KNOWN_INCOMPAT_FEATURES (JBD2_FEATURE_INCOMPAT_REVOKE | \
JBD2_FEATURE_INCOMPAT_64BIT | \
JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT | \
- JBD2_FEATURE_INCOMPAT_CSUM_V2)
+ JBD2_FEATURE_INCOMPAT_CSUM_V2 | \
+ JBD2_FEATURE_INCOMPAT_CSUM_V3)
#ifdef __KERNEL__
extern int jbd2_journal_blocks_per_page(struct inode *inode);
extern size_t journal_tag_bytes(journal_t *journal);
+static inline int jbd2_journal_has_csum_v2or3(journal_t *journal)
+{
+ if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2) ||
+ JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V3))
+ return 1;
+
+ return 0;
+}
+
/*
* We reserve t_outstanding_credits >> JBD2_CONTROL_BLOCKS_SHIFT for
* transaction control blocks.
};
enum omap_ecc {
- /* 1-bit ECC calculation by GPMC, Error detection by Software */
- OMAP_ECC_HAM1_CODE_HW = 0,
+ /*
+ * 1-bit ECC: calculation and correction by SW
+ * ECC stored at end of spare area
+ */
+ OMAP_ECC_HAM1_CODE_SW = 0,
+
+ /*
+ * 1-bit ECC: calculation by GPMC, Error detection by Software
+ * ECC layout compatible with ROM code layout
+ */
+ OMAP_ECC_HAM1_CODE_HW,
/* 4-bit ECC calculation by GPMC, Error detection by Software */
OMAP_ECC_BCH4_CODE_HW_DETECTION_SW,
/* 4-bit ECC calculation by GPMC, Error detection by ELM */
* @context: the execution context this fence is a part of
* @seqno_ofs: the offset within @sync_buf
* @seqno: the sequence # to signal on
+ * @cond: fence wait condition
* @ops: the fence_ops for operations on this seqno fence
*
* This function initializes a struct seqno_fence with passed parameters,
* the device whose settings are being modified.
* @transfer: adds a message to the controller's transfer queue.
* @cleanup: frees controller-specific state
+ * @can_dma: determine whether this master supports DMA
* @queued: whether this master is providing an internal message queue
* @kworker: thread struct for message pump
* @kworker_task: pointer to task for message pump kworker thread
* @cur_msg: the currently in-flight message
* @cur_msg_prepared: spi_prepare_message was called for the currently
* in-flight message
+ * @cur_msg_mapped: message has been mapped for DMA
* @xfer_completion: used by core transfer_one_message()
* @busy: message pump is busy
* @running: message pump is running
* @cs_gpios: Array of GPIOs to use as chip select lines; one per CS
* number. Any individual value may be -ENOENT for CS lines that
* are not GPIOs (driven by the SPI controller itself).
+ * @dma_tx: DMA transmit channel
+ * @dma_rx: DMA receive channel
+ * @dummy_rx: dummy receive buffer for full-duplex devices
+ * @dummy_tx: dummy transmit buffer for full-duplex devices
*
* Each SPI master controller can communicate with one or more @spi_device
* children. These make a small bus, sharing MOSI, MISO and SCK signals
* addresses for each transfer buffer
* @complete: called to report transaction completions
* @context: the argument to complete() when it's called
+ * @frame_length: the total number of bytes in the message
* @actual_length: the total number of bytes that were transferred in all
* successful segments
* @status: zero for success, else negative errno
--- /dev/null
+/*
+ * usbip.h
+ *
+ * USBIP uapi defines and function prototypes etc.
+*/
+
+#ifndef _UAPI_LINUX_USBIP_H
+#define _UAPI_LINUX_USBIP_H
+
+/* usbip device status - exported in usbip device sysfs status */
+enum usbip_device_status {
+ /* sdev is available. */
+ SDEV_ST_AVAILABLE = 0x01,
+ /* sdev is now used. */
+ SDEV_ST_USED,
+ /* sdev is unusable because of a fatal error. */
+ SDEV_ST_ERROR,
+
+ /* vdev does not connect a remote device. */
+ VDEV_ST_NULL,
+ /* vdev is used, but the USB address is not assigned yet */
+ VDEV_ST_NOTASSIGNED,
+ VDEV_ST_USED,
+ VDEV_ST_ERROR
+};
+#endif /* _UAPI_LINUX_USBIP_H */
#ifndef _UAPI_LINUX_XATTR_H
#define _UAPI_LINUX_XATTR_H
-#ifdef __UAPI_DEF_XATTR
+#if __UAPI_DEF_XATTR
#define __USE_KERNEL_XATTR_DEFS
#define XATTR_CREATE 0x1 /* set value, fail if attr already exists */
char __weak kexec_purgatory[0];
size_t __weak kexec_purgatory_size = 0;
+#ifdef CONFIG_KEXEC_FILE
static int kexec_calculate_store_digests(struct kimage *image);
+#endif
/* Location of the reserved area for the crash kernel */
struct resource crashk_res = {
return ret;
}
+#ifdef CONFIG_KEXEC_FILE
static int copy_file_from_fd(int fd, void **buf, unsigned long *buf_len)
{
struct fd f = fdget(fd);
kfree(image);
return ret;
}
+#else /* CONFIG_KEXEC_FILE */
+static inline void kimage_file_post_load_cleanup(struct kimage *image) { }
+#endif /* CONFIG_KEXEC_FILE */
static int kimage_is_destination_range(struct kimage *image,
unsigned long start,
}
#endif
+#ifdef CONFIG_KEXEC_FILE
SYSCALL_DEFINE5(kexec_file_load, int, kernel_fd, int, initrd_fd,
unsigned long, cmdline_len, const char __user *, cmdline_ptr,
unsigned long, flags)
return ret;
}
+#endif /* CONFIG_KEXEC_FILE */
+
void crash_kexec(struct pt_regs *regs)
{
/* Take the kexec_mutex here to prevent sys_kexec_load
subsys_initcall(crash_save_vmcoreinfo_init);
+#ifdef CONFIG_KEXEC_FILE
static int __kexec_add_segment(struct kimage *image, char *buf,
unsigned long bufsz, unsigned long mem,
unsigned long memsz)
return 0;
}
+#endif /* CONFIG_KEXEC_FILE */
/*
* Move into place and start executing a preloaded standalone
end = res->end;
BUG_ON(start >= end);
- read_lock(&resource_lock);
-
- if (first_level_children_only) {
- p = iomem_resource.child;
+ if (first_level_children_only)
sibling_only = true;
- } else
- p = &iomem_resource;
- while ((p = next_resource(p, sibling_only))) {
+ read_lock(&resource_lock);
+
+ for (p = iomem_resource.child; p; p = next_resource(p, sibling_only)) {
if (p->flags != res->flags)
continue;
if (name && strcmp(p->name, name))
work = &cpu_buffer->irq_work;
}
- work->waiters_pending = true;
poll_wait(filp, &work->waiters, poll_table);
+ work->waiters_pending = true;
+ /*
+ * There's a tight race between setting the waiters_pending and
+ * checking if the ring buffer is empty. Once the waiters_pending bit
+ * is set, the next event will wake the task up, but we can get stuck
+ * if there's only a single event in.
+ *
+ * FIXME: Ideally, we need a memory barrier on the writer side as well,
+ * but adding a memory barrier to all events will cause too much of a
+ * performance hit in the fast path. We only need a memory barrier when
+ * the buffer goes from empty to having content. But as this race is
+ * extremely small, and it's not a problem if another event comes in, we
+ * will fix it later.
+ */
+ smp_mb();
if ((cpu == RING_BUFFER_ALL_CPUS && !ring_buffer_empty(buffer)) ||
(cpu != RING_BUFFER_ALL_CPUS && !ring_buffer_empty_cpu(buffer, cpu)))
the full mutex checks enabled with (CONFIG_PROVE_LOCKING) this
will test all possible w/w mutex interface abuse with the
exception of simply not acquiring all the required locks.
+ Note that this feature can introduce significant overhead, so
+ it really should not be enabled in a production or distro kernel,
+ even a debug kernel. If you are a driver writer, enable it. If
+ you are a distro, do not.
config DEBUG_LOCK_ALLOC
bool "Lock debugging: detect incorrect freeing of live locks"
either tracing or lock debugging.
config STACKTRACE
- bool
+ bool "Stack backtrace support"
depends on STACKTRACE_SUPPORT
+ help
+ This option causes the kernel to create a /proc/pid/stack for
+ every process, showing its current stack trace.
+ It is also used by various kernel debugging features that require
+ stack trace generation.
config DEBUG_KOBJECT
bool "kobject debugging"
if (hugetlb_cgroup_disabled())
return;
- VM_BUG_ON(!spin_is_locked(&hugetlb_lock));
+ lockdep_assert_held(&hugetlb_lock);
h_cg = hugetlb_cgroup_from_page(page);
if (unlikely(!h_cg))
return;
phys_addr_t align, phys_addr_t start,
phys_addr_t end, int nid)
{
- int ret;
- phys_addr_t kernel_end;
+ phys_addr_t kernel_end, ret;
/* pump up @end */
if (end == MEMBLOCK_ALLOC_ACCESSIBLE)
unsigned long pfn = pte_pfn(pte);
if (HAVE_PTE_SPECIAL) {
- if (likely(!pte_special(pte) || pte_numa(pte)))
+ if (likely(!pte_special(pte)))
goto check_pfn;
if (vma->vm_flags & (VM_PFNMAP | VM_MIXEDMAP))
return NULL;
}
}
+ if (is_zero_pfn(pfn))
+ return NULL;
check_pfn:
if (unlikely(pfn > highest_memmap_pfn)) {
print_bad_pte(vma, addr, pte, NULL);
return NULL;
}
- if (is_zero_pfn(pfn))
- return NULL;
-
/*
* NOTE! We still have PageReserved() pages in the page tables.
* eg. VDSO mappings can cause them to exist.
pmd_t entry = *pmdp;
if (pmd_numa(entry))
entry = pmd_mknonnuma(entry);
- set_pmd_at(vma->vm_mm, address, pmdp, pmd_mknotpresent(*pmdp));
+ set_pmd_at(vma->vm_mm, address, pmdp, pmd_mknotpresent(entry));
flush_tlb_range(vma, address, address + HPAGE_PMD_SIZE);
}
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
.total_size = zbud_zpool_total_size,
};
+MODULE_ALIAS("zpool-zbud");
#endif /* CONFIG_ZPOOL */
/*****************
driver = zpool_get_driver(type);
if (!driver) {
- request_module(type);
+ request_module("zpool-%s", type);
driver = zpool_get_driver(type);
}
.total_size = zs_zpool_total_size,
};
+MODULE_ALIAS("zpool-zsmalloc");
#endif /* CONFIG_ZPOOL */
/* per-cpu VM mapping areas for zspage accesses that cross page boundaries */
# Check for improperly formed commit descriptions
if ($in_commit_log &&
$line =~ /\bcommit\s+[0-9a-f]{5,}/i &&
- $line !~ /\b[Cc]ommit [0-9a-f]{12,16} \("/) {
+ $line !~ /\b[Cc]ommit [0-9a-f]{12,40} \("/) {
$line =~ /\b(c)ommit\s+([0-9a-f]{5,})/i;
my $init_char = $1;
my $orig_commit = lc($2);
my $desc = 'commit description';
($id, $desc) = git_commit_info($orig_commit, $id, $desc);
ERROR("GIT_COMMIT_ID",
- "Please use 12 to 16 chars for the git commit ID like: '${init_char}ommit $id (\"$desc\")'\n" . $herecurr);
+ "Please use 12 or more chars for the git commit ID like: '${init_char}ommit $id (\"$desc\")'\n" . $herecurr);
}
# Check for added, moved or deleted files
$prototype =~ s/^noinline +//;
$prototype =~ s/__init +//;
$prototype =~ s/__init_or_module +//;
+ $prototype =~ s/__meminit +//;
$prototype =~ s/__must_check +//;
$prototype =~ s/__weak +//;
my $define = $prototype =~ s/^#\s*define\s+//; #ak added
* Use filesystem name if filesystem does not support rename()
* operation.
*/
- if (!inode->i_op->rename)
+ if (!inode->i_op->rename && !inode->i_op->rename2)
goto prepend_filesystem_name;
}
/* Prepend device name. */
* Get local name for filesystems without rename() operation
* or dentry without vfsmount.
*/
- if (!path->mnt || !inode->i_op->rename)
+ if (!path->mnt ||
+ (!inode->i_op->rename && !inode->i_op->rename2))
pos = tomoyo_get_local_path(path->dentry, buf,
buf_len - 1);
/* Get absolute name for the rest. */
* snd_info_get_line - read one line from the procfs buffer
* @buffer: the procfs buffer
* @line: the buffer to store
- * @len: the max. buffer size - 1
+ * @len: the max. buffer size
*
* Reads one line from the buffer and stores the string.
*
buffer->stop = 1;
if (c == '\n')
break;
- if (len) {
+ if (len > 1) {
len--;
*line++ = c;
}
},
[SNDRV_PCM_FORMAT_DSD_U8] = {
.width = 8, .phys = 8, .le = 1, .signd = 0,
- .silence = {},
+ .silence = { 0x69 },
},
[SNDRV_PCM_FORMAT_DSD_U16_LE] = {
.width = 16, .phys = 16, .le = 1, .signd = 0,
- .silence = {},
+ .silence = { 0x69, 0x69 },
},
/* FIXME: the following three formats are not defined properly yet */
[SNDRV_PCM_FORMAT_MPEG] = {
*/
#ifndef CT20K1REG_H
-#define CT20k1REG_H
+#define CT20K1REG_H
/* 20k1 registers */
#define DSPXRAM_START 0x000000
#define I2SD_R 0x19L
#endif /* CT20K1REG_H */
-
-
*/
#ifndef __CA0132_REGS_H
-#define __CA0312_REGS_H
+#define __CA0132_REGS_H
#define DSP_CHIP_OFFSET 0x100000
#define DSP_DBGCNTL_MODULE_OFFSET 0xE30
ALC292_FIXUP_TPT440_DOCK,
ALC283_FIXUP_BXBT2807_MIC,
ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED,
+ ALC282_FIXUP_ASPIRE_V5_PINS,
};
static const struct hda_fixup alc269_fixups[] = {
.chained_before = true,
.chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE
},
+ [ALC282_FIXUP_ASPIRE_V5_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x90a60130 },
+ { 0x14, 0x90170110 },
+ { 0x17, 0x40000008 },
+ { 0x18, 0x411111f0 },
+ { 0x19, 0x411111f0 },
+ { 0x1a, 0x411111f0 },
+ { 0x1b, 0x411111f0 },
+ { 0x1d, 0x40f89b2d },
+ { 0x1e, 0x411111f0 },
+ { 0x21, 0x0321101f },
+ { },
+ },
+ },
};
SND_PCI_QUIRK(0x1025, 0x0740, "Acer AO725", ALC271_FIXUP_HP_GATE_MIC_JACK),
SND_PCI_QUIRK(0x1025, 0x0742, "Acer AO756", ALC271_FIXUP_HP_GATE_MIC_JACK),
SND_PCI_QUIRK(0x1025, 0x0775, "Acer Aspire E1-572", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572),
+ SND_PCI_QUIRK(0x1025, 0x079b, "Acer Aspire V5-573G", ALC282_FIXUP_ASPIRE_V5_PINS),
SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
SND_PCI_QUIRK(0x1028, 0x05bd, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x05be, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
uname_M := $(shell uname -m 2>/dev/null || echo not)
ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/)
ifeq ($(ARCH),i386)
- ARCH := X86
+ ARCH := x86
CFLAGS := -DCONFIG_X86_32 -D__i386__
endif
ifeq ($(ARCH),x86_64)
- ARCH := X86
+ ARCH := x86
CFLAGS := -DCONFIG_X86_64 -D__x86_64__
endif
CFLAGS += -I../../../../usr/include/
all:
-ifeq ($(ARCH),X86)
+ifeq ($(ARCH),x86)
gcc $(CFLAGS) msgque.c -o msgque_test
else
echo "Not an x86 target, can't build msgque selftest"
uname_M := $(shell uname -m 2>/dev/null || echo not)
ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/)
ifeq ($(ARCH),i386)
- ARCH := X86
+ ARCH := x86
CFLAGS := -DCONFIG_X86_32 -D__i386__
endif
ifeq ($(ARCH),x86_64)
- ARCH := X86
+ ARCH := x86
CFLAGS := -DCONFIG_X86_64 -D__x86_64__
endif
CFLAGS += -I../../../../arch/x86/include/
all:
-ifeq ($(ARCH),X86)
+ifeq ($(ARCH),x86)
gcc $(CFLAGS) kcmp_test.c -o kcmp_test
else
echo "Not an x86 target, can't build kcmp selftest"
uname_M := $(shell uname -m 2>/dev/null || echo not)
ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/)
ifeq ($(ARCH),i386)
- ARCH := X86
+ ARCH := x86
endif
ifeq ($(ARCH),x86_64)
- ARCH := X86
+ ARCH := x86
endif
CFLAGS += -D_FILE_OFFSET_BITS=64
CFLAGS += -I../../../../include/
all:
-ifeq ($(ARCH),X86)
+ifeq ($(ARCH),x86)
gcc $(CFLAGS) memfd_test.c -o memfd_test
else
echo "Not an x86 target, can't build memfd selftest"
endif
run_tests: all
-ifeq ($(ARCH),X86)
+ifeq ($(ARCH),x86)
gcc $(CFLAGS) memfd_test.c -o memfd_test
endif
@./memfd_test || echo "memfd_test: [FAIL]"
build_fuse:
-ifeq ($(ARCH),X86)
+ifeq ($(ARCH),x86)
gcc $(CFLAGS) fuse_mnt.c `pkg-config fuse --cflags --libs` -o fuse_mnt
gcc $(CFLAGS) fuse_test.c -o fuse_test
else
--- /dev/null
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache/
+config.guess
+config.h
+config.h.in
+config.log
+config.status
+config.sub
+configure
+depcomp
+install-sh
+libsrc/Makefile
+libsrc/Makefile.in
+libtool
+ltmain.sh
+missing
+src/Makefile
+src/Makefile.in
+stamp-h1
+libsrc/libusbip.la
+libsrc/libusbip_la-names.lo
+libsrc/libusbip_la-usbip_common.lo
+libsrc/libusbip_la-usbip_host_driver.lo
+libsrc/libusbip_la-vhci_driver.lo
+src/usbip
+src/usbipd
--- /dev/null
+Takahiro Hirofuchi
+Robert Leibl
+matt mooney <mfm@muteddisk.com>
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
--- /dev/null
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
+2006, 2007 Free Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+Briefly, the shell commands `./configure; make; make install' should
+configure, build, and install this package. The following
+more-detailed instructions are generic; see the `README' file for
+instructions specific to this package.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You need `configure.ac' if
+you want to change it or regenerate `configure' using a newer version
+of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system.
+
+ Running `configure' might take a while. While running, it prints
+ some messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+ 6. Often, you can also type `make uninstall' to remove the installed
+ files again.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about. Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here
+is an example:
+
+ ./configure CC=c99 CFLAGS=-g LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you can use GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ With a non-GNU `make', it is safer to compile the package for one
+architecture at a time in the source code directory. After you have
+installed the package for one architecture, use `make distclean' before
+reconfiguring for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' installs the package's commands under
+`/usr/local/bin', include files under `/usr/local/include', etc. You
+can specify an installation prefix other than `/usr/local' by giving
+`configure' the option `--prefix=PREFIX'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+pass the option `--exec-prefix=PREFIX' to `configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the option `--target=TYPE' to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+Unfortunately, this technique does not work for `CONFIG_SHELL' due to
+an Autoconf bug. Until the bug is fixed you can use this workaround:
+
+ CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+ Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
+
--- /dev/null
+SUBDIRS := libsrc src
+includedir = @includedir@/usbip
+include_HEADERS := $(addprefix libsrc/, \
+ usbip_common.h vhci_driver.h usbip_host_driver.h)
+
+dist_man_MANS := $(addprefix doc/, usbip.8 usbipd.8)
--- /dev/null
+#
+# README for usbip-utils
+#
+# Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+# 2005-2008 Takahiro Hirofuchi
+
+
+[Requirements]
+ - USB/IP device drivers
+ Found in the staging directory of the Linux kernel.
+
+ - libudev >= 2.0
+ libudev library
+
+ - libwrap0-dev
+ tcp wrapper library
+
+ - gcc >= 4.0
+
+ - libtool, automake >= 1.9, autoconf >= 2.5.0, pkg-config
+
+[Optional]
+ - hwdata
+ Contains USB device identification data.
+
+
+[Install]
+ 0. Generate configuration scripts.
+ $ ./autogen.sh
+
+ 1. Compile & install the userspace utilities.
+ $ ./configure [--with-tcp-wrappers=no] [--with-usbids-dir=<dir>]
+ $ make install
+
+ 2. Compile & install USB/IP drivers.
+
+
+[Usage]
+ server:# (Physically attach your USB device.)
+
+ server:# insmod usbip-core.ko
+ server:# insmod usbip-host.ko
+
+ server:# usbipd -D
+ - Start usbip daemon.
+
+ server:# usbip list -l
+ - List driver assignments for USB devices.
+
+ server:# usbip bind --busid 1-2
+ - Bind usbip-host.ko to the device with busid 1-2.
+ - The USB device 1-2 is now exportable to other hosts!
+ - Use `usbip unbind --busid 1-2' to stop exporting the device.
+
+ client:# insmod usbip-core.ko
+ client:# insmod vhci-hcd.ko
+
+ client:# usbip list --remote <host>
+ - List exported USB devices on the <host>.
+
+ client:# usbip attach --remote <host> --busid 1-2
+ - Connect the remote USB device.
+
+ client:# usbip port
+ - Show virtual port status.
+
+ client:# usbip detach --port <port>
+ - Detach the USB device.
+
+
+[Example]
+---------------------------
+ SERVER SIDE
+---------------------------
+Physically attach your USB devices to this host.
+
+ trois:# insmod path/to/usbip-core.ko
+ trois:# insmod path/to/usbip-host.ko
+ trois:# usbipd -D
+
+In another terminal, let's look up what USB devices are physically
+attached to this host.
+
+ trois:# usbip list -l
+ Local USB devices
+ =================
+ - busid 1-1 (05a9:a511)
+ 1-1:1.0 -> ov511
+
+ - busid 3-2 (0711:0902)
+ 3-2:1.0 -> none
+
+ - busid 3-3.1 (08bb:2702)
+ 3-3.1:1.0 -> snd-usb-audio
+ 3-3.1:1.1 -> snd-usb-audio
+
+ - busid 3-3.2 (04bb:0206)
+ 3-3.2:1.0 -> usb-storage
+
+ - busid 3-3 (0409:0058)
+ 3-3:1.0 -> hub
+
+ - busid 4-1 (046d:08b2)
+ 4-1:1.0 -> none
+ 4-1:1.1 -> none
+ 4-1:1.2 -> none
+
+ - busid 5-2 (058f:9254)
+ 5-2:1.0 -> hub
+
+A USB storage device of busid 3-3.2 is now bound to the usb-storage
+driver. To export this device, we first mark the device as
+"exportable"; the device is bound to the usbip-host driver. Please
+remember you can not export a USB hub.
+
+Mark the device of busid 3-3.2 as exportable:
+
+ trois:# usbip --debug bind --busid 3-3.2
+ ...
+ usbip debug: usbip_bind.c:162:[unbind_other] 3-3.2:1.0 -> usb-storage
+ ...
+ bind device on busid 3-3.2: complete
+
+ trois:# usbip list -l
+ Local USB devices
+ =================
+ ...
+
+ - busid 3-3.2 (04bb:0206)
+ 3-3.2:1.0 -> usbip-host
+ ...
+
+---------------------------
+ CLIENT SIDE
+---------------------------
+First, let's list available remote devices that are marked as
+exportable on the host.
+
+ deux:# insmod path/to/usbip-core.ko
+ deux:# insmod path/to/vhci-hcd.ko
+
+ deux:# usbip list --remote 10.0.0.3
+ Exportable USB devices
+ ======================
+ - 10.0.0.3
+ 1-1: Prolific Technology, Inc. : unknown product (067b:3507)
+ : /sys/devices/pci0000:00/0000:00:1f.2/usb1/1-1
+ : (Defined at Interface level) / unknown subclass / unknown protocol (00/00/00)
+ : 0 - Mass Storage / SCSI / Bulk (Zip) (08/06/50)
+
+ 1-2.2.1: Apple Computer, Inc. : unknown product (05ac:0203)
+ : /sys/devices/pci0000:00/0000:00:1f.2/usb1/1-2/1-2.2/1-2.2.1
+ : (Defined at Interface level) / unknown subclass / unknown protocol (00/00/00)
+ : 0 - Human Interface Devices / Boot Interface Subclass / Keyboard (03/01/01)
+
+ 1-2.2.3: OmniVision Technologies, Inc. : OV511+ WebCam (05a9:a511)
+ : /sys/devices/pci0000:00/0000:00:1f.2/usb1/1-2/1-2.2/1-2.2.3
+ : (Defined at Interface level) / unknown subclass / unknown protocol (00/00/00)
+ : 0 - Vendor Specific Class / unknown subclass / unknown protocol (ff/00/00)
+
+ 3-1: Logitech, Inc. : QuickCam Pro 4000 (046d:08b2)
+ : /sys/devices/pci0000:00/0000:00:1e.0/0000:02:0a.0/usb3/3-1
+ : (Defined at Interface level) / unknown subclass / unknown protocol (00/00/00)
+ : 0 - Data / unknown subclass / unknown protocol (0a/ff/00)
+ : 1 - Audio / Control Device / unknown protocol (01/01/00)
+ : 2 - Audio / Streaming / unknown protocol (01/02/00)
+
+Attach a remote USB device:
+
+ deux:# usbip attach --remote 10.0.0.3 --busid 1-1
+ port 0 attached
+
+Show the devices attached to this client:
+
+ deux:# usbip port
+ Port 00: <Port in Use> at Full Speed(12Mbps)
+ Prolific Technology, Inc. : unknown product (067b:3507)
+ 6-1 -> usbip://10.0.0.3:3240/1-1 (remote bus/dev 001/004)
+ 6-1:1.0 used by usb-storage
+ /sys/class/scsi_device/0:0:0:0/device
+ /sys/class/scsi_host/host0/device
+ /sys/block/sda/device
+
+Detach the imported device:
+
+ deux:# usbip detach --port 0
+ port 0 detached
+
+
+[Checklist]
+ - See 'Debug Tips' on the project wiki.
+ - http://usbip.wiki.sourceforge.net/how-to-debug-usbip
+ - usbip-host.ko must be bound to the target device.
+ - See /proc/bus/usb/devices and find "Driver=..." lines of the device.
+ - Shutdown firewall.
+ - usbip now uses TCP port 3240.
+ - Disable SELinux.
+ - Check the kernel and daemon messages.
+
+
+[Contact]
+ Mailing List: linux-usb@vger.kernel.org
--- /dev/null
+#!/bin/sh -x
+
+#aclocal
+#autoheader
+#libtoolize --copy --force
+#automake-1.9 -acf
+#autoconf
+
+autoreconf -i -f -v
--- /dev/null
+#!/bin/sh
+
+if [ -r Makefile ]; then
+ make distclean
+fi
+
+FILES="aclocal.m4 autom4te.cache compile config.guess config.h.in config.log \
+ config.status config.sub configure cscope.out depcomp install-sh \
+ libsrc/Makefile libsrc/Makefile.in libtool ltmain.sh Makefile \
+ Makefile.in missing src/Makefile src/Makefile.in"
+
+rm -vRf $FILES
--- /dev/null
+dnl Process this file with autoconf to produce a configure script.
+
+AC_PREREQ(2.59)
+AC_INIT([usbip-utils], [2.0], [linux-usb@vger.kernel.org])
+AC_DEFINE([USBIP_VERSION], [0x00000111], [binary-coded decimal version number])
+
+CURRENT=0
+REVISION=1
+AGE=0
+AC_SUBST([LIBUSBIP_VERSION], [$CURRENT:$REVISION:$AGE], [library version])
+
+AC_CONFIG_SRCDIR([src/usbipd.c])
+AC_CONFIG_HEADERS([config.h])
+
+AM_INIT_AUTOMAKE([foreign])
+LT_INIT
+
+# Silent build for automake >= 1.11
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+AC_SUBST([EXTRA_CFLAGS], ["-Wall -Werror -Wextra -std=gnu99"])
+
+# Checks for programs.
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_MAKE_SET
+
+# Checks for header files.
+AC_HEADER_DIRENT
+AC_HEADER_STDC
+AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdint.h stdlib.h dnl
+ string.h sys/socket.h syslog.h unistd.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_TYPE_INT32_T
+AC_TYPE_SIZE_T
+AC_TYPE_SSIZE_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT8_T
+
+# Checks for library functions.
+AC_FUNC_REALLOC
+AC_CHECK_FUNCS([memset mkdir regcomp socket strchr strerror strstr dnl
+ strtoul])
+
+AC_CHECK_HEADER([libudev.h],
+ [AC_CHECK_LIB([udev], [udev_new],
+ [LIBS="$LIBS -ludev"],
+ [AC_MSG_ERROR([Missing udev library!])])],
+ [AC_MSG_ERROR([Missing /usr/include/libudev.h])])
+
+# Checks for libwrap library.
+AC_MSG_CHECKING([whether to use the libwrap (TCP wrappers) library])
+AC_ARG_WITH([tcp-wrappers],
+ [AS_HELP_STRING([--with-tcp-wrappers],
+ [use the libwrap (TCP wrappers) library])],
+ dnl [ACTION-IF-GIVEN]
+ [if test "$withval" = "yes"; then
+ AC_MSG_RESULT([yes])
+ AC_MSG_CHECKING([for hosts_access in -lwrap])
+ saved_LIBS="$LIBS"
+ LIBS="-lwrap $saved_LIBS"
+ AC_TRY_LINK(
+ [int hosts_access(); int allow_severity, deny_severity;],
+ [hosts_access()],
+ [AC_MSG_RESULT([yes]);
+ AC_DEFINE([HAVE_LIBWRAP], [1],
+ [use tcp wrapper]) wrap_LIB="-lwrap"],
+ [AC_MSG_RESULT([not found]); exit 1])
+ else
+ AC_MSG_RESULT([no]);
+ fi],
+ dnl [ACTION-IF-NOT-GIVEN]
+ [AC_MSG_RESULT([(default)])
+ AC_MSG_CHECKING([for hosts_access in -lwrap])
+ saved_LIBS="$LIBS"
+ LIBS="-lwrap $saved_LIBS"
+ AC_TRY_LINK(
+ [int hosts_access(); int allow_severity, deny_severity;],
+ [hosts_access()],
+ [AC_MSG_RESULT([yes]);
+ AC_DEFINE([HAVE_LIBWRAP], [1], [use tcp wrapper])],
+ [AC_MSG_RESULT([no]); LIBS="$saved_LIBS"])])
+
+# Sets directory containing usb.ids.
+AC_ARG_WITH([usbids-dir],
+ [AS_HELP_STRING([--with-usbids-dir=DIR],
+ [where usb.ids is found (default /usr/share/hwdata/)])],
+ [USBIDS_DIR=$withval], [USBIDS_DIR="/usr/share/hwdata/"])
+AC_SUBST([USBIDS_DIR])
+
+# use _FORTIFY_SOURCE
+AC_MSG_CHECKING([whether to use fortify])
+AC_ARG_WITH([fortify],
+ [AS_HELP_STRING([--with-fortify],
+ [use _FORTIFY_SROUCE option when compiling)])],
+ dnl [ACTION-IF-GIVEN]
+ [if test "$withval" = "yes"; then
+ AC_MSG_RESULT([yes])
+ CFLAGS="$CFLAGS -D_FORTIFY_SOURCE -O"
+ else
+ AC_MSG_RESULT([no])
+ CFLAGS="$CFLAGS -U_FORTIFY_SOURCE"
+ fi
+ ],
+ dnl [ACTION-IF-NOT-GIVEN]
+ [AC_MSG_RESULT([default])])
+
+AC_CONFIG_FILES([Makefile libsrc/Makefile src/Makefile])
+AC_OUTPUT
--- /dev/null
+.TH USBIP "8" "February 2009" "usbip" "System Administration Utilities"
+.SH NAME
+usbip \- manage USB/IP devices
+.SH SYNOPSIS
+.B usbip
+[\fIoptions\fR] <\fIcommand\fR> <\fIargs\fR>
+
+.SH DESCRIPTION
+On a USB/IP server, devices can be listed, bound, and unbound using
+this program. On a USB/IP client, devices exported by USB/IP servers
+can be listed, attached and detached.
+
+.SH OPTIONS
+.HP
+\fB\-\-debug\fR
+.IP
+Print debugging information.
+.PP
+
+.HP
+\fB\-\-log\fR
+.IP
+Log to syslog.
+.PP
+
+.HP
+\fB\-\-tcp-port PORT\fR
+.IP
+Connect to PORT on remote host (used for attach and list --remote).
+.PP
+
+.SH COMMANDS
+.HP
+\fBversion\fR
+.IP
+Show version and exit.
+.PP
+
+.HP
+\fBhelp\fR [\fIcommand\fR]
+.IP
+Print the program help message, or help on a specific command, and
+then exit.
+.PP
+
+.HP
+\fBattach\fR \-\-remote=<\fIhost\fR> \-\-busid=<\fIbus_id\fR>
+.IP
+Attach a remote USB device.
+.PP
+
+.HP
+\fBdetach\fR \-\-port=<\fIport\fR>
+.IP
+Detach an imported USB device.
+.PP
+
+.HP
+\fBbind\fR \-\-busid=<\fIbusid\fR>
+.IP
+Make a device exportable.
+.PP
+
+.HP
+\fBunbind\fR \-\-busid=<\fIbusid\fR>
+.IP
+Stop exporting a device so it can be used by a local driver.
+.PP
+
+.HP
+\fBlist\fR \-\-remote=<\fIhost\fR>
+.IP
+List USB devices exported by a remote host.
+.PP
+
+.HP
+\fBlist\fR \-\-local
+.IP
+List local USB devices.
+.PP
+
+
+.SH EXAMPLES
+
+ client:# usbip list --remote=server
+ - List exportable usb devices on the server.
+
+ client:# usbip attach --remote=server --busid=1-2
+ - Connect the remote USB device.
+
+ client:# usbip detach --port=0
+ - Detach the usb device.
+
+.SH "SEE ALSO"
+\fBusbipd\fP\fB(8)\fB\fP
--- /dev/null
+.TH USBIP "8" "February 2009" "usbip" "System Administration Utilities"
+.SH NAME
+usbipd \- USB/IP server daemon
+.SH SYNOPSIS
+.B usbipd
+[\fIoptions\fR]
+
+.SH DESCRIPTION
+.B usbipd
+provides USB/IP clients access to exported USB devices.
+
+Devices have to explicitly be exported using
+.B usbip bind
+before usbipd makes them available to other hosts.
+
+The daemon accepts connections from USB/IP clients
+on TCP port 3240 by default.
+
+.SH OPTIONS
+.HP
+\fB\-4\fR, \fB\-\-ipv4\fR
+.IP
+Bind to IPv4. Default is both.
+.PP
+
+.HP
+\fB\-6\fR, \fB\-\-ipv6\fR
+.IP
+Bind to IPv6. Default is both.
+.PP
+
+.HP
+\fB\-D\fR, \fB\-\-daemon\fR
+.IP
+Run as a daemon process.
+.PP
+
+.HP
+\fB\-d\fR, \fB\-\-debug\fR
+.IP
+Print debugging information.
+.PP
+
+.HP
+\fB\-PFILE\fR, \fB\-\-pid FILE\fR
+.IP
+Write process id to FILE.
+.br
+If no FILE specified, use /var/run/usbipd.pid
+.PP
+
+\fB\-tPORT\fR, \fB\-\-tcp\-port PORT\fR
+.IP
+Listen on TCP/IP port PORT.
+.PP
+
+\fB\-h\fR, \fB\-\-help\fR
+.IP
+Print the program help message and exit.
+.PP
+
+.HP
+\fB\-v\fR, \fB\-\-version\fR
+.IP
+Show version.
+.PP
+
+.SH LIMITATIONS
+
+.B usbipd
+offers no authentication or authorization for USB/IP. Any
+USB/IP client can connect and use exported devices.
+
+.SH EXAMPLES
+
+ server:# modprobe usbip
+
+ server:# usbipd -D
+ - Start usbip daemon.
+
+ server:# usbip list --local
+ - List driver assignments for usb devices.
+
+ server:# usbip bind --busid=1-2
+ - Bind usbip-host.ko to the device of busid 1-2.
+ - A usb device 1-2 is now exportable to other hosts!
+ - Use 'usbip unbind --busid=1-2' when you want to shutdown exporting and use the device locally.
+
+.SH "SEE ALSO"
+\fBusbip\fP\fB(8)\fB\fP
+
--- /dev/null
+libusbip_la_CPPFLAGS = -DUSBIDS_FILE='"@USBIDS_DIR@/usb.ids"'
+libusbip_la_CFLAGS = @EXTRA_CFLAGS@
+libusbip_la_LDFLAGS = -version-info @LIBUSBIP_VERSION@
+
+lib_LTLIBRARIES := libusbip.la
+libusbip_la_SOURCES := names.c names.h usbip_host_driver.c usbip_host_driver.h \
+ usbip_common.c usbip_common.h vhci_driver.c vhci_driver.h \
+ sysfs_utils.c sysfs_utils.h
--- /dev/null
+#ifndef _LIST_H
+#define _LIST_H
+
+/* Stripped down implementation of linked list taken
+ * from the Linux Kernel.
+ */
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+static inline void INIT_LIST_HEAD(struct list_head *list)
+{
+ list->next = list;
+ list->prev = list;
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+#define POISON_POINTER_DELTA 0
+#define LIST_POISON1 ((void *) 0x00100100 + POISON_POINTER_DELTA)
+#define LIST_POISON2 ((void *) 0x00200200 + POISON_POINTER_DELTA)
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty() on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void __list_del_entry(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+}
+
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->next = LIST_POISON1;
+ entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+/**
+ * list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+#endif
--- /dev/null
+/*
+ * names.c -- USB name database manipulation routines
+ *
+ * Copyright (C) 1999, 2000 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.
+ *
+ * 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.
+ *
+ *
+ *
+ *
+ *
+ * Copyright (C) 2005 Takahiro Hirofuchi
+ * - names_deinit() is added.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "names.h"
+#include "usbip_common.h"
+
+struct vendor {
+ struct vendor *next;
+ u_int16_t vendorid;
+ char name[1];
+};
+
+struct product {
+ struct product *next;
+ u_int16_t vendorid, productid;
+ char name[1];
+};
+
+struct class {
+ struct class *next;
+ u_int8_t classid;
+ char name[1];
+};
+
+struct subclass {
+ struct subclass *next;
+ u_int8_t classid, subclassid;
+ char name[1];
+};
+
+struct protocol {
+ struct protocol *next;
+ u_int8_t classid, subclassid, protocolid;
+ char name[1];
+};
+
+struct genericstrtable {
+ struct genericstrtable *next;
+ unsigned int num;
+ char name[1];
+};
+
+
+#define HASH1 0x10
+#define HASH2 0x02
+#define HASHSZ 16
+
+static unsigned int hashnum(unsigned int num)
+{
+ unsigned int mask1 = HASH1 << 27, mask2 = HASH2 << 27;
+
+ for (; mask1 >= HASH1; mask1 >>= 1, mask2 >>= 1)
+ if (num & mask1)
+ num ^= mask2;
+ return num & (HASHSZ-1);
+}
+
+
+static struct vendor *vendors[HASHSZ] = { NULL, };
+static struct product *products[HASHSZ] = { NULL, };
+static struct class *classes[HASHSZ] = { NULL, };
+static struct subclass *subclasses[HASHSZ] = { NULL, };
+static struct protocol *protocols[HASHSZ] = { NULL, };
+
+const char *names_vendor(u_int16_t vendorid)
+{
+ struct vendor *v;
+
+ v = vendors[hashnum(vendorid)];
+ for (; v; v = v->next)
+ if (v->vendorid == vendorid)
+ return v->name;
+ return NULL;
+}
+
+const char *names_product(u_int16_t vendorid, u_int16_t productid)
+{
+ struct product *p;
+
+ p = products[hashnum((vendorid << 16) | productid)];
+ for (; p; p = p->next)
+ if (p->vendorid == vendorid && p->productid == productid)
+ return p->name;
+ return NULL;
+}
+
+const char *names_class(u_int8_t classid)
+{
+ struct class *c;
+
+ c = classes[hashnum(classid)];
+ for (; c; c = c->next)
+ if (c->classid == classid)
+ return c->name;
+ return NULL;
+}
+
+const char *names_subclass(u_int8_t classid, u_int8_t subclassid)
+{
+ struct subclass *s;
+
+ s = subclasses[hashnum((classid << 8) | subclassid)];
+ for (; s; s = s->next)
+ if (s->classid == classid && s->subclassid == subclassid)
+ return s->name;
+ return NULL;
+}
+
+const char *names_protocol(u_int8_t classid, u_int8_t subclassid,
+ u_int8_t protocolid)
+{
+ struct protocol *p;
+
+ p = protocols[hashnum((classid << 16) | (subclassid << 8)
+ | protocolid)];
+ for (; p; p = p->next)
+ if (p->classid == classid && p->subclassid == subclassid &&
+ p->protocolid == protocolid)
+ return p->name;
+ return NULL;
+}
+
+/* add a cleanup function by takahiro */
+struct pool {
+ struct pool *next;
+ void *mem;
+};
+
+static struct pool *pool_head;
+
+static void *my_malloc(size_t size)
+{
+ struct pool *p;
+
+ p = calloc(1, sizeof(struct pool));
+ if (!p)
+ return NULL;
+
+ p->mem = calloc(1, size);
+ if (!p->mem) {
+ free(p);
+ return NULL;
+ }
+
+ p->next = pool_head;
+ pool_head = p;
+
+ return p->mem;
+}
+
+void names_free(void)
+{
+ struct pool *pool;
+
+ if (!pool_head)
+ return;
+
+ for (pool = pool_head; pool != NULL; ) {
+ struct pool *tmp;
+
+ if (pool->mem)
+ free(pool->mem);
+
+ tmp = pool;
+ pool = pool->next;
+ free(tmp);
+ }
+}
+
+static int new_vendor(const char *name, u_int16_t vendorid)
+{
+ struct vendor *v;
+ unsigned int h = hashnum(vendorid);
+
+ v = vendors[h];
+ for (; v; v = v->next)
+ if (v->vendorid == vendorid)
+ return -1;
+ v = my_malloc(sizeof(struct vendor) + strlen(name));
+ if (!v)
+ return -1;
+ strcpy(v->name, name);
+ v->vendorid = vendorid;
+ v->next = vendors[h];
+ vendors[h] = v;
+ return 0;
+}
+
+static int new_product(const char *name, u_int16_t vendorid,
+ u_int16_t productid)
+{
+ struct product *p;
+ unsigned int h = hashnum((vendorid << 16) | productid);
+
+ p = products[h];
+ for (; p; p = p->next)
+ if (p->vendorid == vendorid && p->productid == productid)
+ return -1;
+ p = my_malloc(sizeof(struct product) + strlen(name));
+ if (!p)
+ return -1;
+ strcpy(p->name, name);
+ p->vendorid = vendorid;
+ p->productid = productid;
+ p->next = products[h];
+ products[h] = p;
+ return 0;
+}
+
+static int new_class(const char *name, u_int8_t classid)
+{
+ struct class *c;
+ unsigned int h = hashnum(classid);
+
+ c = classes[h];
+ for (; c; c = c->next)
+ if (c->classid == classid)
+ return -1;
+ c = my_malloc(sizeof(struct class) + strlen(name));
+ if (!c)
+ return -1;
+ strcpy(c->name, name);
+ c->classid = classid;
+ c->next = classes[h];
+ classes[h] = c;
+ return 0;
+}
+
+static int new_subclass(const char *name, u_int8_t classid, u_int8_t subclassid)
+{
+ struct subclass *s;
+ unsigned int h = hashnum((classid << 8) | subclassid);
+
+ s = subclasses[h];
+ for (; s; s = s->next)
+ if (s->classid == classid && s->subclassid == subclassid)
+ return -1;
+ s = my_malloc(sizeof(struct subclass) + strlen(name));
+ if (!s)
+ return -1;
+ strcpy(s->name, name);
+ s->classid = classid;
+ s->subclassid = subclassid;
+ s->next = subclasses[h];
+ subclasses[h] = s;
+ return 0;
+}
+
+static int new_protocol(const char *name, u_int8_t classid, u_int8_t subclassid,
+ u_int8_t protocolid)
+{
+ struct protocol *p;
+ unsigned int h = hashnum((classid << 16) | (subclassid << 8)
+ | protocolid);
+
+ p = protocols[h];
+ for (; p; p = p->next)
+ if (p->classid == classid && p->subclassid == subclassid
+ && p->protocolid == protocolid)
+ return -1;
+ p = my_malloc(sizeof(struct protocol) + strlen(name));
+ if (!p)
+ return -1;
+ strcpy(p->name, name);
+ p->classid = classid;
+ p->subclassid = subclassid;
+ p->protocolid = protocolid;
+ p->next = protocols[h];
+ protocols[h] = p;
+ return 0;
+}
+
+static void parse(FILE *f)
+{
+ char buf[512], *cp;
+ unsigned int linectr = 0;
+ int lastvendor = -1;
+ int lastclass = -1;
+ int lastsubclass = -1;
+ int lasthut = -1;
+ int lastlang = -1;
+ unsigned int u;
+
+ while (fgets(buf, sizeof(buf), f)) {
+ linectr++;
+ /* remove line ends */
+ cp = strchr(buf, '\r');
+ if (cp)
+ *cp = 0;
+ cp = strchr(buf, '\n');
+ if (cp)
+ *cp = 0;
+ if (buf[0] == '#' || !buf[0])
+ continue;
+ cp = buf;
+ if (buf[0] == 'P' && buf[1] == 'H' && buf[2] == 'Y' &&
+ buf[3] == 'S' && buf[4] == 'D' &&
+ buf[5] == 'E' && buf[6] == 'S' && /*isspace(buf[7])*/
+ buf[7] == ' ') {
+ continue;
+ }
+ if (buf[0] == 'P' && buf[1] == 'H' &&
+ buf[2] == 'Y' && /*isspace(buf[3])*/ buf[3] == ' ') {
+ continue;
+ }
+ if (buf[0] == 'B' && buf[1] == 'I' && buf[2] == 'A' &&
+ buf[3] == 'S' && /*isspace(buf[4])*/ buf[4] == ' ') {
+ continue;
+ }
+ if (buf[0] == 'L' && /*isspace(buf[1])*/ buf[1] == ' ') {
+ lasthut = lastclass = lastvendor = lastsubclass = -1;
+ /*
+ * set 1 as pseudo-id to indicate that the parser is
+ * in a `L' section.
+ */
+ lastlang = 1;
+ continue;
+ }
+ if (buf[0] == 'C' && /*isspace(buf[1])*/ buf[1] == ' ') {
+ /* class spec */
+ cp = buf+2;
+ while (isspace(*cp))
+ cp++;
+ if (!isxdigit(*cp)) {
+ err("Invalid class spec at line %u", linectr);
+ continue;
+ }
+ u = strtoul(cp, &cp, 16);
+ while (isspace(*cp))
+ cp++;
+ if (!*cp) {
+ err("Invalid class spec at line %u", linectr);
+ continue;
+ }
+ if (new_class(cp, u))
+ err("Duplicate class spec at line %u class %04x %s",
+ linectr, u, cp);
+ dbg("line %5u class %02x %s", linectr, u, cp);
+ lasthut = lastlang = lastvendor = lastsubclass = -1;
+ lastclass = u;
+ continue;
+ }
+ if (buf[0] == 'A' && buf[1] == 'T' && isspace(buf[2])) {
+ /* audio terminal type spec */
+ continue;
+ }
+ if (buf[0] == 'H' && buf[1] == 'C' && buf[2] == 'C'
+ && isspace(buf[3])) {
+ /* HID Descriptor bCountryCode */
+ continue;
+ }
+ if (isxdigit(*cp)) {
+ /* vendor */
+ u = strtoul(cp, &cp, 16);
+ while (isspace(*cp))
+ cp++;
+ if (!*cp) {
+ err("Invalid vendor spec at line %u", linectr);
+ continue;
+ }
+ if (new_vendor(cp, u))
+ err("Duplicate vendor spec at line %u vendor %04x %s",
+ linectr, u, cp);
+ dbg("line %5u vendor %04x %s", linectr, u, cp);
+ lastvendor = u;
+ lasthut = lastlang = lastclass = lastsubclass = -1;
+ continue;
+ }
+ if (buf[0] == '\t' && isxdigit(buf[1])) {
+ /* product or subclass spec */
+ u = strtoul(buf+1, &cp, 16);
+ while (isspace(*cp))
+ cp++;
+ if (!*cp) {
+ err("Invalid product/subclass spec at line %u",
+ linectr);
+ continue;
+ }
+ if (lastvendor != -1) {
+ if (new_product(cp, lastvendor, u))
+ err("Duplicate product spec at line %u product %04x:%04x %s",
+ linectr, lastvendor, u, cp);
+ dbg("line %5u product %04x:%04x %s", linectr,
+ lastvendor, u, cp);
+ continue;
+ }
+ if (lastclass != -1) {
+ if (new_subclass(cp, lastclass, u))
+ err("Duplicate subclass spec at line %u class %02x:%02x %s",
+ linectr, lastclass, u, cp);
+ dbg("line %5u subclass %02x:%02x %s", linectr,
+ lastclass, u, cp);
+ lastsubclass = u;
+ continue;
+ }
+ if (lasthut != -1) {
+ /* do not store hut */
+ continue;
+ }
+ if (lastlang != -1) {
+ /* do not store langid */
+ continue;
+ }
+ err("Product/Subclass spec without prior Vendor/Class spec at line %u",
+ linectr);
+ continue;
+ }
+ if (buf[0] == '\t' && buf[1] == '\t' && isxdigit(buf[2])) {
+ /* protocol spec */
+ u = strtoul(buf+2, &cp, 16);
+ while (isspace(*cp))
+ cp++;
+ if (!*cp) {
+ err("Invalid protocol spec at line %u",
+ linectr);
+ continue;
+ }
+ if (lastclass != -1 && lastsubclass != -1) {
+ if (new_protocol(cp, lastclass, lastsubclass,
+ u))
+ err("Duplicate protocol spec at line %u class %02x:%02x:%02x %s",
+ linectr, lastclass, lastsubclass,
+ u, cp);
+ dbg("line %5u protocol %02x:%02x:%02x %s",
+ linectr, lastclass, lastsubclass, u, cp);
+ continue;
+ }
+ err("Protocol spec without prior Class and Subclass spec at line %u",
+ linectr);
+ continue;
+ }
+ if (buf[0] == 'H' && buf[1] == 'I' &&
+ buf[2] == 'D' && /*isspace(buf[3])*/ buf[3] == ' ') {
+ continue;
+ }
+ if (buf[0] == 'H' && buf[1] == 'U' &&
+ buf[2] == 'T' && /*isspace(buf[3])*/ buf[3] == ' ') {
+ lastlang = lastclass = lastvendor = lastsubclass = -1;
+ /*
+ * set 1 as pseudo-id to indicate that the parser is
+ * in a `HUT' section.
+ */
+ lasthut = 1;
+ continue;
+ }
+ if (buf[0] == 'R' && buf[1] == ' ')
+ continue;
+
+ if (buf[0] == 'V' && buf[1] == 'T')
+ continue;
+
+ err("Unknown line at line %u", linectr);
+ }
+}
+
+
+int names_init(char *n)
+{
+ FILE *f;
+
+ f = fopen(n, "r");
+ if (!f)
+ return errno;
+
+ parse(f);
+ fclose(f);
+ return 0;
+}
--- /dev/null
+/*
+ * names.h -- USB name database manipulation routines
+ *
+ * Copyright (C) 1999, 2000 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.
+ *
+ * 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.
+ *
+ *
+ *
+ * Copyright (C) 2005 Takahiro Hirofuchi
+ * - names_free() is added.
+ */
+
+#ifndef _NAMES_H
+#define _NAMES_H
+
+#include <sys/types.h>
+
+/* used by usbip_common.c */
+extern const char *names_vendor(u_int16_t vendorid);
+extern const char *names_product(u_int16_t vendorid, u_int16_t productid);
+extern const char *names_class(u_int8_t classid);
+extern const char *names_subclass(u_int8_t classid, u_int8_t subclassid);
+extern const char *names_protocol(u_int8_t classid, u_int8_t subclassid,
+ u_int8_t protocolid);
+extern int names_init(char *n);
+extern void names_free(void);
+
+#endif /* _NAMES_H */
--- /dev/null
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "sysfs_utils.h"
+#include "usbip_common.h"
+
+int write_sysfs_attribute(const char *attr_path, const char *new_value,
+ size_t len)
+{
+ int fd;
+ int length;
+
+ fd = open(attr_path, O_WRONLY);
+ if (fd < 0) {
+ dbg("error opening attribute %s", attr_path);
+ return -1;
+ }
+
+ length = write(fd, new_value, len);
+ if (length < 0) {
+ dbg("error writing to attribute %s", attr_path);
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+
+ return 0;
+}
--- /dev/null
+
+#ifndef __SYSFS_UTILS_H
+#define __SYSFS_UTILS_H
+
+int write_sysfs_attribute(const char *attr_path, const char *new_value,
+ size_t len);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2005-2007 Takahiro Hirofuchi
+ */
+
+#include <libudev.h>
+#include "usbip_common.h"
+#include "names.h"
+
+#undef PROGNAME
+#define PROGNAME "libusbip"
+
+int usbip_use_syslog;
+int usbip_use_stderr;
+int usbip_use_debug;
+
+extern struct udev *udev_context;
+
+struct speed_string {
+ int num;
+ char *speed;
+ char *desc;
+};
+
+static const struct speed_string speed_strings[] = {
+ { USB_SPEED_UNKNOWN, "unknown", "Unknown Speed"},
+ { USB_SPEED_LOW, "1.5", "Low Speed(1.5Mbps)" },
+ { USB_SPEED_FULL, "12", "Full Speed(12Mbps)" },
+ { USB_SPEED_HIGH, "480", "High Speed(480Mbps)" },
+ { USB_SPEED_WIRELESS, "53.3-480", "Wireless"},
+ { USB_SPEED_SUPER, "5000", "Super Speed(5000Mbps)" },
+ { 0, NULL, NULL }
+};
+
+struct portst_string {
+ int num;
+ char *desc;
+};
+
+static struct portst_string portst_strings[] = {
+ { SDEV_ST_AVAILABLE, "Device Available" },
+ { SDEV_ST_USED, "Device in Use" },
+ { SDEV_ST_ERROR, "Device Error"},
+ { VDEV_ST_NULL, "Port Available"},
+ { VDEV_ST_NOTASSIGNED, "Port Initializing"},
+ { VDEV_ST_USED, "Port in Use"},
+ { VDEV_ST_ERROR, "Port Error"},
+ { 0, NULL}
+};
+
+const char *usbip_status_string(int32_t status)
+{
+ for (int i = 0; portst_strings[i].desc != NULL; i++)
+ if (portst_strings[i].num == status)
+ return portst_strings[i].desc;
+
+ return "Unknown Status";
+}
+
+const char *usbip_speed_string(int num)
+{
+ for (int i = 0; speed_strings[i].speed != NULL; i++)
+ if (speed_strings[i].num == num)
+ return speed_strings[i].desc;
+
+ return "Unknown Speed";
+}
+
+
+#define DBG_UDEV_INTEGER(name)\
+ dbg("%-20s = %x", to_string(name), (int) udev->name)
+
+#define DBG_UINF_INTEGER(name)\
+ dbg("%-20s = %x", to_string(name), (int) uinf->name)
+
+void dump_usb_interface(struct usbip_usb_interface *uinf)
+{
+ char buff[100];
+
+ usbip_names_get_class(buff, sizeof(buff),
+ uinf->bInterfaceClass,
+ uinf->bInterfaceSubClass,
+ uinf->bInterfaceProtocol);
+ dbg("%-20s = %s", "Interface(C/SC/P)", buff);
+}
+
+void dump_usb_device(struct usbip_usb_device *udev)
+{
+ char buff[100];
+
+ dbg("%-20s = %s", "path", udev->path);
+ dbg("%-20s = %s", "busid", udev->busid);
+
+ usbip_names_get_class(buff, sizeof(buff),
+ udev->bDeviceClass,
+ udev->bDeviceSubClass,
+ udev->bDeviceProtocol);
+ dbg("%-20s = %s", "Device(C/SC/P)", buff);
+
+ DBG_UDEV_INTEGER(bcdDevice);
+
+ usbip_names_get_product(buff, sizeof(buff),
+ udev->idVendor,
+ udev->idProduct);
+ dbg("%-20s = %s", "Vendor/Product", buff);
+
+ DBG_UDEV_INTEGER(bNumConfigurations);
+ DBG_UDEV_INTEGER(bNumInterfaces);
+
+ dbg("%-20s = %s", "speed",
+ usbip_speed_string(udev->speed));
+
+ DBG_UDEV_INTEGER(busnum);
+ DBG_UDEV_INTEGER(devnum);
+}
+
+
+int read_attr_value(struct udev_device *dev, const char *name,
+ const char *format)
+{
+ const char *attr;
+ int num = 0;
+ int ret;
+
+ attr = udev_device_get_sysattr_value(dev, name);
+ if (!attr) {
+ err("udev_device_get_sysattr_value failed");
+ goto err;
+ }
+
+ /* The client chooses the device configuration
+ * when attaching it so right after being bound
+ * to usbip-host on the server the device will
+ * have no configuration.
+ * Therefore, attributes such as bConfigurationValue
+ * and bNumInterfaces will not exist and sscanf will
+ * fail. Check for these cases and don't treat them
+ * as errors.
+ */
+
+ ret = sscanf(attr, format, &num);
+ if (ret < 1) {
+ if (strcmp(name, "bConfigurationValue") &&
+ strcmp(name, "bNumInterfaces")) {
+ err("sscanf failed for attribute %s", name);
+ goto err;
+ }
+ }
+
+err:
+
+ return num;
+}
+
+
+int read_attr_speed(struct udev_device *dev)
+{
+ const char *speed;
+
+ speed = udev_device_get_sysattr_value(dev, "speed");
+ if (!speed) {
+ err("udev_device_get_sysattr_value failed");
+ goto err;
+ }
+
+ for (int i = 0; speed_strings[i].speed != NULL; i++) {
+ if (!strcmp(speed, speed_strings[i].speed))
+ return speed_strings[i].num;
+ }
+
+err:
+
+ return USB_SPEED_UNKNOWN;
+}
+
+#define READ_ATTR(object, type, dev, name, format) \
+ do { \
+ (object)->name = (type) read_attr_value(dev, to_string(name), \
+ format); \
+ } while (0)
+
+
+int read_usb_device(struct udev_device *sdev, struct usbip_usb_device *udev)
+{
+ uint32_t busnum, devnum;
+ const char *path, *name;
+
+ READ_ATTR(udev, uint8_t, sdev, bDeviceClass, "%02x\n");
+ READ_ATTR(udev, uint8_t, sdev, bDeviceSubClass, "%02x\n");
+ READ_ATTR(udev, uint8_t, sdev, bDeviceProtocol, "%02x\n");
+
+ READ_ATTR(udev, uint16_t, sdev, idVendor, "%04x\n");
+ READ_ATTR(udev, uint16_t, sdev, idProduct, "%04x\n");
+ READ_ATTR(udev, uint16_t, sdev, bcdDevice, "%04x\n");
+
+ READ_ATTR(udev, uint8_t, sdev, bConfigurationValue, "%02x\n");
+ READ_ATTR(udev, uint8_t, sdev, bNumConfigurations, "%02x\n");
+ READ_ATTR(udev, uint8_t, sdev, bNumInterfaces, "%02x\n");
+
+ READ_ATTR(udev, uint8_t, sdev, devnum, "%d\n");
+ udev->speed = read_attr_speed(sdev);
+
+ path = udev_device_get_syspath(sdev);
+ name = udev_device_get_sysname(sdev);
+
+ strncpy(udev->path, path, SYSFS_PATH_MAX);
+ strncpy(udev->busid, name, SYSFS_BUS_ID_SIZE);
+
+ sscanf(name, "%u-%u", &busnum, &devnum);
+ udev->busnum = busnum;
+
+ return 0;
+}
+
+int read_usb_interface(struct usbip_usb_device *udev, int i,
+ struct usbip_usb_interface *uinf)
+{
+ char busid[SYSFS_BUS_ID_SIZE];
+ struct udev_device *sif;
+
+ sprintf(busid, "%s:%d.%d", udev->busid, udev->bConfigurationValue, i);
+
+ sif = udev_device_new_from_subsystem_sysname(udev_context, "usb", busid);
+ if (!sif) {
+ err("udev_device_new_from_subsystem_sysname %s failed", busid);
+ return -1;
+ }
+
+ READ_ATTR(uinf, uint8_t, sif, bInterfaceClass, "%02x\n");
+ READ_ATTR(uinf, uint8_t, sif, bInterfaceSubClass, "%02x\n");
+ READ_ATTR(uinf, uint8_t, sif, bInterfaceProtocol, "%02x\n");
+
+ return 0;
+}
+
+int usbip_names_init(char *f)
+{
+ return names_init(f);
+}
+
+void usbip_names_free(void)
+{
+ names_free();
+}
+
+void usbip_names_get_product(char *buff, size_t size, uint16_t vendor,
+ uint16_t product)
+{
+ const char *prod, *vend;
+
+ prod = names_product(vendor, product);
+ if (!prod)
+ prod = "unknown product";
+
+
+ vend = names_vendor(vendor);
+ if (!vend)
+ vend = "unknown vendor";
+
+ snprintf(buff, size, "%s : %s (%04x:%04x)", vend, prod, vendor, product);
+}
+
+void usbip_names_get_class(char *buff, size_t size, uint8_t class,
+ uint8_t subclass, uint8_t protocol)
+{
+ const char *c, *s, *p;
+
+ if (class == 0 && subclass == 0 && protocol == 0) {
+ snprintf(buff, size, "(Defined at Interface level) (%02x/%02x/%02x)", class, subclass, protocol);
+ return;
+ }
+
+ p = names_protocol(class, subclass, protocol);
+ if (!p)
+ p = "unknown protocol";
+
+ s = names_subclass(class, subclass);
+ if (!s)
+ s = "unknown subclass";
+
+ c = names_class(class);
+ if (!c)
+ c = "unknown class";
+
+ snprintf(buff, size, "%s / %s / %s (%02x/%02x/%02x)", c, s, p, class, subclass, protocol);
+}
--- /dev/null
+/*
+ * Copyright (C) 2005-2007 Takahiro Hirofuchi
+ */
+
+#ifndef __USBIP_COMMON_H
+#define __USBIP_COMMON_H
+
+#include <libudev.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <syslog.h>
+#include <unistd.h>
+#include <linux/usb/ch9.h>
+#include "../../uapi/usbip.h"
+
+#ifndef USBIDS_FILE
+#define USBIDS_FILE "/usr/share/hwdata/usb.ids"
+#endif
+
+#ifndef VHCI_STATE_PATH
+#define VHCI_STATE_PATH "/var/run/vhci_hcd"
+#endif
+
+/* kernel module names */
+#define USBIP_CORE_MOD_NAME "usbip-core"
+#define USBIP_HOST_DRV_NAME "usbip-host"
+#define USBIP_VHCI_DRV_NAME "vhci_hcd"
+
+/* sysfs constants */
+#define SYSFS_MNT_PATH "/sys"
+#define SYSFS_BUS_NAME "bus"
+#define SYSFS_BUS_TYPE "usb"
+#define SYSFS_DRIVERS_NAME "drivers"
+
+#define SYSFS_PATH_MAX 256
+#define SYSFS_BUS_ID_SIZE 32
+
+extern int usbip_use_syslog;
+extern int usbip_use_stderr;
+extern int usbip_use_debug ;
+
+#define PROGNAME "usbip"
+
+#define pr_fmt(fmt) "%s: %s: " fmt "\n", PROGNAME
+#define dbg_fmt(fmt) pr_fmt("%s:%d:[%s] " fmt), "debug", \
+ __FILE__, __LINE__, __func__
+
+#define err(fmt, args...) \
+ do { \
+ if (usbip_use_syslog) { \
+ syslog(LOG_ERR, pr_fmt(fmt), "error", ##args); \
+ } \
+ if (usbip_use_stderr) { \
+ fprintf(stderr, pr_fmt(fmt), "error", ##args); \
+ } \
+ } while (0)
+
+#define info(fmt, args...) \
+ do { \
+ if (usbip_use_syslog) { \
+ syslog(LOG_INFO, pr_fmt(fmt), "info", ##args); \
+ } \
+ if (usbip_use_stderr) { \
+ fprintf(stderr, pr_fmt(fmt), "info", ##args); \
+ } \
+ } while (0)
+
+#define dbg(fmt, args...) \
+ do { \
+ if (usbip_use_debug) { \
+ if (usbip_use_syslog) { \
+ syslog(LOG_DEBUG, dbg_fmt(fmt), ##args); \
+ } \
+ if (usbip_use_stderr) { \
+ fprintf(stderr, dbg_fmt(fmt), ##args); \
+ } \
+ } \
+ } while (0)
+
+#define BUG() \
+ do { \
+ err("sorry, it's a bug!"); \
+ abort(); \
+ } while (0)
+
+struct usbip_usb_interface {
+ uint8_t bInterfaceClass;
+ uint8_t bInterfaceSubClass;
+ uint8_t bInterfaceProtocol;
+ uint8_t padding; /* alignment */
+} __attribute__((packed));
+
+struct usbip_usb_device {
+ char path[SYSFS_PATH_MAX];
+ char busid[SYSFS_BUS_ID_SIZE];
+
+ uint32_t busnum;
+ uint32_t devnum;
+ uint32_t speed;
+
+ uint16_t idVendor;
+ uint16_t idProduct;
+ uint16_t bcdDevice;
+
+ uint8_t bDeviceClass;
+ uint8_t bDeviceSubClass;
+ uint8_t bDeviceProtocol;
+ uint8_t bConfigurationValue;
+ uint8_t bNumConfigurations;
+ uint8_t bNumInterfaces;
+} __attribute__((packed));
+
+#define to_string(s) #s
+
+void dump_usb_interface(struct usbip_usb_interface *);
+void dump_usb_device(struct usbip_usb_device *);
+int read_usb_device(struct udev_device *sdev, struct usbip_usb_device *udev);
+int read_attr_value(struct udev_device *dev, const char *name,
+ const char *format);
+int read_usb_interface(struct usbip_usb_device *udev, int i,
+ struct usbip_usb_interface *uinf);
+
+const char *usbip_speed_string(int num);
+const char *usbip_status_string(int32_t status);
+
+int usbip_names_init(char *);
+void usbip_names_free(void);
+void usbip_names_get_product(char *buff, size_t size, uint16_t vendor,
+ uint16_t product);
+void usbip_names_get_class(char *buff, size_t size, uint8_t class,
+ uint8_t subclass, uint8_t protocol);
+
+#endif /* __USBIP_COMMON_H */
--- /dev/null
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <errno.h>
+#include <unistd.h>
+
+#include <libudev.h>
+
+#include "usbip_common.h"
+#include "usbip_host_driver.h"
+#include "list.h"
+#include "sysfs_utils.h"
+
+#undef PROGNAME
+#define PROGNAME "libusbip"
+
+struct usbip_host_driver *host_driver;
+struct udev *udev_context;
+
+static int32_t read_attr_usbip_status(struct usbip_usb_device *udev)
+{
+ char status_attr_path[SYSFS_PATH_MAX];
+ int fd;
+ int length;
+ char status;
+ int value = 0;
+
+ snprintf(status_attr_path, SYSFS_PATH_MAX, "%s/usbip_status",
+ udev->path);
+
+ fd = open(status_attr_path, O_RDONLY);
+ if (fd < 0) {
+ err("error opening attribute %s", status_attr_path);
+ return -1;
+ }
+
+ length = read(fd, &status, 1);
+ if (length < 0) {
+ err("error reading attribute %s", status_attr_path);
+ close(fd);
+ return -1;
+ }
+
+ value = atoi(&status);
+
+ return value;
+}
+
+static
+struct usbip_exported_device *usbip_exported_device_new(const char *sdevpath)
+{
+ struct usbip_exported_device *edev = NULL;
+ struct usbip_exported_device *edev_old;
+ size_t size;
+ int i;
+
+ edev = calloc(1, sizeof(struct usbip_exported_device));
+
+ edev->sudev = udev_device_new_from_syspath(udev_context, sdevpath);
+ if (!edev->sudev) {
+ err("udev_device_new_from_syspath: %s", sdevpath);
+ goto err;
+ }
+
+ read_usb_device(edev->sudev, &edev->udev);
+
+ edev->status = read_attr_usbip_status(&edev->udev);
+ if (edev->status < 0)
+ goto err;
+
+ /* reallocate buffer to include usb interface data */
+ size = sizeof(struct usbip_exported_device) +
+ edev->udev.bNumInterfaces * sizeof(struct usbip_usb_interface);
+
+ edev_old = edev;
+ edev = realloc(edev, size);
+ if (!edev) {
+ edev = edev_old;
+ dbg("realloc failed");
+ goto err;
+ }
+
+ for (i = 0; i < edev->udev.bNumInterfaces; i++)
+ read_usb_interface(&edev->udev, i, &edev->uinf[i]);
+
+ return edev;
+err:
+ if (edev->sudev)
+ udev_device_unref(edev->sudev);
+ if (edev)
+ free(edev);
+
+ return NULL;
+}
+
+static int refresh_exported_devices(void)
+{
+ struct usbip_exported_device *edev;
+ struct udev_enumerate *enumerate;
+ struct udev_list_entry *devices, *dev_list_entry;
+ struct udev_device *dev;
+ const char *path;
+ const char *driver;
+
+ enumerate = udev_enumerate_new(udev_context);
+ udev_enumerate_add_match_subsystem(enumerate, "usb");
+ udev_enumerate_scan_devices(enumerate);
+
+ devices = udev_enumerate_get_list_entry(enumerate);
+
+ udev_list_entry_foreach(dev_list_entry, devices) {
+ path = udev_list_entry_get_name(dev_list_entry);
+ dev = udev_device_new_from_syspath(udev_context, path);
+ if (dev == NULL)
+ continue;
+
+ /* Check whether device uses usbip-host driver. */
+ driver = udev_device_get_driver(dev);
+ if (driver != NULL && !strcmp(driver, USBIP_HOST_DRV_NAME)) {
+ edev = usbip_exported_device_new(path);
+ if (!edev) {
+ dbg("usbip_exported_device_new failed");
+ continue;
+ }
+
+ list_add(&edev->node, &host_driver->edev_list);
+ host_driver->ndevs++;
+ }
+ }
+
+ return 0;
+}
+
+static void usbip_exported_device_destroy(void)
+{
+ struct list_head *i, *tmp;
+ struct usbip_exported_device *edev;
+
+ list_for_each_safe(i, tmp, &host_driver->edev_list) {
+ edev = list_entry(i, struct usbip_exported_device, node);
+ list_del(i);
+ free(edev);
+ }
+}
+
+int usbip_host_driver_open(void)
+{
+ int rc;
+
+ udev_context = udev_new();
+ if (!udev_context) {
+ err("udev_new failed");
+ return -1;
+ }
+
+ host_driver = calloc(1, sizeof(*host_driver));
+
+ host_driver->ndevs = 0;
+ INIT_LIST_HEAD(&host_driver->edev_list);
+
+ rc = refresh_exported_devices();
+ if (rc < 0)
+ goto err_free_host_driver;
+
+ return 0;
+
+err_free_host_driver:
+ free(host_driver);
+ host_driver = NULL;
+
+ udev_unref(udev_context);
+
+ return -1;
+}
+
+void usbip_host_driver_close(void)
+{
+ if (!host_driver)
+ return;
+
+ usbip_exported_device_destroy();
+
+ free(host_driver);
+ host_driver = NULL;
+
+ udev_unref(udev_context);
+}
+
+int usbip_host_refresh_device_list(void)
+{
+ int rc;
+
+ usbip_exported_device_destroy();
+
+ host_driver->ndevs = 0;
+ INIT_LIST_HEAD(&host_driver->edev_list);
+
+ rc = refresh_exported_devices();
+ if (rc < 0)
+ return -1;
+
+ return 0;
+}
+
+int usbip_host_export_device(struct usbip_exported_device *edev, int sockfd)
+{
+ char attr_name[] = "usbip_sockfd";
+ char sockfd_attr_path[SYSFS_PATH_MAX];
+ char sockfd_buff[30];
+ int ret;
+
+ if (edev->status != SDEV_ST_AVAILABLE) {
+ dbg("device not available: %s", edev->udev.busid);
+ switch (edev->status) {
+ case SDEV_ST_ERROR:
+ dbg("status SDEV_ST_ERROR");
+ break;
+ case SDEV_ST_USED:
+ dbg("status SDEV_ST_USED");
+ break;
+ default:
+ dbg("status unknown: 0x%x", edev->status);
+ }
+ return -1;
+ }
+
+ /* only the first interface is true */
+ snprintf(sockfd_attr_path, sizeof(sockfd_attr_path), "%s/%s",
+ edev->udev.path, attr_name);
+
+ snprintf(sockfd_buff, sizeof(sockfd_buff), "%d\n", sockfd);
+
+ ret = write_sysfs_attribute(sockfd_attr_path, sockfd_buff,
+ strlen(sockfd_buff));
+ if (ret < 0) {
+ err("write_sysfs_attribute failed: sockfd %s to %s",
+ sockfd_buff, sockfd_attr_path);
+ return ret;
+ }
+
+ info("connect: %s", edev->udev.busid);
+
+ return ret;
+}
+
+struct usbip_exported_device *usbip_host_get_device(int num)
+{
+ struct list_head *i;
+ struct usbip_exported_device *edev;
+ int cnt = 0;
+
+ list_for_each(i, &host_driver->edev_list) {
+ edev = list_entry(i, struct usbip_exported_device, node);
+ if (num == cnt)
+ return edev;
+ else
+ cnt++;
+ }
+
+ return NULL;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __USBIP_HOST_DRIVER_H
+#define __USBIP_HOST_DRIVER_H
+
+#include <stdint.h>
+#include "usbip_common.h"
+#include "list.h"
+
+struct usbip_host_driver {
+ int ndevs;
+ /* list of exported device */
+ struct list_head edev_list;
+};
+
+struct usbip_exported_device {
+ struct udev_device *sudev;
+ int32_t status;
+ struct usbip_usb_device udev;
+ struct list_head node;
+ struct usbip_usb_interface uinf[];
+};
+
+extern struct usbip_host_driver *host_driver;
+
+int usbip_host_driver_open(void);
+void usbip_host_driver_close(void);
+
+int usbip_host_refresh_device_list(void);
+int usbip_host_export_device(struct usbip_exported_device *edev, int sockfd);
+struct usbip_exported_device *usbip_host_get_device(int num);
+
+#endif /* __USBIP_HOST_DRIVER_H */
--- /dev/null
+/*
+ * Copyright (C) 2005-2007 Takahiro Hirofuchi
+ */
+
+#include "usbip_common.h"
+#include "vhci_driver.h"
+#include <limits.h>
+#include <netdb.h>
+#include <libudev.h>
+#include "sysfs_utils.h"
+
+#undef PROGNAME
+#define PROGNAME "libusbip"
+
+struct usbip_vhci_driver *vhci_driver;
+struct udev *udev_context;
+
+static struct usbip_imported_device *
+imported_device_init(struct usbip_imported_device *idev, char *busid)
+{
+ struct udev_device *sudev;
+
+ sudev = udev_device_new_from_subsystem_sysname(udev_context,
+ "usb", busid);
+ if (!sudev) {
+ dbg("udev_device_new_from_subsystem_sysname failed: %s", busid);
+ goto err;
+ }
+ read_usb_device(sudev, &idev->udev);
+ udev_device_unref(sudev);
+
+ return idev;
+
+err:
+ return NULL;
+}
+
+
+
+static int parse_status(const char *value)
+{
+ int ret = 0;
+ char *c;
+
+
+ for (int i = 0; i < vhci_driver->nports; i++)
+ memset(&vhci_driver->idev[i], 0, sizeof(vhci_driver->idev[i]));
+
+
+ /* skip a header line */
+ c = strchr(value, '\n');
+ if (!c)
+ return -1;
+ c++;
+
+ while (*c != '\0') {
+ int port, status, speed, devid;
+ unsigned long socket;
+ char lbusid[SYSFS_BUS_ID_SIZE];
+
+ ret = sscanf(c, "%d %d %d %x %lx %31s\n",
+ &port, &status, &speed,
+ &devid, &socket, lbusid);
+
+ if (ret < 5) {
+ dbg("sscanf failed: %d", ret);
+ BUG();
+ }
+
+ dbg("port %d status %d speed %d devid %x",
+ port, status, speed, devid);
+ dbg("socket %lx lbusid %s", socket, lbusid);
+
+
+ /* if a device is connected, look at it */
+ {
+ struct usbip_imported_device *idev = &vhci_driver->idev[port];
+
+ idev->port = port;
+ idev->status = status;
+
+ idev->devid = devid;
+
+ idev->busnum = (devid >> 16);
+ idev->devnum = (devid & 0x0000ffff);
+
+ if (idev->status != VDEV_ST_NULL
+ && idev->status != VDEV_ST_NOTASSIGNED) {
+ idev = imported_device_init(idev, lbusid);
+ if (!idev) {
+ dbg("imported_device_init failed");
+ return -1;
+ }
+ }
+ }
+
+
+ /* go to the next line */
+ c = strchr(c, '\n');
+ if (!c)
+ break;
+ c++;
+ }
+
+ dbg("exit");
+
+ return 0;
+}
+
+static int refresh_imported_device_list(void)
+{
+ const char *attr_status;
+
+ attr_status = udev_device_get_sysattr_value(vhci_driver->hc_device,
+ "status");
+ if (!attr_status) {
+ err("udev_device_get_sysattr_value failed");
+ return -1;
+ }
+
+ return parse_status(attr_status);
+}
+
+static int get_nports(void)
+{
+ char *c;
+ int nports = 0;
+ const char *attr_status;
+
+ attr_status = udev_device_get_sysattr_value(vhci_driver->hc_device,
+ "status");
+ if (!attr_status) {
+ err("udev_device_get_sysattr_value failed");
+ return -1;
+ }
+
+ /* skip a header line */
+ c = strchr(attr_status, '\n');
+ if (!c)
+ return 0;
+ c++;
+
+ while (*c != '\0') {
+ /* go to the next line */
+ c = strchr(c, '\n');
+ if (!c)
+ return nports;
+ c++;
+ nports += 1;
+ }
+
+ return nports;
+}
+
+/*
+ * Read the given port's record.
+ *
+ * To avoid buffer overflow we will read the entire line and
+ * validate each part's size. The initial buffer is padded by 4 to
+ * accommodate the 2 spaces, 1 newline and an additional character
+ * which is needed to properly validate the 3rd part without it being
+ * truncated to an acceptable length.
+ */
+static int read_record(int rhport, char *host, unsigned long host_len,
+ char *port, unsigned long port_len, char *busid)
+{
+ int part;
+ FILE *file;
+ char path[PATH_MAX+1];
+ char *buffer, *start, *end;
+ char delim[] = {' ', ' ', '\n'};
+ int max_len[] = {(int)host_len, (int)port_len, SYSFS_BUS_ID_SIZE};
+ size_t buffer_len = host_len + port_len + SYSFS_BUS_ID_SIZE + 4;
+
+ buffer = malloc(buffer_len);
+ if (!buffer)
+ return -1;
+
+ snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport);
+
+ file = fopen(path, "r");
+ if (!file) {
+ err("fopen");
+ free(buffer);
+ return -1;
+ }
+
+ if (fgets(buffer, buffer_len, file) == NULL) {
+ err("fgets");
+ free(buffer);
+ fclose(file);
+ return -1;
+ }
+ fclose(file);
+
+ /* validate the length of each of the 3 parts */
+ start = buffer;
+ for (part = 0; part < 3; part++) {
+ end = strchr(start, delim[part]);
+ if (end == NULL || (end - start) > max_len[part]) {
+ free(buffer);
+ return -1;
+ }
+ start = end + 1;
+ }
+
+ if (sscanf(buffer, "%s %s %s\n", host, port, busid) != 3) {
+ err("sscanf");
+ free(buffer);
+ return -1;
+ }
+
+ free(buffer);
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+int usbip_vhci_driver_open(void)
+{
+ udev_context = udev_new();
+ if (!udev_context) {
+ err("udev_new failed");
+ return -1;
+ }
+
+ vhci_driver = calloc(1, sizeof(struct usbip_vhci_driver));
+
+ /* will be freed in usbip_driver_close() */
+ vhci_driver->hc_device =
+ udev_device_new_from_subsystem_sysname(udev_context,
+ USBIP_VHCI_BUS_TYPE,
+ USBIP_VHCI_DRV_NAME);
+ if (!vhci_driver->hc_device) {
+ err("udev_device_new_from_subsystem_sysname failed");
+ goto err;
+ }
+
+ vhci_driver->nports = get_nports();
+
+ dbg("available ports: %d", vhci_driver->nports);
+
+ if (refresh_imported_device_list())
+ goto err;
+
+ return 0;
+
+err:
+ udev_device_unref(vhci_driver->hc_device);
+
+ if (vhci_driver)
+ free(vhci_driver);
+
+ vhci_driver = NULL;
+
+ udev_unref(udev_context);
+
+ return -1;
+}
+
+
+void usbip_vhci_driver_close(void)
+{
+ if (!vhci_driver)
+ return;
+
+ udev_device_unref(vhci_driver->hc_device);
+
+ free(vhci_driver);
+
+ vhci_driver = NULL;
+
+ udev_unref(udev_context);
+}
+
+
+int usbip_vhci_refresh_device_list(void)
+{
+
+ if (refresh_imported_device_list())
+ goto err;
+
+ return 0;
+err:
+ dbg("failed to refresh device list");
+ return -1;
+}
+
+
+int usbip_vhci_get_free_port(void)
+{
+ for (int i = 0; i < vhci_driver->nports; i++) {
+ if (vhci_driver->idev[i].status == VDEV_ST_NULL)
+ return i;
+ }
+
+ return -1;
+}
+
+int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
+ uint32_t speed) {
+ char buff[200]; /* what size should be ? */
+ char attach_attr_path[SYSFS_PATH_MAX];
+ char attr_attach[] = "attach";
+ const char *path;
+ int ret;
+
+ snprintf(buff, sizeof(buff), "%u %d %u %u",
+ port, sockfd, devid, speed);
+ dbg("writing: %s", buff);
+
+ path = udev_device_get_syspath(vhci_driver->hc_device);
+ snprintf(attach_attr_path, sizeof(attach_attr_path), "%s/%s",
+ path, attr_attach);
+ dbg("attach attribute path: %s", attach_attr_path);
+
+ ret = write_sysfs_attribute(attach_attr_path, buff, strlen(buff));
+ if (ret < 0) {
+ dbg("write_sysfs_attribute failed");
+ return -1;
+ }
+
+ dbg("attached port: %d", port);
+
+ return 0;
+}
+
+static unsigned long get_devid(uint8_t busnum, uint8_t devnum)
+{
+ return (busnum << 16) | devnum;
+}
+
+/* will be removed */
+int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum,
+ uint8_t devnum, uint32_t speed)
+{
+ int devid = get_devid(busnum, devnum);
+
+ return usbip_vhci_attach_device2(port, sockfd, devid, speed);
+}
+
+int usbip_vhci_detach_device(uint8_t port)
+{
+ char detach_attr_path[SYSFS_PATH_MAX];
+ char attr_detach[] = "detach";
+ char buff[200]; /* what size should be ? */
+ const char *path;
+ int ret;
+
+ snprintf(buff, sizeof(buff), "%u", port);
+ dbg("writing: %s", buff);
+
+ path = udev_device_get_syspath(vhci_driver->hc_device);
+ snprintf(detach_attr_path, sizeof(detach_attr_path), "%s/%s",
+ path, attr_detach);
+ dbg("detach attribute path: %s", detach_attr_path);
+
+ ret = write_sysfs_attribute(detach_attr_path, buff, strlen(buff));
+ if (ret < 0) {
+ dbg("write_sysfs_attribute failed");
+ return -1;
+ }
+
+ dbg("detached port: %d", port);
+
+ return 0;
+}
+
+int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev)
+{
+ char product_name[100];
+ char host[NI_MAXHOST] = "unknown host";
+ char serv[NI_MAXSERV] = "unknown port";
+ char remote_busid[SYSFS_BUS_ID_SIZE];
+ int ret;
+ int read_record_error = 0;
+
+ if (idev->status == VDEV_ST_NULL || idev->status == VDEV_ST_NOTASSIGNED)
+ return 0;
+
+ ret = read_record(idev->port, host, sizeof(host), serv, sizeof(serv),
+ remote_busid);
+ if (ret) {
+ err("read_record");
+ read_record_error = 1;
+ }
+
+ printf("Port %02d: <%s> at %s\n", idev->port,
+ usbip_status_string(idev->status),
+ usbip_speed_string(idev->udev.speed));
+
+ usbip_names_get_product(product_name, sizeof(product_name),
+ idev->udev.idVendor, idev->udev.idProduct);
+
+ printf(" %s\n", product_name);
+
+ if (!read_record_error) {
+ printf("%10s -> usbip://%s:%s/%s\n", idev->udev.busid,
+ host, serv, remote_busid);
+ printf("%10s -> remote bus/dev %03d/%03d\n", " ",
+ idev->busnum, idev->devnum);
+ } else {
+ printf("%10s -> unknown host, remote port and remote busid\n",
+ idev->udev.busid);
+ printf("%10s -> remote bus/dev %03d/%03d\n", " ",
+ idev->busnum, idev->devnum);
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2005-2007 Takahiro Hirofuchi
+ */
+
+#ifndef __VHCI_DRIVER_H
+#define __VHCI_DRIVER_H
+
+#include <libudev.h>
+#include <stdint.h>
+
+#include "usbip_common.h"
+
+#define USBIP_VHCI_BUS_TYPE "platform"
+#define MAXNPORT 128
+
+struct usbip_imported_device {
+ uint8_t port;
+ uint32_t status;
+
+ uint32_t devid;
+
+ uint8_t busnum;
+ uint8_t devnum;
+
+ /* usbip_class_device list */
+ struct usbip_usb_device udev;
+};
+
+struct usbip_vhci_driver {
+
+ /* /sys/devices/platform/vhci_hcd */
+ struct udev_device *hc_device;
+
+ int nports;
+ struct usbip_imported_device idev[MAXNPORT];
+};
+
+
+extern struct usbip_vhci_driver *vhci_driver;
+
+int usbip_vhci_driver_open(void);
+void usbip_vhci_driver_close(void);
+
+int usbip_vhci_refresh_device_list(void);
+
+
+int usbip_vhci_get_free_port(void);
+int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
+ uint32_t speed);
+
+/* will be removed */
+int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum,
+ uint8_t devnum, uint32_t speed);
+
+int usbip_vhci_detach_device(uint8_t port);
+
+int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev);
+
+#endif /* __VHCI_DRIVER_H */
--- /dev/null
+AM_CPPFLAGS = -I$(top_srcdir)/libsrc -DUSBIDS_FILE='"@USBIDS_DIR@/usb.ids"'
+AM_CFLAGS = @EXTRA_CFLAGS@
+LDADD = $(top_builddir)/libsrc/libusbip.la
+
+sbin_PROGRAMS := usbip usbipd
+
+usbip_SOURCES := usbip.h utils.h usbip.c utils.c usbip_network.c \
+ usbip_attach.c usbip_detach.c usbip_list.c \
+ usbip_bind.c usbip_unbind.c usbip_port.c
+
+usbipd_SOURCES := usbip_network.h usbipd.c usbip_network.c
--- /dev/null
+/*
+ * command structure borrowed from udev
+ * (git://git.kernel.org/pub/scm/linux/hotplug/udev.git)
+ *
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <getopt.h>
+#include <syslog.h>
+
+#include "usbip_common.h"
+#include "usbip_network.h"
+#include "usbip.h"
+
+static int usbip_help(int argc, char *argv[]);
+static int usbip_version(int argc, char *argv[]);
+
+static const char usbip_version_string[] = PACKAGE_STRING;
+
+static const char usbip_usage_string[] =
+ "usbip [--debug] [--log] [--tcp-port PORT] [version]\n"
+ " [help] <command> <args>\n";
+
+static void usbip_usage(void)
+{
+ printf("usage: %s", usbip_usage_string);
+}
+
+struct command {
+ const char *name;
+ int (*fn)(int argc, char *argv[]);
+ const char *help;
+ void (*usage)(void);
+};
+
+static const struct command cmds[] = {
+ {
+ .name = "help",
+ .fn = usbip_help,
+ .help = NULL,
+ .usage = NULL
+ },
+ {
+ .name = "version",
+ .fn = usbip_version,
+ .help = NULL,
+ .usage = NULL
+ },
+ {
+ .name = "attach",
+ .fn = usbip_attach,
+ .help = "Attach a remote USB device",
+ .usage = usbip_attach_usage
+ },
+ {
+ .name = "detach",
+ .fn = usbip_detach,
+ .help = "Detach a remote USB device",
+ .usage = usbip_detach_usage
+ },
+ {
+ .name = "list",
+ .fn = usbip_list,
+ .help = "List exportable or local USB devices",
+ .usage = usbip_list_usage
+ },
+ {
+ .name = "bind",
+ .fn = usbip_bind,
+ .help = "Bind device to " USBIP_HOST_DRV_NAME ".ko",
+ .usage = usbip_bind_usage
+ },
+ {
+ .name = "unbind",
+ .fn = usbip_unbind,
+ .help = "Unbind device from " USBIP_HOST_DRV_NAME ".ko",
+ .usage = usbip_unbind_usage
+ },
+ {
+ .name = "port",
+ .fn = usbip_port_show,
+ .help = "Show imported USB devices",
+ .usage = NULL
+ },
+ { NULL, NULL, NULL, NULL }
+};
+
+static int usbip_help(int argc, char *argv[])
+{
+ const struct command *cmd;
+ int i;
+ int ret = 0;
+
+ if (argc > 1 && argv++) {
+ for (i = 0; cmds[i].name != NULL; i++)
+ if (!strcmp(cmds[i].name, argv[0]) && cmds[i].usage) {
+ cmds[i].usage();
+ goto done;
+ }
+ ret = -1;
+ }
+
+ usbip_usage();
+ printf("\n");
+ for (cmd = cmds; cmd->name != NULL; cmd++)
+ if (cmd->help != NULL)
+ printf(" %-10s %s\n", cmd->name, cmd->help);
+ printf("\n");
+done:
+ return ret;
+}
+
+static int usbip_version(int argc, char *argv[])
+{
+ (void) argc;
+ (void) argv;
+
+ printf(PROGNAME " (%s)\n", usbip_version_string);
+ return 0;
+}
+
+static int run_command(const struct command *cmd, int argc, char *argv[])
+{
+ dbg("running command: `%s'", cmd->name);
+ return cmd->fn(argc, argv);
+}
+
+int main(int argc, char *argv[])
+{
+ static const struct option opts[] = {
+ { "debug", no_argument, NULL, 'd' },
+ { "log", no_argument, NULL, 'l' },
+ { "tcp-port", required_argument, NULL, 't' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ char *cmd;
+ int opt;
+ int i, rc = -1;
+
+ usbip_use_stderr = 1;
+ opterr = 0;
+ for (;;) {
+ opt = getopt_long(argc, argv, "+dlt:", opts, NULL);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'd':
+ usbip_use_debug = 1;
+ break;
+ case 'l':
+ usbip_use_syslog = 1;
+ openlog("", LOG_PID, LOG_USER);
+ break;
+ case 't':
+ usbip_setup_port_number(optarg);
+ break;
+ case '?':
+ printf("usbip: invalid option\n");
+ default:
+ usbip_usage();
+ goto out;
+ }
+ }
+
+ cmd = argv[optind];
+ if (cmd) {
+ for (i = 0; cmds[i].name != NULL; i++)
+ if (!strcmp(cmds[i].name, cmd)) {
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+ rc = run_command(&cmds[i], argc, argv);
+ goto out;
+ }
+ }
+
+ /* invalid command */
+ usbip_help(0, NULL);
+out:
+ return (rc > -1 ? EXIT_SUCCESS : EXIT_FAILURE);
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __USBIP_H
+#define __USBIP_H
+
+#ifdef HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
+/* usbip commands */
+int usbip_attach(int argc, char *argv[]);
+int usbip_detach(int argc, char *argv[]);
+int usbip_list(int argc, char *argv[]);
+int usbip_bind(int argc, char *argv[]);
+int usbip_unbind(int argc, char *argv[]);
+int usbip_port_show(int argc, char *argv[]);
+
+void usbip_attach_usage(void);
+void usbip_detach_usage(void);
+void usbip_list_usage(void);
+void usbip_bind_usage(void);
+void usbip_unbind_usage(void);
+
+#endif /* __USBIP_H */
--- /dev/null
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/stat.h>
+
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <fcntl.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "vhci_driver.h"
+#include "usbip_common.h"
+#include "usbip_network.h"
+#include "usbip.h"
+
+static const char usbip_attach_usage_string[] =
+ "usbip attach <args>\n"
+ " -r, --remote=<host> The machine with exported USB devices\n"
+ " -b, --busid=<busid> Busid of the device on <host>\n";
+
+void usbip_attach_usage(void)
+{
+ printf("usage: %s", usbip_attach_usage_string);
+}
+
+#define MAX_BUFF 100
+static int record_connection(char *host, char *port, char *busid, int rhport)
+{
+ int fd;
+ char path[PATH_MAX+1];
+ char buff[MAX_BUFF+1];
+ int ret;
+
+ ret = mkdir(VHCI_STATE_PATH, 0700);
+ if (ret < 0) {
+ /* if VHCI_STATE_PATH exists, then it better be a directory */
+ if (errno == EEXIST) {
+ struct stat s;
+
+ ret = stat(VHCI_STATE_PATH, &s);
+ if (ret < 0)
+ return -1;
+ if (!(s.st_mode & S_IFDIR))
+ return -1;
+ } else
+ return -1;
+ }
+
+ snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport);
+
+ fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU);
+ if (fd < 0)
+ return -1;
+
+ snprintf(buff, MAX_BUFF, "%s %s %s\n",
+ host, port, busid);
+
+ ret = write(fd, buff, strlen(buff));
+ if (ret != (ssize_t) strlen(buff)) {
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+
+ return 0;
+}
+
+static int import_device(int sockfd, struct usbip_usb_device *udev)
+{
+ int rc;
+ int port;
+
+ rc = usbip_vhci_driver_open();
+ if (rc < 0) {
+ err("open vhci_driver");
+ return -1;
+ }
+
+ port = usbip_vhci_get_free_port();
+ if (port < 0) {
+ err("no free port");
+ usbip_vhci_driver_close();
+ return -1;
+ }
+
+ rc = usbip_vhci_attach_device(port, sockfd, udev->busnum,
+ udev->devnum, udev->speed);
+ if (rc < 0) {
+ err("import device");
+ usbip_vhci_driver_close();
+ return -1;
+ }
+
+ usbip_vhci_driver_close();
+
+ return port;
+}
+
+static int query_import_device(int sockfd, char *busid)
+{
+ int rc;
+ struct op_import_request request;
+ struct op_import_reply reply;
+ uint16_t code = OP_REP_IMPORT;
+
+ memset(&request, 0, sizeof(request));
+ memset(&reply, 0, sizeof(reply));
+
+ /* send a request */
+ rc = usbip_net_send_op_common(sockfd, OP_REQ_IMPORT, 0);
+ if (rc < 0) {
+ err("send op_common");
+ return -1;
+ }
+
+ strncpy(request.busid, busid, SYSFS_BUS_ID_SIZE-1);
+
+ PACK_OP_IMPORT_REQUEST(0, &request);
+
+ rc = usbip_net_send(sockfd, (void *) &request, sizeof(request));
+ if (rc < 0) {
+ err("send op_import_request");
+ return -1;
+ }
+
+ /* receive a reply */
+ rc = usbip_net_recv_op_common(sockfd, &code);
+ if (rc < 0) {
+ err("recv op_common");
+ return -1;
+ }
+
+ rc = usbip_net_recv(sockfd, (void *) &reply, sizeof(reply));
+ if (rc < 0) {
+ err("recv op_import_reply");
+ return -1;
+ }
+
+ PACK_OP_IMPORT_REPLY(0, &reply);
+
+ /* check the reply */
+ if (strncmp(reply.udev.busid, busid, SYSFS_BUS_ID_SIZE)) {
+ err("recv different busid %s", reply.udev.busid);
+ return -1;
+ }
+
+ /* import a device */
+ return import_device(sockfd, &reply.udev);
+}
+
+static int attach_device(char *host, char *busid)
+{
+ int sockfd;
+ int rc;
+ int rhport;
+
+ sockfd = usbip_net_tcp_connect(host, usbip_port_string);
+ if (sockfd < 0) {
+ err("tcp connect");
+ return -1;
+ }
+
+ rhport = query_import_device(sockfd, busid);
+ if (rhport < 0) {
+ err("query");
+ return -1;
+ }
+
+ close(sockfd);
+
+ rc = record_connection(host, usbip_port_string, busid, rhport);
+ if (rc < 0) {
+ err("record connection");
+ return -1;
+ }
+
+ return 0;
+}
+
+int usbip_attach(int argc, char *argv[])
+{
+ static const struct option opts[] = {
+ { "remote", required_argument, NULL, 'r' },
+ { "busid", required_argument, NULL, 'b' },
+ { NULL, 0, NULL, 0 }
+ };
+ char *host = NULL;
+ char *busid = NULL;
+ int opt;
+ int ret = -1;
+
+ for (;;) {
+ opt = getopt_long(argc, argv, "r:b:", opts, NULL);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'r':
+ host = optarg;
+ break;
+ case 'b':
+ busid = optarg;
+ break;
+ default:
+ goto err_out;
+ }
+ }
+
+ if (!host || !busid)
+ goto err_out;
+
+ ret = attach_device(host, busid);
+ goto out;
+
+err_out:
+ usbip_attach_usage();
+out:
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <libudev.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <getopt.h>
+
+#include "usbip_common.h"
+#include "utils.h"
+#include "usbip.h"
+#include "sysfs_utils.h"
+
+enum unbind_status {
+ UNBIND_ST_OK,
+ UNBIND_ST_USBIP_HOST,
+ UNBIND_ST_FAILED
+};
+
+static const char usbip_bind_usage_string[] =
+ "usbip bind <args>\n"
+ " -b, --busid=<busid> Bind " USBIP_HOST_DRV_NAME ".ko to device "
+ "on <busid>\n";
+
+void usbip_bind_usage(void)
+{
+ printf("usage: %s", usbip_bind_usage_string);
+}
+
+/* call at unbound state */
+static int bind_usbip(char *busid)
+{
+ char attr_name[] = "bind";
+ char bind_attr_path[SYSFS_PATH_MAX];
+ int rc = -1;
+
+ snprintf(bind_attr_path, sizeof(bind_attr_path), "%s/%s/%s/%s/%s/%s",
+ SYSFS_MNT_PATH, SYSFS_BUS_NAME, SYSFS_BUS_TYPE,
+ SYSFS_DRIVERS_NAME, USBIP_HOST_DRV_NAME, attr_name);
+
+ rc = write_sysfs_attribute(bind_attr_path, busid, strlen(busid));
+ if (rc < 0) {
+ err("error binding device %s to driver: %s", busid,
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+/* buggy driver may cause dead lock */
+static int unbind_other(char *busid)
+{
+ enum unbind_status status = UNBIND_ST_OK;
+
+ char attr_name[] = "unbind";
+ char unbind_attr_path[SYSFS_PATH_MAX];
+ int rc = -1;
+
+ struct udev *udev;
+ struct udev_device *dev;
+ const char *driver;
+ const char *bDevClass;
+
+ /* Create libudev context. */
+ udev = udev_new();
+
+ /* Get the device. */
+ dev = udev_device_new_from_subsystem_sysname(udev, "usb", busid);
+ if (!dev) {
+ dbg("unable to find device with bus ID %s", busid);
+ goto err_close_busid_dev;
+ }
+
+ /* Check what kind of device it is. */
+ bDevClass = udev_device_get_sysattr_value(dev, "bDeviceClass");
+ if (!bDevClass) {
+ dbg("unable to get bDevClass device attribute");
+ goto err_close_busid_dev;
+ }
+
+ if (!strncmp(bDevClass, "09", strlen(bDevClass))) {
+ dbg("skip unbinding of hub");
+ goto err_close_busid_dev;
+ }
+
+ /* Get the device driver. */
+ driver = udev_device_get_driver(dev);
+ if (!driver) {
+ /* No driver bound to this device. */
+ goto out;
+ }
+
+ if (!strncmp(USBIP_HOST_DRV_NAME, driver,
+ strlen(USBIP_HOST_DRV_NAME))) {
+ /* Already bound to usbip-host. */
+ status = UNBIND_ST_USBIP_HOST;
+ goto out;
+ }
+
+ /* Unbind device from driver. */
+ snprintf(unbind_attr_path, sizeof(unbind_attr_path), "%s/%s/%s/%s/%s/%s",
+ SYSFS_MNT_PATH, SYSFS_BUS_NAME, SYSFS_BUS_TYPE,
+ SYSFS_DRIVERS_NAME, driver, attr_name);
+
+ rc = write_sysfs_attribute(unbind_attr_path, busid, strlen(busid));
+ if (rc < 0) {
+ err("error unbinding device %s from driver", busid);
+ goto err_close_busid_dev;
+ }
+
+ goto out;
+
+err_close_busid_dev:
+ status = UNBIND_ST_FAILED;
+out:
+ udev_device_unref(dev);
+ udev_unref(udev);
+
+ return status;
+}
+
+static int bind_device(char *busid)
+{
+ int rc;
+ struct udev *udev;
+ struct udev_device *dev;
+
+ /* Check whether the device with this bus ID exists. */
+ udev = udev_new();
+ dev = udev_device_new_from_subsystem_sysname(udev, "usb", busid);
+ if (!dev) {
+ err("device with the specified bus ID does not exist");
+ return -1;
+ }
+ udev_unref(udev);
+
+ rc = unbind_other(busid);
+ if (rc == UNBIND_ST_FAILED) {
+ err("could not unbind driver from device on busid %s", busid);
+ return -1;
+ } else if (rc == UNBIND_ST_USBIP_HOST) {
+ err("device on busid %s is already bound to %s", busid,
+ USBIP_HOST_DRV_NAME);
+ return -1;
+ }
+
+ rc = modify_match_busid(busid, 1);
+ if (rc < 0) {
+ err("unable to bind device on %s", busid);
+ return -1;
+ }
+
+ rc = bind_usbip(busid);
+ if (rc < 0) {
+ err("could not bind device to %s", USBIP_HOST_DRV_NAME);
+ modify_match_busid(busid, 0);
+ return -1;
+ }
+
+ info("bind device on busid %s: complete", busid);
+
+ return 0;
+}
+
+int usbip_bind(int argc, char *argv[])
+{
+ static const struct option opts[] = {
+ { "busid", required_argument, NULL, 'b' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int opt;
+ int ret = -1;
+
+ for (;;) {
+ opt = getopt_long(argc, argv, "b:", opts, NULL);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'b':
+ ret = bind_device(optarg);
+ goto out;
+ default:
+ goto err_out;
+ }
+ }
+
+err_out:
+ usbip_bind_usage();
+out:
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <ctype.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <getopt.h>
+#include <unistd.h>
+
+#include "vhci_driver.h"
+#include "usbip_common.h"
+#include "usbip_network.h"
+#include "usbip.h"
+
+static const char usbip_detach_usage_string[] =
+ "usbip detach <args>\n"
+ " -p, --port=<port> " USBIP_VHCI_DRV_NAME
+ " port the device is on\n";
+
+void usbip_detach_usage(void)
+{
+ printf("usage: %s", usbip_detach_usage_string);
+}
+
+static int detach_port(char *port)
+{
+ int ret;
+ uint8_t portnum;
+ char path[PATH_MAX+1];
+
+ for (unsigned int i = 0; i < strlen(port); i++)
+ if (!isdigit(port[i])) {
+ err("invalid port %s", port);
+ return -1;
+ }
+
+ /* check max port */
+
+ portnum = atoi(port);
+
+ /* remove the port state file */
+
+ snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", portnum);
+
+ remove(path);
+ rmdir(VHCI_STATE_PATH);
+
+ ret = usbip_vhci_driver_open();
+ if (ret < 0) {
+ err("open vhci_driver");
+ return -1;
+ }
+
+ ret = usbip_vhci_detach_device(portnum);
+ if (ret < 0)
+ return -1;
+
+ usbip_vhci_driver_close();
+
+ return ret;
+}
+
+int usbip_detach(int argc, char *argv[])
+{
+ static const struct option opts[] = {
+ { "port", required_argument, NULL, 'p' },
+ { NULL, 0, NULL, 0 }
+ };
+ int opt;
+ int ret = -1;
+
+ for (;;) {
+ opt = getopt_long(argc, argv, "p:", opts, NULL);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'p':
+ ret = detach_port(optarg);
+ goto out;
+ default:
+ goto err_out;
+ }
+ }
+
+err_out:
+ usbip_detach_usage();
+out:
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/types.h>
+#include <libudev.h>
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <getopt.h>
+#include <netdb.h>
+#include <unistd.h>
+
+#include "usbip_common.h"
+#include "usbip_network.h"
+#include "usbip.h"
+
+static const char usbip_list_usage_string[] =
+ "usbip list [-p|--parsable] <args>\n"
+ " -p, --parsable Parsable list format\n"
+ " -r, --remote=<host> List the exportable USB devices on <host>\n"
+ " -l, --local List the local USB devices\n";
+
+void usbip_list_usage(void)
+{
+ printf("usage: %s", usbip_list_usage_string);
+}
+
+static int get_exported_devices(char *host, int sockfd)
+{
+ char product_name[100];
+ char class_name[100];
+ struct op_devlist_reply reply;
+ uint16_t code = OP_REP_DEVLIST;
+ struct usbip_usb_device udev;
+ struct usbip_usb_interface uintf;
+ unsigned int i;
+ int rc, j;
+
+ rc = usbip_net_send_op_common(sockfd, OP_REQ_DEVLIST, 0);
+ if (rc < 0) {
+ dbg("usbip_net_send_op_common failed");
+ return -1;
+ }
+
+ rc = usbip_net_recv_op_common(sockfd, &code);
+ if (rc < 0) {
+ dbg("usbip_net_recv_op_common failed");
+ return -1;
+ }
+
+ memset(&reply, 0, sizeof(reply));
+ rc = usbip_net_recv(sockfd, &reply, sizeof(reply));
+ if (rc < 0) {
+ dbg("usbip_net_recv_op_devlist failed");
+ return -1;
+ }
+ PACK_OP_DEVLIST_REPLY(0, &reply);
+ dbg("exportable devices: %d\n", reply.ndev);
+
+ if (reply.ndev == 0) {
+ info("no exportable devices found on %s", host);
+ return 0;
+ }
+
+ printf("Exportable USB devices\n");
+ printf("======================\n");
+ printf(" - %s\n", host);
+
+ for (i = 0; i < reply.ndev; i++) {
+ memset(&udev, 0, sizeof(udev));
+ rc = usbip_net_recv(sockfd, &udev, sizeof(udev));
+ if (rc < 0) {
+ dbg("usbip_net_recv failed: usbip_usb_device[%d]", i);
+ return -1;
+ }
+ usbip_net_pack_usb_device(0, &udev);
+
+ usbip_names_get_product(product_name, sizeof(product_name),
+ udev.idVendor, udev.idProduct);
+ usbip_names_get_class(class_name, sizeof(class_name),
+ udev.bDeviceClass, udev.bDeviceSubClass,
+ udev.bDeviceProtocol);
+ printf("%11s: %s\n", udev.busid, product_name);
+ printf("%11s: %s\n", "", udev.path);
+ printf("%11s: %s\n", "", class_name);
+
+ for (j = 0; j < udev.bNumInterfaces; j++) {
+ rc = usbip_net_recv(sockfd, &uintf, sizeof(uintf));
+ if (rc < 0) {
+ err("usbip_net_recv failed: usbip_usb_intf[%d]",
+ j);
+
+ return -1;
+ }
+ usbip_net_pack_usb_interface(0, &uintf);
+
+ usbip_names_get_class(class_name, sizeof(class_name),
+ uintf.bInterfaceClass,
+ uintf.bInterfaceSubClass,
+ uintf.bInterfaceProtocol);
+ printf("%11s: %2d - %s\n", "", j, class_name);
+ }
+
+ printf("\n");
+ }
+
+ return 0;
+}
+
+static int list_exported_devices(char *host)
+{
+ int rc;
+ int sockfd;
+
+ sockfd = usbip_net_tcp_connect(host, usbip_port_string);
+ if (sockfd < 0) {
+ err("could not connect to %s:%s: %s", host,
+ usbip_port_string, gai_strerror(sockfd));
+ return -1;
+ }
+ dbg("connected to %s:%s", host, usbip_port_string);
+
+ rc = get_exported_devices(host, sockfd);
+ if (rc < 0) {
+ err("failed to get device list from %s", host);
+ return -1;
+ }
+
+ close(sockfd);
+
+ return 0;
+}
+
+static void print_device(const char *busid, const char *vendor,
+ const char *product, bool parsable)
+{
+ if (parsable)
+ printf("busid=%s#usbid=%.4s:%.4s#", busid, vendor, product);
+ else
+ printf(" - busid %s (%.4s:%.4s)\n", busid, vendor, product);
+}
+
+static void print_product_name(char *product_name, bool parsable)
+{
+ if (!parsable)
+ printf(" %s\n", product_name);
+}
+
+static int list_devices(bool parsable)
+{
+ struct udev *udev;
+ struct udev_enumerate *enumerate;
+ struct udev_list_entry *devices, *dev_list_entry;
+ struct udev_device *dev;
+ const char *path;
+ const char *idVendor;
+ const char *idProduct;
+ const char *bConfValue;
+ const char *bNumIntfs;
+ const char *busid;
+ char product_name[128];
+ int ret = -1;
+
+ /* Create libudev context. */
+ udev = udev_new();
+
+ /* Create libudev device enumeration. */
+ enumerate = udev_enumerate_new(udev);
+
+ /* Take only USB devices that are not hubs and do not have
+ * the bInterfaceNumber attribute, i.e. are not interfaces.
+ */
+ udev_enumerate_add_match_subsystem(enumerate, "usb");
+ udev_enumerate_add_nomatch_sysattr(enumerate, "bDeviceClass", "09");
+ udev_enumerate_add_nomatch_sysattr(enumerate, "bInterfaceNumber", NULL);
+ udev_enumerate_scan_devices(enumerate);
+
+ devices = udev_enumerate_get_list_entry(enumerate);
+
+ /* Show information about each device. */
+ udev_list_entry_foreach(dev_list_entry, devices) {
+ path = udev_list_entry_get_name(dev_list_entry);
+ dev = udev_device_new_from_syspath(udev, path);
+
+ /* Get device information. */
+ idVendor = udev_device_get_sysattr_value(dev, "idVendor");
+ idProduct = udev_device_get_sysattr_value(dev, "idProduct");
+ bConfValue = udev_device_get_sysattr_value(dev, "bConfigurationValue");
+ bNumIntfs = udev_device_get_sysattr_value(dev, "bNumInterfaces");
+ busid = udev_device_get_sysname(dev);
+ if (!idVendor || !idProduct || !bConfValue || !bNumIntfs) {
+ err("problem getting device attributes: %s",
+ strerror(errno));
+ goto err_out;
+ }
+
+ /* Get product name. */
+ usbip_names_get_product(product_name, sizeof(product_name),
+ strtol(idVendor, NULL, 16),
+ strtol(idProduct, NULL, 16));
+
+ /* Print information. */
+ print_device(busid, idVendor, idProduct, parsable);
+ print_product_name(product_name, parsable);
+
+ printf("\n");
+
+ udev_device_unref(dev);
+ }
+
+ ret = 0;
+
+err_out:
+ udev_enumerate_unref(enumerate);
+ udev_unref(udev);
+
+ return ret;
+}
+
+int usbip_list(int argc, char *argv[])
+{
+ static const struct option opts[] = {
+ { "parsable", no_argument, NULL, 'p' },
+ { "remote", required_argument, NULL, 'r' },
+ { "local", no_argument, NULL, 'l' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ bool parsable = false;
+ int opt;
+ int ret = -1;
+
+ if (usbip_names_init(USBIDS_FILE))
+ err("failed to open %s", USBIDS_FILE);
+
+ for (;;) {
+ opt = getopt_long(argc, argv, "pr:l", opts, NULL);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'p':
+ parsable = true;
+ break;
+ case 'r':
+ ret = list_exported_devices(optarg);
+ goto out;
+ case 'l':
+ ret = list_devices(parsable);
+ goto out;
+ default:
+ goto err_out;
+ }
+ }
+
+err_out:
+ usbip_list_usage();
+out:
+ usbip_names_free();
+
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/socket.h>
+
+#include <string.h>
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/tcp.h>
+#include <unistd.h>
+
+#ifdef HAVE_LIBWRAP
+#include <tcpd.h>
+#endif
+
+#include "usbip_common.h"
+#include "usbip_network.h"
+
+int usbip_port = 3240;
+char *usbip_port_string = "3240";
+
+void usbip_setup_port_number(char *arg)
+{
+ dbg("parsing port arg '%s'", arg);
+ char *end;
+ unsigned long int port = strtoul(arg, &end, 10);
+
+ if (end == arg) {
+ err("port: could not parse '%s' as a decimal integer", arg);
+ return;
+ }
+
+ if (*end != '\0') {
+ err("port: garbage at end of '%s'", arg);
+ return;
+ }
+
+ if (port > UINT16_MAX) {
+ err("port: %s too high (max=%d)",
+ arg, UINT16_MAX);
+ return;
+ }
+
+ usbip_port = port;
+ usbip_port_string = arg;
+ info("using port %d (\"%s\")", usbip_port, usbip_port_string);
+}
+
+void usbip_net_pack_uint32_t(int pack, uint32_t *num)
+{
+ uint32_t i;
+
+ if (pack)
+ i = htonl(*num);
+ else
+ i = ntohl(*num);
+
+ *num = i;
+}
+
+void usbip_net_pack_uint16_t(int pack, uint16_t *num)
+{
+ uint16_t i;
+
+ if (pack)
+ i = htons(*num);
+ else
+ i = ntohs(*num);
+
+ *num = i;
+}
+
+void usbip_net_pack_usb_device(int pack, struct usbip_usb_device *udev)
+{
+ usbip_net_pack_uint32_t(pack, &udev->busnum);
+ usbip_net_pack_uint32_t(pack, &udev->devnum);
+ usbip_net_pack_uint32_t(pack, &udev->speed);
+
+ usbip_net_pack_uint16_t(pack, &udev->idVendor);
+ usbip_net_pack_uint16_t(pack, &udev->idProduct);
+ usbip_net_pack_uint16_t(pack, &udev->bcdDevice);
+}
+
+void usbip_net_pack_usb_interface(int pack __attribute__((unused)),
+ struct usbip_usb_interface *udev
+ __attribute__((unused)))
+{
+ /* uint8_t members need nothing */
+}
+
+static ssize_t usbip_net_xmit(int sockfd, void *buff, size_t bufflen,
+ int sending)
+{
+ ssize_t nbytes;
+ ssize_t total = 0;
+
+ if (!bufflen)
+ return 0;
+
+ do {
+ if (sending)
+ nbytes = send(sockfd, buff, bufflen, 0);
+ else
+ nbytes = recv(sockfd, buff, bufflen, MSG_WAITALL);
+
+ if (nbytes <= 0)
+ return -1;
+
+ buff = (void *)((intptr_t) buff + nbytes);
+ bufflen -= nbytes;
+ total += nbytes;
+
+ } while (bufflen > 0);
+
+ return total;
+}
+
+ssize_t usbip_net_recv(int sockfd, void *buff, size_t bufflen)
+{
+ return usbip_net_xmit(sockfd, buff, bufflen, 0);
+}
+
+ssize_t usbip_net_send(int sockfd, void *buff, size_t bufflen)
+{
+ return usbip_net_xmit(sockfd, buff, bufflen, 1);
+}
+
+int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status)
+{
+ struct op_common op_common;
+ int rc;
+
+ memset(&op_common, 0, sizeof(op_common));
+
+ op_common.version = USBIP_VERSION;
+ op_common.code = code;
+ op_common.status = status;
+
+ PACK_OP_COMMON(1, &op_common);
+
+ rc = usbip_net_send(sockfd, &op_common, sizeof(op_common));
+ if (rc < 0) {
+ dbg("usbip_net_send failed: %d", rc);
+ return -1;
+ }
+
+ return 0;
+}
+
+int usbip_net_recv_op_common(int sockfd, uint16_t *code)
+{
+ struct op_common op_common;
+ int rc;
+
+ memset(&op_common, 0, sizeof(op_common));
+
+ rc = usbip_net_recv(sockfd, &op_common, sizeof(op_common));
+ if (rc < 0) {
+ dbg("usbip_net_recv failed: %d", rc);
+ goto err;
+ }
+
+ PACK_OP_COMMON(0, &op_common);
+
+ if (op_common.version != USBIP_VERSION) {
+ dbg("version mismatch: %d %d", op_common.version,
+ USBIP_VERSION);
+ goto err;
+ }
+
+ switch (*code) {
+ case OP_UNSPEC:
+ break;
+ default:
+ if (op_common.code != *code) {
+ dbg("unexpected pdu %#0x for %#0x", op_common.code,
+ *code);
+ goto err;
+ }
+ }
+
+ if (op_common.status != ST_OK) {
+ dbg("request failed at peer: %d", op_common.status);
+ goto err;
+ }
+
+ *code = op_common.code;
+
+ return 0;
+err:
+ return -1;
+}
+
+int usbip_net_set_reuseaddr(int sockfd)
+{
+ const int val = 1;
+ int ret;
+
+ ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+ if (ret < 0)
+ dbg("setsockopt: SO_REUSEADDR");
+
+ return ret;
+}
+
+int usbip_net_set_nodelay(int sockfd)
+{
+ const int val = 1;
+ int ret;
+
+ ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
+ if (ret < 0)
+ dbg("setsockopt: TCP_NODELAY");
+
+ return ret;
+}
+
+int usbip_net_set_keepalive(int sockfd)
+{
+ const int val = 1;
+ int ret;
+
+ ret = setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val));
+ if (ret < 0)
+ dbg("setsockopt: SO_KEEPALIVE");
+
+ return ret;
+}
+
+int usbip_net_set_v6only(int sockfd)
+{
+ const int val = 1;
+ int ret;
+
+ ret = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
+ if (ret < 0)
+ dbg("setsockopt: IPV6_V6ONLY");
+
+ return ret;
+}
+
+/*
+ * IPv6 Ready
+ */
+int usbip_net_tcp_connect(char *hostname, char *service)
+{
+ struct addrinfo hints, *res, *rp;
+ int sockfd;
+ int ret;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ /* get all possible addresses */
+ ret = getaddrinfo(hostname, service, &hints, &res);
+ if (ret < 0) {
+ dbg("getaddrinfo: %s service %s: %s", hostname, service,
+ gai_strerror(ret));
+ return ret;
+ }
+
+ /* try the addresses */
+ for (rp = res; rp; rp = rp->ai_next) {
+ sockfd = socket(rp->ai_family, rp->ai_socktype,
+ rp->ai_protocol);
+ if (sockfd < 0)
+ continue;
+
+ /* should set TCP_NODELAY for usbip */
+ usbip_net_set_nodelay(sockfd);
+ /* TODO: write code for heartbeat */
+ usbip_net_set_keepalive(sockfd);
+
+ if (connect(sockfd, rp->ai_addr, rp->ai_addrlen) == 0)
+ break;
+
+ close(sockfd);
+ }
+
+ freeaddrinfo(res);
+
+ if (!rp)
+ return EAI_SYSTEM;
+
+ return sockfd;
+}
--- /dev/null
+/*
+ * Copyright (C) 2005-2007 Takahiro Hirofuchi
+ */
+
+#ifndef __USBIP_NETWORK_H
+#define __USBIP_NETWORK_H
+
+#ifdef HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
+#include <sys/types.h>
+
+#include <stdint.h>
+
+extern int usbip_port;
+extern char *usbip_port_string;
+void usbip_setup_port_number(char *arg);
+
+/* ---------------------------------------------------------------------- */
+/* Common header for all the kinds of PDUs. */
+struct op_common {
+ uint16_t version;
+
+#define OP_REQUEST (0x80 << 8)
+#define OP_REPLY (0x00 << 8)
+ uint16_t code;
+
+ /* add more error code */
+#define ST_OK 0x00
+#define ST_NA 0x01
+ uint32_t status; /* op_code status (for reply) */
+
+} __attribute__((packed));
+
+#define PACK_OP_COMMON(pack, op_common) do {\
+ usbip_net_pack_uint16_t(pack, &(op_common)->version);\
+ usbip_net_pack_uint16_t(pack, &(op_common)->code);\
+ usbip_net_pack_uint32_t(pack, &(op_common)->status);\
+} while (0)
+
+/* ---------------------------------------------------------------------- */
+/* Dummy Code */
+#define OP_UNSPEC 0x00
+#define OP_REQ_UNSPEC OP_UNSPEC
+#define OP_REP_UNSPEC OP_UNSPEC
+
+/* ---------------------------------------------------------------------- */
+/* Retrieve USB device information. (still not used) */
+#define OP_DEVINFO 0x02
+#define OP_REQ_DEVINFO (OP_REQUEST | OP_DEVINFO)
+#define OP_REP_DEVINFO (OP_REPLY | OP_DEVINFO)
+
+struct op_devinfo_request {
+ char busid[SYSFS_BUS_ID_SIZE];
+} __attribute__((packed));
+
+struct op_devinfo_reply {
+ struct usbip_usb_device udev;
+ struct usbip_usb_interface uinf[];
+} __attribute__((packed));
+
+/* ---------------------------------------------------------------------- */
+/* Import a remote USB device. */
+#define OP_IMPORT 0x03
+#define OP_REQ_IMPORT (OP_REQUEST | OP_IMPORT)
+#define OP_REP_IMPORT (OP_REPLY | OP_IMPORT)
+
+struct op_import_request {
+ char busid[SYSFS_BUS_ID_SIZE];
+} __attribute__((packed));
+
+struct op_import_reply {
+ struct usbip_usb_device udev;
+// struct usbip_usb_interface uinf[];
+} __attribute__((packed));
+
+#define PACK_OP_IMPORT_REQUEST(pack, request) do {\
+} while (0)
+
+#define PACK_OP_IMPORT_REPLY(pack, reply) do {\
+ usbip_net_pack_usb_device(pack, &(reply)->udev);\
+} while (0)
+
+/* ---------------------------------------------------------------------- */
+/* Export a USB device to a remote host. */
+#define OP_EXPORT 0x06
+#define OP_REQ_EXPORT (OP_REQUEST | OP_EXPORT)
+#define OP_REP_EXPORT (OP_REPLY | OP_EXPORT)
+
+struct op_export_request {
+ struct usbip_usb_device udev;
+} __attribute__((packed));
+
+struct op_export_reply {
+ int returncode;
+} __attribute__((packed));
+
+
+#define PACK_OP_EXPORT_REQUEST(pack, request) do {\
+ usbip_net_pack_usb_device(pack, &(request)->udev);\
+} while (0)
+
+#define PACK_OP_EXPORT_REPLY(pack, reply) do {\
+} while (0)
+
+/* ---------------------------------------------------------------------- */
+/* un-Export a USB device from a remote host. */
+#define OP_UNEXPORT 0x07
+#define OP_REQ_UNEXPORT (OP_REQUEST | OP_UNEXPORT)
+#define OP_REP_UNEXPORT (OP_REPLY | OP_UNEXPORT)
+
+struct op_unexport_request {
+ struct usbip_usb_device udev;
+} __attribute__((packed));
+
+struct op_unexport_reply {
+ int returncode;
+} __attribute__((packed));
+
+#define PACK_OP_UNEXPORT_REQUEST(pack, request) do {\
+ usbip_net_pack_usb_device(pack, &(request)->udev);\
+} while (0)
+
+#define PACK_OP_UNEXPORT_REPLY(pack, reply) do {\
+} while (0)
+
+/* ---------------------------------------------------------------------- */
+/* Negotiate IPSec encryption key. (still not used) */
+#define OP_CRYPKEY 0x04
+#define OP_REQ_CRYPKEY (OP_REQUEST | OP_CRYPKEY)
+#define OP_REP_CRYPKEY (OP_REPLY | OP_CRYPKEY)
+
+struct op_crypkey_request {
+ /* 128bit key */
+ uint32_t key[4];
+} __attribute__((packed));
+
+struct op_crypkey_reply {
+ uint32_t __reserved;
+} __attribute__((packed));
+
+
+/* ---------------------------------------------------------------------- */
+/* Retrieve the list of exported USB devices. */
+#define OP_DEVLIST 0x05
+#define OP_REQ_DEVLIST (OP_REQUEST | OP_DEVLIST)
+#define OP_REP_DEVLIST (OP_REPLY | OP_DEVLIST)
+
+struct op_devlist_request {
+} __attribute__((packed));
+
+struct op_devlist_reply {
+ uint32_t ndev;
+ /* followed by reply_extra[] */
+} __attribute__((packed));
+
+struct op_devlist_reply_extra {
+ struct usbip_usb_device udev;
+ struct usbip_usb_interface uinf[];
+} __attribute__((packed));
+
+#define PACK_OP_DEVLIST_REQUEST(pack, request) do {\
+} while (0)
+
+#define PACK_OP_DEVLIST_REPLY(pack, reply) do {\
+ usbip_net_pack_uint32_t(pack, &(reply)->ndev);\
+} while (0)
+
+void usbip_net_pack_uint32_t(int pack, uint32_t *num);
+void usbip_net_pack_uint16_t(int pack, uint16_t *num);
+void usbip_net_pack_usb_device(int pack, struct usbip_usb_device *udev);
+void usbip_net_pack_usb_interface(int pack, struct usbip_usb_interface *uinf);
+
+ssize_t usbip_net_recv(int sockfd, void *buff, size_t bufflen);
+ssize_t usbip_net_send(int sockfd, void *buff, size_t bufflen);
+int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status);
+int usbip_net_recv_op_common(int sockfd, uint16_t *code);
+int usbip_net_set_reuseaddr(int sockfd);
+int usbip_net_set_nodelay(int sockfd);
+int usbip_net_set_keepalive(int sockfd);
+int usbip_net_set_v6only(int sockfd);
+int usbip_net_tcp_connect(char *hostname, char *port);
+
+#endif /* __USBIP_NETWORK_H */
--- /dev/null
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * 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.
+ */
+
+#include "vhci_driver.h"
+#include "usbip_common.h"
+
+static int list_imported_devices(void)
+{
+ int i;
+ struct usbip_imported_device *idev;
+ int ret;
+
+ ret = usbip_vhci_driver_open();
+ if (ret < 0) {
+ err("open vhci_driver");
+ return -1;
+ }
+
+ printf("Imported USB devices\n");
+ printf("====================\n");
+
+ for (i = 0; i < vhci_driver->nports; i++) {
+ idev = &vhci_driver->idev[i];
+
+ if (usbip_vhci_imported_device_dump(idev) < 0)
+ ret = -1;
+ }
+
+ usbip_vhci_driver_close();
+
+ return ret;
+
+}
+
+int usbip_port_show(__attribute__((unused)) int argc,
+ __attribute__((unused)) char *argv[])
+{
+ int ret;
+
+ ret = list_imported_devices();
+ if (ret < 0)
+ err("list imported devices");
+
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <libudev.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <getopt.h>
+
+#include "usbip_common.h"
+#include "utils.h"
+#include "usbip.h"
+#include "sysfs_utils.h"
+
+static const char usbip_unbind_usage_string[] =
+ "usbip unbind <args>\n"
+ " -b, --busid=<busid> Unbind " USBIP_HOST_DRV_NAME ".ko from "
+ "device on <busid>\n";
+
+void usbip_unbind_usage(void)
+{
+ printf("usage: %s", usbip_unbind_usage_string);
+}
+
+static int unbind_device(char *busid)
+{
+ char bus_type[] = "usb";
+ int rc, ret = -1;
+
+ char unbind_attr_name[] = "unbind";
+ char unbind_attr_path[SYSFS_PATH_MAX];
+ char rebind_attr_name[] = "rebind";
+ char rebind_attr_path[SYSFS_PATH_MAX];
+
+ struct udev *udev;
+ struct udev_device *dev;
+ const char *driver;
+
+ /* Create libudev context. */
+ udev = udev_new();
+
+ /* Check whether the device with this bus ID exists. */
+ dev = udev_device_new_from_subsystem_sysname(udev, "usb", busid);
+ if (!dev) {
+ err("device with the specified bus ID does not exist");
+ goto err_close_udev;
+ }
+
+ /* Check whether the device is using usbip-host driver. */
+ driver = udev_device_get_driver(dev);
+ if (!driver || strcmp(driver, "usbip-host")) {
+ err("device is not bound to usbip-host driver");
+ goto err_close_udev;
+ }
+
+ /* Unbind device from driver. */
+ snprintf(unbind_attr_path, sizeof(unbind_attr_path), "%s/%s/%s/%s/%s/%s",
+ SYSFS_MNT_PATH, SYSFS_BUS_NAME, bus_type, SYSFS_DRIVERS_NAME,
+ USBIP_HOST_DRV_NAME, unbind_attr_name);
+
+ rc = write_sysfs_attribute(unbind_attr_path, busid, strlen(busid));
+ if (rc < 0) {
+ err("error unbinding device %s from driver", busid);
+ goto err_close_udev;
+ }
+
+ /* Notify driver of unbind. */
+ rc = modify_match_busid(busid, 0);
+ if (rc < 0) {
+ err("unable to unbind device on %s", busid);
+ goto err_close_udev;
+ }
+
+ /* Trigger new probing. */
+ snprintf(rebind_attr_path, sizeof(unbind_attr_path), "%s/%s/%s/%s/%s/%s",
+ SYSFS_MNT_PATH, SYSFS_BUS_NAME, bus_type, SYSFS_DRIVERS_NAME,
+ USBIP_HOST_DRV_NAME, rebind_attr_name);
+
+ rc = write_sysfs_attribute(rebind_attr_path, busid, strlen(busid));
+ if (rc < 0) {
+ err("error rebinding");
+ goto err_close_udev;
+ }
+
+ ret = 0;
+ info("unbind device on busid %s: complete", busid);
+
+err_close_udev:
+ udev_device_unref(dev);
+ udev_unref(udev);
+
+ return ret;
+}
+
+int usbip_unbind(int argc, char *argv[])
+{
+ static const struct option opts[] = {
+ { "busid", required_argument, NULL, 'b' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int opt;
+ int ret = -1;
+
+ for (;;) {
+ opt = getopt_long(argc, argv, "b:", opts, NULL);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'b':
+ ret = unbind_device(optarg);
+ goto out;
+ default:
+ goto err_out;
+ }
+ }
+
+err_out:
+ usbip_unbind_usage();
+out:
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#ifdef HAVE_LIBWRAP
+#include <tcpd.h>
+#endif
+
+#include <getopt.h>
+#include <signal.h>
+#include <poll.h>
+
+#include "usbip_host_driver.h"
+#include "usbip_common.h"
+#include "usbip_network.h"
+#include "list.h"
+
+#undef PROGNAME
+#define PROGNAME "usbipd"
+#define MAXSOCKFD 20
+
+#define MAIN_LOOP_TIMEOUT 10
+
+#define DEFAULT_PID_FILE "/var/run/" PROGNAME ".pid"
+
+static const char usbip_version_string[] = PACKAGE_STRING;
+
+static const char usbipd_help_string[] =
+ "usage: usbipd [options]\n"
+ "\n"
+ " -4, --ipv4\n"
+ " Bind to IPv4. Default is both.\n"
+ "\n"
+ " -6, --ipv6\n"
+ " Bind to IPv6. Default is both.\n"
+ "\n"
+ " -D, --daemon\n"
+ " Run as a daemon process.\n"
+ "\n"
+ " -d, --debug\n"
+ " Print debugging information.\n"
+ "\n"
+ " -PFILE, --pid FILE\n"
+ " Write process id to FILE.\n"
+ " If no FILE specified, use " DEFAULT_PID_FILE "\n"
+ "\n"
+ " -tPORT, --tcp-port PORT\n"
+ " Listen on TCP/IP port PORT.\n"
+ "\n"
+ " -h, --help\n"
+ " Print this help.\n"
+ "\n"
+ " -v, --version\n"
+ " Show version.\n";
+
+static void usbipd_help(void)
+{
+ printf("%s\n", usbipd_help_string);
+}
+
+static int recv_request_import(int sockfd)
+{
+ struct op_import_request req;
+ struct op_common reply;
+ struct usbip_exported_device *edev;
+ struct usbip_usb_device pdu_udev;
+ struct list_head *i;
+ int found = 0;
+ int error = 0;
+ int rc;
+
+ memset(&req, 0, sizeof(req));
+ memset(&reply, 0, sizeof(reply));
+
+ rc = usbip_net_recv(sockfd, &req, sizeof(req));
+ if (rc < 0) {
+ dbg("usbip_net_recv failed: import request");
+ return -1;
+ }
+ PACK_OP_IMPORT_REQUEST(0, &req);
+
+ list_for_each(i, &host_driver->edev_list) {
+ edev = list_entry(i, struct usbip_exported_device, node);
+ if (!strncmp(req.busid, edev->udev.busid, SYSFS_BUS_ID_SIZE)) {
+ info("found requested device: %s", req.busid);
+ found = 1;
+ break;
+ }
+ }
+
+ if (found) {
+ /* should set TCP_NODELAY for usbip */
+ usbip_net_set_nodelay(sockfd);
+
+ /* export device needs a TCP/IP socket descriptor */
+ rc = usbip_host_export_device(edev, sockfd);
+ if (rc < 0)
+ error = 1;
+ } else {
+ info("requested device not found: %s", req.busid);
+ error = 1;
+ }
+
+ rc = usbip_net_send_op_common(sockfd, OP_REP_IMPORT,
+ (!error ? ST_OK : ST_NA));
+ if (rc < 0) {
+ dbg("usbip_net_send_op_common failed: %#0x", OP_REP_IMPORT);
+ return -1;
+ }
+
+ if (error) {
+ dbg("import request busid %s: failed", req.busid);
+ return -1;
+ }
+
+ memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev));
+ usbip_net_pack_usb_device(1, &pdu_udev);
+
+ rc = usbip_net_send(sockfd, &pdu_udev, sizeof(pdu_udev));
+ if (rc < 0) {
+ dbg("usbip_net_send failed: devinfo");
+ return -1;
+ }
+
+ dbg("import request busid %s: complete", req.busid);
+
+ return 0;
+}
+
+static int send_reply_devlist(int connfd)
+{
+ struct usbip_exported_device *edev;
+ struct usbip_usb_device pdu_udev;
+ struct usbip_usb_interface pdu_uinf;
+ struct op_devlist_reply reply;
+ struct list_head *j;
+ int rc, i;
+
+ reply.ndev = 0;
+ /* number of exported devices */
+ list_for_each(j, &host_driver->edev_list) {
+ reply.ndev += 1;
+ }
+ info("exportable devices: %d", reply.ndev);
+
+ rc = usbip_net_send_op_common(connfd, OP_REP_DEVLIST, ST_OK);
+ if (rc < 0) {
+ dbg("usbip_net_send_op_common failed: %#0x", OP_REP_DEVLIST);
+ return -1;
+ }
+ PACK_OP_DEVLIST_REPLY(1, &reply);
+
+ rc = usbip_net_send(connfd, &reply, sizeof(reply));
+ if (rc < 0) {
+ dbg("usbip_net_send failed: %#0x", OP_REP_DEVLIST);
+ return -1;
+ }
+
+ list_for_each(j, &host_driver->edev_list) {
+ edev = list_entry(j, struct usbip_exported_device, node);
+ dump_usb_device(&edev->udev);
+ memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev));
+ usbip_net_pack_usb_device(1, &pdu_udev);
+
+ rc = usbip_net_send(connfd, &pdu_udev, sizeof(pdu_udev));
+ if (rc < 0) {
+ dbg("usbip_net_send failed: pdu_udev");
+ return -1;
+ }
+
+ for (i = 0; i < edev->udev.bNumInterfaces; i++) {
+ dump_usb_interface(&edev->uinf[i]);
+ memcpy(&pdu_uinf, &edev->uinf[i], sizeof(pdu_uinf));
+ usbip_net_pack_usb_interface(1, &pdu_uinf);
+
+ rc = usbip_net_send(connfd, &pdu_uinf,
+ sizeof(pdu_uinf));
+ if (rc < 0) {
+ err("usbip_net_send failed: pdu_uinf");
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int recv_request_devlist(int connfd)
+{
+ struct op_devlist_request req;
+ int rc;
+
+ memset(&req, 0, sizeof(req));
+
+ rc = usbip_net_recv(connfd, &req, sizeof(req));
+ if (rc < 0) {
+ dbg("usbip_net_recv failed: devlist request");
+ return -1;
+ }
+
+ rc = send_reply_devlist(connfd);
+ if (rc < 0) {
+ dbg("send_reply_devlist failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int recv_pdu(int connfd)
+{
+ uint16_t code = OP_UNSPEC;
+ int ret;
+
+ ret = usbip_net_recv_op_common(connfd, &code);
+ if (ret < 0) {
+ dbg("could not receive opcode: %#0x", code);
+ return -1;
+ }
+
+ ret = usbip_host_refresh_device_list();
+ if (ret < 0) {
+ dbg("could not refresh device list: %d", ret);
+ return -1;
+ }
+
+ info("received request: %#0x(%d)", code, connfd);
+ switch (code) {
+ case OP_REQ_DEVLIST:
+ ret = recv_request_devlist(connfd);
+ break;
+ case OP_REQ_IMPORT:
+ ret = recv_request_import(connfd);
+ break;
+ case OP_REQ_DEVINFO:
+ case OP_REQ_CRYPKEY:
+ default:
+ err("received an unknown opcode: %#0x", code);
+ ret = -1;
+ }
+
+ if (ret == 0)
+ info("request %#0x(%d): complete", code, connfd);
+ else
+ info("request %#0x(%d): failed", code, connfd);
+
+ return ret;
+}
+
+#ifdef HAVE_LIBWRAP
+static int tcpd_auth(int connfd)
+{
+ struct request_info request;
+ int rc;
+
+ request_init(&request, RQ_DAEMON, PROGNAME, RQ_FILE, connfd, 0);
+ fromhost(&request);
+ rc = hosts_access(&request);
+ if (rc == 0)
+ return -1;
+
+ return 0;
+}
+#endif
+
+static int do_accept(int listenfd)
+{
+ int connfd;
+ struct sockaddr_storage ss;
+ socklen_t len = sizeof(ss);
+ char host[NI_MAXHOST], port[NI_MAXSERV];
+ int rc;
+
+ memset(&ss, 0, sizeof(ss));
+
+ connfd = accept(listenfd, (struct sockaddr *)&ss, &len);
+ if (connfd < 0) {
+ err("failed to accept connection");
+ return -1;
+ }
+
+ rc = getnameinfo((struct sockaddr *)&ss, len, host, sizeof(host),
+ port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV);
+ if (rc)
+ err("getnameinfo: %s", gai_strerror(rc));
+
+#ifdef HAVE_LIBWRAP
+ rc = tcpd_auth(connfd);
+ if (rc < 0) {
+ info("denied access from %s", host);
+ close(connfd);
+ return -1;
+ }
+#endif
+ info("connection from %s:%s", host, port);
+
+ return connfd;
+}
+
+int process_request(int listenfd)
+{
+ pid_t childpid;
+ int connfd;
+
+ connfd = do_accept(listenfd);
+ if (connfd < 0)
+ return -1;
+ childpid = fork();
+ if (childpid == 0) {
+ close(listenfd);
+ recv_pdu(connfd);
+ exit(0);
+ }
+ close(connfd);
+ return 0;
+}
+
+static void addrinfo_to_text(struct addrinfo *ai, char buf[],
+ const size_t buf_size)
+{
+ char hbuf[NI_MAXHOST];
+ char sbuf[NI_MAXSERV];
+ int rc;
+
+ buf[0] = '\0';
+
+ rc = getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf, sizeof(hbuf),
+ sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV);
+ if (rc)
+ err("getnameinfo: %s", gai_strerror(rc));
+
+ snprintf(buf, buf_size, "%s:%s", hbuf, sbuf);
+}
+
+static int listen_all_addrinfo(struct addrinfo *ai_head, int sockfdlist[],
+ int maxsockfd)
+{
+ struct addrinfo *ai;
+ int ret, nsockfd = 0;
+ const size_t ai_buf_size = NI_MAXHOST + NI_MAXSERV + 2;
+ char ai_buf[ai_buf_size];
+
+ for (ai = ai_head; ai && nsockfd < maxsockfd; ai = ai->ai_next) {
+ int sock;
+
+ addrinfo_to_text(ai, ai_buf, ai_buf_size);
+ dbg("opening %s", ai_buf);
+ sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (sock < 0) {
+ err("socket: %s: %d (%s)",
+ ai_buf, errno, strerror(errno));
+ continue;
+ }
+
+ usbip_net_set_reuseaddr(sock);
+ usbip_net_set_nodelay(sock);
+ /* We use seperate sockets for IPv4 and IPv6
+ * (see do_standalone_mode()) */
+ usbip_net_set_v6only(sock);
+
+ if (sock >= FD_SETSIZE) {
+ err("FD_SETSIZE: %s: sock=%d, max=%d",
+ ai_buf, sock, FD_SETSIZE);
+ close(sock);
+ continue;
+ }
+
+ ret = bind(sock, ai->ai_addr, ai->ai_addrlen);
+ if (ret < 0) {
+ err("bind: %s: %d (%s)",
+ ai_buf, errno, strerror(errno));
+ close(sock);
+ continue;
+ }
+
+ ret = listen(sock, SOMAXCONN);
+ if (ret < 0) {
+ err("listen: %s: %d (%s)",
+ ai_buf, errno, strerror(errno));
+ close(sock);
+ continue;
+ }
+
+ info("listening on %s", ai_buf);
+ sockfdlist[nsockfd++] = sock;
+ }
+
+ return nsockfd;
+}
+
+static struct addrinfo *do_getaddrinfo(char *host, int ai_family)
+{
+ struct addrinfo hints, *ai_head;
+ int rc;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = ai_family;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+
+ rc = getaddrinfo(host, usbip_port_string, &hints, &ai_head);
+ if (rc) {
+ err("failed to get a network address %s: %s", usbip_port_string,
+ gai_strerror(rc));
+ return NULL;
+ }
+
+ return ai_head;
+}
+
+static void signal_handler(int i)
+{
+ dbg("received '%s' signal", strsignal(i));
+}
+
+static void set_signal(void)
+{
+ struct sigaction act;
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = signal_handler;
+ sigemptyset(&act.sa_mask);
+ sigaction(SIGTERM, &act, NULL);
+ sigaction(SIGINT, &act, NULL);
+ act.sa_handler = SIG_IGN;
+ sigaction(SIGCLD, &act, NULL);
+}
+
+static const char *pid_file;
+
+static void write_pid_file(void)
+{
+ if (pid_file) {
+ dbg("creating pid file %s", pid_file);
+ FILE *fp;
+
+ fp = fopen(pid_file, "w");
+ if (!fp) {
+ err("pid_file: %s: %d (%s)",
+ pid_file, errno, strerror(errno));
+ return;
+ }
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ }
+}
+
+static void remove_pid_file(void)
+{
+ if (pid_file) {
+ dbg("removing pid file %s", pid_file);
+ unlink(pid_file);
+ }
+}
+
+static int do_standalone_mode(int daemonize, int ipv4, int ipv6)
+{
+ struct addrinfo *ai_head;
+ int sockfdlist[MAXSOCKFD];
+ int nsockfd, family;
+ int i, terminate;
+ struct pollfd *fds;
+ struct timespec timeout;
+ sigset_t sigmask;
+
+ if (usbip_host_driver_open()) {
+ err("please load " USBIP_CORE_MOD_NAME ".ko and "
+ USBIP_HOST_DRV_NAME ".ko!");
+ return -1;
+ }
+
+ if (daemonize) {
+ if (daemon(0, 0) < 0) {
+ err("daemonizing failed: %s", strerror(errno));
+ usbip_host_driver_close();
+ return -1;
+ }
+ umask(0);
+ usbip_use_syslog = 1;
+ }
+ set_signal();
+ write_pid_file();
+
+ info("starting " PROGNAME " (%s)", usbip_version_string);
+
+ /*
+ * To suppress warnings on systems with bindv6only disabled
+ * (default), we use seperate sockets for IPv6 and IPv4 and set
+ * IPV6_V6ONLY on the IPv6 sockets.
+ */
+ if (ipv4 && ipv6)
+ family = AF_UNSPEC;
+ else if (ipv4)
+ family = AF_INET;
+ else
+ family = AF_INET6;
+
+ ai_head = do_getaddrinfo(NULL, family);
+ if (!ai_head) {
+ usbip_host_driver_close();
+ return -1;
+ }
+ nsockfd = listen_all_addrinfo(ai_head, sockfdlist,
+ sizeof(sockfdlist) / sizeof(*sockfdlist));
+ freeaddrinfo(ai_head);
+ if (nsockfd <= 0) {
+ err("failed to open a listening socket");
+ usbip_host_driver_close();
+ return -1;
+ }
+
+ dbg("listening on %d address%s", nsockfd, (nsockfd == 1) ? "" : "es");
+
+ fds = calloc(nsockfd, sizeof(struct pollfd));
+ for (i = 0; i < nsockfd; i++) {
+ fds[i].fd = sockfdlist[i];
+ fds[i].events = POLLIN;
+ }
+ timeout.tv_sec = MAIN_LOOP_TIMEOUT;
+ timeout.tv_nsec = 0;
+
+ sigfillset(&sigmask);
+ sigdelset(&sigmask, SIGTERM);
+ sigdelset(&sigmask, SIGINT);
+
+ terminate = 0;
+ while (!terminate) {
+ int r;
+
+ r = ppoll(fds, nsockfd, &timeout, &sigmask);
+ if (r < 0) {
+ dbg("%s", strerror(errno));
+ terminate = 1;
+ } else if (r) {
+ for (i = 0; i < nsockfd; i++) {
+ if (fds[i].revents & POLLIN) {
+ dbg("read event on fd[%d]=%d",
+ i, sockfdlist[i]);
+ process_request(sockfdlist[i]);
+ }
+ }
+ } else {
+ dbg("heartbeat timeout on ppoll()");
+ }
+ }
+
+ info("shutting down " PROGNAME);
+ free(fds);
+ usbip_host_driver_close();
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ static const struct option longopts[] = {
+ { "ipv4", no_argument, NULL, '4' },
+ { "ipv6", no_argument, NULL, '6' },
+ { "daemon", no_argument, NULL, 'D' },
+ { "daemon", no_argument, NULL, 'D' },
+ { "debug", no_argument, NULL, 'd' },
+ { "pid", optional_argument, NULL, 'P' },
+ { "tcp-port", required_argument, NULL, 't' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'v' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ enum {
+ cmd_standalone_mode = 1,
+ cmd_help,
+ cmd_version
+ } cmd;
+
+ int daemonize = 0;
+ int ipv4 = 0, ipv6 = 0;
+ int opt, rc = -1;
+
+ pid_file = NULL;
+
+ usbip_use_stderr = 1;
+ usbip_use_syslog = 0;
+
+ if (geteuid() != 0)
+ err("not running as root?");
+
+ cmd = cmd_standalone_mode;
+ for (;;) {
+ opt = getopt_long(argc, argv, "46DdP::t:hv", longopts, NULL);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case '4':
+ ipv4 = 1;
+ break;
+ case '6':
+ ipv6 = 1;
+ break;
+ case 'D':
+ daemonize = 1;
+ break;
+ case 'd':
+ usbip_use_debug = 1;
+ break;
+ case 'h':
+ cmd = cmd_help;
+ break;
+ case 'P':
+ pid_file = optarg ? optarg : DEFAULT_PID_FILE;
+ break;
+ case 't':
+ usbip_setup_port_number(optarg);
+ break;
+ case 'v':
+ cmd = cmd_version;
+ break;
+ case '?':
+ usbipd_help();
+ default:
+ goto err_out;
+ }
+ }
+
+ if (!ipv4 && !ipv6)
+ ipv4 = ipv6 = 1;
+
+ switch (cmd) {
+ case cmd_standalone_mode:
+ rc = do_standalone_mode(daemonize, ipv4, ipv6);
+ remove_pid_file();
+ break;
+ case cmd_version:
+ printf(PROGNAME " (%s)\n", usbip_version_string);
+ rc = 0;
+ break;
+ case cmd_help:
+ usbipd_help();
+ rc = 0;
+ break;
+ default:
+ usbipd_help();
+ goto err_out;
+ }
+
+err_out:
+ return (rc > -1 ? EXIT_SUCCESS : EXIT_FAILURE);
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "usbip_common.h"
+#include "utils.h"
+#include "sysfs_utils.h"
+
+int modify_match_busid(char *busid, int add)
+{
+ char attr_name[] = "match_busid";
+ char command[SYSFS_BUS_ID_SIZE + 4];
+ char match_busid_attr_path[SYSFS_PATH_MAX];
+ int rc;
+
+ snprintf(match_busid_attr_path, sizeof(match_busid_attr_path),
+ "%s/%s/%s/%s/%s/%s", SYSFS_MNT_PATH, SYSFS_BUS_NAME,
+ SYSFS_BUS_TYPE, SYSFS_DRIVERS_NAME, USBIP_HOST_DRV_NAME,
+ attr_name);
+
+ if (add)
+ snprintf(command, SYSFS_BUS_ID_SIZE + 4, "add %s", busid);
+ else
+ snprintf(command, SYSFS_BUS_ID_SIZE + 4, "del %s", busid);
+
+ rc = write_sysfs_attribute(match_busid_attr_path, command,
+ sizeof(command));
+ if (rc < 0) {
+ dbg("failed to write match_busid: %s", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UTILS_H
+#define __UTILS_H
+
+int modify_match_busid(char *busid, int add);
+
+#endif /* __UTILS_H */
+