From 7690037d5b99f4579b61bed815ada8925b4290cc Mon Sep 17 00:00:00 2001 From: Jeremiah Mahler Date: Sun, 21 Dec 2014 05:29:20 -0800 Subject: doc: usbmon: fix wording "be reading until" The usbmon documentation uses "be reading until" which is an unusual wording. Change it to "read until it" which is more clear. Signed-off-by: Jeremiah Mahler Signed-off-by: Greg Kroah-Hartman --- Documentation/usb/usbmon.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/usb/usbmon.txt b/Documentation/usb/usbmon.txt index c42bb9cd3b43..be2ccb9067ba 100644 --- a/Documentation/usb/usbmon.txt +++ b/Documentation/usb/usbmon.txt @@ -72,7 +72,7 @@ to listen on a single bus, otherwise, to listen on all buses, type: # cat /sys/kernel/debug/usb/usbmon/0u > /tmp/1.mon.out -This process will be reading until killed. Naturally, the output can be +This process will read until it is killed. Naturally, the output can be redirected to a desirable location. This is preferred, because it is going to be quite long. -- cgit v1.2.3 From 11a6322a93f17af28f2b5609993facd157c01e03 Mon Sep 17 00:00:00 2001 From: Jeremiah Mahler Date: Sun, 21 Dec 2014 05:30:04 -0800 Subject: doc: usbmon: fix spelling s/unpriviledged/unprivileged/ Signed-off-by: Jeremiah Mahler Signed-off-by: Greg Kroah-Hartman --- Documentation/usb/usbmon.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/usb/usbmon.txt b/Documentation/usb/usbmon.txt index be2ccb9067ba..28425f736756 100644 --- a/Documentation/usb/usbmon.txt +++ b/Documentation/usb/usbmon.txt @@ -231,7 +231,7 @@ number. Number zero (/dev/usbmon0) is special and means "all buses". Note that specific naming policy is set by your Linux distribution. If you create /dev/usbmon0 by hand, make sure that it is owned by root -and has mode 0600. Otherwise, unpriviledged users will be able to snoop +and has mode 0600. Otherwise, unprivileged users will be able to snoop keyboard traffic. The following ioctl calls are available, with MON_IOC_MAGIC 0x92: -- cgit v1.2.3 From 96e418543d27212d73f2fec76490f833baa27e3b Mon Sep 17 00:00:00 2001 From: Wu Liang feng Date: Wed, 24 Dec 2014 18:22:18 +0800 Subject: dt-bindings: usb-ehci: Add an optional property "needs-reset-on-resume" Add device-tree bindings for EHCI so we can use "needs-reset-on-resume" property to force EHCI reset after resume if necessary. This is necessary on platforms like rk3288 that need a reset after resume to detect if a device has been disconnected during suspend time. Signed-off-by: Wu Liang feng Reviewed-by: Julius Werner Reviewed-by: Doug Anderson Reviewed-by: Tomasz Figa Reviewed-by: Pawel Osciak Reviewed-by: Sonny Rao Acked-by: Alan Stern Tested-by: Doug Anderson Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/usb-ehci.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/usb-ehci.txt b/Documentation/devicetree/bindings/usb/usb-ehci.txt index 43c1a4e06767..0b04fdff9d5a 100644 --- a/Documentation/devicetree/bindings/usb/usb-ehci.txt +++ b/Documentation/devicetree/bindings/usb/usb-ehci.txt @@ -12,6 +12,7 @@ Optional properties: - big-endian-regs : boolean, set this for hcds with big-endian registers - big-endian-desc : boolean, set this for hcds with big-endian descriptors - big-endian : boolean, for hcds with big-endian-regs + big-endian-desc + - needs-reset-on-resume : boolean, set this to force EHCI reset after resume - clocks : a list of phandle + clock specifier pairs - phys : phandle + phy specifier pair - phy-names : "usb" -- cgit v1.2.3 From b797ef4e167b68dc5d6bfcb1b77f7f077dddb825 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:24 +0100 Subject: Documentation: usb: gadget_serial: update generic serial setup instruction Using module parameters to specify accepted Vendor ID, Product ID is considered legacy now. Update the documentation to reflect it. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget_serial.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/usb/gadget_serial.txt b/Documentation/usb/gadget_serial.txt index 61e67f6a20a0..6b4a88a8c8e3 100644 --- a/Documentation/usb/gadget_serial.txt +++ b/Documentation/usb/gadget_serial.txt @@ -236,8 +236,12 @@ I: If#= 0 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=serial E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms -You must explicitly load the usbserial driver with parameters to -configure it to recognize the gadget serial device, like this: +You must load the usbserial driver and explicitly set its parameters +to configure it to recognize the gadget serial device, like this: + + echo 0x0525 0xA4A6 >/sys/bus/usb-serial/drivers/generic/new_id + +The legacy way is to use module parameters: modprobe usbserial vendor=0x0525 product=0xA4A6 -- cgit v1.2.3 From e38eb2c8cb435729579576b2b5bc5247ebcd6f5b Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:25 +0100 Subject: Documentation: usb: ACM function testing The newly added file will be used to provide descriptions of how to test the functions of USB gadgets. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 Documentation/usb/gadget-testing.txt (limited to 'Documentation') diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt new file mode 100644 index 000000000000..2a448f812f0e --- /dev/null +++ b/Documentation/usb/gadget-testing.txt @@ -0,0 +1,34 @@ +This file summarizes information on basic testing of USB functions +provided by gadgets. + +1. ACM function + + +1. ACM function +=============== + +The function is provided by usb_f_acm.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "acm". +The ACM function provides just one attribute in its function directory: + + port_num + +The attribute is read-only. + +There can be at most 4 ACM/generic serial/OBEX ports in the system. + + +Testing the ACM function +------------------------ + +On the host: cat > /dev/ttyACM +On the device : cat /dev/ttyGS + +then the other way round + +On the device: cat > /dev/ttyGS +On the host: cat /dev/ttyACM -- cgit v1.2.3 From d5862ca6da545ec63ec2d2a8c687bf214c87e00e Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:26 +0100 Subject: Documentation: usb: ECM function testing Summary of how to test ECM function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'Documentation') diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index 2a448f812f0e..7df8785fc70d 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -2,6 +2,7 @@ This file summarizes information on basic testing of USB functions provided by gadgets. 1. ACM function +2. ECM function 1. ACM function @@ -32,3 +33,36 @@ then the other way round On the device: cat > /dev/ttyGS On the host: cat /dev/ttyACM + +2. ECM function +=============== + +The function is provided by usb_f_ecm.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "ecm". +The ECM function provides these attributes in its function directory: + + ifname - network device interface name associated with this + function instance + qmult - queue length multiplier for high and super speed + host_addr - MAC address of host's end of this + Ethernet over USB link + dev_addr - MAC address of device's end of this + Ethernet over USB link + +and after creating the functions/ecm. they contain default +values: qmult is 5, dev_addr and host_addr are randomly selected. +Except for ifname they can be written to until the function is linked to a +configuration. The ifname is read-only and contains the name of the interface +which was assigned by the net core, e. g. usb0. + +Testing the ECM function +------------------------ + +Configure IP addresses of the device and the host. Then: + +On the device: ping +On the host: ping -- cgit v1.2.3 From 7bfbc6e3fbd9ae35b28db1839689b7eb4025c93d Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:27 +0100 Subject: Documentation: usb: ECM subset function testing Summary of how to test ECM subset function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'Documentation') diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index 7df8785fc70d..b40db750890f 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -3,6 +3,7 @@ provided by gadgets. 1. ACM function 2. ECM function +3. ECM subset function 1. ACM function @@ -66,3 +67,36 @@ Configure IP addresses of the device and the host. Then: On the device: ping On the host: ping + +3. ECM subset function +====================== + +The function is provided by usb_f_ecm_subset.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "geth". +The ECM subset function provides these attributes in its function directory: + + ifname - network device interface name associated with this + function instance + qmult - queue length multiplier for high and super speed + host_addr - MAC address of host's end of this + Ethernet over USB link + dev_addr - MAC address of device's end of this + Ethernet over USB link + +and after creating the functions/ecm. they contain default +values: qmult is 5, dev_addr and host_addr are randomly selected. +Except for ifname they can be written to until the function is linked to a +configuration. The ifname is read-only and contains the name of the interface +which was assigned by the net core, e. g. usb0. + +Testing the ECM subset function +------------------------------- + +Configure IP addresses of the device and the host. Then: + +On the device: ping +On the host: ping -- cgit v1.2.3 From 4ca560a6d31d1deae004a9977adf64766e87a021 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:28 +0100 Subject: Documentation: usb: EEM function testing Summary of how to test EEM function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'Documentation') diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index b40db750890f..50b0a6cc6674 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -4,6 +4,7 @@ provided by gadgets. 1. ACM function 2. ECM function 3. ECM subset function +4. EEM function 1. ACM function @@ -100,3 +101,36 @@ Configure IP addresses of the device and the host. Then: On the device: ping On the host: ping + +4. EEM function +=============== + +The function is provided by usb_f_eem.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "eem". +The EEM function provides these attributes in its function directory: + + ifname - network device interface name associated with this + function instance + qmult - queue length multiplier for high and super speed + host_addr - MAC address of host's end of this + Ethernet over USB link + dev_addr - MAC address of device's end of this + Ethernet over USB link + +and after creating the functions/eem. they contain default +values: qmult is 5, dev_addr and host_addr are randomly selected. +Except for ifname they can be written to until the function is linked to a +configuration. The ifname is read-only and contains the name of the interface +which was assigned by the net core, e. g. usb0. + +Testing the EEM function +------------------------ + +Configure IP addresses of the device and the host. Then: + +On the device: ping +On the host: ping -- cgit v1.2.3 From 2c0f62f9e4f2c450342369dfd5858db51bc90fe3 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:29 +0100 Subject: Documentation: usb: FFS function testing Summary of how to test FFS (FunctionFS) function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'Documentation') diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index 50b0a6cc6674..141a1d01f312 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -5,6 +5,7 @@ provided by gadgets. 2. ECM function 3. ECM subset function 4. EEM function +5. FFS function 1. ACM function @@ -134,3 +135,26 @@ Configure IP addresses of the device and the host. Then: On the device: ping On the host: ping + +5. FFS function +=============== + +The function is provided by usb_f_fs.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "ffs". +The function directory is intentionally empty and not modifiable. + +After creating the directory there is a new instance (a "device") of FunctionFS +available in the system. Once a "device" is available, the user should follow +the standard procedure for using FunctionFS (mount it, run the userspace +process which implements the function proper). The gadget should be enabled +by writing a suitable string to usb_gadget//UDC. + +Testing the FFS function +------------------------ + +On the device: start the function's userspace daemon, enable the gadget +On the host: use the USB function provided by the device -- cgit v1.2.3 From f7e3c3cd2401b3531a3ac4e279a08d03adb722e3 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:30 +0100 Subject: Documentation: usb: HID function testing Summary of how to test HID function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 47 ++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'Documentation') diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index 141a1d01f312..f117e5c84a1e 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -6,6 +6,7 @@ provided by gadgets. 3. ECM subset function 4. EEM function 5. FFS function +6. HID function 1. ACM function @@ -158,3 +159,49 @@ Testing the FFS function On the device: start the function's userspace daemon, enable the gadget On the host: use the USB function provided by the device + +6. HID function +=============== + +The function is provided by usb_f_hid.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "hid". +The HID function provides these attributes in its function directory: + + protocol - HID protocol to use + report_desc - data to be used in HID reports, except data + passed with /dev/hidg + report_length - HID report length + subclass - HID subclass to use + +For a keyboard the protocol and the subclass are 1, the report_length is 8, +while the report_desc is: + +$ hd my_report_desc +00000000 05 01 09 06 a1 01 05 07 19 e0 29 e7 15 00 25 01 |..........)...%.| +00000010 75 01 95 08 81 02 95 01 75 08 81 03 95 05 75 01 |u.......u.....u.| +00000020 05 08 19 01 29 05 91 02 95 01 75 03 91 03 95 06 |....).....u.....| +00000030 75 08 15 00 25 65 05 07 19 00 29 65 81 00 c0 |u...%e....)e...| +0000003f + +Such a sequence of bytes can be stored to the attribute with echo: + +$ echo -ne \\x05\\x01\\x09\\x06\\xa1..... + +Testing the HID function +------------------------ + +Device: +- create the gadget +- connect the gadget to a host, preferably not the one used +to control the gadget +- run a program which writes to /dev/hidg, e.g. +a userspace program found in Documentation/usb/gadget_hid.txt: + +$ ./hid_gadget_test /dev/hidg0 keyboard + +Host: +- observe the keystrokes from the gadget -- cgit v1.2.3 From ec91aff7630df1e1864d95f5efd25c212d484837 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:31 +0100 Subject: Documentation: usb: LOOPBACK function testing Summary of how to test LOOPBACK function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'Documentation') diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index f117e5c84a1e..b13491d1e352 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -7,6 +7,7 @@ provided by gadgets. 4. EEM function 5. FFS function 6. HID function +7. LOOPBACK function 1. ACM function @@ -205,3 +206,25 @@ $ ./hid_gadget_test /dev/hidg0 keyboard Host: - observe the keystrokes from the gadget + +7. LOOPBACK function +==================== + +The function is provided by usb_f_ss_lb.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "Loopback". +The LOOPBACK function provides these attributes in its function directory: + + qlen - depth of loopback queue + bulk_buflen - buffer length + +Testing the LOOPBACK function +----------------------------- + +device: run the gadget +host: test-usb + +http://www.linux-usb.org/usbtest/testusb.c -- cgit v1.2.3 From cdbe287d271637cb90f239d6a826e2aed555c1d7 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:32 +0100 Subject: Documentation: usb: MASS STORAGE function testing Summary of how to test MASS STORAGE function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 54 ++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) (limited to 'Documentation') diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index b13491d1e352..01b9ffe2eabc 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -8,6 +8,7 @@ provided by gadgets. 5. FFS function 6. HID function 7. LOOPBACK function +8. MASS STORAGE function 1. ACM function @@ -228,3 +229,56 @@ device: run the gadget host: test-usb http://www.linux-usb.org/usbtest/testusb.c + +8. MASS STORAGE function +======================== + +The function is provided by usb_f_mass_storage.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "mass_storage". +The MASS STORAGE function provides these attributes in its directory: +files: + + stall - Set to permit function to halt bulk endpoints. + Disabled on some USB devices known not to work + correctly. You should set it to true. + num_buffers - Number of pipeline buffers. Valid numbers + are 2..4. Available only if + CONFIG_USB_GADGET_DEBUG_FILES is set. + +and a default lun.0 directory corresponding to SCSI LUN #0. + +A new lun can be added with mkdir: + +$ mkdir functions/mass_storage.0/partition.5 + +Lun numbering does not have to be continuous, except for lun #0 which is +created by default. A maximum of 8 luns can be specified and they all must be +named following the . scheme. The numbers can be 0..8. +Probably a good convention is to name the luns "lun.", +although it is not mandatory. + +In each lun directory there are the following attribute files: + + file - The path to the backing file for the LUN. + Required if LUN is not marked as removable. + ro - Flag specifying access to the LUN shall be + read-only. This is implied if CD-ROM emulation + is enabled as well as when it was impossible + to open "filename" in R/W mode. + removable - Flag specifying that LUN shall be indicated as + being removable. + cdrom - Flag specifying that LUN shall be reported as + being a CD-ROM. + nofua - Flag specifying that FUA flag + in SCSI WRITE(10,12) + +Testing the MASS STORAGE function +--------------------------------- + +device: connect the gadget, enable it +host: dmesg, see the USB drives appear (if system configured to automatically +mount) -- cgit v1.2.3 From 0d6be59a1d8a93086cd81e1c59f8d60ba35b40a2 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:33 +0100 Subject: Documentation: usb: MIDI function testing Summary of how to test MIDI function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 84 ++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) (limited to 'Documentation') diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index 01b9ffe2eabc..c2f148d245a5 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -9,6 +9,7 @@ provided by gadgets. 6. HID function 7. LOOPBACK function 8. MASS STORAGE function +9. MIDI function 1. ACM function @@ -282,3 +283,86 @@ Testing the MASS STORAGE function device: connect the gadget, enable it host: dmesg, see the USB drives appear (if system configured to automatically mount) + +9. MIDI function +================ + +The function is provided by usb_f_midi.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "midi". +The MIDI function provides these attributes in its function directory: + + buflen - MIDI buffer length + id - ID string for the USB MIDI adapter + in_ports - number of MIDI input ports + index - index value for the USB MIDI adapter + out_ports - number of MIDI output ports + qlen - USB read request queue length + +Testing the MIDI function +------------------------- + +There are two cases: playing a mid from the gadget to +the host and playing a mid from the host to the gadget. + +1) Playing a mid from the gadget to the host +host) + +$ arecordmidi -l + Port Client name Port name + 14:0 Midi Through Midi Through Port-0 + 24:0 MIDI Gadget MIDI Gadget MIDI 1 +$ arecordmidi -p 24:0 from_gadget.mid + +gadget) + +$ aplaymidi -l + Port Client name Port name + 20:0 f_midi f_midi + +$ aplaymidi -p 20:0 to_host.mid + +2) Playing a mid from the host to the gadget +gadget) + +$ arecordmidi -l + Port Client name Port name + 20:0 f_midi f_midi + +$ arecordmidi -p 20:0 from_host.mid + +host) + +$ aplaymidi -l + Port Client name Port name + 14:0 Midi Through Midi Through Port-0 + 24:0 MIDI Gadget MIDI Gadget MIDI 1 + +$ aplaymidi -p24:0 to_gadget.mid + +The from_gadget.mid should sound identical to the to_host.mid. +The from_host.id should sound identical to the to_gadget.mid. + +MIDI files can be played to speakers/headphones with e.g. timidity installed + +$ aplaymidi -l + Port Client name Port name + 14:0 Midi Through Midi Through Port-0 + 24:0 MIDI Gadget MIDI Gadget MIDI 1 +128:0 TiMidity TiMidity port 0 +128:1 TiMidity TiMidity port 1 +128:2 TiMidity TiMidity port 2 +128:3 TiMidity TiMidity port 3 + +$ aplaymidi -p 128:0 file.mid + +MIDI ports can be logically connected using the aconnect utility, e.g.: + +$ aconnect 24:0 128:0 # try it on the host + +After the gadget's MIDI port is connected to timidity's MIDI port, +whatever is played at the gadget side with aplaymidi -l is audible +in host's speakers/headphones. -- cgit v1.2.3 From 4d0fa79e0ef78f516888068e240d38650eb404d2 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:34 +0100 Subject: Documentation: usb: NCM function testing Summary of how to test NCM function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'Documentation') diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index c2f148d245a5..493f79b96f91 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -10,6 +10,7 @@ provided by gadgets. 7. LOOPBACK function 8. MASS STORAGE function 9. MIDI function +10. NCM function 1. ACM function @@ -366,3 +367,36 @@ $ aconnect 24:0 128:0 # try it on the host After the gadget's MIDI port is connected to timidity's MIDI port, whatever is played at the gadget side with aplaymidi -l is audible in host's speakers/headphones. + +10. NCM function +================ + +The function is provided by usb_f_ncm.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "ncm". +The NCM function provides these attributes in its function directory: + + ifname - network device interface name associated with this + function instance + qmult - queue length multiplier for high and super speed + host_addr - MAC address of host's end of this + Ethernet over USB link + dev_addr - MAC address of device's end of this + Ethernet over USB link + +and after creating the functions/ncm. they contain default +values: qmult is 5, dev_addr and host_addr are randomly selected. +Except for ifname they can be written to until the function is linked to a +configuration. The ifname is read-only and contains the name of the interface +which was assigned by the net core, e. g. usb0. + +Testing the NCM function +------------------------ + +Configure IP addresses of the device and the host. Then: + +On the device: ping +On the host: ping -- cgit v1.2.3 From d81b85dcf186f6fa4b59e5b8181e85366ebfdd61 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:35 +0100 Subject: Documentation: usb: OBEX function testing Summary of how to test OBEX function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'Documentation') diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index 493f79b96f91..c8ae54022ec8 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -11,6 +11,7 @@ provided by gadgets. 8. MASS STORAGE function 9. MIDI function 10. NCM function +11. OBEX function 1. ACM function @@ -400,3 +401,31 @@ Configure IP addresses of the device and the host. Then: On the device: ping On the host: ping + +11. OBEX function +================= + +The function is provided by usb_f_obex.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "obex". +The OBEX function provides just one attribute in its function directory: + + port_num + +The attribute is read-only. + +There can be at most 4 ACM/generic serial/OBEX ports in the system. + +Testing the OBEX function +------------------------- + +On device: seriald -f /dev/ttyGS -s 1024 +On host: serialc -v -p -i -a1 -s1024 \ + -t -r + +where seriald and serialc are Felipe's utilities found here: + +https://git.gitorious.org/usb/usb-tools.git master -- cgit v1.2.3 From da2907d2b08c2495c2e39f799b9ccbf76fd1aa58 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:36 +0100 Subject: Documentation: usb: PHONET function testing Summary of how to test PHONET function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 64 ++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) (limited to 'Documentation') diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index c8ae54022ec8..02d4366a847d 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -12,6 +12,7 @@ provided by gadgets. 9. MIDI function 10. NCM function 11. OBEX function +12. PHONET function 1. ACM function @@ -429,3 +430,66 @@ On host: serialc -v -p -i -a1 -s1024 \ where seriald and serialc are Felipe's utilities found here: https://git.gitorious.org/usb/usb-tools.git master + +12. PHONET function +=================== + +The function is provided by usb_f_phonet.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "phonet". +The PHONET function provides just one attribute in its function directory: + + ifname - network device interface name associated with this + function instance + +Testing the PHONET function +--------------------------- + +It is not possible to test the SOCK_STREAM protocol without a specific piece +of hardware, so only SOCK_DGRAM has been tested. For the latter to work, +in the past I had to apply the patch mentioned here: + +http://www.spinics.net/lists/linux-usb/msg85689.html + +These tools are required: + +git://git.gitorious.org/meego-cellular/phonet-utils.git + +On the host: + +$ ./phonet -a 0x10 -i usbpn0 +$ ./pnroute add 0x6c usbpn0 +$./pnroute add 0x10 usbpn0 +$ ifconfig usbpn0 up + +On the device: + +$ ./phonet -a 0x6c -i upnlink0 +$ ./pnroute add 0x10 upnlink0 +$ ifconfig upnlink0 up + +Then a test program can be used: + +http://www.spinics.net/lists/linux-usb/msg85690.html + +On the device: + +$ ./pnxmit -a 0x6c -r + +On the host: + +$ ./pnxmit -a 0x10 -s 0x6c + +As a result some data should be sent from host to device. +Then the other way round: + +On the host: + +$ ./pnxmit -a 0x10 -r + +On the device: + +$ ./pnxmit -a 0x6c -s 0x10 -- cgit v1.2.3 From ddb722449cfcfe30d3c3ed2cd404551e1c3c3670 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:37 +0100 Subject: Documentation: usb: RNDIS function testing Summary of how to test RNDIS function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'Documentation') diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index 02d4366a847d..61bc5f5934e1 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -13,6 +13,7 @@ provided by gadgets. 10. NCM function 11. OBEX function 12. PHONET function +13. RNDIS function 1. ACM function @@ -493,3 +494,38 @@ $ ./pnxmit -a 0x10 -r On the device: $ ./pnxmit -a 0x6c -s 0x10 + +13. RNDIS function +================== + +The function is provided by usb_f_rndis.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "rndis". +The RNDIS function provides these attributes in its function directory: + + ifname - network device interface name associated with this + function instance + qmult - queue length multiplier for high and super speed + host_addr - MAC address of host's end of this + Ethernet over USB link + dev_addr - MAC address of device's end of this + Ethernet over USB link + +and after creating the functions/rndis. they contain default +values: qmult is 5, dev_addr and host_addr are randomly selected. +Except for ifname they can be written to until the function is linked to a +configuration. The ifname is read-only and contains the name of the interface +which was assigned by the net core, e. g. usb0. + +By default there can be only 1 RNDIS interface in the system. + +Testing the RNDIS function +-------------------------- + +Configure IP addresses of the device and the host. Then: + +On the device: ping +On the host: ping -- cgit v1.2.3 From 4dfcec8a2f9e22ac409ae512d5ff6acced8c588f Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:38 +0100 Subject: Documentation: usb: SERIAL function testing Summary of how to test SERIAL function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'Documentation') diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index 61bc5f5934e1..bc57c65e76b6 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -14,6 +14,7 @@ provided by gadgets. 11. OBEX function 12. PHONET function 13. RNDIS function +14. SERIAL function 1. ACM function @@ -529,3 +530,33 @@ Configure IP addresses of the device and the host. Then: On the device: ping On the host: ping + +14. SERIAL function +=================== + +The function is provided by usb_f_gser.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "gser". +The SERIAL function provides just one attribute in its function directory: + + port_num + +The attribute is read-only. + +There can be at most 4 ACM/generic serial/OBEX ports in the system. + +Testing the SERIAL function +--------------------------- + +On host: insmod usbserial + echo VID PID >/sys/bus/usb-serial/drivers/generic/new_id +On host: cat > /dev/ttyUSB +On target: cat /dev/ttyGS + +then the other way round + +On target: cat > /dev/ttyGS +On host: cat /dev/ttyUSB -- cgit v1.2.3 From 480a912be4eb51e30aec721731b9cf476b2ee861 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:39 +0100 Subject: Documentation: usb: SOURCESINK function testing Summary of how to test SOURCESINK function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'Documentation') diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index bc57c65e76b6..73a5b0c34174 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -15,6 +15,7 @@ provided by gadgets. 12. PHONET function 13. RNDIS function 14. SERIAL function +15. SOURCESINK function 1. ACM function @@ -560,3 +561,29 @@ then the other way round On target: cat > /dev/ttyGS On host: cat /dev/ttyUSB + +15. SOURCESINK function +======================= + +The function is provided by usb_f_ss_lb.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "SourceSink". +The SOURCESINK function provides these attributes in its function directory: + + pattern - 0 (all zeros), 1 (mod63), 2 (none) + isoc_interval - 1..16 + isoc_maxpacket - 0 - 1023 (fs), 0 - 1024 (hs/ss) + isoc_mult - 0..2 (hs/ss only) + isoc_maxburst - 0..15 (ss only) + bulk_buflen - buffer length + +Testing the SOURCESINK function +------------------------------- + +device: run the gadget +host: test-usb + +http://www.linux-usb.org/usbtest/testusb.c -- cgit v1.2.3 From dae0358694abe0bbca3b83a922698b1f24c4299a Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:40 +0100 Subject: Documentation: usb: UAC1 function testing Summary of how to test UAC1 function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'Documentation') diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index 73a5b0c34174..40d22d82cc5a 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -16,6 +16,7 @@ provided by gadgets. 13. RNDIS function 14. SERIAL function 15. SOURCESINK function +16. UAC1 function 1. ACM function @@ -587,3 +588,29 @@ device: run the gadget host: test-usb http://www.linux-usb.org/usbtest/testusb.c + +16. UAC1 function +================= + +The function is provided by usb_f_uac1.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "uac1". +The uac1 function provides these attributes in its function directory: + + audio_buf_size - audio buffer size + fn_cap - capture pcm device file name + fn_cntl - control device file name + fn_play - playback pcm device file name + req_buf_size - ISO OUT endpoint request buffer size + req_count - ISO OUT endpoint request count + +The attributes have sane default values. + +Testing the UAC1 function +------------------------- + +device: run the gadget +host: aplay -l # should list our USB Audio Gadget -- cgit v1.2.3 From 020c6f93484370672060fabebc49aed5d1a7c58f Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:41 +0100 Subject: Documentation: usb: UAC2 function testing Summary of how to test UAC2 function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 39 ++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'Documentation') diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index 40d22d82cc5a..5ac9dc702c0e 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -17,6 +17,7 @@ provided by gadgets. 14. SERIAL function 15. SOURCESINK function 16. UAC1 function +17. UAC2 function 1. ACM function @@ -614,3 +615,41 @@ Testing the UAC1 function device: run the gadget host: aplay -l # should list our USB Audio Gadget + +17. UAC2 function +================= + +The function is provided by usb_f_uac2.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "uac2". +The uac2 function provides these attributes in its function directory: + + chmask - capture channel mask + c_srate - capture sampling rate + c_ssize - capture sample size (bytes) + p_chmask - playback channel mask + p_srate - playback sampling rate + p_ssize - playback sample size (bytes) + +The attributes have sane default values. + +Testing the UAC2 function +------------------------- + +device: run the gadget +host: aplay -l # should list our USB Audio Gadget + +This function does not require real hardware support, it just +sends a stream of audio data to/from the host. In order to +actually hear something at the device side, a command similar +to this must be used at the device side: + +$ arecord -f dat -t wav -D hw:2,0 | aplay -D hw:0,0 & + +e.g.: + +$ arecord -f dat -t wav -D hw:CARD=UAC2Gadget,DEV=0 | \ +aplay -D default:CARD=OdroidU3 -- cgit v1.2.3 From 9c4f538bfaf696c5cb24ec58fbeba12155c56c31 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 16 Dec 2014 14:56:42 +0100 Subject: Documentation: usb: UVC function testing Summary of how to test UVC function of USB gadget. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/usb/gadget-testing.txt | 73 ++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) (limited to 'Documentation') diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index 5ac9dc702c0e..076ac7ba7f93 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -18,6 +18,7 @@ provided by gadgets. 15. SOURCESINK function 16. UAC1 function 17. UAC2 function +18. UVC function 1. ACM function @@ -653,3 +654,75 @@ e.g.: $ arecord -f dat -t wav -D hw:CARD=UAC2Gadget,DEV=0 | \ aplay -D default:CARD=OdroidU3 + +18. UVC function +================ + +The function is provided by usb_f_uvc.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "uvc". +The uvc function provides these attributes in its function directory: + + streaming_interval - interval for polling endpoint for data transfers + streaming_maxburst - bMaxBurst for super speed companion descriptor + streaming_maxpacket - maximum packet size this endpoint is capable of + sending or receiving when this configuration is + selected + +There are also "control" and "streaming" subdirectories, each of which contain +a number of their subdirectories. There are some sane defaults provided, but +the user must provide the following: + + control header - create in control/header, link from control/class/fs + and/or control/class/ss + streaming header - create in streaming/header, link from + streaming/class/fs and/or streaming/class/hs and/or + streaming/class/ss + format description - create in streaming/mjpeg and/or + streaming/uncompressed + frame description - create in streaming/mjpeg/ and/or in + streaming/uncompressed/ + +Each frame description contains frame interval specification, and each +such specification consists of a number of lines with an inverval value +in each line. The rules stated above are best illustrated with an example: + +# mkdir functions/uvc.usb0/control/header/h +# cd functions/uvc.usb0/control/header/h +# ln -s header/h class/fs +# ln -s header/h class/ss +# mkdir -p functions/uvc.usb0/streaming/uncompressed/u/360p +# cat < functions/uvc.usb0/streaming/uncompressed/u/360p/dwFrameInterval +666666 +1000000 +5000000 +EOF +# cd $GADGET_CONFIGFS_ROOT +# mkdir functions/uvc.usb0/streaming/header/h +# cd functions/uvc.usb0/streaming/header/h +# ln -s ../../uncompressed/u +# cd ../../class/fs +# ln -s ../../header/h +# cd ../../class/hs +# ln -s ../../header/h +# cd ../../class/ss +# ln -s ../../header/h + + +Testing the UVC function +------------------------ + +device: run the gadget, modprobe vivid + +# uvc-gadget -u /dev/video -v /dev/video + +where uvc-gadget is this program: +http://git.ideasonboard.org/uvc-gadget.git + +with these patches: +http://www.spinics.net/lists/linux-usb/msg99220.html + +host: luvcview -f yuv -- cgit v1.2.3 From 6b1b0fe0698f0e4d528a33f43d5c56f7e6e8f756 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 12 Dec 2014 13:08:26 -0200 Subject: Documentation: usb: phy: nop: Fix the description of 'vcc-supply' Since bd27fa44e13830d2b ("usb: phy: generic: Don't use regulator framework for RESET line") we no longer model the reset line as a regulator supply, so adapt the documentation accordingly. Acked-by: Roger Quadros Signed-off-by: Fabio Estevam Signed-off-by: Felipe Balbi --- Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt b/Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt index 1bd37faba05b..8db5b33d6fdc 100644 --- a/Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt +++ b/Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt @@ -13,7 +13,7 @@ Optional properties: - clock-frequency: the clock frequency (in Hz) that the PHY clock must be configured to. -- vcc-supply: phandle to the regulator that provides RESET to the PHY. +- vcc-supply: phandle to the regulator that provides power to the PHY. - reset-gpios: Should specify the GPIO for reset. -- cgit v1.2.3 From 46919a23ee87bbc4eeb6d958471174e26836f0e1 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Wed, 10 Dec 2014 12:34:02 +0100 Subject: usb: gadget: uvc: configfs support in uvc function Add support for using the uvc function as a component of USB gadgets composed with configfs. Acked-by: Laurent Pinchart Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- Documentation/ABI/testing/configfs-usb-gadget-uvc | 265 +++ drivers/usb/gadget/Kconfig | 11 + drivers/usb/gadget/function/Makefile | 2 +- drivers/usb/gadget/function/f_uvc.c | 106 +- drivers/usb/gadget/function/u_uvc.h | 50 + drivers/usb/gadget/function/uvc_configfs.c | 2439 +++++++++++++++++++++ drivers/usb/gadget/function/uvc_configfs.h | 22 + 7 files changed, 2891 insertions(+), 4 deletions(-) create mode 100644 Documentation/ABI/testing/configfs-usb-gadget-uvc create mode 100644 drivers/usb/gadget/function/uvc_configfs.c create mode 100644 drivers/usb/gadget/function/uvc_configfs.h (limited to 'Documentation') diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uvc b/Documentation/ABI/testing/configfs-usb-gadget-uvc new file mode 100644 index 000000000000..2f4a0051b32d --- /dev/null +++ b/Documentation/ABI/testing/configfs-usb-gadget-uvc @@ -0,0 +1,265 @@ +What: /config/usb-gadget/gadget/functions/uvc.name +Date: Dec 2014 +KernelVersion: 3.20 +Description: UVC function directory + + streaming_maxburst - 0..15 (ss only) + streaming_maxpacket - 1..1023 (fs), 1..3072 (hs/ss) + streaming_interval - 1..16 + +What: /config/usb-gadget/gadget/functions/uvc.name/control +Date: Dec 2014 +KernelVersion: 3.20 +Description: Control descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/control/class +Date: Dec 2014 +KernelVersion: 3.20 +Description: Class descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/control/class/ss +Date: Dec 2014 +KernelVersion: 3.20 +Description: Super speed control class descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/control/class/fs +Date: Dec 2014 +KernelVersion: 3.20 +Description: Full speed control class descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/control/terminal +Date: Dec 2014 +KernelVersion: 3.20 +Description: Terminal descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/control/terminal/output +Date: Dec 2014 +KernelVersion: 3.20 +Description: Output terminal descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/control/terminal/output/default +Date: Dec 2014 +KernelVersion: 3.20 +Description: Default output terminal descriptors + + All attributes read only: + iTerminal - index of string descriptor + bSourceID - id of the terminal to which this terminal + is connected + bAssocTerminal - id of the input terminal to which this output + terminal is associated + wTerminalType - terminal type + bTerminalID - a non-zero id of this terminal + +What: /config/usb-gadget/gadget/functions/uvc.name/control/terminal/camera +Date: Dec 2014 +KernelVersion: 3.20 +Description: Camera terminal descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/control/terminal/camera/default +Date: Dec 2014 +KernelVersion: 3.20 +Description: Default camera terminal descriptors + + All attributes read only: + bmControls - bitmap specifying which controls are + supported for the video stream + wOcularFocalLength - the value of Locular + wObjectiveFocalLengthMax- the value of Lmin + wObjectiveFocalLengthMin- the value of Lmax + iTerminal - index of string descriptor + bAssocTerminal - id of the output terminal to which + this terminal is connected + wTerminalType - terminal type + bTerminalID - a non-zero id of this terminal + +What: /config/usb-gadget/gadget/functions/uvc.name/control/processing +Date: Dec 2014 +KernelVersion: 3.20 +Description: Processing unit descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/control/processing/default +Date: Dec 2014 +KernelVersion: 3.20 +Description: Default processing unit descriptors + + All attributes read only: + iProcessing - index of string descriptor + bmControls - bitmap specifying which controls are + supported for the video stream + wMaxMultiplier - maximum digital magnification x100 + bSourceID - id of the terminal to which this unit is + connected + bUnitID - a non-zero id of this unit + +What: /config/usb-gadget/gadget/functions/uvc.name/control/header +Date: Dec 2014 +KernelVersion: 3.20 +Description: Control header descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/control/header/name +Date: Dec 2014 +KernelVersion: 3.20 +Description: Specific control header descriptors + +dwClockFrequency +bcdUVC +What: /config/usb-gadget/gadget/functions/uvc.name/streaming +Date: Dec 2014 +KernelVersion: 3.20 +Description: Streaming descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/class +Date: Dec 2014 +KernelVersion: 3.20 +Description: Streaming class descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/class/ss +Date: Dec 2014 +KernelVersion: 3.20 +Description: Super speed streaming class descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/class/hs +Date: Dec 2014 +KernelVersion: 3.20 +Description: High speed streaming class descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/class/fs +Date: Dec 2014 +KernelVersion: 3.20 +Description: Full speed streaming class descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/color_matching +Date: Dec 2014 +KernelVersion: 3.20 +Description: Color matching descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/color_matching/default +Date: Dec 2014 +KernelVersion: 3.20 +Description: Default color matching descriptors + + All attributes read only: + bMatrixCoefficients - matrix used to compute luma and + chroma values from the color primaries + bTransferCharacteristics- optoelectronic transfer + characteristic of the source picutre, + also called the gamma function + bColorPrimaries - color primaries and the reference + white + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/mjpeg +Date: Dec 2014 +KernelVersion: 3.20 +Description: MJPEG format descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/mjpeg/name +Date: Dec 2014 +KernelVersion: 3.20 +Description: Specific MJPEG format descriptors + + All attributes read only, + except bmaControls and bDefaultFrameIndex: + bmaControls - this format's data for bmaControls in + the streaming header + bmInterfaceFlags - specifies interlace information, + read-only + bAspectRatioY - the X dimension of the picture aspect + ratio, read-only + bAspectRatioX - the Y dimension of the picture aspect + ratio, read-only + bmFlags - characteristics of this format, + read-only + bDefaultFrameIndex - optimum frame index for this stream + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/mjpeg/name/name +Date: Dec 2014 +KernelVersion: 3.20 +Description: Specific MJPEG frame descriptors + + dwFrameInterval - indicates how frame interval can be + programmed; a number of values + separated by newline can be specified + dwDefaultFrameInterval - the frame interval the device would + like to use as default + dwMaxVideoFrameBufferSize- the maximum number of bytes the + compressor will produce for a video + frame or still image + dwMaxBitRate - the maximum bit rate at the shortest + frame interval in bps + dwMinBitRate - the minimum bit rate at the longest + frame interval in bps + wHeight - height of decoded bitmap frame in px + wWidth - width of decoded bitmam frame in px + bmCapabilities - still image support, fixed frame-rate + support + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/uncompressed +Date: Dec 2014 +KernelVersion: 3.20 +Description: Uncompressed format descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/uncompressed/name +Date: Dec 2014 +KernelVersion: 3.20 +Description: Specific uncompressed format descriptors + + bmaControls - this format's data for bmaControls in + the streaming header + bmInterfaceFlags - specifies interlace information, + read-only + bAspectRatioY - the X dimension of the picture aspect + ratio, read-only + bAspectRatioX - the Y dimension of the picture aspect + ratio, read-only + bDefaultFrameIndex - optimum frame index for this stream + bBitsPerPixel - number of bits per pixel used to + specify color in the decoded video + frame + guidFormat - globally unique id used to identify + stream-encoding format + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/uncompressed/name/name +Date: Dec 2014 +KernelVersion: 3.20 +Description: Specific uncompressed frame descriptors + + dwFrameInterval - indicates how frame interval can be + programmed; a number of values + separated by newline can be specified + dwDefaultFrameInterval - the frame interval the device would + like to use as default + dwMaxVideoFrameBufferSize- the maximum number of bytes the + compressor will produce for a video + frame or still image + dwMaxBitRate - the maximum bit rate at the shortest + frame interval in bps + dwMinBitRate - the minimum bit rate at the longest + frame interval in bps + wHeight - height of decoded bitmap frame in px + wWidth - width of decoded bitmam frame in px + bmCapabilities - still image support, fixed frame-rate + support + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/header +Date: Dec 2014 +KernelVersion: 3.20 +Description: Streaming header descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/header/name +Date: Dec 2014 +KernelVersion: 3.20 +Description: Specific streaming header descriptors + + All attributes read only: + bTriggerUsage - how the host software will respond to + a hardware trigger interrupt event + bTriggerSupport - flag specifying if hardware + triggering is supported + bStillCaptureMethod - method of still image caputre + supported + bTerminalLink - id of the output terminal to which + the video endpoint of this interface + is connected + bmInfo - capabilities of this video streaming + interface diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 747ef53bda14..65f7f1265522 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -423,6 +423,17 @@ config USB_CONFIGFS_F_HID For more information, see Documentation/usb/gadget_hid.txt. +config USB_CONFIGFS_F_UVC + boolean "USB Webcam function" + depends on USB_CONFIGFS + depends on VIDEO_DEV + select VIDEOBUF2_VMALLOC + select USB_F_UVC + help + The Webcam function acts as a composite USB Audio and Video Class + device. It provides a userspace API to process UVC control requests + and stream video data to the host. + source "drivers/usb/gadget/legacy/Kconfig" endchoice diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index dd68091d92f0..f71b1aaa0edf 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -36,7 +36,7 @@ usb_f_uac1-y := f_uac1.o u_uac1.o obj-$(CONFIG_USB_F_UAC1) += usb_f_uac1.o usb_f_uac2-y := f_uac2.o obj-$(CONFIG_USB_F_UAC2) += usb_f_uac2.o -usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o +usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_configfs.o obj-$(CONFIG_USB_F_UVC) += usb_f_uvc.o usb_f_midi-y := f_midi.o obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 62ca0c5c7f6e..76891adfba7a 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -27,10 +27,11 @@ #include #include +#include "u_uvc.h" #include "uvc.h" +#include "uvc_configfs.h" #include "uvc_v4l2.h" #include "uvc_video.h" -#include "u_uvc.h" unsigned int uvc_gadget_trace_param; @@ -788,25 +789,104 @@ static void uvc_free_inst(struct usb_function_instance *f) { struct f_uvc_opts *opts = fi_to_f_uvc_opts(f); + mutex_destroy(&opts->lock); kfree(opts); } static struct usb_function_instance *uvc_alloc_inst(void) { struct f_uvc_opts *opts; + struct uvc_camera_terminal_descriptor *cd; + struct uvc_processing_unit_descriptor *pd; + struct uvc_output_terminal_descriptor *od; + struct uvc_color_matching_descriptor *md; + struct uvc_descriptor_header **ctl_cls; opts = kzalloc(sizeof(*opts), GFP_KERNEL); if (!opts) return ERR_PTR(-ENOMEM); opts->func_inst.free_func_inst = uvc_free_inst; - + mutex_init(&opts->lock); + + cd = &opts->uvc_camera_terminal; + cd->bLength = UVC_DT_CAMERA_TERMINAL_SIZE(3); + cd->bDescriptorType = USB_DT_CS_INTERFACE; + cd->bDescriptorSubType = UVC_VC_INPUT_TERMINAL; + cd->bTerminalID = 1; + cd->wTerminalType = cpu_to_le16(0x0201); + cd->bAssocTerminal = 0; + cd->iTerminal = 0; + cd->wObjectiveFocalLengthMin = cpu_to_le16(0); + cd->wObjectiveFocalLengthMax = cpu_to_le16(0); + cd->wOcularFocalLength = cpu_to_le16(0); + cd->bControlSize = 3; + cd->bmControls[0] = 2; + cd->bmControls[1] = 0; + cd->bmControls[2] = 0; + + pd = &opts->uvc_processing; + pd->bLength = UVC_DT_PROCESSING_UNIT_SIZE(2); + pd->bDescriptorType = USB_DT_CS_INTERFACE; + pd->bDescriptorSubType = UVC_VC_PROCESSING_UNIT; + pd->bUnitID = 2; + pd->bSourceID = 1; + pd->wMaxMultiplier = cpu_to_le16(16*1024); + pd->bControlSize = 2; + pd->bmControls[0] = 1; + pd->bmControls[1] = 0; + pd->iProcessing = 0; + + od = &opts->uvc_output_terminal; + od->bLength = UVC_DT_OUTPUT_TERMINAL_SIZE; + od->bDescriptorType = USB_DT_CS_INTERFACE; + od->bDescriptorSubType = UVC_VC_OUTPUT_TERMINAL; + od->bTerminalID = 3; + od->wTerminalType = cpu_to_le16(0x0101); + od->bAssocTerminal = 0; + od->bSourceID = 2; + od->iTerminal = 0; + + md = &opts->uvc_color_matching; + md->bLength = UVC_DT_COLOR_MATCHING_SIZE; + md->bDescriptorType = USB_DT_CS_INTERFACE; + md->bDescriptorSubType = UVC_VS_COLORFORMAT; + md->bColorPrimaries = 1; + md->bTransferCharacteristics = 1; + md->bMatrixCoefficients = 4; + + /* Prepare fs control class descriptors for configfs-based gadgets */ + ctl_cls = opts->uvc_fs_control_cls; + ctl_cls[0] = NULL; /* assigned elsewhere by configfs */ + ctl_cls[1] = (struct uvc_descriptor_header *)cd; + ctl_cls[2] = (struct uvc_descriptor_header *)pd; + ctl_cls[3] = (struct uvc_descriptor_header *)od; + ctl_cls[4] = NULL; /* NULL-terminate */ + opts->fs_control = + (const struct uvc_descriptor_header * const *)ctl_cls; + + /* Prepare hs control class descriptors for configfs-based gadgets */ + ctl_cls = opts->uvc_ss_control_cls; + ctl_cls[0] = NULL; /* assigned elsewhere by configfs */ + ctl_cls[1] = (struct uvc_descriptor_header *)cd; + ctl_cls[2] = (struct uvc_descriptor_header *)pd; + ctl_cls[3] = (struct uvc_descriptor_header *)od; + ctl_cls[4] = NULL; /* NULL-terminate */ + opts->ss_control = + (const struct uvc_descriptor_header * const *)ctl_cls; + + opts->streaming_interval = 1; + opts->streaming_maxpacket = 1024; + + uvcg_attach_configfs(opts); return &opts->func_inst; } static void uvc_free(struct usb_function *f) { struct uvc_device *uvc = to_uvc(f); - + struct f_uvc_opts *opts = container_of(f->fi, struct f_uvc_opts, + func_inst); + --opts->refcnt; kfree(uvc); } @@ -832,6 +912,7 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi) { struct uvc_device *uvc; struct f_uvc_opts *opts; + struct uvc_descriptor_header **strm_cls; uvc = kzalloc(sizeof(*uvc), GFP_KERNEL); if (uvc == NULL) @@ -840,11 +921,30 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi) uvc->state = UVC_STATE_DISCONNECTED; opts = fi_to_f_uvc_opts(fi); + mutex_lock(&opts->lock); + if (opts->uvc_fs_streaming_cls) { + strm_cls = opts->uvc_fs_streaming_cls; + opts->fs_streaming = + (const struct uvc_descriptor_header * const *)strm_cls; + } + if (opts->uvc_hs_streaming_cls) { + strm_cls = opts->uvc_hs_streaming_cls; + opts->hs_streaming = + (const struct uvc_descriptor_header * const *)strm_cls; + } + if (opts->uvc_ss_streaming_cls) { + strm_cls = opts->uvc_ss_streaming_cls; + opts->ss_streaming = + (const struct uvc_descriptor_header * const *)strm_cls; + } + uvc->desc.fs_control = opts->fs_control; uvc->desc.ss_control = opts->ss_control; uvc->desc.fs_streaming = opts->fs_streaming; uvc->desc.hs_streaming = opts->hs_streaming; uvc->desc.ss_streaming = opts->ss_streaming; + ++opts->refcnt; + mutex_unlock(&opts->lock); /* Register the function. */ uvc->func.name = "uvc"; diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h index c0706a3d7019..4676b60a5063 100644 --- a/drivers/usb/gadget/function/u_uvc.h +++ b/drivers/usb/gadget/function/u_uvc.h @@ -17,6 +17,7 @@ #define U_UVC_H #include +#include #define fi_to_f_uvc_opts(f) container_of(f, struct f_uvc_opts, func_inst) @@ -26,11 +27,60 @@ struct f_uvc_opts { unsigned int streaming_interval; unsigned int streaming_maxpacket; unsigned int streaming_maxburst; + + /* + * Control descriptors array pointers for full-/high-speed and + * super-speed. They point by default to the uvc_fs_control_cls and + * uvc_ss_control_cls arrays respectively. Legacy gadgets must + * override them in their gadget bind callback. + */ const struct uvc_descriptor_header * const *fs_control; const struct uvc_descriptor_header * const *ss_control; + + /* + * Streaming descriptors array pointers for full-speed, high-speed and + * super-speed. They will point to the uvc_[fhs]s_streaming_cls arrays + * for configfs-based gadgets. Legacy gadgets must initialize them in + * their gadget bind callback. + */ const struct uvc_descriptor_header * const *fs_streaming; const struct uvc_descriptor_header * const *hs_streaming; const struct uvc_descriptor_header * const *ss_streaming; + + /* Default control descriptors for configfs-based gadgets. */ + struct uvc_camera_terminal_descriptor uvc_camera_terminal; + struct uvc_processing_unit_descriptor uvc_processing; + struct uvc_output_terminal_descriptor uvc_output_terminal; + struct uvc_color_matching_descriptor uvc_color_matching; + + /* + * Control descriptors pointers arrays for full-/high-speed and + * super-speed. The first element is a configurable control header + * descriptor, the other elements point to the fixed default control + * descriptors. Used by configfs only, must not be touched by legacy + * gadgets. + */ + struct uvc_descriptor_header *uvc_fs_control_cls[5]; + struct uvc_descriptor_header *uvc_ss_control_cls[5]; + + /* + * Streaming descriptors for full-speed, high-speed and super-speed. + * Used by configfs only, must not be touched by legacy gadgets. The + * arrays are allocated at runtime as the number of descriptors isn't + * known in advance. + */ + struct uvc_descriptor_header **uvc_fs_streaming_cls; + struct uvc_descriptor_header **uvc_hs_streaming_cls; + struct uvc_descriptor_header **uvc_ss_streaming_cls; + + /* + * Read/write access to configfs attributes is handled by configfs. + * + * This lock protects the descriptors from concurrent access by + * read/write and symlink creation/removal. + */ + struct mutex lock; + int refcnt; }; void uvc_set_trace_param(unsigned int trace); diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c new file mode 100644 index 000000000000..33d92ab3c031 --- /dev/null +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -0,0 +1,2439 @@ +/* + * uvc_configfs.c + * + * Configfs support for the uvc function. + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz + * + * 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 "u_uvc.h" +#include "uvc_configfs.h" + +#define UVCG_STREAMING_CONTROL_SIZE 1 + +#define CONFIGFS_ATTR_OPS_RO(_item) \ +static ssize_t _item##_attr_show(struct config_item *item, \ + struct configfs_attribute *attr, \ + char *page) \ +{ \ + struct _item *_item = to_##_item(item); \ + struct _item##_attribute *_item##_attr = \ + container_of(attr, struct _item##_attribute, attr); \ + ssize_t ret = 0; \ + \ + if (_item##_attr->show) \ + ret = _item##_attr->show(_item, page); \ + return ret; \ +} + +static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item); + +/* control/header/ */ +DECLARE_UVC_HEADER_DESCRIPTOR(1); + +struct uvcg_control_header { + struct config_item item; + struct UVC_HEADER_DESCRIPTOR(1) desc; + unsigned linked; +}; + +struct uvcg_control_header *to_uvcg_control_header(struct config_item *item) +{ + return container_of(item, struct uvcg_control_header, item); +} + +CONFIGFS_ATTR_STRUCT(uvcg_control_header); +CONFIGFS_ATTR_OPS(uvcg_control_header); + +static struct configfs_item_operations uvcg_control_header_item_ops = { + .show_attribute = uvcg_control_header_attr_show, + .store_attribute = uvcg_control_header_attr_store, +}; + +#define UVCG_CTRL_HDR_ATTR(cname, aname, conv, str2u, uxx, vnoc, limit) \ +static ssize_t uvcg_control_header_##cname##_show( \ + struct uvcg_control_header *ch, char *page) \ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;\ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = ch->item.ci_parent->ci_parent->ci_parent; \ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%d\n", conv(ch->desc.aname)); \ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +static ssize_t \ +uvcg_control_header_##cname##_store(struct uvcg_control_header *ch, \ + const char *page, size_t len) \ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;\ + int ret; \ + uxx num; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = ch->item.ci_parent->ci_parent->ci_parent; \ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + if (ch->linked || opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = str2u(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + if (num > limit) { \ + ret = -EINVAL; \ + goto end; \ + } \ + ch->desc.aname = vnoc(num); \ + ret = len; \ +end: \ + mutex_unlock(&opts->lock); \ + mutex_unlock(su_mutex); \ + return ret; \ +} \ + \ +static struct uvcg_control_header_attribute \ + uvcg_control_header_##cname = \ + __CONFIGFS_ATTR(aname, S_IRUGO | S_IWUSR, \ + uvcg_control_header_##cname##_show, \ + uvcg_control_header_##cname##_store) + +UVCG_CTRL_HDR_ATTR(bcd_uvc, bcdUVC, le16_to_cpu, kstrtou16, u16, cpu_to_le16, + 0xffff); + +UVCG_CTRL_HDR_ATTR(dw_clock_frequency, dwClockFrequency, le32_to_cpu, kstrtou32, + u32, cpu_to_le32, 0x7fffffff); + +#undef UVCG_CTRL_HDR_ATTR + +static struct configfs_attribute *uvcg_control_header_attrs[] = { + &uvcg_control_header_bcd_uvc.attr, + &uvcg_control_header_dw_clock_frequency.attr, + NULL, +}; + +struct config_item_type uvcg_control_header_type = { + .ct_item_ops = &uvcg_control_header_item_ops, + .ct_attrs = uvcg_control_header_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_item *uvcg_control_header_make(struct config_group *group, + const char *name) +{ + struct uvcg_control_header *h; + + h = kzalloc(sizeof(*h), GFP_KERNEL); + if (!h) + return ERR_CAST(h); + + h->desc.bLength = UVC_DT_HEADER_SIZE(1); + h->desc.bDescriptorType = USB_DT_CS_INTERFACE; + h->desc.bDescriptorSubType = UVC_VC_HEADER; + h->desc.bcdUVC = cpu_to_le16(0x0100); + h->desc.dwClockFrequency = cpu_to_le32(48000000); + + config_item_init_type_name(&h->item, name, &uvcg_control_header_type); + + return &h->item; +} + +void uvcg_control_header_drop(struct config_group *group, + struct config_item *item) +{ + struct uvcg_control_header *h = to_uvcg_control_header(item); + + kfree(h); +} + +/* control/header */ +static struct uvcg_control_header_grp { + struct config_group group; +} uvcg_control_header_grp; + +static struct configfs_group_operations uvcg_control_header_grp_ops = { + .make_item = uvcg_control_header_make, + .drop_item = uvcg_control_header_drop, +}; + +static struct config_item_type uvcg_control_header_grp_type = { + .ct_group_ops = &uvcg_control_header_grp_ops, + .ct_owner = THIS_MODULE, +}; + +/* control/processing/default */ +static struct uvcg_default_processing { + struct config_group group; +} uvcg_default_processing; + +static inline struct uvcg_default_processing +*to_uvcg_default_processing(struct config_item *item) +{ + return container_of(to_config_group(item), + struct uvcg_default_processing, group); +} + +CONFIGFS_ATTR_STRUCT(uvcg_default_processing); +CONFIGFS_ATTR_OPS_RO(uvcg_default_processing); + +static struct configfs_item_operations uvcg_default_processing_item_ops = { + .show_attribute = uvcg_default_processing_attr_show, +}; + +#define UVCG_DEFAULT_PROCESSING_ATTR(cname, aname, conv) \ +static ssize_t uvcg_default_processing_##cname##_show( \ + struct uvcg_default_processing *dp, char *page) \ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &dp->group.cg_subsys->su_mutex; \ + struct uvc_processing_unit_descriptor *pd; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = dp->group.cg_item.ci_parent->ci_parent->ci_parent; \ + opts = to_f_uvc_opts(opts_item); \ + pd = &opts->uvc_processing; \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%d\n", conv(pd->aname)); \ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +static struct uvcg_default_processing_attribute \ + uvcg_default_processing_##cname = \ + __CONFIGFS_ATTR_RO(aname, uvcg_default_processing_##cname##_show) + +#define identity_conv(x) (x) + +UVCG_DEFAULT_PROCESSING_ATTR(b_unit_id, bUnitID, identity_conv); +UVCG_DEFAULT_PROCESSING_ATTR(b_source_id, bSourceID, identity_conv); +UVCG_DEFAULT_PROCESSING_ATTR(w_max_multiplier, wMaxMultiplier, le16_to_cpu); +UVCG_DEFAULT_PROCESSING_ATTR(i_processing, iProcessing, identity_conv); + +#undef identity_conv + +#undef UVCG_DEFAULT_PROCESSING_ATTR + +static ssize_t uvcg_default_processing_bm_controls_show( + struct uvcg_default_processing *dp, char *page) +{ + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &dp->group.cg_subsys->su_mutex; + struct uvc_processing_unit_descriptor *pd; + int result, i; + char *pg = page; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = dp->group.cg_item.ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + pd = &opts->uvc_processing; + + mutex_lock(&opts->lock); + for (result = 0, i = 0; i < pd->bControlSize; ++i) { + result += sprintf(pg, "%d\n", pd->bmControls[i]); + pg = page + result; + } + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + + return result; +} + +static struct uvcg_default_processing_attribute + uvcg_default_processing_bm_controls = + __CONFIGFS_ATTR_RO(bmControls, + uvcg_default_processing_bm_controls_show); + +static struct configfs_attribute *uvcg_default_processing_attrs[] = { + &uvcg_default_processing_b_unit_id.attr, + &uvcg_default_processing_b_source_id.attr, + &uvcg_default_processing_w_max_multiplier.attr, + &uvcg_default_processing_bm_controls.attr, + &uvcg_default_processing_i_processing.attr, + NULL, +}; + +static struct config_item_type uvcg_default_processing_type = { + .ct_item_ops = &uvcg_default_processing_item_ops, + .ct_attrs = uvcg_default_processing_attrs, + .ct_owner = THIS_MODULE, +}; + +/* struct uvcg_processing {}; */ + +static struct config_group *uvcg_processing_default_groups[] = { + &uvcg_default_processing.group, + NULL, +}; + +/* control/processing */ +static struct uvcg_processing_grp { + struct config_group group; +} uvcg_processing_grp; + +static struct config_item_type uvcg_processing_grp_type = { + .ct_owner = THIS_MODULE, +}; + +/* control/terminal/camera/default */ +static struct uvcg_default_camera { + struct config_group group; +} uvcg_default_camera; + +static inline struct uvcg_default_camera +*to_uvcg_default_camera(struct config_item *item) +{ + return container_of(to_config_group(item), + struct uvcg_default_camera, group); +} + +CONFIGFS_ATTR_STRUCT(uvcg_default_camera); +CONFIGFS_ATTR_OPS_RO(uvcg_default_camera); + +static struct configfs_item_operations uvcg_default_camera_item_ops = { + .show_attribute = uvcg_default_camera_attr_show, +}; + +#define UVCG_DEFAULT_CAMERA_ATTR(cname, aname, conv) \ +static ssize_t uvcg_default_camera_##cname##_show( \ + struct uvcg_default_camera *dc, char *page) \ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex; \ + struct uvc_camera_terminal_descriptor *cd; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent-> \ + ci_parent; \ + opts = to_f_uvc_opts(opts_item); \ + cd = &opts->uvc_camera_terminal; \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%d\n", conv(cd->aname)); \ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + \ + return result; \ +} \ + \ +static struct uvcg_default_camera_attribute \ + uvcg_default_camera_##cname = \ + __CONFIGFS_ATTR_RO(aname, uvcg_default_camera_##cname##_show) + +#define identity_conv(x) (x) + +UVCG_DEFAULT_CAMERA_ATTR(b_terminal_id, bTerminalID, identity_conv); +UVCG_DEFAULT_CAMERA_ATTR(w_terminal_type, wTerminalType, le16_to_cpu); +UVCG_DEFAULT_CAMERA_ATTR(b_assoc_terminal, bAssocTerminal, identity_conv); +UVCG_DEFAULT_CAMERA_ATTR(i_terminal, iTerminal, identity_conv); +UVCG_DEFAULT_CAMERA_ATTR(w_objective_focal_length_min, wObjectiveFocalLengthMin, + le16_to_cpu); +UVCG_DEFAULT_CAMERA_ATTR(w_objective_focal_length_max, wObjectiveFocalLengthMax, + le16_to_cpu); +UVCG_DEFAULT_CAMERA_ATTR(w_ocular_focal_length, wOcularFocalLength, + le16_to_cpu); + +#undef identity_conv + +#undef UVCG_DEFAULT_CAMERA_ATTR + +static ssize_t uvcg_default_camera_bm_controls_show( + struct uvcg_default_camera *dc, char *page) +{ + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex; + struct uvc_camera_terminal_descriptor *cd; + int result, i; + char *pg = page; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent-> + ci_parent; + opts = to_f_uvc_opts(opts_item); + cd = &opts->uvc_camera_terminal; + + mutex_lock(&opts->lock); + for (result = 0, i = 0; i < cd->bControlSize; ++i) { + result += sprintf(pg, "%d\n", cd->bmControls[i]); + pg = page + result; + } + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + return result; +} + +static struct uvcg_default_camera_attribute + uvcg_default_camera_bm_controls = + __CONFIGFS_ATTR_RO(bmControls, uvcg_default_camera_bm_controls_show); + +static struct configfs_attribute *uvcg_default_camera_attrs[] = { + &uvcg_default_camera_b_terminal_id.attr, + &uvcg_default_camera_w_terminal_type.attr, + &uvcg_default_camera_b_assoc_terminal.attr, + &uvcg_default_camera_i_terminal.attr, + &uvcg_default_camera_w_objective_focal_length_min.attr, + &uvcg_default_camera_w_objective_focal_length_max.attr, + &uvcg_default_camera_w_ocular_focal_length.attr, + &uvcg_default_camera_bm_controls.attr, + NULL, +}; + +static struct config_item_type uvcg_default_camera_type = { + .ct_item_ops = &uvcg_default_camera_item_ops, + .ct_attrs = uvcg_default_camera_attrs, + .ct_owner = THIS_MODULE, +}; + +/* struct uvcg_camera {}; */ + +static struct config_group *uvcg_camera_default_groups[] = { + &uvcg_default_camera.group, + NULL, +}; + +/* control/terminal/camera */ +static struct uvcg_camera_grp { + struct config_group group; +} uvcg_camera_grp; + +static struct config_item_type uvcg_camera_grp_type = { + .ct_owner = THIS_MODULE, +}; + +/* control/terminal/output/default */ +static struct uvcg_default_output { + struct config_group group; +} uvcg_default_output; + +static inline struct uvcg_default_output +*to_uvcg_default_output(struct config_item *item) +{ + return container_of(to_config_group(item), + struct uvcg_default_output, group); +} + +CONFIGFS_ATTR_STRUCT(uvcg_default_output); +CONFIGFS_ATTR_OPS_RO(uvcg_default_output); + +static struct configfs_item_operations uvcg_default_output_item_ops = { + .show_attribute = uvcg_default_output_attr_show, +}; + +#define UVCG_DEFAULT_OUTPUT_ATTR(cname, aname, conv) \ +static ssize_t uvcg_default_output_##cname##_show( \ + struct uvcg_default_output *dout, char *page) \ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &dout->group.cg_subsys->su_mutex; \ + struct uvc_output_terminal_descriptor *cd; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = dout->group.cg_item.ci_parent->ci_parent-> \ + ci_parent->ci_parent; \ + opts = to_f_uvc_opts(opts_item); \ + cd = &opts->uvc_output_terminal; \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%d\n", conv(cd->aname)); \ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + \ + return result; \ +} \ + \ +static struct uvcg_default_output_attribute \ + uvcg_default_output_##cname = \ + __CONFIGFS_ATTR_RO(aname, uvcg_default_output_##cname##_show) + +#define identity_conv(x) (x) + +UVCG_DEFAULT_OUTPUT_ATTR(b_terminal_id, bTerminalID, identity_conv); +UVCG_DEFAULT_OUTPUT_ATTR(w_terminal_type, wTerminalType, le16_to_cpu); +UVCG_DEFAULT_OUTPUT_ATTR(b_assoc_terminal, bAssocTerminal, identity_conv); +UVCG_DEFAULT_OUTPUT_ATTR(b_source_id, bSourceID, identity_conv); +UVCG_DEFAULT_OUTPUT_ATTR(i_terminal, iTerminal, identity_conv); + +#undef identity_conv + +#undef UVCG_DEFAULT_OUTPUT_ATTR + +static struct configfs_attribute *uvcg_default_output_attrs[] = { + &uvcg_default_output_b_terminal_id.attr, + &uvcg_default_output_w_terminal_type.attr, + &uvcg_default_output_b_assoc_terminal.attr, + &uvcg_default_output_b_source_id.attr, + &uvcg_default_output_i_terminal.attr, + NULL, +}; + +static struct config_item_type uvcg_default_output_type = { + .ct_item_ops = &uvcg_default_output_item_ops, + .ct_attrs = uvcg_default_output_attrs, + .ct_owner = THIS_MODULE, +}; + +/* struct uvcg_output {}; */ + +static struct config_group *uvcg_output_default_groups[] = { + &uvcg_default_output.group, + NULL, +}; + +/* control/terminal/output */ +static struct uvcg_output_grp { + struct config_group group; +} uvcg_output_grp; + +static struct config_item_type uvcg_output_grp_type = { + .ct_owner = THIS_MODULE, +}; + +static struct config_group *uvcg_terminal_default_groups[] = { + &uvcg_camera_grp.group, + &uvcg_output_grp.group, + NULL, +}; + +/* control/terminal */ +static struct uvcg_terminal_grp { + struct config_group group; +} uvcg_terminal_grp; + +static struct config_item_type uvcg_terminal_grp_type = { + .ct_owner = THIS_MODULE, +}; + +/* control/class/{fs} */ +static struct uvcg_control_class { + struct config_group group; +} uvcg_control_class_fs, uvcg_control_class_ss; + + +static inline struct uvc_descriptor_header +**uvcg_get_ctl_class_arr(struct config_item *i, struct f_uvc_opts *o) +{ + struct uvcg_control_class *cl = container_of(to_config_group(i), + struct uvcg_control_class, group); + + if (cl == &uvcg_control_class_fs) + return o->uvc_fs_control_cls; + + if (cl == &uvcg_control_class_ss) + return o->uvc_ss_control_cls; + + return NULL; +} + +static int uvcg_control_class_allow_link(struct config_item *src, + struct config_item *target) +{ + struct config_item *control, *header; + struct f_uvc_opts *opts; + struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; + struct uvc_descriptor_header **class_array; + struct uvcg_control_header *target_hdr; + int ret = -EINVAL; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + control = src->ci_parent->ci_parent; + header = config_group_find_item(to_config_group(control), "header"); + if (!header || target->ci_parent != header) + goto out; + + opts = to_f_uvc_opts(control->ci_parent); + + mutex_lock(&opts->lock); + + class_array = uvcg_get_ctl_class_arr(src, opts); + if (!class_array) + goto unlock; + if (opts->refcnt || class_array[0]) { + ret = -EBUSY; + goto unlock; + } + + target_hdr = to_uvcg_control_header(target); + ++target_hdr->linked; + class_array[0] = (struct uvc_descriptor_header *)&target_hdr->desc; + ret = 0; + +unlock: + mutex_unlock(&opts->lock); +out: + mutex_unlock(su_mutex); + return ret; +} + +static int uvcg_control_class_drop_link(struct config_item *src, + struct config_item *target) +{ + struct config_item *control, *header; + struct f_uvc_opts *opts; + struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; + struct uvc_descriptor_header **class_array; + struct uvcg_control_header *target_hdr; + int ret = -EINVAL; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + control = src->ci_parent->ci_parent; + header = config_group_find_item(to_config_group(control), "header"); + if (!header || target->ci_parent != header) + goto out; + + opts = to_f_uvc_opts(control->ci_parent); + + mutex_lock(&opts->lock); + + class_array = uvcg_get_ctl_class_arr(src, opts); + if (!class_array) + goto unlock; + if (opts->refcnt) { + ret = -EBUSY; + goto unlock; + } + + target_hdr = to_uvcg_control_header(target); + --target_hdr->linked; + class_array[0] = NULL; + ret = 0; + +unlock: + mutex_unlock(&opts->lock); +out: + mutex_unlock(su_mutex); + return ret; +} + +static struct configfs_item_operations uvcg_control_class_item_ops = { + .allow_link = uvcg_control_class_allow_link, + .drop_link = uvcg_control_class_drop_link, +}; + +static struct config_item_type uvcg_control_class_type = { + .ct_item_ops = &uvcg_control_class_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct config_group *uvcg_control_class_default_groups[] = { + &uvcg_control_class_fs.group, + &uvcg_control_class_ss.group, + NULL, +}; + +/* control/class */ +static struct uvcg_control_class_grp { + struct config_group group; +} uvcg_control_class_grp; + +static struct config_item_type uvcg_control_class_grp_type = { + .ct_owner = THIS_MODULE, +}; + +static struct config_group *uvcg_control_default_groups[] = { + &uvcg_control_header_grp.group, + &uvcg_processing_grp.group, + &uvcg_terminal_grp.group, + &uvcg_control_class_grp.group, + NULL, +}; + +/* control */ +static struct uvcg_control_grp { + struct config_group group; +} uvcg_control_grp; + +static struct config_item_type uvcg_control_grp_type = { + .ct_owner = THIS_MODULE, +}; + +/* streaming/uncompressed */ +static struct uvcg_uncompressed_grp { + struct config_group group; +} uvcg_uncompressed_grp; + +/* streaming/mjpeg */ +static struct uvcg_mjpeg_grp { + struct config_group group; +} uvcg_mjpeg_grp; + +static struct config_item *fmt_parent[] = { + &uvcg_uncompressed_grp.group.cg_item, + &uvcg_mjpeg_grp.group.cg_item, +}; + +enum uvcg_format_type { + UVCG_UNCOMPRESSED = 0, + UVCG_MJPEG, +}; + +struct uvcg_format { + struct config_group group; + enum uvcg_format_type type; + unsigned linked; + unsigned num_frames; + __u8 bmaControls[UVCG_STREAMING_CONTROL_SIZE]; +}; + +struct uvcg_format *to_uvcg_format(struct config_item *item) +{ + return container_of(to_config_group(item), struct uvcg_format, group); +} + +static ssize_t uvcg_format_bma_controls_show(struct uvcg_format *f, char *page) +{ + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &f->group.cg_subsys->su_mutex; + int result, i; + char *pg = page; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = f->group.cg_item.ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + result = sprintf(pg, "0x"); + pg += result; + for (i = 0; i < UVCG_STREAMING_CONTROL_SIZE; ++i) { + result += sprintf(pg, "%x\n", f->bmaControls[i]); + pg = page + result; + } + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + return result; +} + +static ssize_t uvcg_format_bma_controls_store(struct uvcg_format *ch, + const char *page, size_t len) +{ + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &ch->group.cg_subsys->su_mutex; + int ret = -EINVAL; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = ch->group.cg_item.ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + if (ch->linked || opts->refcnt) { + ret = -EBUSY; + goto end; + } + + if (len < 4 || *page != '0' || + (*(page + 1) != 'x' && *(page + 1) != 'X')) + goto end; + ret = hex2bin(ch->bmaControls, page + 2, 1); + if (ret < 0) + goto end; + ret = len; +end: + mutex_unlock(&opts->lock); + mutex_unlock(su_mutex); + return ret; +} + +struct uvcg_format_ptr { + struct uvcg_format *fmt; + struct list_head entry; +}; + +/* streaming/header/ */ +struct uvcg_streaming_header { + struct config_item item; + struct uvc_input_header_descriptor desc; + unsigned linked; + struct list_head formats; + unsigned num_fmt; +}; + +struct uvcg_streaming_header *to_uvcg_streaming_header(struct config_item *item) +{ + return container_of(item, struct uvcg_streaming_header, item); +} + +CONFIGFS_ATTR_STRUCT(uvcg_streaming_header); +CONFIGFS_ATTR_OPS(uvcg_streaming_header); + +static int uvcg_streaming_header_allow_link(struct config_item *src, + struct config_item *target) +{ + struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; + struct config_item *opts_item; + struct f_uvc_opts *opts; + struct uvcg_streaming_header *src_hdr; + struct uvcg_format *target_fmt = NULL; + struct uvcg_format_ptr *format_ptr; + int i, ret = -EINVAL; + + src_hdr = to_uvcg_streaming_header(src); + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = src->ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + + if (src_hdr->linked) { + ret = -EBUSY; + goto out; + } + + for (i = 0; i < ARRAY_SIZE(fmt_parent); ++i) + if (target->ci_parent == fmt_parent[i]) + break; + if (i == ARRAY_SIZE(fmt_parent)) + goto out; + + target_fmt = container_of(to_config_group(target), struct uvcg_format, + group); + if (!target_fmt) + goto out; + + format_ptr = kzalloc(sizeof(*format_ptr), GFP_KERNEL); + if (!format_ptr) { + ret = PTR_ERR(format_ptr); + goto out; + } + ret = 0; + format_ptr->fmt = target_fmt; + list_add_tail(&format_ptr->entry, &src_hdr->formats); + ++src_hdr->num_fmt; + +out: + mutex_unlock(&opts->lock); + mutex_unlock(su_mutex); + return ret; +} + +static int uvcg_streaming_header_drop_link(struct config_item *src, + struct config_item *target) +{ + struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; + struct config_item *opts_item; + struct f_uvc_opts *opts; + struct uvcg_streaming_header *src_hdr; + struct uvcg_format *target_fmt = NULL; + struct uvcg_format_ptr *format_ptr, *tmp; + int ret = -EINVAL; + + src_hdr = to_uvcg_streaming_header(src); + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = src->ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + target_fmt = container_of(to_config_group(target), struct uvcg_format, + group); + if (!target_fmt) + goto out; + + list_for_each_entry_safe(format_ptr, tmp, &src_hdr->formats, entry) + if (format_ptr->fmt == target_fmt) { + list_del(&format_ptr->entry); + kfree(format_ptr); + --src_hdr->num_fmt; + break; + } + +out: + mutex_unlock(&opts->lock); + mutex_unlock(su_mutex); + return ret; + +} + +static struct configfs_item_operations uvcg_streaming_header_item_ops = { + .show_attribute = uvcg_streaming_header_attr_show, + .store_attribute = uvcg_streaming_header_attr_store, + .allow_link = uvcg_streaming_header_allow_link, + .drop_link = uvcg_streaming_header_drop_link, +}; + +#define UVCG_STREAMING_HEADER_ATTR(cname, aname, conv) \ +static ssize_t uvcg_streaming_header_##cname##_show( \ + struct uvcg_streaming_header *sh, char *page) \ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &sh->item.ci_group->cg_subsys->su_mutex;\ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = sh->item.ci_parent->ci_parent->ci_parent; \ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%d\n", conv(sh->desc.aname)); \ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +static struct uvcg_streaming_header_attribute \ + uvcg_streaming_header_##cname = \ + __CONFIGFS_ATTR_RO(aname, uvcg_streaming_header_##cname##_show) + +#define identity_conv(x) (x) + +UVCG_STREAMING_HEADER_ATTR(bm_info, bmInfo, identity_conv); +UVCG_STREAMING_HEADER_ATTR(b_terminal_link, bTerminalLink, identity_conv); +UVCG_STREAMING_HEADER_ATTR(b_still_capture_method, bStillCaptureMethod, + identity_conv); +UVCG_STREAMING_HEADER_ATTR(b_trigger_support, bTriggerSupport, identity_conv); +UVCG_STREAMING_HEADER_ATTR(b_trigger_usage, bTriggerUsage, identity_conv); + +#undef identity_conv + +#undef UVCG_STREAMING_HEADER_ATTR + +static struct configfs_attribute *uvcg_streaming_header_attrs[] = { + &uvcg_streaming_header_bm_info.attr, + &uvcg_streaming_header_b_terminal_link.attr, + &uvcg_streaming_header_b_still_capture_method.attr, + &uvcg_streaming_header_b_trigger_support.attr, + &uvcg_streaming_header_b_trigger_usage.attr, + NULL, +}; + +struct config_item_type uvcg_streaming_header_type = { + .ct_item_ops = &uvcg_streaming_header_item_ops, + .ct_attrs = uvcg_streaming_header_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_item +*uvcg_streaming_header_make(struct config_group *group, const char *name) +{ + struct uvcg_streaming_header *h; + + h = kzalloc(sizeof(*h), GFP_KERNEL); + if (!h) + return ERR_CAST(h); + + INIT_LIST_HEAD(&h->formats); + h->desc.bDescriptorType = USB_DT_CS_INTERFACE; + h->desc.bDescriptorSubType = UVC_VS_INPUT_HEADER; + h->desc.bTerminalLink = 3; + h->desc.bControlSize = UVCG_STREAMING_CONTROL_SIZE; + + config_item_init_type_name(&h->item, name, &uvcg_streaming_header_type); + + return &h->item; +} + +void uvcg_streaming_header_drop(struct config_group *group, + struct config_item *item) +{ + struct uvcg_streaming_header *h = to_uvcg_streaming_header(item); + + kfree(h); +} + +/* streaming/header */ +static struct uvcg_streaming_header_grp { + struct config_group group; +} uvcg_streaming_header_grp; + +static struct configfs_group_operations uvcg_streaming_header_grp_ops = { + .make_item = uvcg_streaming_header_make, + .drop_item = uvcg_streaming_header_drop, +}; + +static struct config_item_type uvcg_streaming_header_grp_type = { + .ct_group_ops = &uvcg_streaming_header_grp_ops, + .ct_owner = THIS_MODULE, +}; + +/* streaming/// */ +struct uvcg_frame { + struct { + u8 b_length; + u8 b_descriptor_type; + u8 b_descriptor_subtype; + u8 b_frame_index; + u8 bm_capabilities; + u16 w_width; + u16 w_height; + u32 dw_min_bit_rate; + u32 dw_max_bit_rate; + u32 dw_max_video_frame_buffer_size; + u32 dw_default_frame_interval; + u8 b_frame_interval_type; + } __attribute__((packed)) frame; + u32 *dw_frame_interval; + enum uvcg_format_type fmt_type; + struct config_item item; +}; + +struct uvcg_frame *to_uvcg_frame(struct config_item *item) +{ + return container_of(item, struct uvcg_frame, item); +} + +CONFIGFS_ATTR_STRUCT(uvcg_frame); +CONFIGFS_ATTR_OPS(uvcg_frame); + +static struct configfs_item_operations uvcg_frame_item_ops = { + .show_attribute = uvcg_frame_attr_show, + .store_attribute = uvcg_frame_attr_store, +}; + +#define UVCG_FRAME_ATTR(cname, aname, conv, str2u, uxx, vnoc, limit) \ +static ssize_t uvcg_frame_##cname##_show(struct uvcg_frame *f, char *page)\ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &f->item.ci_group->cg_subsys->su_mutex;\ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = f->item.ci_parent->ci_parent->ci_parent->ci_parent; \ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%d\n", conv(f->frame.cname)); \ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +static ssize_t uvcg_frame_##cname##_store(struct uvcg_frame *f, \ + const char *page, size_t len)\ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct uvcg_format *fmt; \ + struct mutex *su_mutex = &f->item.ci_group->cg_subsys->su_mutex;\ + int ret; \ + uxx num; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = f->item.ci_parent->ci_parent->ci_parent->ci_parent; \ + opts = to_f_uvc_opts(opts_item); \ + fmt = to_uvcg_format(f->item.ci_parent); \ + \ + mutex_lock(&opts->lock); \ + if (fmt->linked || opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = str2u(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + if (num > limit) { \ + ret = -EINVAL; \ + goto end; \ + } \ + f->frame.cname = vnoc(num); \ + ret = len; \ +end: \ + mutex_unlock(&opts->lock); \ + mutex_unlock(su_mutex); \ + return ret; \ +} \ + \ +static struct uvcg_frame_attribute \ + uvcg_frame_##cname = \ + __CONFIGFS_ATTR(aname, S_IRUGO | S_IWUSR, \ + uvcg_frame_##cname##_show, \ + uvcg_frame_##cname##_store) + +#define identity_conv(x) (x) + +UVCG_FRAME_ATTR(bm_capabilities, bmCapabilities, identity_conv, kstrtou8, u8, + identity_conv, 0xFF); +UVCG_FRAME_ATTR(w_width, wWidth, le16_to_cpu, kstrtou16, u16, cpu_to_le16, + 0xFFFF); +UVCG_FRAME_ATTR(w_height, wHeight, le16_to_cpu, kstrtou16, u16, cpu_to_le16, + 0xFFFF); +UVCG_FRAME_ATTR(dw_min_bit_rate, dwMinBitRate, le32_to_cpu, kstrtou32, u32, + cpu_to_le32, 0xFFFFFFFF); +UVCG_FRAME_ATTR(dw_max_bit_rate, dwMaxBitRate, le32_to_cpu, kstrtou32, u32, + cpu_to_le32, 0xFFFFFFFF); +UVCG_FRAME_ATTR(dw_max_video_frame_buffer_size, dwMaxVideoFrameBufferSize, + le32_to_cpu, kstrtou32, u32, cpu_to_le32, 0xFFFFFFFF); +UVCG_FRAME_ATTR(dw_default_frame_interval, dwDefaultFrameInterval, + le32_to_cpu, kstrtou32, u32, cpu_to_le32, 0xFFFFFFFF); + +#undef identity_conv + +#undef UVCG_FRAME_ATTR + +static ssize_t uvcg_frame_dw_frame_interval_show(struct uvcg_frame *frm, + char *page) +{ + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &frm->item.ci_group->cg_subsys->su_mutex; + int result, i; + char *pg = page; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = frm->item.ci_parent->ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + for (result = 0, i = 0; i < frm->frame.b_frame_interval_type; ++i) { + result += sprintf(pg, "%d\n", + le32_to_cpu(frm->dw_frame_interval[i])); + pg = page + result; + } + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + return result; +} + +static inline int __uvcg_count_frm_intrv(char *buf, void *priv) +{ + ++*((int *)priv); + return 0; +} + +static inline int __uvcg_fill_frm_intrv(char *buf, void *priv) +{ + u32 num, **interv; + int ret; + + ret = kstrtou32(buf, 0, &num); + if (ret) + return ret; + if (num > 0xFFFFFFFF) + return -EINVAL; + + interv = priv; + **interv = cpu_to_le32(num); + ++*interv; + + return 0; +} + +static int __uvcg_iter_frm_intrv(const char *page, size_t len, + int (*fun)(char *, void *), void *priv) +{ + /* sign, base 2 representation, newline, terminator */ + char buf[1 + sizeof(u32) * 8 + 1 + 1]; + const char *pg = page; + int i, ret; + + if (!fun) + return -EINVAL; + + while (pg - page < len) { + i = 0; + while (i < sizeof(buf) && (pg - page < len) && + *pg != '\0' && *pg != '\n') + buf[i++] = *pg++; + if (i == sizeof(buf)) + return -EINVAL; + while ((pg - page < len) && (*pg == '\0' || *pg == '\n')) + ++pg; + buf[i] = '\0'; + ret = fun(buf, priv); + if (ret) + return ret; + } + + return 0; +} + +static ssize_t uvcg_frame_dw_frame_interval_store(struct uvcg_frame *ch, + const char *page, size_t len) +{ + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct uvcg_format *fmt; + struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex; + int ret = 0, n = 0; + u32 *frm_intrv, *tmp; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = ch->item.ci_parent->ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + fmt = to_uvcg_format(ch->item.ci_parent); + + mutex_lock(&opts->lock); + if (fmt->linked || opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = __uvcg_iter_frm_intrv(page, len, __uvcg_count_frm_intrv, &n); + if (ret) + goto end; + + tmp = frm_intrv = kcalloc(n, sizeof(u32), GFP_KERNEL); + if (!frm_intrv) { + ret = -ENOMEM; + goto end; + } + + ret = __uvcg_iter_frm_intrv(page, len, __uvcg_fill_frm_intrv, &tmp); + if (ret) { + kfree(frm_intrv); + goto end; + } + + kfree(ch->dw_frame_interval); + ch->dw_frame_interval = frm_intrv; + ch->frame.b_frame_interval_type = n; + ret = len; + +end: + mutex_unlock(&opts->lock); + mutex_unlock(su_mutex); + return ret; +} + +static struct uvcg_frame_attribute + uvcg_frame_dw_frame_interval = + __CONFIGFS_ATTR(dwFrameInterval, S_IRUGO | S_IWUSR, + uvcg_frame_dw_frame_interval_show, + uvcg_frame_dw_frame_interval_store); + +static struct configfs_attribute *uvcg_frame_attrs[] = { + &uvcg_frame_bm_capabilities.attr, + &uvcg_frame_w_width.attr, + &uvcg_frame_w_height.attr, + &uvcg_frame_dw_min_bit_rate.attr, + &uvcg_frame_dw_max_bit_rate.attr, + &uvcg_frame_dw_max_video_frame_buffer_size.attr, + &uvcg_frame_dw_default_frame_interval.attr, + &uvcg_frame_dw_frame_interval.attr, + NULL, +}; + +struct config_item_type uvcg_frame_type = { + .ct_item_ops = &uvcg_frame_item_ops, + .ct_attrs = uvcg_frame_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_item *uvcg_frame_make(struct config_group *group, + const char *name) +{ + struct uvcg_frame *h; + struct uvcg_format *fmt; + struct f_uvc_opts *opts; + struct config_item *opts_item; + + h = kzalloc(sizeof(*h), GFP_KERNEL); + if (!h) + return ERR_CAST(h); + + h->frame.b_descriptor_type = USB_DT_CS_INTERFACE; + h->frame.b_frame_index = 1; + h->frame.w_width = cpu_to_le16(640); + h->frame.w_height = cpu_to_le16(360); + h->frame.dw_min_bit_rate = cpu_to_le32(18432000); + h->frame.dw_max_bit_rate = cpu_to_le32(55296000); + h->frame.dw_max_video_frame_buffer_size = cpu_to_le32(460800); + h->frame.dw_default_frame_interval = cpu_to_le32(666666); + + opts_item = group->cg_item.ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + fmt = to_uvcg_format(&group->cg_item); + if (fmt->type == UVCG_UNCOMPRESSED) { + h->frame.b_descriptor_subtype = UVC_VS_FRAME_UNCOMPRESSED; + h->fmt_type = UVCG_UNCOMPRESSED; + } else if (fmt->type == UVCG_MJPEG) { + h->frame.b_descriptor_subtype = UVC_VS_FRAME_MJPEG; + h->fmt_type = UVCG_MJPEG; + } else { + mutex_unlock(&opts->lock); + return ERR_PTR(-EINVAL); + } + ++fmt->num_frames; + mutex_unlock(&opts->lock); + + config_item_init_type_name(&h->item, name, &uvcg_frame_type); + + return &h->item; +} + +void uvcg_frame_drop(struct config_group *group, struct config_item *item) +{ + struct uvcg_frame *h = to_uvcg_frame(item); + struct uvcg_format *fmt; + struct f_uvc_opts *opts; + struct config_item *opts_item; + + opts_item = group->cg_item.ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + fmt = to_uvcg_format(&group->cg_item); + --fmt->num_frames; + kfree(h); + mutex_unlock(&opts->lock); +} + +/* streaming/uncompressed/ */ +struct uvcg_uncompressed { + struct uvcg_format fmt; + struct uvc_format_uncompressed desc; +}; + +struct uvcg_uncompressed *to_uvcg_uncompressed(struct config_item *item) +{ + return container_of( + container_of(to_config_group(item), struct uvcg_format, group), + struct uvcg_uncompressed, fmt); +} + +CONFIGFS_ATTR_STRUCT(uvcg_uncompressed); +CONFIGFS_ATTR_OPS(uvcg_uncompressed); + +static struct configfs_item_operations uvcg_uncompressed_item_ops = { + .show_attribute = uvcg_uncompressed_attr_show, + .store_attribute = uvcg_uncompressed_attr_store, +}; + +static struct configfs_group_operations uvcg_uncompressed_group_ops = { + .make_item = uvcg_frame_make, + .drop_item = uvcg_frame_drop, +}; + +static ssize_t uvcg_uncompressed_guid_format_show(struct uvcg_uncompressed *ch, + char *page) +{ + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + memcpy(page, ch->desc.guidFormat, sizeof(ch->desc.guidFormat)); + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + + return sizeof(ch->desc.guidFormat); +} + +static ssize_t uvcg_uncompressed_guid_format_store(struct uvcg_uncompressed *ch, + const char *page, size_t len) +{ + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex; + int ret; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + if (ch->fmt.linked || opts->refcnt) { + ret = -EBUSY; + goto end; + } + + memcpy(ch->desc.guidFormat, page, + min(sizeof(ch->desc.guidFormat), len)); + ret = sizeof(ch->desc.guidFormat); + +end: + mutex_unlock(&opts->lock); + mutex_unlock(su_mutex); + return ret; +} + +static struct uvcg_uncompressed_attribute uvcg_uncompressed_guid_format = + __CONFIGFS_ATTR(guidFormat, S_IRUGO | S_IWUSR, + uvcg_uncompressed_guid_format_show, + uvcg_uncompressed_guid_format_store); + + +#define UVCG_UNCOMPRESSED_ATTR_RO(cname, aname, conv) \ +static ssize_t uvcg_uncompressed_##cname##_show( \ + struct uvcg_uncompressed *u, char *page) \ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%d\n", conv(u->desc.aname)); \ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +static struct uvcg_uncompressed_attribute \ + uvcg_uncompressed_##cname = \ + __CONFIGFS_ATTR_RO(aname, uvcg_uncompressed_##cname##_show) + +#define UVCG_UNCOMPRESSED_ATTR(cname, aname, conv) \ +static ssize_t uvcg_uncompressed_##cname##_show( \ + struct uvcg_uncompressed *u, char *page) \ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%d\n", conv(u->desc.aname)); \ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +static ssize_t \ +uvcg_uncompressed_##cname##_store(struct uvcg_uncompressed *u, \ + const char *page, size_t len) \ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int ret; \ + u8 num; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + if (u->fmt.linked || opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = kstrtou8(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + if (num > 255) { \ + ret = -EINVAL; \ + goto end; \ + } \ + u->desc.aname = num; \ + ret = len; \ +end: \ + mutex_unlock(&opts->lock); \ + mutex_unlock(su_mutex); \ + return ret; \ +} \ + \ +static struct uvcg_uncompressed_attribute \ + uvcg_uncompressed_##cname = \ + __CONFIGFS_ATTR(aname, S_IRUGO | S_IWUSR, \ + uvcg_uncompressed_##cname##_show, \ + uvcg_uncompressed_##cname##_store) + +#define identity_conv(x) (x) + +UVCG_UNCOMPRESSED_ATTR(b_bits_per_pixel, bBitsPerPixel, identity_conv); +UVCG_UNCOMPRESSED_ATTR(b_default_frame_index, bDefaultFrameIndex, + identity_conv); +UVCG_UNCOMPRESSED_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, identity_conv); +UVCG_UNCOMPRESSED_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, identity_conv); +UVCG_UNCOMPRESSED_ATTR_RO(bm_interface_flags, bmInterfaceFlags, identity_conv); + +#undef identity_conv + +#undef UVCG_UNCOMPRESSED_ATTR +#undef UVCG_UNCOMPRESSED_ATTR_RO + +static inline ssize_t +uvcg_uncompressed_bma_controls_show(struct uvcg_uncompressed *unc, char *page) +{ + return uvcg_format_bma_controls_show(&unc->fmt, page); +} + +static inline ssize_t +uvcg_uncompressed_bma_controls_store(struct uvcg_uncompressed *ch, + const char *page, size_t len) +{ + return uvcg_format_bma_controls_store(&ch->fmt, page, len); +} + +static struct uvcg_uncompressed_attribute uvcg_uncompressed_bma_controls = + __CONFIGFS_ATTR(bmaControls, S_IRUGO | S_IWUSR, + uvcg_uncompressed_bma_controls_show, + uvcg_uncompressed_bma_controls_store); + +static struct configfs_attribute *uvcg_uncompressed_attrs[] = { + &uvcg_uncompressed_guid_format.attr, + &uvcg_uncompressed_b_bits_per_pixel.attr, + &uvcg_uncompressed_b_default_frame_index.attr, + &uvcg_uncompressed_b_aspect_ratio_x.attr, + &uvcg_uncompressed_b_aspect_ratio_y.attr, + &uvcg_uncompressed_bm_interface_flags.attr, + &uvcg_uncompressed_bma_controls.attr, + NULL, +}; + +struct config_item_type uvcg_uncompressed_type = { + .ct_item_ops = &uvcg_uncompressed_item_ops, + .ct_group_ops = &uvcg_uncompressed_group_ops, + .ct_attrs = uvcg_uncompressed_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_group *uvcg_uncompressed_make(struct config_group *group, + const char *name) +{ + static char guid[] = { + 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 + }; + struct uvcg_uncompressed *h; + + h = kzalloc(sizeof(*h), GFP_KERNEL); + if (!h) + return ERR_CAST(h); + + h->desc.bLength = UVC_DT_FORMAT_UNCOMPRESSED_SIZE; + h->desc.bDescriptorType = USB_DT_CS_INTERFACE; + h->desc.bDescriptorSubType = UVC_VS_FORMAT_UNCOMPRESSED; + memcpy(h->desc.guidFormat, guid, sizeof(guid)); + h->desc.bBitsPerPixel = 16; + h->desc.bDefaultFrameIndex = 1; + h->desc.bAspectRatioX = 0; + h->desc.bAspectRatioY = 0; + h->desc.bmInterfaceFlags = 0; + h->desc.bCopyProtect = 0; + + h->fmt.type = UVCG_UNCOMPRESSED; + config_group_init_type_name(&h->fmt.group, name, + &uvcg_uncompressed_type); + + return &h->fmt.group; +} + +void uvcg_uncompressed_drop(struct config_group *group, + struct config_item *item) +{ + struct uvcg_uncompressed *h = to_uvcg_uncompressed(item); + + kfree(h); +} + +static struct configfs_group_operations uvcg_uncompressed_grp_ops = { + .make_group = uvcg_uncompressed_make, + .drop_item = uvcg_uncompressed_drop, +}; + +static struct config_item_type uvcg_uncompressed_grp_type = { + .ct_group_ops = &uvcg_uncompressed_grp_ops, + .ct_owner = THIS_MODULE, +}; + +/* streaming/mjpeg/ */ +struct uvcg_mjpeg { + struct uvcg_format fmt; + struct uvc_format_mjpeg desc; +}; + +struct uvcg_mjpeg *to_uvcg_mjpeg(struct config_item *item) +{ + return container_of( + container_of(to_config_group(item), struct uvcg_format, group), + struct uvcg_mjpeg, fmt); +} + +CONFIGFS_ATTR_STRUCT(uvcg_mjpeg); +CONFIGFS_ATTR_OPS(uvcg_mjpeg); + +static struct configfs_item_operations uvcg_mjpeg_item_ops = { + .show_attribute = uvcg_mjpeg_attr_show, + .store_attribute = uvcg_mjpeg_attr_store, +}; + +static struct configfs_group_operations uvcg_mjpeg_group_ops = { + .make_item = uvcg_frame_make, + .drop_item = uvcg_frame_drop, +}; + +#define UVCG_MJPEG_ATTR_RO(cname, aname, conv) \ +static ssize_t uvcg_mjpeg_##cname##_show(struct uvcg_mjpeg *u, char *page)\ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%d\n", conv(u->desc.aname)); \ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +static struct uvcg_mjpeg_attribute \ + uvcg_mjpeg_##cname = \ + __CONFIGFS_ATTR_RO(aname, uvcg_mjpeg_##cname##_show) + +#define UVCG_MJPEG_ATTR(cname, aname, conv) \ +static ssize_t uvcg_mjpeg_##cname##_show(struct uvcg_mjpeg *u, char *page)\ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%d\n", conv(u->desc.aname)); \ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +static ssize_t \ +uvcg_mjpeg_##cname##_store(struct uvcg_mjpeg *u, \ + const char *page, size_t len) \ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int ret; \ + u8 num; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + if (u->fmt.linked || opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = kstrtou8(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + if (num > 255) { \ + ret = -EINVAL; \ + goto end; \ + } \ + u->desc.aname = num; \ + ret = len; \ +end: \ + mutex_unlock(&opts->lock); \ + mutex_unlock(su_mutex); \ + return ret; \ +} \ + \ +static struct uvcg_mjpeg_attribute \ + uvcg_mjpeg_##cname = \ + __CONFIGFS_ATTR(aname, S_IRUGO | S_IWUSR, \ + uvcg_mjpeg_##cname##_show, \ + uvcg_mjpeg_##cname##_store) + +#define identity_conv(x) (x) + +UVCG_MJPEG_ATTR(b_default_frame_index, bDefaultFrameIndex, + identity_conv); +UVCG_MJPEG_ATTR_RO(bm_flags, bmFlags, identity_conv); +UVCG_MJPEG_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, identity_conv); +UVCG_MJPEG_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, identity_conv); +UVCG_MJPEG_ATTR_RO(bm_interface_flags, bmInterfaceFlags, identity_conv); + +#undef identity_conv + +#undef UVCG_MJPEG_ATTR +#undef UVCG_MJPEG_ATTR_RO + +static inline ssize_t +uvcg_mjpeg_bma_controls_show(struct uvcg_mjpeg *unc, char *page) +{ + return uvcg_format_bma_controls_show(&unc->fmt, page); +} + +static inline ssize_t +uvcg_mjpeg_bma_controls_store(struct uvcg_mjpeg *ch, + const char *page, size_t len) +{ + return uvcg_format_bma_controls_store(&ch->fmt, page, len); +} + +static struct uvcg_mjpeg_attribute uvcg_mjpeg_bma_controls = + __CONFIGFS_ATTR(bmaControls, S_IRUGO | S_IWUSR, + uvcg_mjpeg_bma_controls_show, + uvcg_mjpeg_bma_controls_store); + +static struct configfs_attribute *uvcg_mjpeg_attrs[] = { + &uvcg_mjpeg_b_default_frame_index.attr, + &uvcg_mjpeg_bm_flags.attr, + &uvcg_mjpeg_b_aspect_ratio_x.attr, + &uvcg_mjpeg_b_aspect_ratio_y.attr, + &uvcg_mjpeg_bm_interface_flags.attr, + &uvcg_mjpeg_bma_controls.attr, + NULL, +}; + +struct config_item_type uvcg_mjpeg_type = { + .ct_item_ops = &uvcg_mjpeg_item_ops, + .ct_group_ops = &uvcg_mjpeg_group_ops, + .ct_attrs = uvcg_mjpeg_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_group *uvcg_mjpeg_make(struct config_group *group, + const char *name) +{ + struct uvcg_mjpeg *h; + + h = kzalloc(sizeof(*h), GFP_KERNEL); + if (!h) + return ERR_CAST(h); + + h->desc.bLength = UVC_DT_FORMAT_MJPEG_SIZE; + h->desc.bDescriptorType = USB_DT_CS_INTERFACE; + h->desc.bDescriptorSubType = UVC_VS_FORMAT_MJPEG; + h->desc.bDefaultFrameIndex = 1; + h->desc.bAspectRatioX = 0; + h->desc.bAspectRatioY = 0; + h->desc.bmInterfaceFlags = 0; + h->desc.bCopyProtect = 0; + + h->fmt.type = UVCG_MJPEG; + config_group_init_type_name(&h->fmt.group, name, + &uvcg_mjpeg_type); + + return &h->fmt.group; +} + +void uvcg_mjpeg_drop(struct config_group *group, + struct config_item *item) +{ + struct uvcg_mjpeg *h = to_uvcg_mjpeg(item); + + kfree(h); +} + +static struct configfs_group_operations uvcg_mjpeg_grp_ops = { + .make_group = uvcg_mjpeg_make, + .drop_item = uvcg_mjpeg_drop, +}; + +static struct config_item_type uvcg_mjpeg_grp_type = { + .ct_group_ops = &uvcg_mjpeg_grp_ops, + .ct_owner = THIS_MODULE, +}; + +/* streaming/color_matching/default */ +static struct uvcg_default_color_matching { + struct config_group group; +} uvcg_default_color_matching; + +static inline struct uvcg_default_color_matching +*to_uvcg_default_color_matching(struct config_item *item) +{ + return container_of(to_config_group(item), + struct uvcg_default_color_matching, group); +} + +CONFIGFS_ATTR_STRUCT(uvcg_default_color_matching); +CONFIGFS_ATTR_OPS_RO(uvcg_default_color_matching); + +static struct configfs_item_operations uvcg_default_color_matching_item_ops = { + .show_attribute = uvcg_default_color_matching_attr_show, +}; + +#define UVCG_DEFAULT_COLOR_MATCHING_ATTR(cname, aname, conv) \ +static ssize_t uvcg_default_color_matching_##cname##_show( \ + struct uvcg_default_color_matching *dc, char *page) \ +{ \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex; \ + struct uvc_color_matching_descriptor *cd; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent; \ + opts = to_f_uvc_opts(opts_item); \ + cd = &opts->uvc_color_matching; \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%d\n", conv(cd->aname)); \ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +static struct uvcg_default_color_matching_attribute \ + uvcg_default_color_matching_##cname = \ + __CONFIGFS_ATTR_RO(aname, uvcg_default_color_matching_##cname##_show) + +#define identity_conv(x) (x) + +UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_color_primaries, bColorPrimaries, + identity_conv); +UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_transfer_characteristics, + bTransferCharacteristics, identity_conv); +UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_matrix_coefficients, bMatrixCoefficients, + identity_conv); + +#undef identity_conv + +#undef UVCG_DEFAULT_COLOR_MATCHING_ATTR + +static struct configfs_attribute *uvcg_default_color_matching_attrs[] = { + &uvcg_default_color_matching_b_color_primaries.attr, + &uvcg_default_color_matching_b_transfer_characteristics.attr, + &uvcg_default_color_matching_b_matrix_coefficients.attr, + NULL, +}; + +static struct config_item_type uvcg_default_color_matching_type = { + .ct_item_ops = &uvcg_default_color_matching_item_ops, + .ct_attrs = uvcg_default_color_matching_attrs, + .ct_owner = THIS_MODULE, +}; + +/* struct uvcg_color_matching {}; */ + +static struct config_group *uvcg_color_matching_default_groups[] = { + &uvcg_default_color_matching.group, + NULL, +}; + +/* streaming/color_matching */ +static struct uvcg_color_matching_grp { + struct config_group group; +} uvcg_color_matching_grp; + +static struct config_item_type uvcg_color_matching_grp_type = { + .ct_owner = THIS_MODULE, +}; + +/* streaming/class/{fs|hs|ss} */ +static struct uvcg_streaming_class { + struct config_group group; +} uvcg_streaming_class_fs, uvcg_streaming_class_hs, uvcg_streaming_class_ss; + + +static inline struct uvc_descriptor_header +***__uvcg_get_stream_class_arr(struct config_item *i, struct f_uvc_opts *o) +{ + struct uvcg_streaming_class *cl = container_of(to_config_group(i), + struct uvcg_streaming_class, group); + + if (cl == &uvcg_streaming_class_fs) + return &o->uvc_fs_streaming_cls; + + if (cl == &uvcg_streaming_class_hs) + return &o->uvc_hs_streaming_cls; + + if (cl == &uvcg_streaming_class_ss) + return &o->uvc_ss_streaming_cls; + + return NULL; +} + +enum uvcg_strm_type { + UVCG_HEADER = 0, + UVCG_FORMAT, + UVCG_FRAME +}; + +static int __uvcg_iter_strm_cls(void *priv1, void *priv2, void *priv3, + int (*fun)(void *, void *, void *, int, enum uvcg_strm_type type)) +{ + struct uvcg_streaming_header *h = priv1; + struct uvcg_format_ptr *f; + struct config_group *grp; + struct config_item *item; + struct uvcg_frame *frm; + int ret, i, j; + + if (!fun) + return -EINVAL; + + i = j = 0; + ret = fun(h, priv2, priv3, 0, UVCG_HEADER); + if (ret) + return ret; + list_for_each_entry(f, &h->formats, entry) { + ret = fun(f->fmt, priv2, priv3, i++, UVCG_FORMAT); + if (ret) + return ret; + grp = &f->fmt->group; + list_for_each_entry(item, &grp->cg_children, ci_entry) { + frm = to_uvcg_frame(item); + ret = fun(frm, priv2, priv3, j++, UVCG_FRAME); + if (ret) + return ret; + } + } + + return ret; +} + +static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n, + enum uvcg_strm_type type) +{ + size_t *size = priv2; + size_t *count = priv3; + + switch (type) { + case UVCG_HEADER: { + struct uvcg_streaming_header *h = priv1; + + *size += sizeof(h->desc); + /* bmaControls */ + *size += h->num_fmt * UVCG_STREAMING_CONTROL_SIZE; + } + break; + case UVCG_FORMAT: { + struct uvcg_format *fmt = priv1; + + if (fmt->type == UVCG_UNCOMPRESSED) { + struct uvcg_uncompressed *u = + container_of(fmt, struct uvcg_uncompressed, + fmt); + + *size += sizeof(u->desc); + } else if (fmt->type == UVCG_MJPEG) { + struct uvcg_mjpeg *m = + container_of(fmt, struct uvcg_mjpeg, fmt); + + *size += sizeof(m->desc); + } else { + return -EINVAL; + } + } + break; + case UVCG_FRAME: { + struct uvcg_frame *frm = priv1; + int sz = sizeof(frm->dw_frame_interval); + + *size += sizeof(frm->frame); + *size += frm->frame.b_frame_interval_type * sz; + } + break; + } + + ++*count; + + return 0; +} + +static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n, + enum uvcg_strm_type type) +{ + void **dest = priv2; + struct uvc_descriptor_header ***array = priv3; + size_t sz; + + **array = *dest; + ++*array; + + switch (type) { + case UVCG_HEADER: { + struct uvc_input_header_descriptor *ihdr = *dest; + struct uvcg_streaming_header *h = priv1; + struct uvcg_format_ptr *f; + + memcpy(*dest, &h->desc, sizeof(h->desc)); + *dest += sizeof(h->desc); + sz = UVCG_STREAMING_CONTROL_SIZE; + list_for_each_entry(f, &h->formats, entry) { + memcpy(*dest, f->fmt->bmaControls, sz); + *dest += sz; + } + ihdr->bLength = sizeof(h->desc) + h->num_fmt * sz; + ihdr->bNumFormats = h->num_fmt; + } + break; + case UVCG_FORMAT: { + struct uvcg_format *fmt = priv1; + + if (fmt->type == UVCG_UNCOMPRESSED) { + struct uvc_format_uncompressed *unc = *dest; + struct uvcg_uncompressed *u = + container_of(fmt, struct uvcg_uncompressed, + fmt); + + memcpy(*dest, &u->desc, sizeof(u->desc)); + *dest += sizeof(u->desc); + unc->bNumFrameDescriptors = fmt->num_frames; + unc->bFormatIndex = n + 1; + } else if (fmt->type == UVCG_MJPEG) { + struct uvc_format_mjpeg *mjp = *dest; + struct uvcg_mjpeg *m = + container_of(fmt, struct uvcg_mjpeg, fmt); + + memcpy(*dest, &m->desc, sizeof(m->desc)); + *dest += sizeof(m->desc); + mjp->bNumFrameDescriptors = fmt->num_frames; + mjp->bFormatIndex = n + 1; + } else { + return -EINVAL; + } + } + break; + case UVCG_FRAME: { + struct uvcg_frame *frm = priv1; + struct uvc_descriptor_header *h = *dest; + + sz = sizeof(frm->frame); + memcpy(*dest, &frm->frame, sz); + *dest += sz; + sz = frm->frame.b_frame_interval_type * + sizeof(*frm->dw_frame_interval); + memcpy(*dest, frm->dw_frame_interval, sz); + *dest += sz; + if (frm->fmt_type == UVCG_UNCOMPRESSED) + h->bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE( + frm->frame.b_frame_interval_type); + else if (frm->fmt_type == UVCG_MJPEG) + h->bLength = UVC_DT_FRAME_MJPEG_SIZE( + frm->frame.b_frame_interval_type); + } + break; + } + + return 0; +} + +static int uvcg_streaming_class_allow_link(struct config_item *src, + struct config_item *target) +{ + struct config_item *streaming, *header; + struct f_uvc_opts *opts; + struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; + struct uvc_descriptor_header ***class_array, **cl_arr; + struct uvcg_streaming_header *target_hdr; + void *data; + size_t size = 0, count = 0; + int ret = -EINVAL; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + streaming = src->ci_parent->ci_parent; + header = config_group_find_item(to_config_group(streaming), "header"); + if (!header || target->ci_parent != header) + goto out; + + opts = to_f_uvc_opts(streaming->ci_parent); + + mutex_lock(&opts->lock); + + class_array = __uvcg_get_stream_class_arr(src, opts); + if (!class_array || *class_array || opts->refcnt) { + ret = -EBUSY; + goto unlock; + } + + target_hdr = to_uvcg_streaming_header(target); + ret = __uvcg_iter_strm_cls(target_hdr, &size, &count, __uvcg_cnt_strm); + if (ret) + goto unlock; + + count += 2; /* color_matching, NULL */ + *class_array = kcalloc(count, sizeof(void *), GFP_KERNEL); + if (!*class_array) { + ret = PTR_ERR(*class_array); + goto unlock; + } + + data = kzalloc(size, GFP_KERNEL); + if (!data) { + kfree(*class_array); + *class_array = NULL; + ret = PTR_ERR(data); + goto unlock; + } + cl_arr = *class_array; + ret = __uvcg_iter_strm_cls(target_hdr, &data, &cl_arr, + __uvcg_fill_strm); + if (ret) { + kfree(*class_array); + *class_array = NULL; + kfree(data); + goto unlock; + } + *cl_arr = (struct uvc_descriptor_header *)&opts->uvc_color_matching; + + ++target_hdr->linked; + ret = 0; + +unlock: + mutex_unlock(&opts->lock); +out: + mutex_unlock(su_mutex); + return ret; +} + +static int uvcg_streaming_class_drop_link(struct config_item *src, + struct config_item *target) +{ + struct config_item *streaming, *header; + struct f_uvc_opts *opts; + struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; + struct uvc_descriptor_header ***class_array; + struct uvcg_streaming_header *target_hdr; + int ret = -EINVAL; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + streaming = src->ci_parent->ci_parent; + header = config_group_find_item(to_config_group(streaming), "header"); + if (!header || target->ci_parent != header) + goto out; + + opts = to_f_uvc_opts(streaming->ci_parent); + + mutex_lock(&opts->lock); + + class_array = __uvcg_get_stream_class_arr(src, opts); + if (!class_array || !*class_array) + goto unlock; + + if (opts->refcnt) { + ret = -EBUSY; + goto unlock; + } + + target_hdr = to_uvcg_streaming_header(target); + --target_hdr->linked; + kfree(**class_array); + kfree(*class_array); + *class_array = NULL; + ret = 0; + +unlock: + mutex_unlock(&opts->lock); +out: + mutex_unlock(su_mutex); + return ret; +} + +static struct configfs_item_operations uvcg_streaming_class_item_ops = { + .allow_link = uvcg_streaming_class_allow_link, + .drop_link = uvcg_streaming_class_drop_link, +}; + +static struct config_item_type uvcg_streaming_class_type = { + .ct_item_ops = &uvcg_streaming_class_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct config_group *uvcg_streaming_class_default_groups[] = { + &uvcg_streaming_class_fs.group, + &uvcg_streaming_class_hs.group, + &uvcg_streaming_class_ss.group, + NULL, +}; + +/* streaming/class */ +static struct uvcg_streaming_class_grp { + struct config_group group; +} uvcg_streaming_class_grp; + +static struct config_item_type uvcg_streaming_class_grp_type = { + .ct_owner = THIS_MODULE, +}; + +static struct config_group *uvcg_streaming_default_groups[] = { + &uvcg_streaming_header_grp.group, + &uvcg_uncompressed_grp.group, + &uvcg_mjpeg_grp.group, + &uvcg_color_matching_grp.group, + &uvcg_streaming_class_grp.group, + NULL, +}; + +/* streaming */ +static struct uvcg_streaming_grp { + struct config_group group; +} uvcg_streaming_grp; + +static struct config_item_type uvcg_streaming_grp_type = { + .ct_owner = THIS_MODULE, +}; + +static struct config_group *uvcg_default_groups[] = { + &uvcg_control_grp.group, + &uvcg_streaming_grp.group, + NULL, +}; + +static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_uvc_opts, + func_inst.group); +} + +CONFIGFS_ATTR_STRUCT(f_uvc_opts); +CONFIGFS_ATTR_OPS(f_uvc_opts); + +static void uvc_attr_release(struct config_item *item) +{ + struct f_uvc_opts *opts = to_f_uvc_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations uvc_item_ops = { + .release = uvc_attr_release, + .show_attribute = f_uvc_opts_attr_show, + .store_attribute = f_uvc_opts_attr_store, +}; + +#define UVCG_OPTS_ATTR(cname, conv, str2u, uxx, vnoc, limit) \ +static ssize_t f_uvc_opts_##cname##_show( \ + struct f_uvc_opts *opts, char *page) \ +{ \ + int result; \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%d\n", conv(opts->cname)); \ + mutex_unlock(&opts->lock); \ + \ + return result; \ +} \ + \ +static ssize_t \ +f_uvc_opts_##cname##_store(struct f_uvc_opts *opts, \ + const char *page, size_t len) \ +{ \ + int ret; \ + uxx num; \ + \ + mutex_lock(&opts->lock); \ + if (opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = str2u(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + if (num > limit) { \ + ret = -EINVAL; \ + goto end; \ + } \ + opts->cname = vnoc(num); \ + ret = len; \ +end: \ + mutex_unlock(&opts->lock); \ + return ret; \ +} \ + \ +static struct f_uvc_opts_attribute \ + f_uvc_opts_attribute_##cname = \ + __CONFIGFS_ATTR(cname, S_IRUGO | S_IWUSR, \ + f_uvc_opts_##cname##_show, \ + f_uvc_opts_##cname##_store) + +#define identity_conv(x) (x) + +UVCG_OPTS_ATTR(streaming_interval, identity_conv, kstrtou8, u8, identity_conv, + 16); +UVCG_OPTS_ATTR(streaming_maxpacket, le16_to_cpu, kstrtou16, u16, le16_to_cpu, + 3072); +UVCG_OPTS_ATTR(streaming_maxburst, identity_conv, kstrtou8, u8, identity_conv, + 15); + +#undef identity_conv + +#undef UVCG_OPTS_ATTR + +static struct configfs_attribute *uvc_attrs[] = { + &f_uvc_opts_attribute_streaming_interval.attr, + &f_uvc_opts_attribute_streaming_maxpacket.attr, + &f_uvc_opts_attribute_streaming_maxburst.attr, + NULL, +}; + +static struct config_item_type uvc_func_type = { + .ct_item_ops = &uvc_item_ops, + .ct_attrs = uvc_attrs, + .ct_owner = THIS_MODULE, +}; + +static inline void uvcg_init_group(struct config_group *g, + struct config_group **default_groups, + const char *name, + struct config_item_type *type) +{ + g->default_groups = default_groups; + config_group_init_type_name(g, name, type); +} + +int uvcg_attach_configfs(struct f_uvc_opts *opts) +{ + config_group_init_type_name(&uvcg_control_header_grp.group, + "header", + &uvcg_control_header_grp_type); + config_group_init_type_name(&uvcg_default_processing.group, + "default", + &uvcg_default_processing_type); + uvcg_init_group(&uvcg_processing_grp.group, + uvcg_processing_default_groups, + "processing", + &uvcg_processing_grp_type); + config_group_init_type_name(&uvcg_default_camera.group, + "default", + &uvcg_default_camera_type); + uvcg_init_group(&uvcg_camera_grp.group, + uvcg_camera_default_groups, + "camera", + &uvcg_camera_grp_type); + config_group_init_type_name(&uvcg_default_output.group, + "default", + &uvcg_default_output_type); + uvcg_init_group(&uvcg_output_grp.group, + uvcg_output_default_groups, + "output", + &uvcg_output_grp_type); + uvcg_init_group(&uvcg_terminal_grp.group, + uvcg_terminal_default_groups, + "terminal", + &uvcg_terminal_grp_type); + config_group_init_type_name(&uvcg_control_class_fs.group, + "fs", + &uvcg_control_class_type); + config_group_init_type_name(&uvcg_control_class_ss.group, + "ss", + &uvcg_control_class_type); + uvcg_init_group(&uvcg_control_class_grp.group, + uvcg_control_class_default_groups, + "class", + &uvcg_control_class_grp_type); + uvcg_init_group(&uvcg_control_grp.group, + uvcg_control_default_groups, + "control", + &uvcg_control_grp_type); + config_group_init_type_name(&uvcg_streaming_header_grp.group, + "header", + &uvcg_streaming_header_grp_type); + config_group_init_type_name(&uvcg_uncompressed_grp.group, + "uncompressed", + &uvcg_uncompressed_grp_type); + config_group_init_type_name(&uvcg_mjpeg_grp.group, + "mjpeg", + &uvcg_mjpeg_grp_type); + config_group_init_type_name(&uvcg_default_color_matching.group, + "default", + &uvcg_default_color_matching_type); + uvcg_init_group(&uvcg_color_matching_grp.group, + uvcg_color_matching_default_groups, + "color_matching", + &uvcg_color_matching_grp_type); + config_group_init_type_name(&uvcg_streaming_class_fs.group, + "fs", + &uvcg_streaming_class_type); + config_group_init_type_name(&uvcg_streaming_class_hs.group, + "hs", + &uvcg_streaming_class_type); + config_group_init_type_name(&uvcg_streaming_class_ss.group, + "ss", + &uvcg_streaming_class_type); + uvcg_init_group(&uvcg_streaming_class_grp.group, + uvcg_streaming_class_default_groups, + "class", + &uvcg_streaming_class_grp_type); + uvcg_init_group(&uvcg_streaming_grp.group, + uvcg_streaming_default_groups, + "streaming", + &uvcg_streaming_grp_type); + uvcg_init_group(&opts->func_inst.group, + uvcg_default_groups, + "", + &uvc_func_type); + return 0; +} diff --git a/drivers/usb/gadget/function/uvc_configfs.h b/drivers/usb/gadget/function/uvc_configfs.h new file mode 100644 index 000000000000..085e67be7c71 --- /dev/null +++ b/drivers/usb/gadget/function/uvc_configfs.h @@ -0,0 +1,22 @@ +/* + * uvc_configfs.h + * + * Configfs support for the uvc function. + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz + * + * 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. + */ +#ifndef UVC_CONFIGFS_H +#define UVC_CONFIGFS_H + +struct f_uvc_opts; + +int uvcg_attach_configfs(struct f_uvc_opts *opts); + +#endif /* UVC_CONFIGFS_H */ -- cgit v1.2.3 From 3280e67536f8a4d4adf8dcde10cb4c4b577c34f4 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 6 Jan 2015 14:46:58 +0100 Subject: usb: atmel_usba_udc: Rework at91sam9rl errata handling at91sam9rl SoC has an erratum forcing us to toggle the BIAS on USB suspend/resume events. This specific handling is only activated when CONFIG_ARCH_AT91SAM9RL is set and this option is only set when building a non-DT kernel, which is problematic since non-DT support for at91sam9rl SoC has been removed. Rework the toggle_bias implementation to attach it to the "at91sam9rl-udc" compatible string. Add new compatible strings to avoid executing at91sam9rl erratum handling on other SoCs. Acked-by: Alexandre Belloni Signed-off-by: Boris Brezillon Signed-off-by: Felipe Balbi --- .../devicetree/bindings/usb/atmel-usb.txt | 5 +- drivers/usb/gadget/udc/atmel_usba_udc.c | 78 ++++++++++++---------- drivers/usb/gadget/udc/atmel_usba_udc.h | 5 ++ 3 files changed, 52 insertions(+), 36 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/atmel-usb.txt b/Documentation/devicetree/bindings/usb/atmel-usb.txt index bc2222ca3f2a..38fee0f66c12 100644 --- a/Documentation/devicetree/bindings/usb/atmel-usb.txt +++ b/Documentation/devicetree/bindings/usb/atmel-usb.txt @@ -51,7 +51,10 @@ usb1: gadget@fffa4000 { Atmel High-Speed USB device controller Required properties: - - compatible: Should be "atmel,at91sam9rl-udc" + - compatible: Should be one of the following + "at91sam9rl-udc" + "at91sam9g45-udc" + "sama5d3-udc" - reg: Address and length of the register set for the device - interrupts: Should contain usba interrupt - ep childnode: To specify the number of endpoints and their properties. diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index ce882371786b..36fd34b77387 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -8,6 +8,7 @@ * published by the Free Software Foundation. */ #include +#include #include #include #include @@ -324,28 +325,12 @@ static int vbus_is_present(struct usba_udc *udc) return 1; } -#if defined(CONFIG_ARCH_AT91SAM9RL) - -#include - -static void toggle_bias(int is_on) -{ - unsigned int uckr = at91_pmc_read(AT91_CKGR_UCKR); - - if (is_on) - at91_pmc_write(AT91_CKGR_UCKR, uckr | AT91_PMC_BIASEN); - else - at91_pmc_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN)); -} - -#else - -static void toggle_bias(int is_on) +static void toggle_bias(struct usba_udc *udc, int is_on) { + if (udc->errata && udc->errata->toggle_bias) + udc->errata->toggle_bias(udc, is_on); } -#endif /* CONFIG_ARCH_AT91SAM9RL */ - static void next_fifo_transaction(struct usba_ep *ep, struct usba_request *req) { unsigned int transaction_len; @@ -1620,7 +1605,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) DBG(DBG_INT, "irq, status=%#08x\n", status); if (status & USBA_DET_SUSPEND) { - toggle_bias(0); + toggle_bias(udc, 0); usba_writel(udc, INT_CLR, USBA_DET_SUSPEND); DBG(DBG_BUS, "Suspend detected\n"); if (udc->gadget.speed != USB_SPEED_UNKNOWN @@ -1632,7 +1617,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) } if (status & USBA_WAKE_UP) { - toggle_bias(1); + toggle_bias(udc, 1); usba_writel(udc, INT_CLR, USBA_WAKE_UP); DBG(DBG_BUS, "Wake Up CPU detected\n"); } @@ -1736,13 +1721,13 @@ static irqreturn_t usba_vbus_irq(int irq, void *devid) vbus = vbus_is_present(udc); if (vbus != udc->vbus_prev) { if (vbus) { - toggle_bias(1); + toggle_bias(udc, 1); usba_writel(udc, CTRL, USBA_ENABLE_MASK); usba_writel(udc, INT_ENB, USBA_END_OF_RESET); } else { udc->gadget.speed = USB_SPEED_UNKNOWN; reset_all_endpoints(udc); - toggle_bias(0); + toggle_bias(udc, 0); usba_writel(udc, CTRL, USBA_DISABLE_MASK); if (udc->driver->disconnect) { spin_unlock(&udc->lock); @@ -1788,7 +1773,7 @@ static int atmel_usba_start(struct usb_gadget *gadget, /* If Vbus is present, enable the controller and wait for reset */ spin_lock_irqsave(&udc->lock, flags); if (vbus_is_present(udc) && udc->vbus_prev == 0) { - toggle_bias(1); + toggle_bias(udc, 1); usba_writel(udc, CTRL, USBA_ENABLE_MASK); usba_writel(udc, INT_ENB, USBA_END_OF_RESET); } @@ -1811,7 +1796,7 @@ static int atmel_usba_stop(struct usb_gadget *gadget) spin_unlock_irqrestore(&udc->lock, flags); /* This will also disable the DP pullup */ - toggle_bias(0); + toggle_bias(udc, 0); usba_writel(udc, CTRL, USBA_DISABLE_MASK); clk_disable_unprepare(udc->hclk); @@ -1823,6 +1808,29 @@ static int atmel_usba_stop(struct usb_gadget *gadget) } #ifdef CONFIG_OF +static void at91sam9rl_toggle_bias(struct usba_udc *udc, int is_on) +{ + unsigned int uckr = at91_pmc_read(AT91_CKGR_UCKR); + + if (is_on) + at91_pmc_write(AT91_CKGR_UCKR, uckr | AT91_PMC_BIASEN); + else + at91_pmc_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN)); +} + +static const struct usba_udc_errata at91sam9rl_errata = { + .toggle_bias = at91sam9rl_toggle_bias, +}; + +static const struct of_device_id atmel_udc_dt_ids[] = { + { .compatible = "atmel,at91sam9rl-udc", .data = &at91sam9rl_errata }, + { .compatible = "atmel,at91sam9g45-udc" }, + { .compatible = "atmel,sama5d3-udc" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, atmel_udc_dt_ids); + static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, struct usba_udc *udc) { @@ -1830,10 +1838,17 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, const char *name; enum of_gpio_flags flags; struct device_node *np = pdev->dev.of_node; + const struct of_device_id *match; struct device_node *pp; int i, ret; struct usba_ep *eps, *ep; + match = of_match_node(atmel_udc_dt_ids, np); + if (!match) + return ERR_PTR(-EINVAL); + + udc->errata = match->data; + udc->num_ep = 0; udc->vbus_pin = of_get_named_gpio_flags(np, "atmel,vbus-gpio", 0, @@ -2024,7 +2039,7 @@ static int usba_udc_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Unable to enable pclk, aborting.\n"); return ret; } - toggle_bias(0); + usba_writel(udc, CTRL, USBA_DISABLE_MASK); clk_disable_unprepare(pclk); @@ -2033,6 +2048,8 @@ static int usba_udc_probe(struct platform_device *pdev) else udc->usba_ep = usba_udc_pdata(pdev, udc); + toggle_bias(udc, 0); + if (IS_ERR(udc->usba_ep)) return PTR_ERR(udc->usba_ep); @@ -2092,15 +2109,6 @@ static int __exit usba_udc_remove(struct platform_device *pdev) return 0; } -#if defined(CONFIG_OF) -static const struct of_device_id atmel_udc_dt_ids[] = { - { .compatible = "atmel,at91sam9rl-udc" }, - { /* sentinel */ } -}; - -MODULE_DEVICE_TABLE(of, atmel_udc_dt_ids); -#endif - static struct platform_driver udc_driver = { .remove = __exit_p(usba_udc_remove), .driver = { diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h index a70706e8cb02..456899e9ee62 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.h +++ b/drivers/usb/gadget/udc/atmel_usba_udc.h @@ -304,6 +304,10 @@ struct usba_request { unsigned int mapped:1; }; +struct usba_udc_errata { + void (*toggle_bias)(struct usba_udc *udc, int is_on); +}; + struct usba_udc { /* Protect hw registers from concurrent modifications */ spinlock_t lock; @@ -314,6 +318,7 @@ struct usba_udc { struct usb_gadget gadget; struct usb_gadget_driver *driver; struct platform_device *pdev; + const struct usba_udc_errata *errata; int irq; int vbus_pin; int vbus_pin_inverted; -- cgit v1.2.3 From 7bdea87a871f217a8a463f9203e6d0c702b7fda9 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sat, 6 Dec 2014 22:05:14 +0100 Subject: usb: phy: nop: device tree documentation for vbus Enhance the phy documentation by adding 2 new optional bindings : - the vbus gpio, which detects usb insertion - the vbus regulator, which provides current drawn from the usb cable Signed-off-by: Robert Jarzmik Signed-off-by: Felipe Balbi --- Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt b/Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt index 8db5b33d6fdc..5be01c859b7a 100644 --- a/Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt +++ b/Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt @@ -17,6 +17,11 @@ Optional properties: - reset-gpios: Should specify the GPIO for reset. +- vbus-detect-gpio: should specify the GPIO detecting a VBus insertion + (see Documentation/devicetree/bindings/gpio/gpio.txt) +- vbus-regulator : should specifiy the regulator supplying current drawn from + the VBus line (see Documentation/devicetree/bindings/regulator/regulator.txt). + Example: hsusb1_phy { @@ -26,8 +31,11 @@ Example: clock-names = "main_clk"; vcc-supply = <&hsusb1_vcc_regulator>; reset-gpios = <&gpio1 7 GPIO_ACTIVE_LOW>; + vbus-detect-gpio = <&gpio2 13 GPIO_ACTIVE_HIGH>; + vbus-regulator = <&vbus_regulator>; }; hsusb1_phy is a NOP USB PHY device that gets its clock from an oscillator and expects that clock to be configured to 19.2MHz by the NOP PHY driver. hsusb1_vcc_regulator provides power to the PHY and GPIO 7 controls RESET. +GPIO 13 detects VBus insertion, and accordingly notifies the vbus-regulator. -- cgit v1.2.3 From b2f93ef30fc64dab53f64bcabe23d9377abf454e Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Fri, 9 Jan 2015 13:38:49 +0100 Subject: Documentation: dt-bindings: add dt binding info for dwc2 g-use-dma Indicate if gadget driver must use dma for usb transfers. Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi --- Documentation/devicetree/bindings/usb/dwc2.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/dwc2.txt b/Documentation/devicetree/bindings/usb/dwc2.txt index 482f815363ef..a7a9cb647a0b 100644 --- a/Documentation/devicetree/bindings/usb/dwc2.txt +++ b/Documentation/devicetree/bindings/usb/dwc2.txt @@ -20,6 +20,7 @@ Optional properties: Refer to phy/phy-bindings.txt for generic phy consumer properties - dr_mode: shall be one of "host", "peripheral" and "otg" Refer to usb/generic.txt +- g-use-dma: enable dma usage in gadget driver. Example: -- cgit v1.2.3 From bb8693a98ebbe863401108d1cc4f8c0bc72e1900 Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Fri, 9 Jan 2015 13:38:53 +0100 Subject: Documentation: dt-bindings: add dt binding info for dwc2 fifo resizing Tested-by: Robert Baldyga Acked-by: Paul Zimmerman Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi --- Documentation/devicetree/bindings/usb/dwc2.txt | 3 +++ 1 file changed, 3 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/dwc2.txt b/Documentation/devicetree/bindings/usb/dwc2.txt index a7a9cb647a0b..fd132cbee70e 100644 --- a/Documentation/devicetree/bindings/usb/dwc2.txt +++ b/Documentation/devicetree/bindings/usb/dwc2.txt @@ -21,6 +21,9 @@ Refer to phy/phy-bindings.txt for generic phy consumer properties - dr_mode: shall be one of "host", "peripheral" and "otg" Refer to usb/generic.txt - g-use-dma: enable dma usage in gadget driver. +- g-rx-fifo-size: size of rx fifo size in gadget mode. +- g-np-tx-fifo-size: size of non-periodic tx fifo size in gadget mode. +- g-tx-fifo-size: size of periodic tx fifo per endpoint (except ep0) in gadget mode. Example: -- cgit v1.2.3 From de6e0f84bebc0daadca46e735f4b2be3e006d05d Mon Sep 17 00:00:00 2001 From: Gabriel FERNANDEZ Date: Wed, 14 Jan 2015 10:54:57 +0100 Subject: phy: miphy28lp: Pass sysconfig register offsets via syscfg dt property. Based on Arnds review comments here https://lkml.org/lkml/2014/11/13/161, update the miphy28lp phy driver to access sysconfig register offsets via syscfg dt property. This is because the reg property should not be mixing address spaces like it does currently for miphy28lp. This change then also aligns us to how other platforms such as keystone and bcm7445 pass there syscon offsets via DT. I have updated the miphy28lp phy driver same way as Peter's implementation. Signed-off-by: Gabriel Fernandez Signed-off-by: Kishon Vijay Abraham I --- .../devicetree/bindings/phy/phy-miphy28lp.txt | 43 ++++++--------- drivers/phy/phy-miphy28lp.c | 61 ++++++++++++---------- 2 files changed, 48 insertions(+), 56 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/phy/phy-miphy28lp.txt b/Documentation/devicetree/bindings/phy/phy-miphy28lp.txt index 46a135dae6b3..89caa885d08c 100644 --- a/Documentation/devicetree/bindings/phy/phy-miphy28lp.txt +++ b/Documentation/devicetree/bindings/phy/phy-miphy28lp.txt @@ -26,6 +26,7 @@ Required properties (port (child) node): filled in "reg". It can also contain the offset of the system configuration registers used as glue-logic to setup the device for SATA/PCIe or USB3 devices. +- st,syscfg : Offset of the parent configuration register. - resets : phandle to the parent reset controller. - reset-names : Associated name must be "miphy-sw-rst". @@ -54,18 +55,12 @@ example: phy_port0: port@9b22000 { reg = <0x9b22000 0xff>, <0x9b09000 0xff>, - <0x9b04000 0xff>, - <0x114 0x4>, /* sysctrl MiPHY cntrl */ - <0x818 0x4>, /* sysctrl MiPHY status*/ - <0xe0 0x4>, /* sysctrl PCIe */ - <0xec 0x4>; /* sysctrl SATA */ + <0x9b04000 0xff>; reg-names = "sata-up", "pcie-up", - "pipew", - "miphy-ctrl-glue", - "miphy-status-glue", - "pcie-glue", - "sata-glue"; + "pipew"; + + st,syscfg = <0x114 0x818 0xe0 0xec>; #phy-cells = <1>; st,osc-rdy; reset-names = "miphy-sw-rst"; @@ -75,18 +70,13 @@ example: phy_port1: port@9b2a000 { reg = <0x9b2a000 0xff>, <0x9b19000 0xff>, - <0x9b14000 0xff>, - <0x118 0x4>, - <0x81c 0x4>, - <0xe4 0x4>, - <0xf0 0x4>; + <0x9b14000 0xff>; reg-names = "sata-up", "pcie-up", - "pipew", - "miphy-ctrl-glue", - "miphy-status-glue", - "pcie-glue", - "sata-glue"; + "pipew"; + + st,syscfg = <0x118 0x81c 0xe4 0xf0>; + #phy-cells = <1>; st,osc-force-ext; reset-names = "miphy-sw-rst"; @@ -95,13 +85,12 @@ example: phy_port2: port@8f95000 { reg = <0x8f95000 0xff>, - <0x8f90000 0xff>, - <0x11c 0x4>, - <0x820 0x4>; + <0x8f90000 0xff>; reg-names = "pipew", - "usb3-up", - "miphy-ctrl-glue", - "miphy-status-glue"; + "usb3-up"; + + st,syscfg = <0x11c 0x820>; + #phy-cells = <1>; reset-names = "miphy-sw-rst"; resets = <&softreset STIH407_MIPHY2_SOFTRESET>; @@ -125,4 +114,4 @@ example: Macro definitions for the supported miphy configuration can be found in: -include/dt-bindings/phy/phy-miphy28lp.h +include/dt-bindings/phy/phy.h diff --git a/drivers/phy/phy-miphy28lp.c b/drivers/phy/phy-miphy28lp.c index 27fa62ce6136..9b2848e6115d 100644 --- a/drivers/phy/phy-miphy28lp.c +++ b/drivers/phy/phy-miphy28lp.c @@ -194,6 +194,14 @@ #define MIPHY_SATA_BANK_NB 3 #define MIPHY_PCIE_BANK_NB 2 +enum { + SYSCFG_CTRL, + SYSCFG_STATUS, + SYSCFG_PCI, + SYSCFG_SATA, + SYSCFG_REG_MAX, +}; + struct miphy28lp_phy { struct phy *phy; struct miphy28lp_dev *phydev; @@ -211,10 +219,7 @@ struct miphy28lp_phy { u32 sata_gen; /* Sysconfig registers offsets needed to configure the device */ - u32 syscfg_miphy_ctrl; - u32 syscfg_miphy_status; - u32 syscfg_pci; - u32 syscfg_sata; + u32 syscfg_reg[SYSCFG_REG_MAX]; u8 type; }; @@ -834,12 +839,12 @@ static int miphy_osc_is_ready(struct miphy28lp_phy *miphy_phy) if (!miphy_phy->osc_rdy) return 0; - if (!miphy_phy->syscfg_miphy_status) + if (!miphy_phy->syscfg_reg[SYSCFG_STATUS]) return -EINVAL; do { - regmap_read(miphy_dev->regmap, miphy_phy->syscfg_miphy_status, - &val); + regmap_read(miphy_dev->regmap, + miphy_phy->syscfg_reg[SYSCFG_STATUS], &val); if ((val & MIPHY_OSC_RDY) != MIPHY_OSC_RDY) cpu_relax(); @@ -888,7 +893,7 @@ static int miphy28lp_setup(struct miphy28lp_phy *miphy_phy, u32 miphy_val) int err; struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; - if (!miphy_phy->syscfg_miphy_ctrl) + if (!miphy_phy->syscfg_reg[SYSCFG_CTRL]) return -EINVAL; err = reset_control_assert(miphy_phy->miphy_rst); @@ -900,7 +905,8 @@ static int miphy28lp_setup(struct miphy28lp_phy *miphy_phy, u32 miphy_val) if (miphy_phy->osc_force_ext) miphy_val |= MIPHY_OSC_FORCE_EXT; - regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_miphy_ctrl, + regmap_update_bits(miphy_dev->regmap, + miphy_phy->syscfg_reg[SYSCFG_CTRL], MIPHY_CTRL_MASK, miphy_val); err = reset_control_deassert(miphy_phy->miphy_rst); @@ -917,8 +923,9 @@ static int miphy28lp_init_sata(struct miphy28lp_phy *miphy_phy) struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; int err, sata_conf = SATA_CTRL_SELECT_SATA; - if ((!miphy_phy->syscfg_sata) || (!miphy_phy->syscfg_pci) - || (!miphy_phy->base)) + if ((!miphy_phy->syscfg_reg[SYSCFG_SATA]) || + (!miphy_phy->syscfg_reg[SYSCFG_PCI]) || + (!miphy_phy->base)) return -EINVAL; dev_info(miphy_dev->dev, "sata-up mode, addr 0x%p\n", miphy_phy->base); @@ -926,10 +933,11 @@ static int miphy28lp_init_sata(struct miphy28lp_phy *miphy_phy) /* Configure the glue-logic */ sata_conf |= ((miphy_phy->sata_gen - SATA_GEN1) << SATA_SPDMODE); - regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_sata, + regmap_update_bits(miphy_dev->regmap, + miphy_phy->syscfg_reg[SYSCFG_SATA], SATA_CTRL_MASK, sata_conf); - regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_pci, + regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_reg[SYSCFG_PCI], PCIE_CTRL_MASK, SATA_CTRL_SELECT_PCIE); /* MiPHY path and clocking init */ @@ -951,17 +959,19 @@ static int miphy28lp_init_pcie(struct miphy28lp_phy *miphy_phy) struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; int err; - if ((!miphy_phy->syscfg_sata) || (!miphy_phy->syscfg_pci) + if ((!miphy_phy->syscfg_reg[SYSCFG_SATA]) || + (!miphy_phy->syscfg_reg[SYSCFG_PCI]) || (!miphy_phy->base) || (!miphy_phy->pipebase)) return -EINVAL; dev_info(miphy_dev->dev, "pcie-up mode, addr 0x%p\n", miphy_phy->base); /* Configure the glue-logic */ - regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_sata, + regmap_update_bits(miphy_dev->regmap, + miphy_phy->syscfg_reg[SYSCFG_SATA], SATA_CTRL_MASK, SATA_CTRL_SELECT_PCIE); - regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_pci, + regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_reg[SYSCFG_PCI], PCIE_CTRL_MASK, SYSCFG_PCIE_PCIE_VAL); /* MiPHY path and clocking init */ @@ -1156,7 +1166,8 @@ static int miphy28lp_probe_resets(struct device_node *node, static int miphy28lp_of_probe(struct device_node *np, struct miphy28lp_phy *miphy_phy) { - struct resource res; + int i; + u32 ctrlreg; miphy_phy->osc_force_ext = of_property_read_bool(np, "st,osc-force-ext"); @@ -1175,18 +1186,10 @@ static int miphy28lp_of_probe(struct device_node *np, if (!miphy_phy->sata_gen) miphy_phy->sata_gen = SATA_GEN1; - if (!miphy28lp_get_resource_byname(np, "miphy-ctrl-glue", &res)) - miphy_phy->syscfg_miphy_ctrl = res.start; - - if (!miphy28lp_get_resource_byname(np, "miphy-status-glue", &res)) - miphy_phy->syscfg_miphy_status = res.start; - - if (!miphy28lp_get_resource_byname(np, "pcie-glue", &res)) - miphy_phy->syscfg_pci = res.start; - - if (!miphy28lp_get_resource_byname(np, "sata-glue", &res)) - miphy_phy->syscfg_sata = res.start; - + for (i = 0; i < SYSCFG_REG_MAX; i++) { + if (!of_property_read_u32_index(np, "st,syscfg", i, &ctrlreg)) + miphy_phy->syscfg_reg[i] = ctrlreg; + } return 0; } -- cgit v1.2.3 From 5402d927ae96bd1413ecc6bac966fd8b6d3b8159 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Fri, 16 Jan 2015 16:04:11 +0100 Subject: phy: phy-stih407-usb: Pass sysconfig register offsets via syscfg property. Based on Arnds review comments here https://lkml.org/lkml/2014/11/13/161, update the phy driver to not use the reg property to access the sysconfig register offsets. This is because other phy's (miphy28, miphy365) have a combination of memory mapped registers and sysconfig control regs, and we shouldn't be mixing address spaces in the reg property. In addition we would ideally like the sysconfig offsets to be passed via DT in a uniform way. This new method will also allow us to support devices which have sysconfig registers in different banks more easily and it is also analagous to how keystone and bcm7745 platforms pass there syscon offsets in DT. This breaks DT compatibility, but this platform is considered WIP, and is only used by a few developers who are upstreaming support for it. Signed-off-by: Peter Griffin Reviewed-by: Arnd Bergmann Signed-off-by: Maxime Coquelin Signed-off-by: Kishon Vijay Abraham I --- .../devicetree/bindings/phy/phy-stih407-usb.txt | 10 ++------- drivers/phy/phy-stih407-usb.c | 25 ++++++++++++---------- 2 files changed, 16 insertions(+), 19 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/phy/phy-stih407-usb.txt b/Documentation/devicetree/bindings/phy/phy-stih407-usb.txt index 1ef8228db73b..de6a706abcdb 100644 --- a/Documentation/devicetree/bindings/phy/phy-stih407-usb.txt +++ b/Documentation/devicetree/bindings/phy/phy-stih407-usb.txt @@ -5,10 +5,7 @@ host controllers (when controlling usb2/1.1 devices) available on STiH407 SoC fa Required properties: - compatible : should be "st,stih407-usb2-phy" -- reg : contain the offset and length of the system configuration registers - used as glue logic to control & parameter phy -- reg-names : the names of the system configuration registers in "reg", should be "param" and "reg" -- st,syscfg : sysconfig register to manage phy parameter at driver level +- st,syscfg : phandle of sysconfig bank plus integer array containing phyparam and phyctrl register offsets - resets : list of phandle and reset specifier pairs. There should be two entries, one for the whole phy and one for the port - reset-names : list of reset signal names. Should be "global" and "port" @@ -19,11 +16,8 @@ Example: usb2_picophy0: usbpicophy@f8 { compatible = "st,stih407-usb2-phy"; - reg = <0xf8 0x04>, /* syscfg 5062 */ - <0xf4 0x04>; /* syscfg 5061 */ - reg-names = "param", "ctrl"; #phy-cells = <0>; - st,syscfg = <&syscfg_core>; + st,syscfg = <&syscfg_core 0x100 0xf4>; resets = <&softreset STIH407_PICOPHY_SOFTRESET>, <&picophyreset STIH407_PICOPHY0_RESET>; reset-names = "global", "port"; diff --git a/drivers/phy/phy-stih407-usb.c b/drivers/phy/phy-stih407-usb.c index 74f0fab3cd8a..1d5ae5f8ef69 100644 --- a/drivers/phy/phy-stih407-usb.c +++ b/drivers/phy/phy-stih407-usb.c @@ -22,6 +22,9 @@ #include #include +#define PHYPARAM_REG 1 +#define PHYCTRL_REG 2 + /* Default PHY_SEL and REFCLKSEL configuration */ #define STIH407_USB_PICOPHY_CTRL_PORT_CONF 0x6 #define STIH407_USB_PICOPHY_CTRL_PORT_MASK 0x1f @@ -93,7 +96,7 @@ static int stih407_usb2_picophy_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; struct phy_provider *phy_provider; struct phy *phy; - struct resource *res; + int ret; phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL); if (!phy_dev) @@ -123,19 +126,19 @@ static int stih407_usb2_picophy_probe(struct platform_device *pdev) return PTR_ERR(phy_dev->regmap); } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl"); - if (!res) { - dev_err(dev, "No ctrl reg found\n"); - return -ENXIO; + ret = of_property_read_u32_index(np, "st,syscfg", PHYPARAM_REG, + &phy_dev->param); + if (ret) { + dev_err(dev, "can't get phyparam offset (%d)\n", ret); + return ret; } - phy_dev->ctrl = res->start; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "param"); - if (!res) { - dev_err(dev, "No param reg found\n"); - return -ENXIO; + ret = of_property_read_u32_index(np, "st,syscfg", PHYCTRL_REG, + &phy_dev->ctrl); + if (ret) { + dev_err(dev, "can't get phyctrl offset (%d)\n", ret); + return ret; } - phy_dev->param = res->start; phy = devm_phy_create(dev, NULL, &stih407_usb2_picophy_data); if (IS_ERR(phy)) { -- cgit v1.2.3 From abd2dbf6bb1b5f3a03a8c76b1a8879da1dd30caa Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Mon, 19 Jan 2015 12:53:17 +0900 Subject: usb: renesas_usbhs: add support for requesting DT DMA This patch adds dma_request_slave_channel_reason() calling to request dma slave channels for multiplatform environment. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi --- Documentation/devicetree/bindings/usb/renesas_usbhs.txt | 2 ++ drivers/usb/renesas_usbhs/fifo.c | 11 ++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/renesas_usbhs.txt b/Documentation/devicetree/bindings/usb/renesas_usbhs.txt index b08c903f8668..61b045b6d50e 100644 --- a/Documentation/devicetree/bindings/usb/renesas_usbhs.txt +++ b/Documentation/devicetree/bindings/usb/renesas_usbhs.txt @@ -14,6 +14,8 @@ Optional properties: function should be enabled - phys: phandle + phy specifier pair - phy-names: must be "usb" + - dmas: Must contain a list of references to DMA specifiers. + - dma-names : Must contain a list of DMA names, "tx" or "rx". Example: usbhs: usb@e6590000 { diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 48e31b94313d..4c086b1cda04 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -1069,12 +1069,21 @@ static void usbhsf_dma_init_pdev(struct usbhs_fifo *fifo) &fifo->rx_slave); } +static void usbhsf_dma_init_dt(struct device *dev, struct usbhs_fifo *fifo) +{ + fifo->tx_chan = dma_request_slave_channel_reason(dev, "tx"); + fifo->rx_chan = dma_request_slave_channel_reason(dev, "rx"); +} + static void usbhsf_dma_init(struct usbhs_priv *priv, struct usbhs_fifo *fifo) { struct device *dev = usbhs_priv_to_dev(priv); - usbhsf_dma_init_pdev(fifo); + if (dev->of_node) + usbhsf_dma_init_dt(dev, fifo); + else + usbhsf_dma_init_pdev(fifo); if (fifo->tx_chan || fifo->rx_chan) dev_dbg(dev, "enable DMAEngine (%s%s%s)\n", -- cgit v1.2.3 From 9b43e5ec89dd0c863959ae0157ad3d693588ab44 Mon Sep 17 00:00:00 2001 From: Yunzhi Li Date: Wed, 21 Jan 2015 18:26:00 +0800 Subject: Documentation: bindings: add dt documentation for Rockchip usb PHY This patch adds a binding that describes the Rockchip usb PHYs found on Rockchip SoCs usb interface. Signed-off-by: Yunzhi Li Signed-off-by: Kishon Vijay Abraham I --- .../devicetree/bindings/phy/rockchip-usb-phy.txt | 37 ++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/rockchip-usb-phy.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/phy/rockchip-usb-phy.txt b/Documentation/devicetree/bindings/phy/rockchip-usb-phy.txt new file mode 100644 index 000000000000..826454ac43bb --- /dev/null +++ b/Documentation/devicetree/bindings/phy/rockchip-usb-phy.txt @@ -0,0 +1,37 @@ +ROCKCHIP USB2 PHY + +Required properties: + - compatible: rockchip,rk3288-usb-phy + - rockchip,grf : phandle to the syscon managing the "general + register files" + - #address-cells: should be 1 + - #size-cells: should be 0 + +Sub-nodes: +Each PHY should be represented as a sub-node. + +Sub-nodes +required properties: +- #phy-cells: should be 0 +- reg: PHY configure reg address offset in GRF + "0x320" - for PHY attach to OTG controller + "0x334" - for PHY attach to HOST0 controller + "0x348" - for PHY attach to HOST1 controller + +Optional Properties: +- clocks : phandle + clock specifier for the phy clocks +- clock-names: string, clock name, must be "phyclk" + +Example: + +usbphy: phy { + compatible = "rockchip,rk3288-usb-phy"; + rockchip,grf = <&grf>; + #address-cells = <1>; + #size-cells = <0>; + + usbphy0: usb-phy0 { + #phy-cells = <0>; + reg = <0x320>; + }; +}; -- cgit v1.2.3 From e4b3d38088df6f3acd40259c8ec32c9bd3bfe3da Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 16 Jan 2015 18:30:37 +0100 Subject: phy: exynos-video-mipi: Fix regression by adding support for PMU regmap After the Exynos Power Management Unit (PMU) driver was converted to the platform device driver in commit 14fc8b93d47323561edf5d482 ("ARM: EXYNOS: Add platform driver support for Exynos PMU") and then PMU device nodes added to Exynos4 DTs in commit 7b9613aca42a5522d269 ("ARM: dts: add PMU syscon node for exynos4") the mipi video phy driver started failing probing, due to overlapping memory mapped register region resources. Now all the Exynos peripheral devices which have registers in the PMU region are supposed to use the regmap provided by the syscon driver. So support for regmap is added in this patch, this unfortunately creates yet another indirection into that supposedly trivial driver. The additional mutex is required because single register is used by PHY pairs (they share bit in a register). An improvement here could be to allow a PHY instance be created with a driver custom mutex, which would then be common for each PHY pair. This would eliminate one of 3 mutexes which need to be taken in the phy_power_on/ phy_power_off code path. However, I tried to keep this bug fix patch possibly simple. This change is needed to make MIPI DSI displays and MIPI CSI-2 camera sensors working again on Exynos4 boards. Cc: Pankaj Dubey Cc: Kukjin Kim Signed-off-by: Sylwester Nawrocki Signed-off-by: Kishon Vijay Abraham I --- .../devicetree/bindings/phy/samsung-phy.txt | 2 +- drivers/phy/phy-exynos-mipi-video.c | 89 ++++++++++++++-------- include/linux/mfd/syscon/exynos4-pmu.h | 21 +++++ 3 files changed, 79 insertions(+), 33 deletions(-) create mode 100644 include/linux/mfd/syscon/exynos4-pmu.h (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/phy/samsung-phy.txt b/Documentation/devicetree/bindings/phy/samsung-phy.txt index d5bad920827f..91e38cfe1f8f 100644 --- a/Documentation/devicetree/bindings/phy/samsung-phy.txt +++ b/Documentation/devicetree/bindings/phy/samsung-phy.txt @@ -3,8 +3,8 @@ Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY Required properties: - compatible : should be "samsung,s5pv210-mipi-video-phy"; -- reg : offset and length of the MIPI DPHY register set; - #phy-cells : from the generic phy bindings, must be 1; +- syscon - phandle to the PMU system controller; For "samsung,s5pv210-mipi-video-phy" compatible PHYs the second cell in the PHY specifier identifies the PHY and its meaning is as follows: diff --git a/drivers/phy/phy-exynos-mipi-video.c b/drivers/phy/phy-exynos-mipi-video.c index 943e0f88a120..f017b2f2a54e 100644 --- a/drivers/phy/phy-exynos-mipi-video.c +++ b/drivers/phy/phy-exynos-mipi-video.c @@ -12,19 +12,18 @@ #include #include #include +#include #include #include #include #include #include +#include #include +#include -/* MIPI_PHYn_CONTROL register offset: n = 0..1 */ +/* MIPI_PHYn_CONTROL reg. offset (for base address from ioremap): n = 0..1 */ #define EXYNOS_MIPI_PHY_CONTROL(n) ((n) * 4) -#define EXYNOS_MIPI_PHY_ENABLE (1 << 0) -#define EXYNOS_MIPI_PHY_SRESETN (1 << 1) -#define EXYNOS_MIPI_PHY_MRESETN (1 << 2) -#define EXYNOS_MIPI_PHY_RESET_MASK (3 << 1) enum exynos_mipi_phy_id { EXYNOS_MIPI_PHY_ID_CSIS0, @@ -38,43 +37,62 @@ enum exynos_mipi_phy_id { ((id) == EXYNOS_MIPI_PHY_ID_DSIM0 || (id) == EXYNOS_MIPI_PHY_ID_DSIM1) struct exynos_mipi_video_phy { - spinlock_t slock; struct video_phy_desc { struct phy *phy; unsigned int index; } phys[EXYNOS_MIPI_PHYS_NUM]; + spinlock_t slock; void __iomem *regs; + struct mutex mutex; + struct regmap *regmap; }; static int __set_phy_state(struct exynos_mipi_video_phy *state, enum exynos_mipi_phy_id id, unsigned int on) { + const unsigned int offset = EXYNOS4_MIPI_PHY_CONTROL(id / 2); void __iomem *addr; - u32 reg, reset; - - addr = state->regs + EXYNOS_MIPI_PHY_CONTROL(id / 2); + u32 val, reset; if (is_mipi_dsim_phy_id(id)) - reset = EXYNOS_MIPI_PHY_MRESETN; - else - reset = EXYNOS_MIPI_PHY_SRESETN; - - spin_lock(&state->slock); - reg = readl(addr); - if (on) - reg |= reset; + reset = EXYNOS4_MIPI_PHY_MRESETN; else - reg &= ~reset; - writel(reg, addr); - - /* Clear ENABLE bit only if MRESETN, SRESETN bits are not set. */ - if (on) - reg |= EXYNOS_MIPI_PHY_ENABLE; - else if (!(reg & EXYNOS_MIPI_PHY_RESET_MASK)) - reg &= ~EXYNOS_MIPI_PHY_ENABLE; + reset = EXYNOS4_MIPI_PHY_SRESETN; + + if (state->regmap) { + mutex_lock(&state->mutex); + regmap_read(state->regmap, offset, &val); + if (on) + val |= reset; + else + val &= ~reset; + regmap_write(state->regmap, offset, val); + if (on) + val |= EXYNOS4_MIPI_PHY_ENABLE; + else if (!(val & EXYNOS4_MIPI_PHY_RESET_MASK)) + val &= ~EXYNOS4_MIPI_PHY_ENABLE; + regmap_write(state->regmap, offset, val); + mutex_unlock(&state->mutex); + } else { + addr = state->regs + EXYNOS_MIPI_PHY_CONTROL(id / 2); + + spin_lock(&state->slock); + val = readl(addr); + if (on) + val |= reset; + else + val &= ~reset; + writel(val, addr); + /* Clear ENABLE bit only if MRESETN, SRESETN bits are not set */ + if (on) + val |= EXYNOS4_MIPI_PHY_ENABLE; + else if (!(val & EXYNOS4_MIPI_PHY_RESET_MASK)) + val &= ~EXYNOS4_MIPI_PHY_ENABLE; + + writel(val, addr); + spin_unlock(&state->slock); + } - writel(reg, addr); - spin_unlock(&state->slock); return 0; } @@ -118,7 +136,6 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev) { struct exynos_mipi_video_phy *state; struct device *dev = &pdev->dev; - struct resource *res; struct phy_provider *phy_provider; unsigned int i; @@ -126,14 +143,22 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev) if (!state) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + state->regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon"); + if (IS_ERR(state->regmap)) { + struct resource *res; - state->regs = devm_ioremap_resource(dev, res); - if (IS_ERR(state->regs)) - return PTR_ERR(state->regs); + dev_info(dev, "regmap lookup failed: %ld\n", + PTR_ERR(state->regmap)); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + state->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(state->regs)) + return PTR_ERR(state->regs); + } dev_set_drvdata(dev, state); spin_lock_init(&state->slock); + mutex_init(&state->mutex); for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) { struct phy *phy = devm_phy_create(dev, NULL, diff --git a/include/linux/mfd/syscon/exynos4-pmu.h b/include/linux/mfd/syscon/exynos4-pmu.h new file mode 100644 index 000000000000..278b1b1549e9 --- /dev/null +++ b/include/linux/mfd/syscon/exynos4-pmu.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#ifndef _LINUX_MFD_SYSCON_PMU_EXYNOS4_H_ +#define _LINUX_MFD_SYSCON_PMU_EXYNOS4_H_ + +/* Exynos4 PMU register definitions */ + +/* MIPI_PHYn_CONTROL register offset: n = 0..1 */ +#define EXYNOS4_MIPI_PHY_CONTROL(n) (0x710 + (n) * 4) +#define EXYNOS4_MIPI_PHY_ENABLE (1 << 0) +#define EXYNOS4_MIPI_PHY_SRESETN (1 << 1) +#define EXYNOS4_MIPI_PHY_MRESETN (1 << 2) +#define EXYNOS4_MIPI_PHY_RESET_MASK (3 << 1) + +#endif /* _LINUX_MFD_SYSCON_PMU_EXYNOS4_H_ */ -- cgit v1.2.3 From bbd3ce86c768928d5db334881e20116c6da4d0c7 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Wed, 7 Jan 2015 15:04:07 +0000 Subject: phy: miphy365x: Pass sysconfig register offsets via syscfg dt property. Based on Arnds review comments here https://lkml.org/lkml/2014/11/13/161, update the miphy365 phy driver to access sysconfig register offsets via syscfg dt property. This is because the reg property should not be mixing address spaces like it does currently for miphy365. This change then also aligns us to how other platforms such as keystone and bcm7445 pass there syscon offsets via DT. This patch breaks DT compatibility, but this platform is considered WIP, and is only used by a few developers who are upstreaming support for it. This change has been done as a single atomic commit to ensure it is bisectable. Signed-off-by: Peter Griffin Reviewed-by: Arnd Bergmann Acked-by: Maxime Coquelin Tested-by: Maxime Coquelin --- .../devicetree/bindings/phy/phy-miphy365x.txt | 15 +++++------ arch/arm/boot/dts/stih416.dtsi | 10 ++++---- drivers/phy/phy-miphy365x.c | 29 ++++++++-------------- 3 files changed, 23 insertions(+), 31 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/phy/phy-miphy365x.txt b/Documentation/devicetree/bindings/phy/phy-miphy365x.txt index 42c880886cf7..9802d5d911aa 100644 --- a/Documentation/devicetree/bindings/phy/phy-miphy365x.txt +++ b/Documentation/devicetree/bindings/phy/phy-miphy365x.txt @@ -6,8 +6,10 @@ for SATA and PCIe. Required properties (controller (parent) node): - compatible : Should be "st,miphy365x-phy" -- st,syscfg : Should be a phandle of the system configuration register group - which contain the SATA, PCIe mode setting bits +- st,syscfg : Phandle / integer array property. Phandle of sysconfig group + containing the miphy registers and integer array should contain + an entry for each port sub-node, specifying the control + register offset inside the sysconfig group. Required nodes : A sub-node is required for each channel the controller provides. Address range information including the usual @@ -26,7 +28,6 @@ Required properties (port (child) node): registers filled in "reg": - sata: For SATA devices - pcie: For PCIe devices - - syscfg: To specify the syscfg based config register Optional properties (port (child) node): - st,sata-gen : Generation of locally attached SATA IP. Expected values @@ -39,20 +40,20 @@ Example: miphy365x_phy: miphy365x@fe382000 { compatible = "st,miphy365x-phy"; - st,syscfg = <&syscfg_rear>; + st,syscfg = <&syscfg_rear 0x824 0x828>; #address-cells = <1>; #size-cells = <1>; ranges; phy_port0: port@fe382000 { - reg = <0xfe382000 0x100>, <0xfe394000 0x100>, <0x824 0x4>; - reg-names = "sata", "pcie", "syscfg"; + reg = <0xfe382000 0x100>, <0xfe394000 0x100>; + reg-names = "sata", "pcie"; #phy-cells = <1>; st,sata-gen = <3>; }; phy_port1: port@fe38a000 { - reg = <0xfe38a000 0x100>, <0xfe804000 0x100>, <0x828 0x4>;; + reg = <0xfe38a000 0x100>, <0xfe804000 0x100>;; reg-names = "sata", "pcie", "syscfg"; #phy-cells = <1>; st,pcie-tx-pol-inv; diff --git a/arch/arm/boot/dts/stih416.dtsi b/arch/arm/boot/dts/stih416.dtsi index fad9073ddeed..85afe01c34fa 100644 --- a/arch/arm/boot/dts/stih416.dtsi +++ b/arch/arm/boot/dts/stih416.dtsi @@ -283,21 +283,21 @@ miphy365x_phy: phy@fe382000 { compatible = "st,miphy365x-phy"; - st,syscfg = <&syscfg_rear>; + st,syscfg = <&syscfg_rear 0x824 0x828>; #address-cells = <1>; #size-cells = <1>; ranges; phy_port0: port@fe382000 { #phy-cells = <1>; - reg = <0xfe382000 0x100>, <0xfe394000 0x100>, <0x824 0x4>; - reg-names = "sata", "pcie", "syscfg"; + reg = <0xfe382000 0x100>, <0xfe394000 0x100>; + reg-names = "sata", "pcie"; }; phy_port1: port@fe38a000 { #phy-cells = <1>; - reg = <0xfe38a000 0x100>, <0xfe804000 0x100>, <0x828 0x4>; - reg-names = "sata", "pcie", "syscfg"; + reg = <0xfe38a000 0x100>, <0xfe804000 0x100>; + reg-names = "sata", "pcie"; }; }; diff --git a/drivers/phy/phy-miphy365x.c b/drivers/phy/phy-miphy365x.c index 6ab43a814ad2..6c80154e8bff 100644 --- a/drivers/phy/phy-miphy365x.c +++ b/drivers/phy/phy-miphy365x.c @@ -141,7 +141,7 @@ struct miphy365x_phy { bool pcie_tx_pol_inv; bool sata_tx_pol_inv; u32 sata_gen; - u64 ctrlreg; + u32 ctrlreg; u8 type; }; @@ -179,7 +179,7 @@ static int miphy365x_set_path(struct miphy365x_phy *miphy_phy, bool sata = (miphy_phy->type == MIPHY_TYPE_SATA); return regmap_update_bits(miphy_dev->regmap, - (unsigned int)miphy_phy->ctrlreg, + miphy_phy->ctrlreg, SYSCFG_SELECT_SATA_MASK, sata << SYSCFG_SELECT_SATA_POS); } @@ -445,7 +445,6 @@ int miphy365x_get_addr(struct device *dev, struct miphy365x_phy *miphy_phy, { struct device_node *phynode = miphy_phy->phy->dev.of_node; const char *name; - const __be32 *taddr; int type = miphy_phy->type; int ret; @@ -455,22 +454,6 @@ int miphy365x_get_addr(struct device *dev, struct miphy365x_phy *miphy_phy, return ret; } - if (!strncmp(name, "syscfg", 6)) { - taddr = of_get_address(phynode, index, NULL, NULL); - if (!taddr) { - dev_err(dev, "failed to fetch syscfg address\n"); - return -EINVAL; - } - - miphy_phy->ctrlreg = of_translate_address(phynode, taddr); - if (miphy_phy->ctrlreg == OF_BAD_ADDR) { - dev_err(dev, "failed to translate syscfg address\n"); - return -EINVAL; - } - - return 0; - } - if (!((!strncmp(name, "sata", 4) && type == MIPHY_TYPE_SATA) || (!strncmp(name, "pcie", 4) && type == MIPHY_TYPE_PCIE))) return 0; @@ -606,7 +589,15 @@ static int miphy365x_probe(struct platform_device *pdev) return ret; phy_set_drvdata(phy, miphy_dev->phys[port]); + port++; + /* sysconfig offsets are indexed from 1 */ + ret = of_property_read_u32_index(np, "st,syscfg", port, + &miphy_phy->ctrlreg); + if (ret) { + dev_err(&pdev->dev, "No sysconfig offset found\n"); + return ret; + } } provider = devm_of_phy_provider_register(&pdev->dev, miphy365x_xlate); -- cgit v1.2.3