diff --git a/Documentation/devicetree/bindings/media/cec.txt b/Documentation/devicetree/bindings/media/cec.txt new file mode 100644 index 00000000..22d7aae3 --- /dev/null +++ b/Documentation/devicetree/bindings/media/cec.txt @@ -0,0 +1,8 @@ +Common bindings for HDMI CEC adapters + +- hdmi-phandle: phandle to the HDMI controller. + +- needs-hpd: if present the CEC support is only available when the HPD + is high. Some boards only let the CEC pin through if the HPD is high, + for example if there is a level converter that uses the HPD to power + up or down. diff --git a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt index ce0e7674..9029b45b 100644 --- a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt +++ b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt @@ -16,6 +16,10 @@ Optional properties: See ../clocks/clock-bindings.txt for details. - clock-names : Must include the following entry: "ext_clock" (External clock provided to the card). +- post-power-on-delay-ms : Delay in ms after powering the card and + de-asserting the reset-gpios (if any) +- power-off-delay-us : Delay in us after asserting the reset-gpios (if any) + during power off of the card. Example: diff --git a/Documentation/devicetree/configfs-overlays.txt b/Documentation/devicetree/configfs-overlays.txt new file mode 100644 index 00000000..5fa43e06 --- /dev/null +++ b/Documentation/devicetree/configfs-overlays.txt @@ -0,0 +1,31 @@ +Howto use the configfs overlay interface. + +A device-tree configfs entry is created in /config/device-tree/overlays +and and it is manipulated using standard file system I/O. +Note that this is a debug level interface, for use by developers and +not necessarily something accessed by normal users due to the +security implications of having direct access to the kernel's device tree. + +* To create an overlay you mkdir the directory: + + # mkdir /config/device-tree/overlays/foo + +* Either you echo the overlay firmware file to the path property file. + + # echo foo.dtbo >/config/device-tree/overlays/foo/path + +* Or you cat the contents of the overlay to the dtbo file + + # cat foo.dtbo >/config/device-tree/overlays/foo/dtbo + +The overlay file will be applied, and devices will be created/destroyed +as required. + +To remove it simply rmdir the directory. + + # rmdir /config/device-tree/overlays/foo + +The rationalle of the dual interface (firmware & direct copy) is that each is +better suited to different use patterns. The firmware interface is what's +intended to be used by hardware managers in the kernel, while the copy interface +make sense for developers (since it avoids problems with namespaces). diff --git a/Documentation/filesystems/configfs/configfs.txt b/Documentation/filesystems/configfs/configfs.txt index af68efdb..e5fe521e 100644 --- a/Documentation/filesystems/configfs/configfs.txt +++ b/Documentation/filesystems/configfs/configfs.txt @@ -51,15 +51,27 @@ configfs tree is always there, whether mounted on /config or not. An item is created via mkdir(2). The item's attributes will also appear at this time. readdir(3) can determine what the attributes are, read(2) can query their default values, and write(2) can store new -values. Like sysfs, attributes should be ASCII text files, preferably -with only one value per file. The same efficiency caveats from sysfs -apply. Don't mix more than one attribute in one attribute file. - -Like sysfs, configfs expects write(2) to store the entire buffer at -once. When writing to configfs attributes, userspace processes should -first read the entire file, modify the portions they wish to change, and -then write the entire buffer back. Attribute files have a maximum size -of one page (PAGE_SIZE, 4096 on i386). +values. Don't mix more than one attribute in one attribute file. + +There are two types of configfs attributes: + +* Normal attributes, which similar to sysfs attributes, are small ASCII text +files, with a maximum size of one page (PAGE_SIZE, 4096 on i386). Preferably +only one value per file should be used, and the same caveats from sysfs apply. +Configfs expects write(2) to store the entire buffer at once. When writing to +normal configfs attributes, userspace processes should first read the entire +file, modify the portions they wish to change, and then write the entire +buffer back. + +* Binary attributes, which are somewhat similar to sysfs binary attributes, +but with a few slight changes to semantics. The PAGE_SIZE limitation does not +apply, but the whole binary item must fit in single kernel vmalloc'ed buffer. +The write(2) calls from user space are buffered, and the attributes' +write_bin_attribute method will be invoked on the final close, therefore it is +imperative for user-space to check the return code of close(2) in order to +verify that the operation finished successfully. +To avoid a malicious user OOMing the kernel, there's a per-binary attribute +maximum buffer value. When an item needs to be destroyed, remove it with rmdir(2). An item cannot be destroyed if any other item has a link to it (via @@ -171,6 +183,7 @@ among other things. For that, it needs a type. struct configfs_item_operations *ct_item_ops; struct configfs_group_operations *ct_group_ops; struct configfs_attribute **ct_attrs; + struct configfs_bin_attribute **ct_bin_attrs; }; The most basic function of a config_item_type is to define what @@ -201,6 +214,32 @@ be called whenever userspace asks for a read(2) on the attribute. If an attribute is writable and provides a ->store method, that method will be be called whenever userspace asks for a write(2) on the attribute. +[struct configfs_bin_attribute] + + struct configfs_attribute { + struct configfs_attribute cb_attr; + void *cb_private; + size_t cb_max_size; + }; + +The binary attribute is used when the one needs to use binary blob to +appear as the contents of a file in the item's configfs directory. +To do so add the binary attribute to the NULL-terminated array +config_item_type->ct_bin_attrs, and the item appears in configfs, the +attribute file will appear with the configfs_bin_attribute->cb_attr.ca_name +filename. configfs_bin_attribute->cb_attr.ca_mode specifies the file +permissions. +The cb_private member is provided for use by the driver, while the +cb_max_size member specifies the maximum amount of vmalloc buffer +to be used. + +If binary attribute is readable and the config_item provides a +ct_item_ops->read_bin_attribute() method, that method will be called +whenever userspace asks for a read(2) on the attribute. The converse +will happen for write(2). The reads/writes are bufferred so only a +single read/write will occur; the attributes' need not concern itself +with it. + [struct config_group] A config_item cannot live in a vacuum. The only way one can be created diff --git a/Documentation/media/kapi/cec-core.rst b/Documentation/media/kapi/cec-core.rst new file mode 100644 index 00000000..d37e107f --- /dev/null +++ b/Documentation/media/kapi/cec-core.rst @@ -0,0 +1,381 @@ +CEC Kernel Support +================== + +The CEC framework provides a unified kernel interface for use with HDMI CEC +hardware. It is designed to handle a multiple types of hardware (receivers, +transmitters, USB dongles). The framework also gives the option to decide +what to do in the kernel driver and what should be handled by userspace +applications. In addition it integrates the remote control passthrough +feature into the kernel's remote control framework. + + +The CEC Protocol +---------------- + +The CEC protocol enables consumer electronic devices to communicate with each +other through the HDMI connection. The protocol uses logical addresses in the +communication. The logical address is strictly connected with the functionality +provided by the device. The TV acting as the communication hub is always +assigned address 0. The physical address is determined by the physical +connection between devices. + +The CEC framework described here is up to date with the CEC 2.0 specification. +It is documented in the HDMI 1.4 specification with the new 2.0 bits documented +in the HDMI 2.0 specification. But for most of the features the freely available +HDMI 1.3a specification is sufficient: + +http://www.microprocessor.org/HDMISpecification13a.pdf + + +CEC Adapter Interface +--------------------- + +The struct cec_adapter represents the CEC adapter hardware. It is created by +calling cec_allocate_adapter() and deleted by calling cec_delete_adapter(): + +.. c:function:: + struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, void *priv, + const char *name, u32 caps, u8 available_las); + +.. c:function:: + void cec_delete_adapter(struct cec_adapter *adap); + +To create an adapter you need to pass the following information: + +ops: + adapter operations which are called by the CEC framework and that you + have to implement. + +priv: + will be stored in adap->priv and can be used by the adapter ops. + Use cec_get_drvdata(adap) to get the priv pointer. + +name: + the name of the CEC adapter. Note: this name will be copied. + +caps: + capabilities of the CEC adapter. These capabilities determine the + capabilities of the hardware and which parts are to be handled + by userspace and which parts are handled by kernelspace. The + capabilities are returned by CEC_ADAP_G_CAPS. + +available_las: + the number of simultaneous logical addresses that this + adapter can handle. Must be 1 <= available_las <= CEC_MAX_LOG_ADDRS. + +To obtain the priv pointer use this helper function: + +.. c:function:: + void *cec_get_drvdata(const struct cec_adapter *adap); + +To register the /dev/cecX device node and the remote control device (if +CEC_CAP_RC is set) you call: + +.. c:function:: + int cec_register_adapter(struct cec_adapter *adap, struct device *parent); + +where parent is the parent device. + +To unregister the devices call: + +.. c:function:: + void cec_unregister_adapter(struct cec_adapter *adap); + +Note: if cec_register_adapter() fails, then call cec_delete_adapter() to +clean up. But if cec_register_adapter() succeeded, then only call +cec_unregister_adapter() to clean up, never cec_delete_adapter(). The +unregister function will delete the adapter automatically once the last user +of that /dev/cecX device has closed its file handle. + + +Implementing the Low-Level CEC Adapter +-------------------------------------- + +The following low-level adapter operations have to be implemented in +your driver: + +.. c:type:: struct cec_adap_ops + +.. code-block:: none + + struct cec_adap_ops + { + /* Low-level callbacks */ + int (*adap_enable)(struct cec_adapter *adap, bool enable); + int (*adap_monitor_all_enable)(struct cec_adapter *adap, bool enable); + int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr); + int (*adap_transmit)(struct cec_adapter *adap, u8 attempts, + u32 signal_free_time, struct cec_msg *msg); + void (*adap_status)(struct cec_adapter *adap, struct seq_file *file); + void (*adap_free)(struct cec_adapter *adap); + + /* High-level callbacks */ + ... + }; + +The five low-level ops deal with various aspects of controlling the CEC adapter +hardware: + + +To enable/disable the hardware: + +.. c:function:: + int (*adap_enable)(struct cec_adapter *adap, bool enable); + +This callback enables or disables the CEC hardware. Enabling the CEC hardware +means powering it up in a state where no logical addresses are claimed. This +op assumes that the physical address (adap->phys_addr) is valid when enable is +true and will not change while the CEC adapter remains enabled. The initial +state of the CEC adapter after calling cec_allocate_adapter() is disabled. + +Note that adap_enable must return 0 if enable is false. + + +To enable/disable the 'monitor all' mode: + +.. c:function:: + int (*adap_monitor_all_enable)(struct cec_adapter *adap, bool enable); + +If enabled, then the adapter should be put in a mode to also monitor messages +that not for us. Not all hardware supports this and this function is only +called if the CEC_CAP_MONITOR_ALL capability is set. This callback is optional +(some hardware may always be in 'monitor all' mode). + +Note that adap_monitor_all_enable must return 0 if enable is false. + + +To program a new logical address: + +.. c:function:: + int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr); + +If logical_addr == CEC_LOG_ADDR_INVALID then all programmed logical addresses +are to be erased. Otherwise the given logical address should be programmed. +If the maximum number of available logical addresses is exceeded, then it +should return -ENXIO. Once a logical address is programmed the CEC hardware +can receive directed messages to that address. + +Note that adap_log_addr must return 0 if logical_addr is CEC_LOG_ADDR_INVALID. + + +To transmit a new message: + +.. c:function:: + int (*adap_transmit)(struct cec_adapter *adap, u8 attempts, + u32 signal_free_time, struct cec_msg *msg); + +This transmits a new message. The attempts argument is the suggested number of +attempts for the transmit. + +The signal_free_time is the number of data bit periods that the adapter should +wait when the line is free before attempting to send a message. This value +depends on whether this transmit is a retry, a message from a new initiator or +a new message for the same initiator. Most hardware will handle this +automatically, but in some cases this information is needed. + +The CEC_FREE_TIME_TO_USEC macro can be used to convert signal_free_time to +microseconds (one data bit period is 2.4 ms). + + +To log the current CEC hardware status: + +.. c:function:: + void (*adap_status)(struct cec_adapter *adap, struct seq_file *file); + +This optional callback can be used to show the status of the CEC hardware. +The status is available through debugfs: cat /sys/kernel/debug/cec/cecX/status + +To free any resources when the adapter is deleted: + +.. c:function:: + void (*adap_free)(struct cec_adapter *adap); + +This optional callback can be used to free any resources that might have been +allocated by the driver. It's called from cec_delete_adapter. + + +Your adapter driver will also have to react to events (typically interrupt +driven) by calling into the framework in the following situations: + +When a transmit finished (successfully or otherwise): + +.. c:function:: + void cec_transmit_done(struct cec_adapter *adap, u8 status, u8 arb_lost_cnt, + u8 nack_cnt, u8 low_drive_cnt, u8 error_cnt); + +or: + +.. c:function:: + void cec_transmit_attempt_done(struct cec_adapter *adap, u8 status); + +The status can be one of: + +CEC_TX_STATUS_OK: + the transmit was successful. + +CEC_TX_STATUS_ARB_LOST: + arbitration was lost: another CEC initiator + took control of the CEC line and you lost the arbitration. + +CEC_TX_STATUS_NACK: + the message was nacked (for a directed message) or + acked (for a broadcast message). A retransmission is needed. + +CEC_TX_STATUS_LOW_DRIVE: + low drive was detected on the CEC bus. This indicates that + a follower detected an error on the bus and requested a + retransmission. + +CEC_TX_STATUS_ERROR: + some unspecified error occurred: this can be one of ARB_LOST + or LOW_DRIVE if the hardware cannot differentiate or something + else entirely. + +CEC_TX_STATUS_MAX_RETRIES: + could not transmit the message after trying multiple times. + Should only be set by the driver if it has hardware support for + retrying messages. If set, then the framework assumes that it + doesn't have to make another attempt to transmit the message + since the hardware did that already. + +The hardware must be able to differentiate between OK, NACK and 'something +else'. + +The \*_cnt arguments are the number of error conditions that were seen. +This may be 0 if no information is available. Drivers that do not support +hardware retry can just set the counter corresponding to the transmit error +to 1, if the hardware does support retry then either set these counters to +0 if the hardware provides no feedback of which errors occurred and how many +times, or fill in the correct values as reported by the hardware. + +The cec_transmit_attempt_done() function is a helper for cases where the +hardware never retries, so the transmit is always for just a single +attempt. It will call cec_transmit_done() in turn, filling in 1 for the +count argument corresponding to the status. Or all 0 if the status was OK. + +When a CEC message was received: + +.. c:function:: + void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg); + +Speaks for itself. + +Implementing the interrupt handler +---------------------------------- + +Typically the CEC hardware provides interrupts that signal when a transmit +finished and whether it was successful or not, and it provides and interrupt +when a CEC message was received. + +The CEC driver should always process the transmit interrupts first before +handling the receive interrupt. The framework expects to see the cec_transmit_done +call before the cec_received_msg call, otherwise it can get confused if the +received message was in reply to the transmitted message. + +Implementing the High-Level CEC Adapter +--------------------------------------- + +The low-level operations drive the hardware, the high-level operations are +CEC protocol driven. The following high-level callbacks are available: + +.. code-block:: none + + struct cec_adap_ops { + /* Low-level callbacks */ + ... + + /* High-level CEC message callback */ + int (*received)(struct cec_adapter *adap, struct cec_msg *msg); + }; + +The received() callback allows the driver to optionally handle a newly +received CEC message + +.. c:function:: + int (*received)(struct cec_adapter *adap, struct cec_msg *msg); + +If the driver wants to process a CEC message, then it can implement this +callback. If it doesn't want to handle this message, then it should return +-ENOMSG, otherwise the CEC framework assumes it processed this message and +it will not do anything with it. + + +CEC framework functions +----------------------- + +CEC Adapter drivers can call the following CEC framework functions: + +.. c:function:: + int cec_transmit_msg(struct cec_adapter *adap, struct cec_msg *msg, + bool block); + +Transmit a CEC message. If block is true, then wait until the message has been +transmitted, otherwise just queue it and return. + +.. c:function:: + void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, + bool block); + +Change the physical address. This function will set adap->phys_addr and +send an event if it has changed. If cec_s_log_addrs() has been called and +the physical address has become valid, then the CEC framework will start +claiming the logical addresses. If block is true, then this function won't +return until this process has finished. + +When the physical address is set to a valid value the CEC adapter will +be enabled (see the adap_enable op). When it is set to CEC_PHYS_ADDR_INVALID, +then the CEC adapter will be disabled. If you change a valid physical address +to another valid physical address, then this function will first set the +address to CEC_PHYS_ADDR_INVALID before enabling the new physical address. + +.. c:function:: + void cec_s_phys_addr_from_edid(struct cec_adapter *adap, + const struct edid *edid); + +A helper function that extracts the physical address from the edid struct +and calls cec_s_phys_addr() with that address, or CEC_PHYS_ADDR_INVALID +if the EDID did not contain a physical address or edid was a NULL pointer. + +.. c:function:: + int cec_s_log_addrs(struct cec_adapter *adap, + struct cec_log_addrs *log_addrs, bool block); + +Claim the CEC logical addresses. Should never be called if CEC_CAP_LOG_ADDRS +is set. If block is true, then wait until the logical addresses have been +claimed, otherwise just queue it and return. To unconfigure all logical +addresses call this function with log_addrs set to NULL or with +log_addrs->num_log_addrs set to 0. The block argument is ignored when +unconfiguring. This function will just return if the physical address is +invalid. Once the physical address becomes valid, then the framework will +attempt to claim these logical addresses. + +CEC Pin framework +----------------- + +Most CEC hardware operates on full CEC messages where the software provides +the message and the hardware handles the low-level CEC protocol. But some +hardware only drives the CEC pin and software has to handle the low-level +CEC protocol. The CEC pin framework was created to handle such devices. + +Note that due to the close-to-realtime requirements it can never be guaranteed +to work 100%. This framework uses highres timers internally, but if a +timer goes off too late by more than 300 microseconds wrong results can +occur. In reality it appears to be fairly reliable. + +One advantage of this low-level implementation is that it can be used as +a cheap CEC analyser, especially if interrupts can be used to detect +CEC pin transitions from low to high or vice versa. + +.. kernel-doc:: include/media/cec-pin.h + +CEC Notifier framework +---------------------- + +Most drm HDMI implementations have an integrated CEC implementation and no +notifier support is needed. But some have independent CEC implementations +that have their own driver. This could be an IP block for an SoC or a +completely separate chip that deals with the CEC pin. For those cases a +drm driver can install a notifier and use the notifier to inform the +CEC driver about changes in the physical address. + +.. kernel-doc:: include/media/cec-notifier.h diff --git a/Documentation/media/uapi/cec/cec-api.rst b/Documentation/media/uapi/cec/cec-api.rst new file mode 100644 index 00000000..b68ca9c1 --- /dev/null +++ b/Documentation/media/uapi/cec/cec-api.rst @@ -0,0 +1,46 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. include:: + +.. _cec: + +######################################### +Part V - Consumer Electronics Control API +######################################### + +This part describes the CEC: Consumer Electronics Control + + +.. only:: html + + .. class:: toc-title + + Table of Contents + +.. toctree:: + :maxdepth: 5 + :numbered: + + cec-intro + cec-funcs + cec-header + + +********************** +Revision and Copyright +********************** +Authors: + +- Verkuil, Hans + + - Initial version. + +**Copyright** |copy| 2016 : Hans Verkuil + +**************** +Revision History +**************** + +:revision: 1.0.0 / 2016-03-17 (*hv*) + +Initial revision diff --git a/Documentation/media/uapi/cec/cec-func-close.rst b/Documentation/media/uapi/cec/cec-func-close.rst new file mode 100644 index 00000000..334358df --- /dev/null +++ b/Documentation/media/uapi/cec/cec-func-close.rst @@ -0,0 +1,47 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _cec-func-close: + +*********** +cec close() +*********** + +Name +==== + +cec-close - Close a cec device + + +Synopsis +======== + +.. code-block:: c + + #include + + +.. c:function:: int close( int fd ) + :name: cec-close + +Arguments +========= + +``fd`` + File descriptor returned by :c:func:`open() `. + + +Description +=========== + +Closes the cec device. Resources associated with the file descriptor are +freed. The device configuration remain unchanged. + + +Return Value +============ + +:c:func:`close() ` returns 0 on success. On error, -1 is returned, and +``errno`` is set appropriately. Possible error codes are: + +``EBADF`` + ``fd`` is not a valid open file descriptor. diff --git a/Documentation/media/uapi/cec/cec-func-ioctl.rst b/Documentation/media/uapi/cec/cec-func-ioctl.rst new file mode 100644 index 00000000..e2b6260b --- /dev/null +++ b/Documentation/media/uapi/cec/cec-func-ioctl.rst @@ -0,0 +1,66 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _cec-func-ioctl: + +*********** +cec ioctl() +*********** + +Name +==== + +cec-ioctl - Control a cec device + +Synopsis +======== + +.. code-block:: c + + #include + + +.. c:function:: int ioctl( int fd, int request, void *argp ) + :name: cec-ioctl + +Arguments +========= + +``fd`` + File descriptor returned by :c:func:`open() `. + +``request`` + CEC ioctl request code as defined in the cec.h header file, for + example :ref:`CEC_ADAP_G_CAPS `. + +``argp`` + Pointer to a request-specific structure. + + +Description +=========== + +The :c:func:`ioctl() ` function manipulates cec device parameters. The +argument ``fd`` must be an open file descriptor. + +The ioctl ``request`` code specifies the cec function to be called. It +has encoded in it whether the argument is an input, output or read/write +parameter, and the size of the argument ``argp`` in bytes. + +Macros and structures definitions specifying cec ioctl requests and +their parameters are located in the cec.h header file. All cec ioctl +requests, their respective function and parameters are specified in +:ref:`cec-user-func`. + + +Return Value +============ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. + +Request-specific error codes are listed in the individual requests +descriptions. + +When an ioctl that takes an output or read/write parameter fails, the +parameter remains unmodified. diff --git a/Documentation/media/uapi/cec/cec-func-open.rst b/Documentation/media/uapi/cec/cec-func-open.rst new file mode 100644 index 00000000..5d6663a6 --- /dev/null +++ b/Documentation/media/uapi/cec/cec-func-open.rst @@ -0,0 +1,78 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _cec-func-open: + +********** +cec open() +********** + +Name +==== + +cec-open - Open a cec device + +Synopsis +======== + +.. code-block:: c + + #include + + +.. c:function:: int open( const char *device_name, int flags ) + :name: cec-open + + +Arguments +========= + +``device_name`` + Device to be opened. + +``flags`` + Open flags. Access mode must be ``O_RDWR``. + + When the ``O_NONBLOCK`` flag is given, the + :ref:`CEC_RECEIVE ` and :ref:`CEC_DQEVENT ` ioctls + will return the ``EAGAIN`` error code when no message or event is available, and + ioctls :ref:`CEC_TRANSMIT `, + :ref:`CEC_ADAP_S_PHYS_ADDR ` and + :ref:`CEC_ADAP_S_LOG_ADDRS ` + all return 0. + + Other flags have no effect. + + +Description +=========== + +To open a cec device applications call :c:func:`open() ` with the +desired device name. The function has no side effects; the device +configuration remain unchanged. + +When the device is opened in read-only mode, attempts to modify its +configuration will result in an error, and ``errno`` will be set to +EBADF. + + +Return Value +============ + +:c:func:`open() ` returns the new file descriptor on success. On error, +-1 is returned, and ``errno`` is set appropriately. Possible error codes +include: + +``EACCES`` + The requested access to the file is not allowed. + +``EMFILE`` + The process already has the maximum number of files open. + +``ENFILE`` + The system limit on the total number of open files has been reached. + +``ENOMEM`` + Insufficient kernel memory was available. + +``ENXIO`` + No device corresponding to this device special file exists. diff --git a/Documentation/media/uapi/cec/cec-func-poll.rst b/Documentation/media/uapi/cec/cec-func-poll.rst new file mode 100644 index 00000000..d49f1ee0 --- /dev/null +++ b/Documentation/media/uapi/cec/cec-func-poll.rst @@ -0,0 +1,77 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _cec-func-poll: + +********** +cec poll() +********** + +Name +==== + +cec-poll - Wait for some event on a file descriptor + + +Synopsis +======== + +.. code-block:: c + + #include + + +.. c:function:: int poll( struct pollfd *ufds, unsigned int nfds, int timeout ) + :name: cec-poll + +Arguments +========= + +``ufds`` + List of FD events to be watched + +``nfds`` + Number of FD events at the \*ufds array + +``timeout`` + Timeout to wait for events + + +Description +=========== + +With the :c:func:`poll() ` function applications can wait for CEC +events. + +On success :c:func:`poll() ` returns the number of file descriptors +that have been selected (that is, file descriptors for which the +``revents`` field of the respective struct :c:type:`pollfd` +is non-zero). CEC devices set the ``POLLIN`` and ``POLLRDNORM`` flags in +the ``revents`` field if there are messages in the receive queue. If the +transmit queue has room for new messages, the ``POLLOUT`` and +``POLLWRNORM`` flags are set. If there are events in the event queue, +then the ``POLLPRI`` flag is set. When the function times out it returns +a value of zero, on failure it returns -1 and the ``errno`` variable is +set appropriately. + +For more details see the :c:func:`poll() ` manual page. + + +Return Value +============ + +On success, :c:func:`poll() ` returns the number structures which have +non-zero ``revents`` fields, or zero if the call timed out. On error -1 +is returned, and the ``errno`` variable is set appropriately: + +``EBADF`` + One or more of the ``ufds`` members specify an invalid file + descriptor. + +``EFAULT`` + ``ufds`` references an inaccessible memory area. + +``EINTR`` + The call was interrupted by a signal. + +``EINVAL`` + The ``nfds`` argument is greater than ``OPEN_MAX``. diff --git a/Documentation/media/uapi/cec/cec-funcs.rst b/Documentation/media/uapi/cec/cec-funcs.rst new file mode 100644 index 00000000..6d696cea --- /dev/null +++ b/Documentation/media/uapi/cec/cec-funcs.rst @@ -0,0 +1,20 @@ +.. _cec-user-func: + +****************** +Function Reference +****************** + + +.. toctree:: + :maxdepth: 1 + + cec-func-open + cec-func-close + cec-func-ioctl + cec-func-poll + cec-ioc-adap-g-caps + cec-ioc-adap-g-log-addrs + cec-ioc-adap-g-phys-addr + cec-ioc-dqevent + cec-ioc-g-mode + cec-ioc-receive diff --git a/Documentation/media/uapi/cec/cec-header.rst b/Documentation/media/uapi/cec/cec-header.rst new file mode 100644 index 00000000..d5a9a282 --- /dev/null +++ b/Documentation/media/uapi/cec/cec-header.rst @@ -0,0 +1,10 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _cec_header: + +*************** +CEC Header File +*************** + +.. kernel-include:: $BUILDDIR/cec.h.rst + diff --git a/Documentation/media/uapi/cec/cec-intro.rst b/Documentation/media/uapi/cec/cec-intro.rst new file mode 100644 index 00000000..07ee2b8f --- /dev/null +++ b/Documentation/media/uapi/cec/cec-intro.rst @@ -0,0 +1,40 @@ +.. _cec-intro: + +Introduction +============ + +HDMI connectors provide a single pin for use by the Consumer Electronics +Control protocol. This protocol allows different devices connected by an +HDMI cable to communicate. The protocol for CEC version 1.4 is defined +in supplements 1 (CEC) and 2 (HEAC or HDMI Ethernet and Audio Return +Channel) of the HDMI 1.4a (:ref:`hdmi`) specification and the +extensions added to CEC version 2.0 are defined in chapter 11 of the +HDMI 2.0 (:ref:`hdmi2`) specification. + +The bitrate is very slow (effectively no more than 36 bytes per second) +and is based on the ancient AV.link protocol used in old SCART +connectors. The protocol closely resembles a crazy Rube Goldberg +contraption and is an unholy mix of low and high level messages. Some +messages, especially those part of the HEAC protocol layered on top of +CEC, need to be handled by the kernel, others can be handled either by +the kernel or by userspace. + +In addition, CEC can be implemented in HDMI receivers, transmitters and +in USB devices that have an HDMI input and an HDMI output and that +control just the CEC pin. + +Drivers that support CEC will create a CEC device node (/dev/cecX) to +give userspace access to the CEC adapter. The +:ref:`CEC_ADAP_G_CAPS` ioctl will tell userspace what it is allowed to do. + +In order to check the support and test it, it is suggested to download +the `v4l-utils `_ package. It +provides three tools to handle CEC: + +- cec-ctl: the Swiss army knife of CEC. Allows you to configure, transmit + and monitor CEC messages. + +- cec-compliance: does a CEC compliance test of a remote CEC device to + determine how compliant the CEC implementation is. + +- cec-follower: emulates a CEC follower. diff --git a/Documentation/media/uapi/cec/cec-ioc-adap-g-caps.rst b/Documentation/media/uapi/cec/cec-ioc-adap-g-caps.rst new file mode 100644 index 00000000..6c1f6efb --- /dev/null +++ b/Documentation/media/uapi/cec/cec-ioc-adap-g-caps.rst @@ -0,0 +1,139 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _CEC_ADAP_G_CAPS: + +********************* +ioctl CEC_ADAP_G_CAPS +********************* + +Name +==== + +CEC_ADAP_G_CAPS - Query device capabilities + +Synopsis +======== + +.. c:function:: int ioctl( int fd, CEC_ADAP_G_CAPS, struct cec_caps *argp ) + :name: CEC_ADAP_G_CAPS + +Arguments +========= + +``fd`` + File descriptor returned by :c:func:`open() `. + +``argp`` + + +Description +=========== + +All cec devices must support :ref:`ioctl CEC_ADAP_G_CAPS `. To query +device information, applications call the ioctl with a pointer to a +struct :c:type:`cec_caps`. The driver fills the structure and +returns the information to the application. The ioctl never fails. + +.. tabularcolumns:: |p{1.2cm}|p{2.5cm}|p{13.8cm}| + +.. c:type:: cec_caps + +.. flat-table:: struct cec_caps + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 16 + + * - char + - ``driver[32]`` + - The name of the cec adapter driver. + * - char + - ``name[32]`` + - The name of this CEC adapter. The combination ``driver`` and + ``name`` must be unique. + * - __u32 + - ``capabilities`` + - The capabilities of the CEC adapter, see + :ref:`cec-capabilities`. + * - __u32 + - ``version`` + - CEC Framework API version, formatted with the ``KERNEL_VERSION()`` + macro. + + +.. tabularcolumns:: |p{4.4cm}|p{2.5cm}|p{10.6cm}| + +.. _cec-capabilities: + +.. flat-table:: CEC Capabilities Flags + :header-rows: 0 + :stub-columns: 0 + :widths: 3 1 8 + + * .. _`CEC-CAP-PHYS-ADDR`: + + - ``CEC_CAP_PHYS_ADDR`` + - 0x00000001 + - Userspace has to configure the physical address by calling + :ref:`ioctl CEC_ADAP_S_PHYS_ADDR `. If + this capability isn't set, then setting the physical address is + handled by the kernel whenever the EDID is set (for an HDMI + receiver) or read (for an HDMI transmitter). + * .. _`CEC-CAP-LOG-ADDRS`: + + - ``CEC_CAP_LOG_ADDRS`` + - 0x00000002 + - Userspace has to configure the logical addresses by calling + :ref:`ioctl CEC_ADAP_S_LOG_ADDRS `. If + this capability isn't set, then the kernel will have configured + this. + * .. _`CEC-CAP-TRANSMIT`: + + - ``CEC_CAP_TRANSMIT`` + - 0x00000004 + - Userspace can transmit CEC messages by calling + :ref:`ioctl CEC_TRANSMIT `. This implies that + userspace can be a follower as well, since being able to transmit + messages is a prerequisite of becoming a follower. If this + capability isn't set, then the kernel will handle all CEC + transmits and process all CEC messages it receives. + * .. _`CEC-CAP-PASSTHROUGH`: + + - ``CEC_CAP_PASSTHROUGH`` + - 0x00000008 + - Userspace can use the passthrough mode by calling + :ref:`ioctl CEC_S_MODE `. + * .. _`CEC-CAP-RC`: + + - ``CEC_CAP_RC`` + - 0x00000010 + - This adapter supports the remote control protocol. + * .. _`CEC-CAP-MONITOR-ALL`: + + - ``CEC_CAP_MONITOR_ALL`` + - 0x00000020 + - The CEC hardware can monitor all messages, not just directed and + broadcast messages. + * .. _`CEC-CAP-NEEDS-HPD`: + + - ``CEC_CAP_NEEDS_HPD`` + - 0x00000040 + - The CEC hardware is only active if the HDMI Hotplug Detect pin is + high. This makes it impossible to use CEC to wake up displays that + set the HPD pin low when in standby mode, but keep the CEC bus + alive. + * .. _`CEC-CAP-MONITOR-PIN`: + + - ``CEC_CAP_MONITOR_PIN`` + - 0x00000080 + - The CEC hardware can monitor CEC pin changes from low to high voltage + and vice versa. When in pin monitoring mode the application will + receive ``CEC_EVENT_PIN_CEC_LOW`` and ``CEC_EVENT_PIN_CEC_HIGH`` events. + + + +Return Value +============ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. diff --git a/Documentation/media/uapi/cec/cec-ioc-adap-g-log-addrs.rst b/Documentation/media/uapi/cec/cec-ioc-adap-g-log-addrs.rst new file mode 100644 index 00000000..84f431a0 --- /dev/null +++ b/Documentation/media/uapi/cec/cec-ioc-adap-g-log-addrs.rst @@ -0,0 +1,371 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _CEC_ADAP_LOG_ADDRS: +.. _CEC_ADAP_G_LOG_ADDRS: +.. _CEC_ADAP_S_LOG_ADDRS: + +**************************************************** +ioctls CEC_ADAP_G_LOG_ADDRS and CEC_ADAP_S_LOG_ADDRS +**************************************************** + +Name +==== + +CEC_ADAP_G_LOG_ADDRS, CEC_ADAP_S_LOG_ADDRS - Get or set the logical addresses + + +Synopsis +======== + +.. c:function:: int ioctl( int fd, CEC_ADAP_G_LOG_ADDRS, struct cec_log_addrs *argp ) + :name: CEC_ADAP_G_LOG_ADDRS + +.. c:function:: int ioctl( int fd, CEC_ADAP_S_LOG_ADDRS, struct cec_log_addrs *argp ) + :name: CEC_ADAP_S_LOG_ADDRS + +Arguments +========= + +``fd`` + File descriptor returned by :c:func:`open() `. + +``argp`` + Pointer to struct :c:type:`cec_log_addrs`. + +Description +=========== + +To query the current CEC logical addresses, applications call +:ref:`ioctl CEC_ADAP_G_LOG_ADDRS ` with a pointer to a +struct :c:type:`cec_log_addrs` where the driver stores the logical addresses. + +To set new logical addresses, applications fill in +struct :c:type:`cec_log_addrs` and call :ref:`ioctl CEC_ADAP_S_LOG_ADDRS ` +with a pointer to this struct. The :ref:`ioctl CEC_ADAP_S_LOG_ADDRS ` +is only available if ``CEC_CAP_LOG_ADDRS`` is set (the ``ENOTTY`` error code is +returned otherwise). The :ref:`ioctl CEC_ADAP_S_LOG_ADDRS ` +can only be called by a file descriptor in initiator mode (see :ref:`CEC_S_MODE`), if not +the ``EBUSY`` error code will be returned. + +To clear existing logical addresses set ``num_log_addrs`` to 0. All other fields +will be ignored in that case. The adapter will go to the unconfigured state and the +``cec_version``, ``vendor_id`` and ``osd_name`` fields are all reset to their default +values (CEC version 2.0, no vendor ID and an empty OSD name). + +If the physical address is valid (see :ref:`ioctl CEC_ADAP_S_PHYS_ADDR `), +then this ioctl will block until all requested logical +addresses have been claimed. If the file descriptor is in non-blocking mode then it will +not wait for the logical addresses to be claimed, instead it just returns 0. + +A :ref:`CEC_EVENT_STATE_CHANGE ` event is sent when the +logical addresses are claimed or cleared. + +Attempting to call :ref:`ioctl CEC_ADAP_S_LOG_ADDRS ` when +logical address types are already defined will return with error ``EBUSY``. + +.. c:type:: cec_log_addrs + +.. tabularcolumns:: |p{1.0cm}|p{8.0cm}|p{7.5cm}| + +.. cssclass:: longtable + +.. flat-table:: struct cec_log_addrs + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 16 + + * - __u8 + - ``log_addr[CEC_MAX_LOG_ADDRS]`` + - The actual logical addresses that were claimed. This is set by the + driver. If no logical address could be claimed, then it is set to + ``CEC_LOG_ADDR_INVALID``. If this adapter is Unregistered, then + ``log_addr[0]`` is set to 0xf and all others to + ``CEC_LOG_ADDR_INVALID``. + * - __u16 + - ``log_addr_mask`` + - The bitmask of all logical addresses this adapter has claimed. If + this adapter is Unregistered then ``log_addr_mask`` sets bit 15 + and clears all other bits. If this adapter is not configured at + all, then ``log_addr_mask`` is set to 0. Set by the driver. + * - __u8 + - ``cec_version`` + - The CEC version that this adapter shall use. See + :ref:`cec-versions`. Used to implement the + ``CEC_MSG_CEC_VERSION`` and ``CEC_MSG_REPORT_FEATURES`` messages. + Note that :ref:`CEC_OP_CEC_VERSION_1_3A ` is not allowed by the CEC + framework. + * - __u8 + - ``num_log_addrs`` + - Number of logical addresses to set up. Must be ≤ + ``available_log_addrs`` as returned by + :ref:`CEC_ADAP_G_CAPS`. All arrays in + this structure are only filled up to index + ``available_log_addrs``-1. The remaining array elements will be + ignored. Note that the CEC 2.0 standard allows for a maximum of 2 + logical addresses, although some hardware has support for more. + ``CEC_MAX_LOG_ADDRS`` is 4. The driver will return the actual + number of logical addresses it could claim, which may be less than + what was requested. If this field is set to 0, then the CEC + adapter shall clear all claimed logical addresses and all other + fields will be ignored. + * - __u32 + - ``vendor_id`` + - The vendor ID is a 24-bit number that identifies the specific + vendor or entity. Based on this ID vendor specific commands may be + defined. If you do not want a vendor ID then set it to + ``CEC_VENDOR_ID_NONE``. + * - __u32 + - ``flags`` + - Flags. See :ref:`cec-log-addrs-flags` for a list of available flags. + * - char + - ``osd_name[15]`` + - The On-Screen Display name as is returned by the + ``CEC_MSG_SET_OSD_NAME`` message. + * - __u8 + - ``primary_device_type[CEC_MAX_LOG_ADDRS]`` + - Primary device type for each logical address. See + :ref:`cec-prim-dev-types` for possible types. + * - __u8 + - ``log_addr_type[CEC_MAX_LOG_ADDRS]`` + - Logical address types. See :ref:`cec-log-addr-types` for + possible types. The driver will update this with the actual + logical address type that it claimed (e.g. it may have to fallback + to :ref:`CEC_LOG_ADDR_TYPE_UNREGISTERED `). + * - __u8 + - ``all_device_types[CEC_MAX_LOG_ADDRS]`` + - CEC 2.0 specific: the bit mask of all device types. See + :ref:`cec-all-dev-types-flags`. It is used in the CEC 2.0 + ``CEC_MSG_REPORT_FEATURES`` message. For CEC 1.4 you can either leave + this field to 0, or fill it in according to the CEC 2.0 guidelines to + give the CEC framework more information about the device type, even + though the framework won't use it directly in the CEC message. + * - __u8 + - ``features[CEC_MAX_LOG_ADDRS][12]`` + - Features for each logical address. It is used in the CEC 2.0 + ``CEC_MSG_REPORT_FEATURES`` message. The 12 bytes include both the + RC Profile and the Device Features. For CEC 1.4 you can either leave + this field to all 0, or fill it in according to the CEC 2.0 guidelines to + give the CEC framework more information about the device type, even + though the framework won't use it directly in the CEC message. + + +.. tabularcolumns:: |p{7.8cm}|p{1.0cm}|p{8.7cm}| + +.. _cec-log-addrs-flags: + +.. flat-table:: Flags for struct cec_log_addrs + :header-rows: 0 + :stub-columns: 0 + :widths: 3 1 4 + + * .. _`CEC-LOG-ADDRS-FL-ALLOW-UNREG-FALLBACK`: + + - ``CEC_LOG_ADDRS_FL_ALLOW_UNREG_FALLBACK`` + - 1 + - By default if no logical address of the requested type can be claimed, then + it will go back to the unconfigured state. If this flag is set, then it will + fallback to the Unregistered logical address. Note that if the Unregistered + logical address was explicitly requested, then this flag has no effect. + * .. _`CEC-LOG-ADDRS-FL-ALLOW-RC-PASSTHRU`: + + - ``CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU`` + - 2 + - By default the ``CEC_MSG_USER_CONTROL_PRESSED`` and ``CEC_MSG_USER_CONTROL_RELEASED`` + messages are only passed on to the follower(s), if any. If this flag is set, + then these messages are also passed on to the remote control input subsystem + and will appear as keystrokes. This features needs to be enabled explicitly. + If CEC is used to enter e.g. passwords, then you may not want to enable this + to avoid trivial snooping of the keystrokes. + * .. _`CEC-LOG-ADDRS-FL-CDC-ONLY`: + + - ``CEC_LOG_ADDRS_FL_CDC_ONLY`` + - 4 + - If this flag is set, then the device is CDC-Only. CDC-Only CEC devices + are CEC devices that can only handle CDC messages. + + All other messages are ignored. + + +.. tabularcolumns:: |p{7.8cm}|p{1.0cm}|p{8.7cm}| + +.. _cec-versions: + +.. flat-table:: CEC Versions + :header-rows: 0 + :stub-columns: 0 + :widths: 3 1 4 + + * .. _`CEC-OP-CEC-VERSION-1-3A`: + + - ``CEC_OP_CEC_VERSION_1_3A`` + - 4 + - CEC version according to the HDMI 1.3a standard. + * .. _`CEC-OP-CEC-VERSION-1-4B`: + + - ``CEC_OP_CEC_VERSION_1_4B`` + - 5 + - CEC version according to the HDMI 1.4b standard. + * .. _`CEC-OP-CEC-VERSION-2-0`: + + - ``CEC_OP_CEC_VERSION_2_0`` + - 6 + - CEC version according to the HDMI 2.0 standard. + + +.. tabularcolumns:: |p{6.6cm}|p{2.2cm}|p{8.7cm}| + +.. _cec-prim-dev-types: + +.. flat-table:: CEC Primary Device Types + :header-rows: 0 + :stub-columns: 0 + :widths: 3 1 4 + + * .. _`CEC-OP-PRIM-DEVTYPE-TV`: + + - ``CEC_OP_PRIM_DEVTYPE_TV`` + - 0 + - Use for a TV. + * .. _`CEC-OP-PRIM-DEVTYPE-RECORD`: + + - ``CEC_OP_PRIM_DEVTYPE_RECORD`` + - 1 + - Use for a recording device. + * .. _`CEC-OP-PRIM-DEVTYPE-TUNER`: + + - ``CEC_OP_PRIM_DEVTYPE_TUNER`` + - 3 + - Use for a device with a tuner. + * .. _`CEC-OP-PRIM-DEVTYPE-PLAYBACK`: + + - ``CEC_OP_PRIM_DEVTYPE_PLAYBACK`` + - 4 + - Use for a playback device. + * .. _`CEC-OP-PRIM-DEVTYPE-AUDIOSYSTEM`: + + - ``CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM`` + - 5 + - Use for an audio system (e.g. an audio/video receiver). + * .. _`CEC-OP-PRIM-DEVTYPE-SWITCH`: + + - ``CEC_OP_PRIM_DEVTYPE_SWITCH`` + - 6 + - Use for a CEC switch. + * .. _`CEC-OP-PRIM-DEVTYPE-VIDEOPROC`: + + - ``CEC_OP_PRIM_DEVTYPE_VIDEOPROC`` + - 7 + - Use for a video processor device. + + +.. tabularcolumns:: |p{6.6cm}|p{2.2cm}|p{8.7cm}| + +.. _cec-log-addr-types: + +.. flat-table:: CEC Logical Address Types + :header-rows: 0 + :stub-columns: 0 + :widths: 3 1 16 + + * .. _`CEC-LOG-ADDR-TYPE-TV`: + + - ``CEC_LOG_ADDR_TYPE_TV`` + - 0 + - Use for a TV. + * .. _`CEC-LOG-ADDR-TYPE-RECORD`: + + - ``CEC_LOG_ADDR_TYPE_RECORD`` + - 1 + - Use for a recording device. + * .. _`CEC-LOG-ADDR-TYPE-TUNER`: + + - ``CEC_LOG_ADDR_TYPE_TUNER`` + - 2 + - Use for a tuner device. + * .. _`CEC-LOG-ADDR-TYPE-PLAYBACK`: + + - ``CEC_LOG_ADDR_TYPE_PLAYBACK`` + - 3 + - Use for a playback device. + * .. _`CEC-LOG-ADDR-TYPE-AUDIOSYSTEM`: + + - ``CEC_LOG_ADDR_TYPE_AUDIOSYSTEM`` + - 4 + - Use for an audio system device. + * .. _`CEC-LOG-ADDR-TYPE-SPECIFIC`: + + - ``CEC_LOG_ADDR_TYPE_SPECIFIC`` + - 5 + - Use for a second TV or for a video processor device. + * .. _`CEC-LOG-ADDR-TYPE-UNREGISTERED`: + + - ``CEC_LOG_ADDR_TYPE_UNREGISTERED`` + - 6 + - Use this if you just want to remain unregistered. Used for pure + CEC switches or CDC-only devices (CDC: Capability Discovery and + Control). + + + +.. tabularcolumns:: |p{6.6cm}|p{2.2cm}|p{8.7cm}| + +.. _cec-all-dev-types-flags: + +.. flat-table:: CEC All Device Types Flags + :header-rows: 0 + :stub-columns: 0 + :widths: 3 1 4 + + * .. _`CEC-OP-ALL-DEVTYPE-TV`: + + - ``CEC_OP_ALL_DEVTYPE_TV`` + - 0x80 + - This supports the TV type. + * .. _`CEC-OP-ALL-DEVTYPE-RECORD`: + + - ``CEC_OP_ALL_DEVTYPE_RECORD`` + - 0x40 + - This supports the Recording type. + * .. _`CEC-OP-ALL-DEVTYPE-TUNER`: + + - ``CEC_OP_ALL_DEVTYPE_TUNER`` + - 0x20 + - This supports the Tuner type. + * .. _`CEC-OP-ALL-DEVTYPE-PLAYBACK`: + + - ``CEC_OP_ALL_DEVTYPE_PLAYBACK`` + - 0x10 + - This supports the Playback type. + * .. _`CEC-OP-ALL-DEVTYPE-AUDIOSYSTEM`: + + - ``CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM`` + - 0x08 + - This supports the Audio System type. + * .. _`CEC-OP-ALL-DEVTYPE-SWITCH`: + + - ``CEC_OP_ALL_DEVTYPE_SWITCH`` + - 0x04 + - This supports the CEC Switch or Video Processing type. + + + +Return Value +============ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. + +The :ref:`ioctl CEC_ADAP_S_LOG_ADDRS ` can return the following +error codes: + +ENOTTY + The ``CEC_CAP_LOG_ADDRS`` capability wasn't set, so this ioctl is not supported. + +EBUSY + The CEC adapter is currently configuring itself, or it is already configured and + ``num_log_addrs`` is non-zero, or another filehandle is in exclusive follower or + initiator mode, or the filehandle is in mode ``CEC_MODE_NO_INITIATOR``. + +EINVAL + The contents of struct :c:type:`cec_log_addrs` is invalid. diff --git a/Documentation/media/uapi/cec/cec-ioc-adap-g-phys-addr.rst b/Documentation/media/uapi/cec/cec-ioc-adap-g-phys-addr.rst new file mode 100644 index 00000000..9e49d4be --- /dev/null +++ b/Documentation/media/uapi/cec/cec-ioc-adap-g-phys-addr.rst @@ -0,0 +1,93 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _CEC_ADAP_PHYS_ADDR: +.. _CEC_ADAP_G_PHYS_ADDR: +.. _CEC_ADAP_S_PHYS_ADDR: + +**************************************************** +ioctls CEC_ADAP_G_PHYS_ADDR and CEC_ADAP_S_PHYS_ADDR +**************************************************** + +Name +==== + +CEC_ADAP_G_PHYS_ADDR, CEC_ADAP_S_PHYS_ADDR - Get or set the physical address + + +Synopsis +======== + +.. c:function:: int ioctl( int fd, CEC_ADAP_G_PHYS_ADDR, __u16 *argp ) + :name: CEC_ADAP_G_PHYS_ADDR + +.. c:function:: int ioctl( int fd, CEC_ADAP_S_PHYS_ADDR, __u16 *argp ) + :name: CEC_ADAP_S_PHYS_ADDR + +Arguments +========= + +``fd`` + File descriptor returned by :c:func:`open() `. + +``argp`` + Pointer to the CEC address. + +Description +=========== + +To query the current physical address applications call +:ref:`ioctl CEC_ADAP_G_PHYS_ADDR ` with a pointer to a __u16 where the +driver stores the physical address. + +To set a new physical address applications store the physical address in +a __u16 and call :ref:`ioctl CEC_ADAP_S_PHYS_ADDR ` with a pointer to +this integer. The :ref:`ioctl CEC_ADAP_S_PHYS_ADDR ` is only available if +``CEC_CAP_PHYS_ADDR`` is set (the ``ENOTTY`` error code will be returned +otherwise). The :ref:`ioctl CEC_ADAP_S_PHYS_ADDR ` can only be called +by a file descriptor in initiator mode (see :ref:`CEC_S_MODE`), if not +the ``EBUSY`` error code will be returned. + +To clear an existing physical address use ``CEC_PHYS_ADDR_INVALID``. +The adapter will go to the unconfigured state. + +If logical address types have been defined (see :ref:`ioctl CEC_ADAP_S_LOG_ADDRS `), +then this ioctl will block until all +requested logical addresses have been claimed. If the file descriptor is in non-blocking mode +then it will not wait for the logical addresses to be claimed, instead it just returns 0. + +A :ref:`CEC_EVENT_STATE_CHANGE ` event is sent when the physical address +changes. + +The physical address is a 16-bit number where each group of 4 bits +represent a digit of the physical address a.b.c.d where the most +significant 4 bits represent 'a'. The CEC root device (usually the TV) +has address 0.0.0.0. Every device that is hooked up to an input of the +TV has address a.0.0.0 (where 'a' is ≥ 1), devices hooked up to those in +turn have addresses a.b.0.0, etc. So a topology of up to 5 devices deep +is supported. The physical address a device shall use is stored in the +EDID of the sink. + +For example, the EDID for each HDMI input of the TV will have a +different physical address of the form a.0.0.0 that the sources will +read out and use as their physical address. + + +Return Value +============ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. + +The :ref:`ioctl CEC_ADAP_S_PHYS_ADDR ` can return the following +error codes: + +ENOTTY + The ``CEC_CAP_PHYS_ADDR`` capability wasn't set, so this ioctl is not supported. + +EBUSY + Another filehandle is in exclusive follower or initiator mode, or the filehandle + is in mode ``CEC_MODE_NO_INITIATOR``. + +EINVAL + The physical address is malformed. diff --git a/Documentation/media/uapi/cec/cec-ioc-dqevent.rst b/Documentation/media/uapi/cec/cec-ioc-dqevent.rst new file mode 100644 index 00000000..b6fd8642 --- /dev/null +++ b/Documentation/media/uapi/cec/cec-ioc-dqevent.rst @@ -0,0 +1,226 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _CEC_DQEVENT: + +***************** +ioctl CEC_DQEVENT +***************** + +Name +==== + +CEC_DQEVENT - Dequeue a CEC event + + +Synopsis +======== + +.. c:function:: int ioctl( int fd, CEC_DQEVENT, struct cec_event *argp ) + :name: CEC_DQEVENT + +Arguments +========= + +``fd`` + File descriptor returned by :c:func:`open() `. + +``argp`` + + +Description +=========== + +CEC devices can send asynchronous events. These can be retrieved by +calling :c:func:`CEC_DQEVENT`. If the file descriptor is in +non-blocking mode and no event is pending, then it will return -1 and +set errno to the ``EAGAIN`` error code. + +The internal event queues are per-filehandle and per-event type. If +there is no more room in a queue then the last event is overwritten with +the new one. This means that intermediate results can be thrown away but +that the latest event is always available. This also means that is it +possible to read two successive events that have the same value (e.g. +two :ref:`CEC_EVENT_STATE_CHANGE ` events with +the same state). In that case the intermediate state changes were lost but +it is guaranteed that the state did change in between the two events. + +.. tabularcolumns:: |p{1.2cm}|p{2.9cm}|p{13.4cm}| + +.. c:type:: cec_event_state_change + +.. flat-table:: struct cec_event_state_change + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 8 + + * - __u16 + - ``phys_addr`` + - The current physical address. This is ``CEC_PHYS_ADDR_INVALID`` if no + valid physical address is set. + * - __u16 + - ``log_addr_mask`` + - The current set of claimed logical addresses. This is 0 if no logical + addresses are claimed or if ``phys_addr`` is ``CEC_PHYS_ADDR_INVALID``. + If bit 15 is set (``1 << CEC_LOG_ADDR_UNREGISTERED``) then this device + has the unregistered logical address. In that case all other bits are 0. + + +.. c:type:: cec_event_lost_msgs + +.. tabularcolumns:: |p{1.0cm}|p{2.0cm}|p{14.5cm}| + +.. flat-table:: struct cec_event_lost_msgs + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 16 + + * - __u32 + - ``lost_msgs`` + - Set to the number of lost messages since the filehandle was opened + or since the last time this event was dequeued for this + filehandle. The messages lost are the oldest messages. So when a + new message arrives and there is no more room, then the oldest + message is discarded to make room for the new one. The internal + size of the message queue guarantees that all messages received in + the last two seconds will be stored. Since messages should be + replied to within a second according to the CEC specification, + this is more than enough. + + +.. tabularcolumns:: |p{1.0cm}|p{4.4cm}|p{2.5cm}|p{9.6cm}| + +.. c:type:: cec_event + +.. flat-table:: struct cec_event + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 1 8 + + * - __u64 + - ``ts`` + - :cspan:`1`\ Timestamp of the event in ns. + + The timestamp has been taken from the ``CLOCK_MONOTONIC`` clock. + + To access the same clock from userspace use :c:func:`clock_gettime`. + * - __u32 + - ``event`` + - :cspan:`1` The CEC event type, see :ref:`cec-events`. + * - __u32 + - ``flags`` + - :cspan:`1` Event flags, see :ref:`cec-event-flags`. + * - union + - (anonymous) + - + - + * - + - struct cec_event_state_change + - ``state_change`` + - The new adapter state as sent by the :ref:`CEC_EVENT_STATE_CHANGE ` + event. + * - + - struct cec_event_lost_msgs + - ``lost_msgs`` + - The number of lost messages as sent by the :ref:`CEC_EVENT_LOST_MSGS ` + event. + + +.. tabularcolumns:: |p{5.6cm}|p{0.9cm}|p{11.0cm}| + +.. _cec-events: + +.. flat-table:: CEC Events Types + :header-rows: 0 + :stub-columns: 0 + :widths: 3 1 16 + + * .. _`CEC-EVENT-STATE-CHANGE`: + + - ``CEC_EVENT_STATE_CHANGE`` + - 1 + - Generated when the CEC Adapter's state changes. When open() is + called an initial event will be generated for that filehandle with + the CEC Adapter's state at that time. + * .. _`CEC-EVENT-LOST-MSGS`: + + - ``CEC_EVENT_LOST_MSGS`` + - 2 + - Generated if one or more CEC messages were lost because the + application didn't dequeue CEC messages fast enough. + * .. _`CEC-EVENT-PIN-CEC-LOW`: + + - ``CEC_EVENT_PIN_CEC_LOW`` + - 3 + - Generated if the CEC pin goes from a high voltage to a low voltage. + Only applies to adapters that have the ``CEC_CAP_MONITOR_PIN`` + capability set. + * .. _`CEC-EVENT-PIN-CEC-HIGH`: + + - ``CEC_EVENT_PIN_CEC_HIGH`` + - 4 + - Generated if the CEC pin goes from a low voltage to a high voltage. + Only applies to adapters that have the ``CEC_CAP_MONITOR_PIN`` + capability set. + * .. _`CEC-EVENT-PIN-HPD-LOW`: + + - ``CEC_EVENT_PIN_HPD_LOW`` + - 5 + - Generated if the HPD pin goes from a high voltage to a low voltage. + Only applies to adapters that have the ``CEC_CAP_MONITOR_PIN`` + capability set. When open() is called, the HPD pin can be read and + if the HPD is low, then an initial event will be generated for that + filehandle. + * .. _`CEC-EVENT-PIN-HPD-HIGH`: + + - ``CEC_EVENT_PIN_HPD_HIGH`` + - 6 + - Generated if the HPD pin goes from a low voltage to a high voltage. + Only applies to adapters that have the ``CEC_CAP_MONITOR_PIN`` + capability set. When open() is called, the HPD pin can be read and + if the HPD is high, then an initial event will be generated for that + filehandle. + + +.. tabularcolumns:: |p{6.0cm}|p{0.6cm}|p{10.9cm}| + +.. _cec-event-flags: + +.. flat-table:: CEC Event Flags + :header-rows: 0 + :stub-columns: 0 + :widths: 3 1 8 + + * .. _`CEC-EVENT-FL-INITIAL-STATE`: + + - ``CEC_EVENT_FL_INITIAL_STATE`` + - 1 + - Set for the initial events that are generated when the device is + opened. See the table above for which events do this. This allows + applications to learn the initial state of the CEC adapter at + open() time. + * .. _`CEC-EVENT-FL-DROPPED-EVENTS`: + + - ``CEC_EVENT_FL_DROPPED_EVENTS`` + - 2 + - Set if one or more events of the given event type have been dropped. + This is an indication that the application cannot keep up. + + + +Return Value +============ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. + +The :ref:`ioctl CEC_DQEVENT ` can return the following +error codes: + +EAGAIN + This is returned when the filehandle is in non-blocking mode and there + are no pending events. + +ERESTARTSYS + An interrupt (e.g. Ctrl-C) arrived while in blocking mode waiting for + events to arrive. diff --git a/Documentation/media/uapi/cec/cec-ioc-g-mode.rst b/Documentation/media/uapi/cec/cec-ioc-g-mode.rst new file mode 100644 index 00000000..508e2e32 --- /dev/null +++ b/Documentation/media/uapi/cec/cec-ioc-g-mode.rst @@ -0,0 +1,293 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _CEC_MODE: +.. _CEC_G_MODE: +.. _CEC_S_MODE: + +******************************** +ioctls CEC_G_MODE and CEC_S_MODE +******************************** + +CEC_G_MODE, CEC_S_MODE - Get or set exclusive use of the CEC adapter + +Synopsis +======== + +.. c:function:: int ioctl( int fd, CEC_G_MODE, __u32 *argp ) + :name: CEC_G_MODE + +.. c:function:: int ioctl( int fd, CEC_S_MODE, __u32 *argp ) + :name: CEC_S_MODE + +Arguments +========= + +``fd`` + File descriptor returned by :c:func:`open() `. + +``argp`` + Pointer to CEC mode. + +Description +=========== + +By default any filehandle can use :ref:`CEC_TRANSMIT`, but in order to prevent +applications from stepping on each others toes it must be possible to +obtain exclusive access to the CEC adapter. This ioctl sets the +filehandle to initiator and/or follower mode which can be exclusive +depending on the chosen mode. The initiator is the filehandle that is +used to initiate messages, i.e. it commands other CEC devices. The +follower is the filehandle that receives messages sent to the CEC +adapter and processes them. The same filehandle can be both initiator +and follower, or this role can be taken by two different filehandles. + +When a CEC message is received, then the CEC framework will decide how +it will be processed. If the message is a reply to an earlier +transmitted message, then the reply is sent back to the filehandle that +is waiting for it. In addition the CEC framework will process it. + +If the message is not a reply, then the CEC framework will process it +first. If there is no follower, then the message is just discarded and a +feature abort is sent back to the initiator if the framework couldn't +process it. If there is a follower, then the message is passed on to the +follower who will use :ref:`ioctl CEC_RECEIVE ` to dequeue +the new message. The framework expects the follower to make the right +decisions. + +The CEC framework will process core messages unless requested otherwise +by the follower. The follower can enable the passthrough mode. In that +case, the CEC framework will pass on most core messages without +processing them and the follower will have to implement those messages. +There are some messages that the core will always process, regardless of +the passthrough mode. See :ref:`cec-core-processing` for details. + +If there is no initiator, then any CEC filehandle can use +:ref:`ioctl CEC_TRANSMIT `. If there is an exclusive +initiator then only that initiator can call +:ref:`CEC_TRANSMIT`. The follower can of course +always call :ref:`ioctl CEC_TRANSMIT `. + +Available initiator modes are: + +.. tabularcolumns:: |p{5.6cm}|p{0.9cm}|p{11.0cm}| + +.. _cec-mode-initiator_e: + +.. flat-table:: Initiator Modes + :header-rows: 0 + :stub-columns: 0 + :widths: 3 1 16 + + * .. _`CEC-MODE-NO-INITIATOR`: + + - ``CEC_MODE_NO_INITIATOR`` + - 0x0 + - This is not an initiator, i.e. it cannot transmit CEC messages or + make any other changes to the CEC adapter. + * .. _`CEC-MODE-INITIATOR`: + + - ``CEC_MODE_INITIATOR`` + - 0x1 + - This is an initiator (the default when the device is opened) and + it can transmit CEC messages and make changes to the CEC adapter, + unless there is an exclusive initiator. + * .. _`CEC-MODE-EXCL-INITIATOR`: + + - ``CEC_MODE_EXCL_INITIATOR`` + - 0x2 + - This is an exclusive initiator and this file descriptor is the + only one that can transmit CEC messages and make changes to the + CEC adapter. If someone else is already the exclusive initiator + then an attempt to become one will return the ``EBUSY`` error code + error. + + +Available follower modes are: + +.. tabularcolumns:: |p{6.6cm}|p{0.9cm}|p{10.0cm}| + +.. _cec-mode-follower_e: + +.. cssclass:: longtable + +.. flat-table:: Follower Modes + :header-rows: 0 + :stub-columns: 0 + :widths: 3 1 16 + + * .. _`CEC-MODE-NO-FOLLOWER`: + + - ``CEC_MODE_NO_FOLLOWER`` + - 0x00 + - This is not a follower (the default when the device is opened). + * .. _`CEC-MODE-FOLLOWER`: + + - ``CEC_MODE_FOLLOWER`` + - 0x10 + - This is a follower and it will receive CEC messages unless there + is an exclusive follower. You cannot become a follower if + :ref:`CEC_CAP_TRANSMIT ` is not set or if :ref:`CEC_MODE_NO_INITIATOR ` + was specified, the ``EINVAL`` error code is returned in that case. + * .. _`CEC-MODE-EXCL-FOLLOWER`: + + - ``CEC_MODE_EXCL_FOLLOWER`` + - 0x20 + - This is an exclusive follower and only this file descriptor will + receive CEC messages for processing. If someone else is already + the exclusive follower then an attempt to become one will return + the ``EBUSY`` error code. You cannot become a follower if + :ref:`CEC_CAP_TRANSMIT ` is not set or if :ref:`CEC_MODE_NO_INITIATOR ` + was specified, the ``EINVAL`` error code is returned in that case. + * .. _`CEC-MODE-EXCL-FOLLOWER-PASSTHRU`: + + - ``CEC_MODE_EXCL_FOLLOWER_PASSTHRU`` + - 0x30 + - This is an exclusive follower and only this file descriptor will + receive CEC messages for processing. In addition it will put the + CEC device into passthrough mode, allowing the exclusive follower + to handle most core messages instead of relying on the CEC + framework for that. If someone else is already the exclusive + follower then an attempt to become one will return the ``EBUSY`` error + code. You cannot become a follower if :ref:`CEC_CAP_TRANSMIT ` + is not set or if :ref:`CEC_MODE_NO_INITIATOR ` was specified, + the ``EINVAL`` error code is returned in that case. + * .. _`CEC-MODE-MONITOR-PIN`: + + - ``CEC_MODE_MONITOR_PIN`` + - 0xd0 + - Put the file descriptor into pin monitoring mode. Can only be used in + combination with :ref:`CEC_MODE_NO_INITIATOR `, + otherwise the ``EINVAL`` error code will be returned. + This mode requires that the :ref:`CEC_CAP_MONITOR_PIN ` + capability is set, otherwise the ``EINVAL`` error code is returned. + While in pin monitoring mode this file descriptor can receive the + ``CEC_EVENT_PIN_CEC_LOW`` and ``CEC_EVENT_PIN_CEC_HIGH`` events to see the + low-level CEC pin transitions. This is very useful for debugging. + This mode is only allowed if the process has the ``CAP_NET_ADMIN`` + capability. If that is not set, then the ``EPERM`` error code is returned. + * .. _`CEC-MODE-MONITOR`: + + - ``CEC_MODE_MONITOR`` + - 0xe0 + - Put the file descriptor into monitor mode. Can only be used in + combination with :ref:`CEC_MODE_NO_INITIATOR `,i + otherwise the ``EINVAL`` error code will be returned. + In monitor mode all messages this CEC + device transmits and all messages it receives (both broadcast + messages and directed messages for one its logical addresses) will + be reported. This is very useful for debugging. This is only + allowed if the process has the ``CAP_NET_ADMIN`` capability. If + that is not set, then the ``EPERM`` error code is returned. + * .. _`CEC-MODE-MONITOR-ALL`: + + - ``CEC_MODE_MONITOR_ALL`` + - 0xf0 + - Put the file descriptor into 'monitor all' mode. Can only be used + in combination with :ref:`CEC_MODE_NO_INITIATOR `, otherwise + the ``EINVAL`` error code will be returned. In 'monitor all' mode all messages + this CEC device transmits and all messages it receives, including + directed messages for other CEC devices will be reported. This is + very useful for debugging, but not all devices support this. This + mode requires that the :ref:`CEC_CAP_MONITOR_ALL ` capability is set, + otherwise the ``EINVAL`` error code is returned. This is only allowed if + the process has the ``CAP_NET_ADMIN`` capability. If that is not + set, then the ``EPERM`` error code is returned. + + +Core message processing details: + +.. tabularcolumns:: |p{6.6cm}|p{10.9cm}| + +.. _cec-core-processing: + +.. flat-table:: Core Message Processing + :header-rows: 0 + :stub-columns: 0 + :widths: 1 8 + + * .. _`CEC-MSG-GET-CEC-VERSION`: + + - ``CEC_MSG_GET_CEC_VERSION`` + - The core will return the CEC version that was set with + :ref:`ioctl CEC_ADAP_S_LOG_ADDRS `, + except when in passthrough mode. In passthrough mode the core + does nothing and this message has to be handled by a follower + instead. + * .. _`CEC-MSG-GIVE-DEVICE-VENDOR-ID`: + + - ``CEC_MSG_GIVE_DEVICE_VENDOR_ID`` + - The core will return the vendor ID that was set with + :ref:`ioctl CEC_ADAP_S_LOG_ADDRS `, + except when in passthrough mode. In passthrough mode the core + does nothing and this message has to be handled by a follower + instead. + * .. _`CEC-MSG-ABORT`: + + - ``CEC_MSG_ABORT`` + - The core will return a Feature Abort message with reason + 'Feature Refused' as per the specification, except when in + passthrough mode. In passthrough mode the core does nothing + and this message has to be handled by a follower instead. + * .. _`CEC-MSG-GIVE-PHYSICAL-ADDR`: + + - ``CEC_MSG_GIVE_PHYSICAL_ADDR`` + - The core will report the current physical address, except when + in passthrough mode. In passthrough mode the core does nothing + and this message has to be handled by a follower instead. + * .. _`CEC-MSG-GIVE-OSD-NAME`: + + - ``CEC_MSG_GIVE_OSD_NAME`` + - The core will report the current OSD name that was set with + :ref:`ioctl CEC_ADAP_S_LOG_ADDRS `, + except when in passthrough mode. In passthrough mode the core + does nothing and this message has to be handled by a follower + instead. + * .. _`CEC-MSG-GIVE-FEATURES`: + + - ``CEC_MSG_GIVE_FEATURES`` + - The core will do nothing if the CEC version is older than 2.0, + otherwise it will report the current features that were set with + :ref:`ioctl CEC_ADAP_S_LOG_ADDRS `, + except when in passthrough mode. In passthrough mode the core + does nothing (for any CEC version) and this message has to be handled + by a follower instead. + * .. _`CEC-MSG-USER-CONTROL-PRESSED`: + + - ``CEC_MSG_USER_CONTROL_PRESSED`` + - If :ref:`CEC_CAP_RC ` is set and if + :ref:`CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU ` + is set, then generate a remote control key + press. This message is always passed on to the follower(s). + * .. _`CEC-MSG-USER-CONTROL-RELEASED`: + + - ``CEC_MSG_USER_CONTROL_RELEASED`` + - If :ref:`CEC_CAP_RC ` is set and if + :ref:`CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU ` + is set, then generate a remote control key + release. This message is always passed on to the follower(s). + * .. _`CEC-MSG-REPORT-PHYSICAL-ADDR`: + + - ``CEC_MSG_REPORT_PHYSICAL_ADDR`` + - The CEC framework will make note of the reported physical address + and then just pass the message on to the follower(s). + + + +Return Value +============ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. + +The :ref:`ioctl CEC_S_MODE ` can return the following +error codes: + +EINVAL + The requested mode is invalid. + +EPERM + Monitor mode is requested without having root permissions + +EBUSY + Someone else is already an exclusive follower or initiator. diff --git a/Documentation/media/uapi/cec/cec-ioc-receive.rst b/Documentation/media/uapi/cec/cec-ioc-receive.rst new file mode 100644 index 00000000..bdad4b19 --- /dev/null +++ b/Documentation/media/uapi/cec/cec-ioc-receive.rst @@ -0,0 +1,344 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _CEC_TRANSMIT: +.. _CEC_RECEIVE: + +*********************************** +ioctls CEC_RECEIVE and CEC_TRANSMIT +*********************************** + +Name +==== + +CEC_RECEIVE, CEC_TRANSMIT - Receive or transmit a CEC message + + +Synopsis +======== + +.. c:function:: int ioctl( int fd, CEC_RECEIVE, struct cec_msg *argp ) + :name: CEC_RECEIVE + +.. c:function:: int ioctl( int fd, CEC_TRANSMIT, struct cec_msg *argp ) + :name: CEC_TRANSMIT + +Arguments +========= + +``fd`` + File descriptor returned by :c:func:`open() `. + +``argp`` + Pointer to struct cec_msg. + +Description +=========== + +To receive a CEC message the application has to fill in the +``timeout`` field of struct :c:type:`cec_msg` and pass it to +:ref:`ioctl CEC_RECEIVE `. +If the file descriptor is in non-blocking mode and there are no received +messages pending, then it will return -1 and set errno to the ``EAGAIN`` +error code. If the file descriptor is in blocking mode and ``timeout`` +is non-zero and no message arrived within ``timeout`` milliseconds, then +it will return -1 and set errno to the ``ETIMEDOUT`` error code. + +A received message can be: + +1. a message received from another CEC device (the ``sequence`` field will + be 0). +2. the result of an earlier non-blocking transmit (the ``sequence`` field will + be non-zero). + +To send a CEC message the application has to fill in the struct +:c:type:`cec_msg` and pass it to :ref:`ioctl CEC_TRANSMIT `. +The :ref:`ioctl CEC_TRANSMIT ` is only available if +``CEC_CAP_TRANSMIT`` is set. If there is no more room in the transmit +queue, then it will return -1 and set errno to the ``EBUSY`` error code. +The transmit queue has enough room for 18 messages (about 1 second worth +of 2-byte messages). Note that the CEC kernel framework will also reply +to core messages (see :ref:`cec-core-processing`), so it is not a good +idea to fully fill up the transmit queue. + +If the file descriptor is in non-blocking mode then the transmit will +return 0 and the result of the transmit will be available via +:ref:`ioctl CEC_RECEIVE ` once the transmit has finished +(including waiting for a reply, if requested). + +The ``sequence`` field is filled in for every transmit and this can be +checked against the received messages to find the corresponding transmit +result. + +Normally calling :ref:`ioctl CEC_TRANSMIT ` when the physical +address is invalid (due to e.g. a disconnect) will return ``ENONET``. + +However, the CEC specification allows sending messages from 'Unregistered' to +'TV' when the physical address is invalid since some TVs pull the hotplug detect +pin of the HDMI connector low when they go into standby, or when switching to +another input. + +When the hotplug detect pin goes low the EDID disappears, and thus the +physical address, but the cable is still connected and CEC still works. +In order to detect/wake up the device it is allowed to send poll and 'Image/Text +View On' messages from initiator 0xf ('Unregistered') to destination 0 ('TV'). + +.. tabularcolumns:: |p{1.0cm}|p{3.5cm}|p{13.0cm}| + +.. c:type:: cec_msg + +.. cssclass:: longtable + +.. flat-table:: struct cec_msg + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 16 + + * - __u64 + - ``tx_ts`` + - Timestamp in ns of when the last byte of the message was transmitted. + The timestamp has been taken from the ``CLOCK_MONOTONIC`` clock. To access + the same clock from userspace use :c:func:`clock_gettime`. + * - __u64 + - ``rx_ts`` + - Timestamp in ns of when the last byte of the message was received. + The timestamp has been taken from the ``CLOCK_MONOTONIC`` clock. To access + the same clock from userspace use :c:func:`clock_gettime`. + * - __u32 + - ``len`` + - The length of the message. For :ref:`ioctl CEC_TRANSMIT ` this is filled in + by the application. The driver will fill this in for + :ref:`ioctl CEC_RECEIVE `. For :ref:`ioctl CEC_TRANSMIT ` it will be + filled in by the driver with the length of the reply message if ``reply`` was set. + * - __u32 + - ``timeout`` + - The timeout in milliseconds. This is the time the device will wait + for a message to be received before timing out. If it is set to 0, + then it will wait indefinitely when it is called by :ref:`ioctl CEC_RECEIVE `. + If it is 0 and it is called by :ref:`ioctl CEC_TRANSMIT `, + then it will be replaced by 1000 if the ``reply`` is non-zero or + ignored if ``reply`` is 0. + * - __u32 + - ``sequence`` + - A non-zero sequence number is automatically assigned by the CEC framework + for all transmitted messages. It is used by the CEC framework when it queues + the transmit result (when transmit was called in non-blocking mode). This + allows the application to associate the received message with the original + transmit. + * - __u32 + - ``flags`` + - Flags. See :ref:`cec-msg-flags` for a list of available flags. + * - __u8 + - ``tx_status`` + - The status bits of the transmitted message. See + :ref:`cec-tx-status` for the possible status values. It is 0 if + this message was received, not transmitted. + * - __u8 + - ``msg[16]`` + - The message payload. For :ref:`ioctl CEC_TRANSMIT ` this is filled in by the + application. The driver will fill this in for :ref:`ioctl CEC_RECEIVE `. + For :ref:`ioctl CEC_TRANSMIT ` it will be filled in by the driver with + the payload of the reply message if ``timeout`` was set. + * - __u8 + - ``reply`` + - Wait until this message is replied. If ``reply`` is 0 and the + ``timeout`` is 0, then don't wait for a reply but return after + transmitting the message. Ignored by :ref:`ioctl CEC_RECEIVE `. + The case where ``reply`` is 0 (this is the opcode for the Feature Abort + message) and ``timeout`` is non-zero is specifically allowed to make it + possible to send a message and wait up to ``timeout`` milliseconds for a + Feature Abort reply. In this case ``rx_status`` will either be set + to :ref:`CEC_RX_STATUS_TIMEOUT ` or + :ref:`CEC_RX_STATUS_FEATURE_ABORT `. + + If the transmitter message is ``CEC_MSG_INITIATE_ARC`` then the ``reply`` + values ``CEC_MSG_REPORT_ARC_INITIATED`` and ``CEC_MSG_REPORT_ARC_TERMINATED`` + are processed differently: either value will match both possible replies. + The reason is that the ``CEC_MSG_INITIATE_ARC`` message is the only CEC + message that has two possible replies other than Feature Abort. The + ``reply`` field will be updated with the actual reply so that it is + synchronized with the contents of the received message. + * - __u8 + - ``rx_status`` + - The status bits of the received message. See + :ref:`cec-rx-status` for the possible status values. It is 0 if + this message was transmitted, not received, unless this is the + reply to a transmitted message. In that case both ``rx_status`` + and ``tx_status`` are set. + * - __u8 + - ``tx_status`` + - The status bits of the transmitted message. See + :ref:`cec-tx-status` for the possible status values. It is 0 if + this message was received, not transmitted. + * - __u8 + - ``tx_arb_lost_cnt`` + - A counter of the number of transmit attempts that resulted in the + Arbitration Lost error. This is only set if the hardware supports + this, otherwise it is always 0. This counter is only valid if the + :ref:`CEC_TX_STATUS_ARB_LOST ` status bit is set. + * - __u8 + - ``tx_nack_cnt`` + - A counter of the number of transmit attempts that resulted in the + Not Acknowledged error. This is only set if the hardware supports + this, otherwise it is always 0. This counter is only valid if the + :ref:`CEC_TX_STATUS_NACK ` status bit is set. + * - __u8 + - ``tx_low_drive_cnt`` + - A counter of the number of transmit attempts that resulted in the + Arbitration Lost error. This is only set if the hardware supports + this, otherwise it is always 0. This counter is only valid if the + :ref:`CEC_TX_STATUS_LOW_DRIVE ` status bit is set. + * - __u8 + - ``tx_error_cnt`` + - A counter of the number of transmit errors other than Arbitration + Lost or Not Acknowledged. This is only set if the hardware + supports this, otherwise it is always 0. This counter is only + valid if the :ref:`CEC_TX_STATUS_ERROR ` status bit is set. + + +.. tabularcolumns:: |p{6.2cm}|p{1.0cm}|p{10.3cm}| + +.. _cec-msg-flags: + +.. flat-table:: Flags for struct cec_msg + :header-rows: 0 + :stub-columns: 0 + :widths: 3 1 4 + + * .. _`CEC-MSG-FL-REPLY-TO-FOLLOWERS`: + + - ``CEC_MSG_FL_REPLY_TO_FOLLOWERS`` + - 1 + - If a CEC transmit expects a reply, then by default that reply is only sent to + the filehandle that called :ref:`ioctl CEC_TRANSMIT `. If this + flag is set, then the reply is also sent to all followers, if any. If the + filehandle that called :ref:`ioctl CEC_TRANSMIT ` is also a + follower, then that filehandle will receive the reply twice: once as the + result of the :ref:`ioctl CEC_TRANSMIT `, and once via + :ref:`ioctl CEC_RECEIVE `. + + +.. tabularcolumns:: |p{5.6cm}|p{0.9cm}|p{11.0cm}| + +.. _cec-tx-status: + +.. flat-table:: CEC Transmit Status + :header-rows: 0 + :stub-columns: 0 + :widths: 3 1 16 + + * .. _`CEC-TX-STATUS-OK`: + + - ``CEC_TX_STATUS_OK`` + - 0x01 + - The message was transmitted successfully. This is mutually + exclusive with :ref:`CEC_TX_STATUS_MAX_RETRIES `. Other bits can still + be set if earlier attempts met with failure before the transmit + was eventually successful. + * .. _`CEC-TX-STATUS-ARB-LOST`: + + - ``CEC_TX_STATUS_ARB_LOST`` + - 0x02 + - CEC line arbitration was lost. + * .. _`CEC-TX-STATUS-NACK`: + + - ``CEC_TX_STATUS_NACK`` + - 0x04 + - Message was not acknowledged. + * .. _`CEC-TX-STATUS-LOW-DRIVE`: + + - ``CEC_TX_STATUS_LOW_DRIVE`` + - 0x08 + - Low drive was detected on the CEC bus. This indicates that a + follower detected an error on the bus and requests a + retransmission. + * .. _`CEC-TX-STATUS-ERROR`: + + - ``CEC_TX_STATUS_ERROR`` + - 0x10 + - Some error occurred. This is used for any errors that do not fit + ``CEC_TX_STATUS_ARB_LOST`` or ``CEC_TX_STATUS_LOW_DRIVE``, either because + the hardware could not tell which error occurred, or because the hardware + tested for other conditions besides those two. + * .. _`CEC-TX-STATUS-MAX-RETRIES`: + + - ``CEC_TX_STATUS_MAX_RETRIES`` + - 0x20 + - The transmit failed after one or more retries. This status bit is + mutually exclusive with :ref:`CEC_TX_STATUS_OK `. Other bits can still + be set to explain which failures were seen. + + +.. tabularcolumns:: |p{5.6cm}|p{0.9cm}|p{11.0cm}| + +.. _cec-rx-status: + +.. flat-table:: CEC Receive Status + :header-rows: 0 + :stub-columns: 0 + :widths: 3 1 16 + + * .. _`CEC-RX-STATUS-OK`: + + - ``CEC_RX_STATUS_OK`` + - 0x01 + - The message was received successfully. + * .. _`CEC-RX-STATUS-TIMEOUT`: + + - ``CEC_RX_STATUS_TIMEOUT`` + - 0x02 + - The reply to an earlier transmitted message timed out. + * .. _`CEC-RX-STATUS-FEATURE-ABORT`: + + - ``CEC_RX_STATUS_FEATURE_ABORT`` + - 0x04 + - The message was received successfully but the reply was + ``CEC_MSG_FEATURE_ABORT``. This status is only set if this message + was the reply to an earlier transmitted message. + + + +Return Value +============ + +On success 0 is returned, on error -1 and the ``errno`` variable is set +appropriately. The generic error codes are described at the +:ref:`Generic Error Codes ` chapter. + +The :ref:`ioctl CEC_RECEIVE ` can return the following +error codes: + +EAGAIN + No messages are in the receive queue, and the filehandle is in non-blocking mode. + +ETIMEDOUT + The ``timeout`` was reached while waiting for a message. + +ERESTARTSYS + The wait for a message was interrupted (e.g. by Ctrl-C). + +The :ref:`ioctl CEC_TRANSMIT ` can return the following +error codes: + +ENOTTY + The ``CEC_CAP_TRANSMIT`` capability wasn't set, so this ioctl is not supported. + +EPERM + The CEC adapter is not configured, i.e. :ref:`ioctl CEC_ADAP_S_LOG_ADDRS ` + has never been called. + +ENONET + The CEC adapter is not configured, i.e. :ref:`ioctl CEC_ADAP_S_LOG_ADDRS ` + was called, but the physical address is invalid so no logical address was claimed. + An exception is made in this case for transmits from initiator 0xf ('Unregistered') + to destination 0 ('TV'). In that case the transmit will proceed as usual. + +EBUSY + Another filehandle is in exclusive follower or initiator mode, or the filehandle + is in mode ``CEC_MODE_NO_INITIATOR``. This is also returned if the transmit + queue is full. + +EINVAL + The contents of struct :c:type:`cec_msg` is invalid. + +ERESTARTSYS + The wait for a successful transmit was interrupted (e.g. by Ctrl-C). diff --git a/MAINTAINERS b/MAINTAINERS index 443bc975..551555a1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2674,6 +2674,22 @@ F: drivers/net/ieee802154/cc2520.c F: include/linux/spi/cc2520.h F: Documentation/devicetree/bindings/net/ieee802154/cc2520.txt +CEC FRAMEWORK +M: Hans Verkuil +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Supported +F: Documentation/media/kapi/cec-core.rst +F: Documentation/media/uapi/cec +F: drivers/media/cec/ +F: drivers/media/rc/keymaps/rc-cec.c +F: include/media/cec.h +F: include/media/cec-notifier.h +F: include/uapi/linux/cec.h +F: include/uapi/linux/cec-funcs.h +F: Documentation/devicetree/bindings/media/cec.txt + CELL BROADBAND ENGINE ARCHITECTURE M: Arnd Bergmann L: linuxppc-dev@lists.ozlabs.org @@ -8657,6 +8673,13 @@ F: include/linux/tracehook.h F: include/uapi/linux/ptrace.h F: kernel/ptrace.c +PULSE8-CEC DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Maintained +F: drivers/media/usb/pulse8-cec/* + PVRUSB2 VIDEO4LINUX DRIVER M: Mike Isely L: pvrusb2@isely.net (subscribers-only) @@ -8889,6 +8912,13 @@ L: linux-fbdev@vger.kernel.org S: Maintained F: drivers/video/fbdev/aty/aty128fb.c +RAINSHADOW-CEC DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Maintained +F: drivers/media/usb/rainshadow-cec/* + RALINK RT2X00 WIRELESS LAN DRIVER P: rt2x00 project M: Stanislaw Gruszka diff --git a/Makefile b/Makefile index c033e03a..85e0cade 100644 --- a/Makefile +++ b/Makefile @@ -380,12 +380,6 @@ PERL = perl PYTHON = python CHECK = sparse -# Use the wrapper for the compiler. This wrapper scans for new -# warnings and causes the build to stop upon encountering them. -ifneq ($(wildcard $(srctree)/scripts/gcc-wrapper.py),) -CC = $(srctree)/scripts/gcc-wrapper.py $(CROSS_COMPILE)gcc -endif - CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \ -Wbitwise -Wno-return-void $(CF) CFLAGS_MODULE = diff --git a/arch/arm/boot/dts/rk3288-miniarm.dts b/arch/arm/boot/dts/rk3288-miniarm.dts index a5c53007..eac9b150 100644 --- a/arch/arm/boot/dts/rk3288-miniarm.dts +++ b/arch/arm/boot/dts/rk3288-miniarm.dts @@ -42,11 +42,22 @@ #include #include "rk3288.dtsi" #include "rk3288-rkisp1.dtsi" -#include "rk3288-linux.dtsi" +#include "rk3288cg-opp.dtsi" / { + model = "ASUS Tinker Board"; compatible = "rockchip,rk3288-miniarm", "rockchip,rk3288"; + chosen { + bootargs = "earlyprintk=uart8250-32bit,0xff690000"; + }; + + cpuinfo { + compatible = "rockchip,cpuinfo"; + nvmem-cells = <&efuse_id>; + nvmem-cell-names = "id"; + }; + memory { device_type = "memory"; reg = <0x0 0x0 0x0 0x80000000>; @@ -67,7 +78,7 @@ wireless-wlan { compatible = "wlan-platdata"; rockchip,grf = <&grf>; - wifi_chip_type = "ap6212"; + wifi_chip_type = "rtl8723bs"; sdio_vref = <1800>; WIFI,host_wake_irq = <&gpio4 30 GPIO_ACTIVE_HIGH>; status = "okay"; @@ -129,16 +140,16 @@ linux,default-trigger="mmc0"; }; - led1-led { + heartbeat-led { gpios=<&gpio1 25 GPIO_ACTIVE_HIGH>; - linux,default-trigger="default-off"; + linux,default-trigger="heartbeat"; }; }; sound { compatible = "simple-audio-card"; simple-audio-card,format = "i2s"; - simple-audio-card,name = "rockchip,miniarm-codec"; + simple-audio-card,name = "HDMI"; simple-audio-card,mclk-fs = <512>; simple-audio-card,cpu { sound-dai = <&i2s>; @@ -204,20 +215,34 @@ cpu0-supply = <&vdd_cpu>; }; +&emmc { + bus-width = <8>; + cap-mmc-highspeed; + disable-wp; + mmc-ddr-1_8v; + mmc-hs200-1_8v; + non-removable; + num-slots = <1>; + pinctrl-names = "default"; + pinctrl-0 = <&emmc_clk &emmc_cmd &emmc_pwr &emmc_bus8>; + status = "okay"; +}; + &gmac { phy-supply = <&vcc33_lan>; phy-mode = "rgmii"; clock_in_out = "input"; + snps,force_thresh_dma_mode; snps,reset-gpio = <&gpio4 7 0>; snps,reset-active-low; - snps,reset-delays-us = <0 10000 1000000>; + snps,reset-delays-us = <0 10000 50000>; assigned-clocks = <&cru SCLK_MAC>; assigned-clock-parents = <&ext_gmac>; pinctrl-names = "default"; pinctrl-0 = <&rgmii_pins>; tx_delay = <0x30>; rx_delay = <0x10>; - status = "ok"; + status = "okay"; }; &dsi0 { @@ -238,6 +263,11 @@ #address-cells = <1>; #size-cells = <0>; #sound-dai-cells = <0>; + rockchip,phy-table = + <74250000 0x8009 0x0004 0x0272>, + <165000000 0x802b 0x0004 0x0209>, + <371250000 0x802d 0x0001 0x0149>, + <0 0x0000 0x0000 0x0000>; status = "okay"; /* Don't use vopl for HDMI */ ports { @@ -545,6 +575,15 @@ &i2s { #sound-dai-cells = <0>; + rockchip,bclk-fs = <128>; + status = "okay"; +}; + +&iep { + status = "okay"; +}; + +&iep_mmu { status = "okay"; }; @@ -558,7 +597,7 @@ &sdio0 { status = "okay"; clock-frequency = <50000000>; - clock-freq-min-max = <200000 50000000>; + max-frequency = <50000000>; bus-width = <4>; cap-sd-highspeed; cap-sdio-irq; @@ -579,7 +618,7 @@ &saradc { vref-supply = <&vcc18_ldo1>; - status ="okay"; + status = "okay"; }; &sdmmc { @@ -604,7 +643,6 @@ &tsadc { rockchip,hw-tshut-mode = <1>; /* tshut mode 0:CRU 1:GPIO */ rockchip,hw-tshut-polarity = <1>; /* tshut polarity 0:LOW 1:HIGH */ - pinctrl-1 = <&otp_out>; status = "okay"; }; @@ -615,6 +653,8 @@ }; &uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_xfer>, <&uart1_cts>, <&uart1_rts>; status = "okay"; }; @@ -627,6 +667,8 @@ }; &uart4 { + pinctrl-names = "default"; + pinctrl-0 = <&uart4_xfer>, <&uart4_cts>, <&uart4_rts>; status = "okay"; }; @@ -644,7 +686,7 @@ }; &usb_otg { - status= "okay"; + status = "okay"; }; &vopb { diff --git a/arch/arm/boot/dts/rk3288-miqi.dts b/arch/arm/boot/dts/rk3288-miqi.dts index a2862c6a..9655365d 100644 --- a/arch/arm/boot/dts/rk3288-miqi.dts +++ b/arch/arm/boot/dts/rk3288-miqi.dts @@ -43,11 +43,22 @@ /dts-v1/; #include #include "rk3288.dtsi" -#include "rk3288-linux.dtsi" +#include "rk3288cg-opp.dtsi" / { + model = "mqmaker MiQi"; compatible = "rockchip,rk3288-miqi", "rockchip,rk3288"; + chosen { + bootargs = "earlyprintk=uart8250-32bit,0xff690000"; + }; + + cpuinfo { + compatible = "rockchip,cpuinfo"; + nvmem-cells = <&efuse_id>; + nvmem-cell-names = "id"; + }; + memory { device_type = "memory"; reg = <0x0 0x0 0x0 0x80000000>; @@ -56,29 +67,14 @@ sound { compatible = "simple-audio-card"; simple-audio-card,format = "i2s"; - simple-audio-card,name = "DW-HDMI"; + simple-audio-card,name = "HDMI"; simple-audio-card,mclk-fs = <512>; - - simple-audio-card,dai-link@0 { /* I2S - S/PDIF */ - format = "i2s"; - cpu { - sound-dai = <&i2s>; - }; - codec { - sound-dai = <&hdmi>; - }; + simple-audio-card,cpu { + sound-dai = <&i2s>; + }; + simple-audio-card,codec { + sound-dai = <&hdmi>; }; - - /* - * If you want to support more cards, - * you can add more dai-link node, - * such as - * - * simple-audio-card,dai-link@1 { - * ...... - * } - */ - }; ext_gmac: external-gmac-clock { @@ -204,6 +200,12 @@ #size-cells = <0>; #sound-dai-cells = <0>; status = "okay"; + /* Don't use vopl for HDMI */ + ports { + hdmi_in: port { + /delete-node/ endpoint@1; + }; + }; }; &hevc_service { @@ -235,14 +237,14 @@ clock_in_out = "input"; snps,reset-gpio = <&gpio4 7 0>; snps,reset-active-low; - snps,reset-delays-us = <0 10000 1000000>; + snps,reset-delays-us = <0 10000 50000>; assigned-clocks = <&cru SCLK_MAC>; assigned-clock-parents = <&ext_gmac>; pinctrl-names = "default"; pinctrl-0 = <&rgmii_pins>; tx_delay = <0x30>; rx_delay = <0x10>; - status = "ok"; + status = "okay"; }; /* ---------------------------------------------------------------------------------- @@ -414,6 +416,15 @@ I2C &i2s { #sound-dai-cells = <0>; + rockchip,bclk-fs = <128>; + status = "okay"; +}; + +&iep { + status = "okay"; +}; + +&iep_mmu { status = "okay"; }; @@ -439,6 +450,17 @@ I2C status = "okay"; }; +&saradc { + vref-supply = <&vcc_18>; + status = "okay"; +}; + +&tsadc { + rockchip,hw-tshut-mode = <0>; + rockchip,hw-tshut-polarity = <0>; + status = "okay"; +}; + /* * Debug Serial Port */ @@ -472,6 +494,10 @@ I2C &vopl { status = "okay"; + /* Don't use vopl for HDMI */ + vopl_out: port { + /delete-node/ endpoint@0; + }; }; &vopl_mmu { @@ -546,4 +572,3 @@ I2C }; }; - diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi index 1b7602f2..8e8a120d 100644 --- a/arch/arm/boot/dts/rk3288.dtsi +++ b/arch/arm/boot/dts/rk3288.dtsi @@ -352,49 +352,57 @@ sdmmc: dwmmc@ff0c0000 { compatible = "rockchip,rk3288-dw-mshc"; - clock-freq-min-max = <400000 150000000>; + max-frequency = <150000000>; clocks = <&cru HCLK_SDMMC>, <&cru SCLK_SDMMC>, <&cru SCLK_SDMMC_DRV>, <&cru SCLK_SDMMC_SAMPLE>; clock-names = "biu", "ciu", "ciu-drive", "ciu-sample"; fifo-depth = <0x100>; interrupts = ; reg = <0x0 0xff0c0000 0x0 0x4000>; + resets = <&cru SRST_MMC0>; + reset-names = "reset"; status = "disabled"; }; sdio0: dwmmc@ff0d0000 { compatible = "rockchip,rk3288-dw-mshc"; - clock-freq-min-max = <400000 150000000>; + max-frequency = <150000000>; clocks = <&cru HCLK_SDIO0>, <&cru SCLK_SDIO0>, <&cru SCLK_SDIO0_DRV>, <&cru SCLK_SDIO0_SAMPLE>; clock-names = "biu", "ciu", "ciu-drive", "ciu-sample"; fifo-depth = <0x100>; interrupts = ; reg = <0x0 0xff0d0000 0x0 0x4000>; + resets = <&cru SRST_SDIO0>; + reset-names = "reset"; status = "disabled"; }; sdio1: dwmmc@ff0e0000 { compatible = "rockchip,rk3288-dw-mshc"; - clock-freq-min-max = <400000 150000000>; + max-frequency = <150000000>; clocks = <&cru HCLK_SDIO1>, <&cru SCLK_SDIO1>, <&cru SCLK_SDIO1_DRV>, <&cru SCLK_SDIO1_SAMPLE>; clock-names = "biu", "ciu", "ciu-drive", "ciu-sample"; fifo-depth = <0x100>; interrupts = ; reg = <0x0 0xff0e0000 0x0 0x4000>; + resets = <&cru SRST_SDIO1>; + reset-names = "reset"; status = "disabled"; }; emmc: dwmmc@ff0f0000 { compatible = "rockchip,rk3288-dw-mshc"; - clock-freq-min-max = <400000 150000000>; + max-frequency = <150000000>; clocks = <&cru HCLK_EMMC>, <&cru SCLK_EMMC>, <&cru SCLK_EMMC_DRV>, <&cru SCLK_EMMC_SAMPLE>; clock-names = "biu", "ciu", "ciu-drive", "ciu-sample"; fifo-depth = <0x100>; interrupts = ; reg = <0x0 0xff0f0000 0x0 0x4000>; + resets = <&cru SRST_EMMC>; + reset-names = "reset"; status = "disabled"; supports-emmc; }; @@ -638,6 +646,7 @@ compatible = "rockchip,rk3288-tsadc"; reg = <0x0 0xff280000 0x0 0x100>; interrupts = ; + rockchip,grf = <&grf>; clocks = <&cru SCLK_TSADC>, <&cru PCLK_TSADC>; clock-names = "tsadc", "apb_pclk"; assigned-clocks = <&cru SCLK_TSADC>; @@ -646,7 +655,7 @@ reset-names = "tsadc-apb"; pinctrl-names = "init", "default", "sleep"; pinctrl-0 = <&otp_gpio>; - pinctrl-1 = <&otp_gpio>; + pinctrl-1 = <&otp_out>; pinctrl-2 = <&otp_gpio>; #thermal-sensor-cells = <1>; rockchip,hw-tshut-temp = <120000>; @@ -972,6 +981,8 @@ <&cru PCLK_MIPI_DSI1>, <&cru SCLK_EDP_24M>, <&cru SCLK_EDP>, + <&cru SCLK_HDMI_CEC>, + <&cru SCLK_HDMI_HDCP>, <&cru SCLK_ISP_JPE>, <&cru SCLK_ISP>, <&cru SCLK_RGA>; @@ -1303,6 +1314,8 @@ resets = <&cru SRST_LCDC0_AXI>, <&cru SRST_LCDC0_AHB>, <&cru SRST_LCDC0_DCLK>; reset-names = "axi", "ahb", "dclk"; iommus = <&vopb_mmu>; + assigned-clocks = <&cru DCLK_VOP0>; + assigned-clock-parents = <&cru PLL_NPLL>; status = "disabled"; vopb_out: port { @@ -1570,10 +1583,10 @@ reg-io-width = <4>; rockchip,grf = <&grf>; interrupts = ; - clocks = <&cru PCLK_HDMI_CTRL>, <&cru SCLK_HDMI_HDCP>; - clock-names = "iahb", "isfr"; + clocks = <&cru PCLK_HDMI_CTRL>, <&cru SCLK_HDMI_HDCP>, <&cru SCLK_HDMI_CEC>; + clock-names = "iahb", "isfr", "cec"; pinctrl-names = "default", "sleep"; - pinctrl-0 = <&hdmi_ddc>; + pinctrl-0 = <&hdmi_ddc>, <&hdmi_cec_c0>; pinctrl-1 = <&hdmi_gpio>; power-domains = <&power RK3288_PD_VIO>; status = "disabled"; @@ -1699,6 +1712,7 @@ operating-points-v2 = <&gpu_opp_table>; #cooling-cells = <2>; /* min followed by max */ power-domains = <&power RK3288_PD_GPU>; + power-off-delay-ms = <200>; status = "disabled"; upthreshold = <75>; @@ -1956,6 +1970,14 @@ &pcfg_pull_none>; }; + hdmi_cec_c0: hdmi-cec-c0 { + rockchip,pins = <7 16 RK_FUNC_2 &pcfg_pull_none>; + }; + + hdmi_cec_c7: hdmi-cec-c7 { + rockchip,pins = <7 23 RK_FUNC_4 &pcfg_pull_none>; + }; + hdmi_ddc: hdmi-ddc { rockchip,pins = <7 19 RK_FUNC_2 &pcfg_pull_none>, <7 20 RK_FUNC_2 &pcfg_pull_none>; diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index d539dee3..68996115 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -301,7 +301,7 @@ static void __dma_free_remap(void *cpu_addr, size_t size) VM_ARM_DMA_CONSISTENT | VM_USERMAP); } -#define DEFAULT_DMA_COHERENT_POOL_SIZE SZ_256K +#define DEFAULT_DMA_COHERENT_POOL_SIZE SZ_2M static struct gen_pool *atomic_pool; static size_t atomic_pool_size = DEFAULT_DMA_COHERENT_POOL_SIZE; diff --git a/arch/arm/plat-samsung/devs.c b/arch/arm/plat-samsung/devs.c index e212f9d8..2ef19ad5 100644 --- a/arch/arm/plat-samsung/devs.c +++ b/arch/arm/plat-samsung/devs.c @@ -10,7 +10,6 @@ * published by the Free Software Foundation. */ -#include #include #include #include diff --git a/arch/arm64/boot/dts/rockchip/rk3328-box-trn9.dts b/arch/arm64/boot/dts/rockchip/rk3328-box-trn9.dts new file mode 100644 index 00000000..81ec7b66 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3328-box-trn9.dts @@ -0,0 +1,702 @@ +/* + * Copyright (c) 2017 Fuzhou Rockchip Electronics Co., Ltd + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Or, alternatively, + * + * b) Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/dts-v1/; +#include "rk3328.dtsi" +#include "rk3328-dram-box-plus-timing.dtsi" + +/ { + model = "Rockchip RK3328 TRN9"; + compatible = "rockchip,rk3328-box-trn9", "rockchip,rk3328"; + + chosen { + bootargs = "swiotlb=1 kpti=0"; + }; + + aliases { + serial0 = &uart2; + serial2 = &uart0; + }; + + xin32k: xin32k { + compatible = "fixed-clock"; + clock-frequency = <32768>; + clock-output-names = "xin32k"; + #clock-cells = <0>; + }; + + gmac_clkin: external-gmac-clock { + compatible = "fixed-clock"; + clock-frequency = <125000000>; + clock-output-names = "gmac_clkin"; + #clock-cells = <0>; + }; + + vcc_phy: vcc-phy-regulator { + compatible = "regulator-fixed"; + regulator-name = "vcc_phy"; + regulator-always-on; + regulator-boot-on; + }; + + vcc_sys: vcc-sys { + compatible = "regulator-fixed"; + regulator-name = "vcc_sys"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + }; + + vcc_sd: sdmmc-regulator { + compatible = "regulator-fixed"; + gpio = <&gpio0 RK_PD6 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc0m1_gpio>; + regulator-name = "vcc_sd"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vcc_io>; + }; + + vcc_host_5v: vcc-host-5v-regulator { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio0 RK_PA0 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&usb30_host_drv>; + regulator-name = "vcc_host_5v"; + regulator-always-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&vcc_sys>; + }; + + vcc_host1_5v: vcc_otg_5v: vcc-host1-5v-regulator { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio0 RK_PA2 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&usb20_host_drv>; + regulator-name = "vcc_host1_5v"; + regulator-always-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&vcc_sys>; + }; + + leds { + compatible = "gpio-leds"; + + power { + gpios = <&rk805 0 GPIO_ACTIVE_LOW>; + linux,default-trigger = "default-on"; + default-state = "on"; + }; + }; + + ir-receiver { + compatible = "gpio-ir-receiver"; + gpios = <&gpio2 RK_PA2 GPIO_ACTIVE_LOW>; + linux,rc-map-name = "rc-trn9"; + pinctrl-0 = <&ir_int>; + pinctrl-names = "default"; + }; + + hdmi-sound { + compatible = "simple-audio-card"; + simple-audio-card,format = "i2s"; + simple-audio-card,mclk-fs = <128>; + simple-audio-card,name = "HDMI"; + simple-audio-card,cpu { + sound-dai = <&i2s0>; + }; + simple-audio-card,codec { + sound-dai = <&hdmi>; + }; + }; + + sound { + compatible = "simple-audio-card"; + simple-audio-card,format = "i2s"; + simple-audio-card,mclk-fs = <256>; + simple-audio-card,name = "I2S"; + simple-audio-card,cpu { + sound-dai = <&i2s1>; + }; + simple-audio-card,codec { + sound-dai = <&codec>; + }; + }; + + spdif-sound { + compatible = "simple-audio-card"; + simple-audio-card,name = "SPDIF"; + simple-audio-card,cpu { + sound-dai = <&spdif>; + }; + simple-audio-card,codec { + sound-dai = <&spdif_out>; + }; + }; + + spdif_out: spdif-out { + compatible = "linux,spdif-dit"; + #sound-dai-cells = <0>; + }; + + sdio_pwrseq: sdio-pwrseq { + compatible = "mmc-pwrseq-simple"; + pinctrl-names = "default"; + pinctrl-0 = <&wifi_enable_h>; + + /* + * On the module itself this is one of these (depending + * on the actual card populated): + * - SDIO_RESET_L_WL_REG_ON + * - PDN (power down when low) + */ + reset-gpios = <&gpio3 RK_PB0 GPIO_ACTIVE_LOW>; + }; + + wireless-bluetooth { + compatible = "bluetooth-platdata"; + BT,power_gpio = <&gpio1 RK_PD0 GPIO_ACTIVE_HIGH>; + BT,wake_host_irq = <&gpio1 RK_PD2 GPIO_ACTIVE_HIGH>; + }; + + wireless-wlan { + compatible = "wlan-platdata"; + rockchip,grf = <&grf>; + wifi_chip_type = "rtl8723bs"; + WIFI,host_wake_irq = <&gpio3 RK_PA1 GPIO_ACTIVE_HIGH>; + }; +}; + +&codec { + #sound-dai-cells = <0>; + status = "okay"; +}; + +&cpu0 { + cpu-supply = <&vdd_arm>; +}; + +&cpu1 { + cpu-supply = <&vdd_arm>; +}; + +&cpu2 { + cpu-supply = <&vdd_arm>; +}; + +&cpu3 { + cpu-supply = <&vdd_arm>; +}; + +&dfi { + status = "okay"; +}; + +&dmc { + center-supply = <&vdd_logic>; + system-status-freq = < + /*system status freq(KHz)*/ + SYS_STATUS_NORMAL 1066000 + SYS_STATUS_REBOOT 1066000 + SYS_STATUS_SUSPEND 1066000 + SYS_STATUS_VIDEO_1080P 1066000 + SYS_STATUS_VIDEO_4K 1066000 + SYS_STATUS_VIDEO_4K_10B 1066000 + SYS_STATUS_PERFORMANCE 1066000 + SYS_STATUS_BOOST 1066000 + >; + status = "okay"; +}; + +&dmc_opp_table { + opp-933000000 { + opp-hz = /bits/ 64 <933000000>; + opp-microvolt = <1150000>; + opp-microvolt-L0 = <1150000>; + opp-microvolt-L1 = <1100000>; + }; + opp-1066000000 { + opp-hz = /bits/ 64 <1066000000>; + opp-microvolt = <1200000>; + opp-microvolt-L0 = <1200000>; + opp-microvolt-L1 = <1175000>; + }; +}; + +&display_subsystem { + status = "okay"; +}; + +&emmc { + bus-width = <8>; + cap-mmc-highspeed; + mmc-hs200-1_8v; + non-removable; + pinctrl-names = "default"; + pinctrl-0 = <&emmc_clk &emmc_cmd &emmc_bus8>; + supports-emmc; + vmmc-supply = <&vcc_io>; + vqmmc-supply = <&vcc18_emmc>; + status = "okay"; +}; + +&gmac2io { + assigned-clocks = <&cru SCLK_MAC2IO>, <&cru SCLK_MAC2IO_EXT>; + assigned-clock-parents = <&gmac_clkin>, <&gmac_clkin>; + clock_in_out = "input"; + phy-supply = <&vcc_phy>; + phy-mode = "rgmii"; + pinctrl-names = "default"; + pinctrl-0 = <&rgmiim1_pins>; + snps,reset-gpio = <&gpio1 RK_PC2 GPIO_ACTIVE_LOW>; + snps,reset-active-low; + snps,reset-delays-us = <0 10000 50000>; + tx_delay = <0x26>; + rx_delay = <0x11>; + status = "okay"; +}; + +&gmac2phy { + phy-supply = <&vcc_phy>; + assigned-clocks = <&cru SCLK_MAC2PHY_SRC>; + assigned-clock-rate = <50000000>; + assigned-clocks = <&cru SCLK_MAC2PHY>; + assigned-clock-parents = <&cru SCLK_MAC2PHY_SRC>; + clock_in_out = "output"; + status = "disabled"; +}; + +&gpu { + status = "okay"; + mali-supply = <&vdd_logic>; +}; + +&h265e { + status = "okay"; +}; + +&h265e_mmu { + status = "okay"; +}; + +&hdmi { + #sound-dai-cells = <0>; + ddc-i2c-scl-high-time-ns = <9625>; + ddc-i2c-scl-low-time-ns = <10000>; + status = "okay"; +}; + +&hdmiphy { + status = "okay"; +}; + +&i2c1 { + status = "okay"; + + rk805: rk805@18 { + compatible = "rockchip,rk805"; + status = "okay"; + reg = <0x18>; + interrupt-parent = <&gpio2>; + interrupts = <6 IRQ_TYPE_LEVEL_LOW>; + #clock-cells = <1>; + clock-output-names = "rk805-clkout1", "rk805-clkout2"; + pinctrl-names = "default"; + pinctrl-0 = <&pmic_int_l>; + rockchip,system-power-controller; + wakeup-source; + gpio-controller; + #gpio-cells = <2>; + + vcc1-supply = <&vcc_sys>; + vcc2-supply = <&vcc_sys>; + vcc3-supply = <&vcc_sys>; + vcc4-supply = <&vcc_sys>; + vcc5-supply = <&vcc_io>; + vcc6-supply = <&vcc_sys>; + + rtc { + status = "okay"; + }; + + pwrkey { + status = "okay"; + }; + + gpio { + status = "okay"; + }; + + regulators { + compatible = "rk805-regulator"; + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + vdd_logic: RK805_DCDC1 { + regulator-compatible = "RK805_DCDC1"; + regulator-name = "vdd_logic"; + regulator-min-microvolt = <712500>; + regulator-max-microvolt = <1450000>; + regulator-initial-mode = <0x1>; + regulator-ramp-delay = <12500>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-mode = <0x2>; + regulator-on-in-suspend; + regulator-suspend-microvolt = <1000000>; + }; + }; + + vdd_arm: RK805_DCDC2 { + regulator-compatible = "RK805_DCDC2"; + regulator-name = "vdd_arm"; + regulator-init-microvolt = <1225000>; + regulator-min-microvolt = <712500>; + regulator-max-microvolt = <1450000>; + regulator-initial-mode = <0x1>; + regulator-ramp-delay = <12500>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-mode = <0x2>; + regulator-on-in-suspend; + regulator-suspend-microvolt = <950000>; + }; + }; + + vcc_ddr: RK805_DCDC3 { + regulator-compatible = "RK805_DCDC3"; + regulator-name = "vcc_ddr"; + regulator-initial-mode = <0x1>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-mode = <0x2>; + regulator-on-in-suspend; + }; + }; + + vcc_io: RK805_DCDC4 { + regulator-compatible = "RK805_DCDC4"; + regulator-name = "vcc_io"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-initial-mode = <0x1>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-mode = <0x2>; + regulator-on-in-suspend; + regulator-suspend-microvolt = <3300000>; + }; + }; + + vcc_18: RK805_LDO1 { + regulator-compatible = "RK805_LDO1"; + regulator-name = "vcc_18"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vcc18_emmc: RK805_LDO2 { + regulator-compatible = "RK805_LDO2"; + regulator-name = "vcc18_emmc"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vdd_10: RK805_LDO3 { + regulator-compatible = "RK805_LDO3"; + regulator-name = "vdd_10"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1000000>; + }; + }; + }; + }; +}; + +&i2s0 { + #sound-dai-cells = <0>; + rockchip,bclk-fs = <128>; + status = "okay"; +}; + +&i2s1 { + #sound-dai-cells = <0>; + status = "okay"; +}; + +&iep { + status = "okay"; +}; + +&iep_mmu { + status = "okay"; +}; + +&io_domains { + status = "okay"; + + vccio1-supply = <&vcc_io>; + vccio2-supply = <&vcc18_emmc>; + vccio3-supply = <&vcc_io>; + vccio4-supply = <&vcc_18>; + vccio5-supply = <&vcc_io>; + vccio6-supply = <&vcc_18>; + pmuio-supply = <&vcc_io>; +}; + +&pinctrl { + pinctrl-names = "default"; + pinctrl-0 = <&clk_32k_out>; + + clk_32k { + clk_32k_out: clk-32k-out { + rockchip,pins = <1 RK_PD4 RK_FUNC_1 &pcfg_pull_none>; + }; + }; + + ir { + ir_int: ir-int { + rockchip,pins = <2 RK_PA2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + pmic { + pmic_int_l: pmic-int-l { + rockchip,pins = <2 RK_PA6 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + + sdio-pwrseq { + wifi_enable_h: wifi-enable-h { + rockchip,pins = <3 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>, + <3 RK_PA1 RK_FUNC_GPIO &pcfg_pull_none_4ma>, + <1 RK_PD0 RK_FUNC_GPIO &pcfg_pull_none>, + <1 RK_PD2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + usb2 { + usb20_host_drv: usb20-host-drv { + rockchip,pins = <0 RK_PA2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + usb3 { + usb30_host_drv: usb30-host-drv { + rockchip,pins = <0 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; +}; + +&rkvdec { + status = "okay"; + vcodec-supply = <&vdd_logic>; +}; + +&rkvdec_mmu { + status = "okay"; +}; + +&sdmmc_ext { + bus-width = <4>; + cap-sd-highspeed; + cap-sdio-irq; + disable-wp; + keep-power-in-suspend; + mmc-pwrseq = <&sdio_pwrseq>; + non-removable; + num-slots = <1>; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc0ext_bus4 &sdmmc0ext_cmd &sdmmc0ext_clk>; + sd-uhs-sdr104; + supports-sdio; + status = "okay"; +}; + +&sdmmc { + bus-width = <4>; + cap-mmc-highspeed; + cap-sd-highspeed; + disable-wp; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc0_clk &sdmmc0_cmd &sdmmc0_dectn &sdmmc0_bus4>; + supports-sd; + vmmc-supply = <&vcc_sd>; + status = "okay"; +}; + +&spdif { + #sound-dai-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&spdifm0_tx>; + status = "okay"; +}; + +&threshold { + temperature = <80000>; /* millicelsius */ +}; + +&target { + temperature = <95000>; /* millicelsius */ +}; + +&soc_crit { + temperature = <100000>; /* millicelsius */ +}; + +&tsadc { + rockchip,hw-tshut-mode = <0>; + rockchip,hw-tshut-polarity = <0>; + rockchip,hw-tshut-temp = <110000>; + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&u2phy { + status = "okay"; +}; + +&u2phy_host { + phy-supply = <&vcc_host1_5v>; + status = "okay"; +}; + +&u2phy_otg { + phy-supply = <&vcc_otg_5v>; + status = "okay"; +}; + +&u3phy { + status = "okay"; +}; + +&u3phy_utmi { + phy-supply = <&vcc_host_5v>; + status = "okay"; +}; + +&u3phy_pipe { + phy-supply = <&vcc_host_5v>; + status = "okay"; +}; + +&usb20_otg { + dr_mode = "host"; + status = "okay"; +}; + +&usb_host0_ehci { + status = "okay"; +}; + +&usb_host0_ohci { + status = "okay"; +}; + +&usbdrd3 { + status = "okay"; +}; + +&usbdrd_dwc3 { + status = "okay"; +}; + +&vop { + status = "okay"; +}; + +&vop_mmu { + status = "okay"; +}; + +&vpu_service { + status = "okay"; +}; + +&vpu_mmu { + status = "okay"; +}; + +&vepu { + status = "okay"; +}; + +&vepu_mmu { + status = "okay"; +}; + +&venc_srv { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3328-box-z28.dts b/arch/arm64/boot/dts/rockchip/rk3328-box-z28.dts new file mode 100644 index 00000000..00a3394c --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3328-box-z28.dts @@ -0,0 +1,598 @@ +/* + * Copyright (c) 2017 Fuzhou Rockchip Electronics Co., Ltd + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Or, alternatively, + * + * b) Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/dts-v1/; +#include "rk3328.dtsi" + +/ { + model = "Rockchip RK3328 Z28"; + compatible = "rockchip,rk3328-box-z28", "rockchip,rk3328"; + + chosen { + bootargs = "earlyprintk=uart8250-32bit,0xff130000 swiotlb=1 kpti=0"; + stdout-path = "serial2:1500000n8"; + }; + + xin32k: xin32k { + compatible = "fixed-clock"; + clock-frequency = <32768>; + clock-output-names = "xin32k"; + #clock-cells = <0>; + }; + + vcc_phy: vcc-phy-regulator { + compatible = "regulator-fixed"; + regulator-name = "vcc_phy"; + regulator-always-on; + regulator-boot-on; + }; + + vcc_sys: vcc-sys { + compatible = "regulator-fixed"; + regulator-name = "vcc_sys"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + }; + + vcc_sd: sdmmc-regulator { + compatible = "regulator-fixed"; + gpio = <&gpio0 RK_PD6 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc0m1_gpio>; + regulator-name = "vcc_sd"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vcc_io>; + }; + + vcc_host_5v: vcc-host-5v-regulator { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio0 RK_PA0 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&usb30_host_drv>; + regulator-name = "vcc_host_5v"; + regulator-always-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&vcc_sys>; + }; + + vcc_host1_5v: vcc_otg_5v: vcc-host1-5v-regulator { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio0 RK_PA2 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&usb20_host_drv>; + regulator-name = "vcc_host1_5v"; + regulator-always-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&vcc_sys>; + }; + + leds { + compatible = "gpio-leds"; + + power { + gpios = <&rk805 0 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "default-on"; + default-state = "on"; + }; + }; + + ir-receiver { + compatible = "gpio-ir-receiver"; + gpios = <&gpio2 RK_PA2 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&ir_int>; + pinctrl-names = "default"; + }; + + hdmi-sound { + compatible = "simple-audio-card"; + simple-audio-card,format = "i2s"; + simple-audio-card,mclk-fs = <128>; + simple-audio-card,name = "HDMI"; + simple-audio-card,cpu { + sound-dai = <&i2s0>; + }; + simple-audio-card,codec { + sound-dai = <&hdmi>; + }; + }; + + spdif-sound { + compatible = "simple-audio-card"; + simple-audio-card,name = "SPDIF"; + simple-audio-card,cpu { + sound-dai = <&spdif>; + }; + simple-audio-card,codec { + sound-dai = <&spdif_out>; + }; + }; + + spdif_out: spdif-out { + compatible = "linux,spdif-dit"; + #sound-dai-cells = <0>; + }; + + wireless-bluetooth { + compatible = "bluetooth-platdata"; + BT,power_gpio = <&gpio2 RK_PC5 GPIO_ACTIVE_HIGH>; + BT,wake_host_irq = <&gpio2 RK_PC0 GPIO_ACTIVE_HIGH>; + }; + + wireless-wlan { + compatible = "wlan-platdata"; + rockchip,grf = <&grf>; + wifi_chip_type = "rtl8188eu"; + WIFI,poweren_gpio = <&gpio2 RK_PC3 GPIO_ACTIVE_HIGH>; + WIFI,reset_gpio = <&gpio2 RK_PC4 GPIO_ACTIVE_HIGH>; + WIFI,host_wake_irq = <&gpio2 RK_PC6 GPIO_ACTIVE_HIGH>; + }; +}; + +&codec { + #sound-dai-cells = <0>; + status = "okay"; +}; + +&cpu0 { + cpu-supply = <&vdd_arm>; +}; + +&cpu1 { + cpu-supply = <&vdd_arm>; +}; + +&cpu2 { + cpu-supply = <&vdd_arm>; +}; + +&cpu3 { + cpu-supply = <&vdd_arm>; +}; + +&dfi { + status = "okay"; +}; + +&dmc { + center-supply = <&vdd_logic>; + status = "okay"; +}; + +&display_subsystem { + status = "okay"; +}; + +&emmc { + bus-width = <8>; + cap-mmc-highspeed; + mmc-hs200-1_8v; + non-removable; + pinctrl-names = "default"; + pinctrl-0 = <&emmc_clk &emmc_cmd &emmc_bus8>; + supports-emmc; + vmmc-supply = <&vcc_io>; + vqmmc-supply = <&vcc18_emmc>; + status = "okay"; +}; + +&gmac2phy { + phy-supply = <&vcc_phy>; + assigned-clocks = <&cru SCLK_MAC2PHY_SRC>; + assigned-clock-rate = <50000000>; + assigned-clocks = <&cru SCLK_MAC2PHY>; + assigned-clock-parents = <&cru SCLK_MAC2PHY_SRC>; + clock_in_out = "output"; + status = "okay"; +}; + +&gpu { + status = "okay"; + mali-supply = <&vdd_logic>; +}; + +&h265e { + status = "okay"; +}; + +&h265e_mmu { + status = "okay"; +}; + +&hdmi { + #sound-dai-cells = <0>; + ddc-i2c-scl-high-time-ns = <9625>; + ddc-i2c-scl-low-time-ns = <10000>; + status = "okay"; +}; + +&hdmiphy { + status = "okay"; +}; + +&i2c1 { + status = "okay"; + + rk805: rk805@18 { + compatible = "rockchip,rk805"; + status = "okay"; + reg = <0x18>; + interrupt-parent = <&gpio2>; + interrupts = <6 IRQ_TYPE_LEVEL_LOW>; + #clock-cells = <1>; + clock-output-names = "rk805-clkout1", "rk805-clkout2"; + pinctrl-names = "default"; + pinctrl-0 = <&pmic_int_l>; + rockchip,system-power-controller; + wakeup-source; + gpio-controller; + #gpio-cells = <2>; + + vcc1-supply = <&vcc_sys>; + vcc2-supply = <&vcc_sys>; + vcc3-supply = <&vcc_sys>; + vcc4-supply = <&vcc_sys>; + vcc5-supply = <&vcc_io>; + vcc6-supply = <&vcc_sys>; + + rtc { + status = "okay"; + }; + + pwrkey { + status = "okay"; + }; + + gpio { + status = "okay"; + }; + + regulators { + compatible = "rk805-regulator"; + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + vdd_logic: RK805_DCDC1 { + regulator-compatible = "RK805_DCDC1"; + regulator-name = "vdd_logic"; + regulator-min-microvolt = <712500>; + regulator-max-microvolt = <1450000>; + regulator-initial-mode = <0x1>; + regulator-ramp-delay = <12500>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-mode = <0x2>; + regulator-on-in-suspend; + regulator-suspend-microvolt = <1000000>; + }; + }; + + vdd_arm: RK805_DCDC2 { + regulator-compatible = "RK805_DCDC2"; + regulator-name = "vdd_arm"; + regulator-init-microvolt = <1225000>; + regulator-min-microvolt = <712500>; + regulator-max-microvolt = <1450000>; + regulator-initial-mode = <0x1>; + regulator-ramp-delay = <12500>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-mode = <0x2>; + regulator-on-in-suspend; + regulator-suspend-microvolt = <950000>; + }; + }; + + vcc_ddr: RK805_DCDC3 { + regulator-compatible = "RK805_DCDC3"; + regulator-name = "vcc_ddr"; + regulator-initial-mode = <0x1>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-mode = <0x2>; + regulator-on-in-suspend; + }; + }; + + vcc_io: RK805_DCDC4 { + regulator-compatible = "RK805_DCDC4"; + regulator-name = "vcc_io"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-initial-mode = <0x1>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-mode = <0x2>; + regulator-on-in-suspend; + regulator-suspend-microvolt = <3300000>; + }; + }; + + vcc_18: RK805_LDO1 { + regulator-compatible = "RK805_LDO1"; + regulator-name = "vcc_18"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vcc18_emmc: RK805_LDO2 { + regulator-compatible = "RK805_LDO2"; + regulator-name = "vcc18_emmc"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vdd_10: RK805_LDO3 { + regulator-compatible = "RK805_LDO3"; + regulator-name = "vdd_10"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1000000>; + }; + }; + }; + }; +}; + +&i2s0 { + #sound-dai-cells = <0>; + rockchip,bclk-fs = <128>; + status = "okay"; +}; + +&iep { + status = "okay"; +}; + +&iep_mmu { + status = "okay"; +}; + +&io_domains { + status = "okay"; + + vccio1-supply = <&vcc_io>; + vccio2-supply = <&vcc18_emmc>; + vccio3-supply = <&vcc_io>; + vccio4-supply = <&vcc_18>; + vccio5-supply = <&vcc_io>; + vccio6-supply = <&vcc_io>; + pmuio-supply = <&vcc_io>; +}; + +&pinctrl { + pinctrl-names = "default"; + pinctrl-0 = <&clk_32k_out>; + + clk_32k { + clk_32k_out: clk-32k-out { + rockchip,pins = <1 RK_PD4 RK_FUNC_1 &pcfg_pull_none>; + }; + }; + + ir { + ir_int: ir-int { + rockchip,pins = <2 RK_PA2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + pmic { + pmic_int_l: pmic-int-l { + rockchip,pins = <2 RK_PA6 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + + usb2 { + usb20_host_drv: usb20-host-drv { + rockchip,pins = <0 RK_PA2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + usb3 { + usb30_host_drv: usb30-host-drv { + rockchip,pins = <0 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; +}; + +&rkvdec { + status = "okay"; + vcodec-supply = <&vdd_logic>; +}; + +&rkvdec_mmu { + status = "okay"; +}; + +&sdmmc { + bus-width = <4>; + cap-mmc-highspeed; + cap-sd-highspeed; + disable-wp; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc0_clk &sdmmc0_cmd &sdmmc0_dectn &sdmmc0_bus4>; + supports-sd; + vmmc-supply = <&vcc_sd>; + status = "okay"; +}; + +&spdif { + #sound-dai-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&spdifm0_tx>; + status = "okay"; +}; + +&threshold { + temperature = <80000>; /* millicelsius */ +}; + +&target { + temperature = <95000>; /* millicelsius */ +}; + +&soc_crit { + temperature = <100000>; /* millicelsius */ +}; + +&tsadc { + rockchip,hw-tshut-mode = <0>; + rockchip,hw-tshut-polarity = <0>; + rockchip,hw-tshut-temp = <110000>; + status = "okay"; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_xfer &uart0_cts>; + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&u2phy { + status = "okay"; +}; + +&u2phy_host { + phy-supply = <&vcc_host1_5v>; + status = "okay"; +}; + +&u2phy_otg { + phy-supply = <&vcc_otg_5v>; + status = "okay"; +}; + +&u3phy { + status = "okay"; +}; + +&u3phy_utmi { + phy-supply = <&vcc_host_5v>; + status = "okay"; +}; + +&u3phy_pipe { + phy-supply = <&vcc_host_5v>; + status = "okay"; +}; + +&usb20_otg { + dr_mode = "host"; + status = "okay"; +}; + +&usb_host0_ehci { + status = "okay"; +}; + +&usb_host0_ohci { + status = "okay"; +}; + +&usbdrd3 { + status = "okay"; +}; + +&usbdrd_dwc3 { + status = "okay"; +}; + +&vop { + status = "okay"; +}; + +&vop_mmu { + status = "okay"; +}; + +&vpu_service { + status = "okay"; +}; + +&vpu_mmu { + status = "okay"; +}; + +&vepu { + status = "okay"; +}; + +&vepu_mmu { + status = "okay"; +}; + +&venc_srv { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3328-box.dts b/arch/arm64/boot/dts/rockchip/rk3328-box.dts new file mode 100644 index 00000000..eae652d5 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3328-box.dts @@ -0,0 +1,648 @@ +/* + * Copyright (c) 2017 Fuzhou Rockchip Electronics Co., Ltd + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Or, alternatively, + * + * b) Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/dts-v1/; +#include "rk3328.dtsi" + +/ { + model = "Rockchip RK3328 BOX"; + compatible = "rockchip,rk3328-box", "rockchip,rk3328"; + + chosen { + bootargs = "earlyprintk=uart8250-32bit,0xff130000 swiotlb=1 kpti=0"; + stdout-path = "serial2:1500000n8"; + }; + + xin32k: xin32k { + compatible = "fixed-clock"; + clock-frequency = <32768>; + clock-output-names = "xin32k"; + #clock-cells = <0>; + }; + + vcc_phy: vcc-phy-regulator { + compatible = "regulator-fixed"; + regulator-name = "vcc_phy"; + regulator-always-on; + regulator-boot-on; + }; + + vcc_sys: vcc-sys { + compatible = "regulator-fixed"; + regulator-name = "vcc_sys"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + }; + + vcc_sd: sdmmc-regulator { + compatible = "regulator-fixed"; + gpio = <&gpio0 RK_PD6 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc0m1_gpio>; + regulator-name = "vcc_sd"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vcc_io>; + }; + + vcc_host_5v: vcc-host-5v-regulator { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio0 RK_PA0 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&usb30_host_drv>; + regulator-name = "vcc_host_5v"; + regulator-always-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&vcc_sys>; + }; + + vcc_host1_5v: vcc_otg_5v: vcc-host1-5v-regulator { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio0 RK_PA2 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&usb20_host_drv>; + regulator-name = "vcc_host1_5v"; + regulator-always-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&vcc_sys>; + }; + + leds { + compatible = "gpio-leds"; + + led1 { + gpios = <&rk805 0 GPIO_ACTIVE_LOW>; + linux,default-trigger = "default-on"; + default-state = "on"; + }; + + led2 { + gpios = <&rk805 1 GPIO_ACTIVE_LOW>; + linux,default-trigger = "mmc0"; + }; + }; + + ir-receiver { + compatible = "gpio-ir-receiver"; + gpios = <&gpio2 RK_PA2 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&ir_int>; + pinctrl-names = "default"; + }; + + hdmi-sound { + compatible = "simple-audio-card"; + simple-audio-card,format = "i2s"; + simple-audio-card,mclk-fs = <128>; + simple-audio-card,name = "HDMI"; + simple-audio-card,cpu { + sound-dai = <&i2s0>; + }; + simple-audio-card,codec { + sound-dai = <&hdmi>; + }; + }; + + spdif-sound { + compatible = "simple-audio-card"; + simple-audio-card,name = "SPDIF"; + simple-audio-card,cpu { + sound-dai = <&spdif>; + }; + simple-audio-card,codec { + sound-dai = <&spdif_out>; + }; + }; + + spdif_out: spdif-out { + compatible = "linux,spdif-dit"; + #sound-dai-cells = <0>; + }; + + sdio_pwrseq: sdio-pwrseq { + compatible = "mmc-pwrseq-simple"; + pinctrl-names = "default"; + pinctrl-0 = <&wifi_enable_h>; + + /* + * On the module itself this is one of these (depending + * on the actual card populated): + * - SDIO_RESET_L_WL_REG_ON + * - PDN (power down when low) + */ + reset-gpios = <&gpio1 RK_PC2 GPIO_ACTIVE_LOW>; + }; + + wireless-bluetooth { + compatible = "bluetooth-platdata"; + uart_rts_gpios = <&gpio1 RK_PB2 GPIO_ACTIVE_LOW>; + pinctrl-names = "default", "rts_gpio"; + pinctrl-0 = <&uart0_rts>; + pinctrl-1 = <&uart0_gpios>; + BT,power_gpio = <&gpio1 RK_PC5 GPIO_ACTIVE_HIGH>; + BT,wake_host_irq = <&gpio1 RK_PD2 GPIO_ACTIVE_HIGH>; + }; + + wireless-wlan { + compatible = "wlan-platdata"; + rockchip,grf = <&grf>; + wifi_chip_type = "rtl8723bs"; + WIFI,host_wake_irq = <&gpio1 RK_PC3 GPIO_ACTIVE_HIGH>; + }; +}; + +&codec { + #sound-dai-cells = <0>; + status = "okay"; +}; + +&cpu0 { + cpu-supply = <&vdd_arm>; +}; + +&cpu1 { + cpu-supply = <&vdd_arm>; +}; + +&cpu2 { + cpu-supply = <&vdd_arm>; +}; + +&cpu3 { + cpu-supply = <&vdd_arm>; +}; + +&dfi { + status = "okay"; +}; + +&dmc { + center-supply = <&vdd_logic>; + status = "okay"; +}; + +&display_subsystem { + status = "okay"; +}; + +&emmc { + bus-width = <8>; + cap-mmc-highspeed; + mmc-hs200-1_8v; + non-removable; + pinctrl-names = "default"; + pinctrl-0 = <&emmc_clk &emmc_cmd &emmc_bus8>; + supports-emmc; + vmmc-supply = <&vcc_io>; + vqmmc-supply = <&vcc18_emmc>; + status = "okay"; +}; + +&gmac2phy { + phy-supply = <&vcc_phy>; + assigned-clocks = <&cru SCLK_MAC2PHY_SRC>; + assigned-clock-rate = <50000000>; + assigned-clocks = <&cru SCLK_MAC2PHY>; + assigned-clock-parents = <&cru SCLK_MAC2PHY_SRC>; + clock_in_out = "output"; + status = "okay"; +}; + +&gpu { + status = "okay"; + mali-supply = <&vdd_logic>; +}; + +&h265e { + status = "okay"; +}; + +&h265e_mmu { + status = "okay"; +}; + +&hdmi { + #sound-dai-cells = <0>; + ddc-i2c-scl-high-time-ns = <9625>; + ddc-i2c-scl-low-time-ns = <10000>; + status = "okay"; +}; + +&hdmiphy { + status = "okay"; +}; + +&i2c1 { + status = "okay"; + + rk805: rk805@18 { + compatible = "rockchip,rk805"; + status = "okay"; + reg = <0x18>; + interrupt-parent = <&gpio2>; + interrupts = <6 IRQ_TYPE_LEVEL_LOW>; + #clock-cells = <1>; + clock-output-names = "rk805-clkout1", "rk805-clkout2"; + pinctrl-names = "default"; + pinctrl-0 = <&pmic_int_l>; + rockchip,system-power-controller; + wakeup-source; + gpio-controller; + #gpio-cells = <2>; + + vcc1-supply = <&vcc_sys>; + vcc2-supply = <&vcc_sys>; + vcc3-supply = <&vcc_sys>; + vcc4-supply = <&vcc_sys>; + vcc5-supply = <&vcc_io>; + vcc6-supply = <&vcc_sys>; + + rtc { + status = "okay"; + }; + + pwrkey { + status = "okay"; + }; + + gpio { + status = "okay"; + }; + + regulators { + compatible = "rk805-regulator"; + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + vdd_logic: RK805_DCDC1 { + regulator-compatible = "RK805_DCDC1"; + regulator-name = "vdd_logic"; + regulator-min-microvolt = <712500>; + regulator-max-microvolt = <1450000>; + regulator-initial-mode = <0x1>; + regulator-ramp-delay = <12500>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-mode = <0x2>; + regulator-on-in-suspend; + regulator-suspend-microvolt = <1000000>; + }; + }; + + vdd_arm: RK805_DCDC2 { + regulator-compatible = "RK805_DCDC2"; + regulator-name = "vdd_arm"; + regulator-init-microvolt = <1225000>; + regulator-min-microvolt = <712500>; + regulator-max-microvolt = <1450000>; + regulator-initial-mode = <0x1>; + regulator-ramp-delay = <12500>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-mode = <0x2>; + regulator-on-in-suspend; + regulator-suspend-microvolt = <950000>; + }; + }; + + vcc_ddr: RK805_DCDC3 { + regulator-compatible = "RK805_DCDC3"; + regulator-name = "vcc_ddr"; + regulator-initial-mode = <0x1>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-mode = <0x2>; + regulator-on-in-suspend; + }; + }; + + vcc_io: RK805_DCDC4 { + regulator-compatible = "RK805_DCDC4"; + regulator-name = "vcc_io"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-initial-mode = <0x1>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-mode = <0x2>; + regulator-on-in-suspend; + regulator-suspend-microvolt = <3300000>; + }; + }; + + vcc_18: RK805_LDO1 { + regulator-compatible = "RK805_LDO1"; + regulator-name = "vcc_18"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vcc18_emmc: RK805_LDO2 { + regulator-compatible = "RK805_LDO2"; + regulator-name = "vcc18_emmc"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vdd_10: RK805_LDO3 { + regulator-compatible = "RK805_LDO3"; + regulator-name = "vdd_10"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1000000>; + }; + }; + }; + }; +}; + +&i2s0 { + #sound-dai-cells = <0>; + rockchip,bclk-fs = <128>; + status = "okay"; +}; + +&iep { + status = "okay"; +}; + +&iep_mmu { + status = "okay"; +}; + +&io_domains { + status = "okay"; + + vccio1-supply = <&vcc_io>; + vccio2-supply = <&vcc18_emmc>; + vccio3-supply = <&vcc_io>; + vccio4-supply = <&vcc_18>; + vccio5-supply = <&vcc_io>; + vccio6-supply = <&vcc_io>; + pmuio-supply = <&vcc_io>; +}; + +&pinctrl { + pinctrl-names = "default"; + pinctrl-0 = <&clk_32k_out>; + + clk_32k { + clk_32k_out: clk-32k-out { + rockchip,pins = <1 RK_PD4 RK_FUNC_1 &pcfg_pull_none>; + }; + }; + + ir { + ir_int: ir-int { + rockchip,pins = <2 RK_PA2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + pmic { + pmic_int_l: pmic-int-l { + rockchip,pins = <2 RK_PA6 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + + sdio-pwrseq { + wifi_enable_h: wifi-enable-h { + rockchip,pins = <1 RK_PC2 RK_FUNC_GPIO &pcfg_pull_none_4ma>, + <1 RK_PC3 RK_FUNC_GPIO &pcfg_pull_none_4ma>; + }; + }; + + usb2 { + usb20_host_drv: usb20-host-drv { + rockchip,pins = <0 RK_PA2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + usb3 { + usb30_host_drv: usb30-host-drv { + rockchip,pins = <0 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + wireless-bluetooth { + uart0_gpios: uart0-gpios { + rockchip,pins = <1 RK_PB2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; +}; + +&rkvdec { + status = "okay"; + vcodec-supply = <&vdd_logic>; +}; + +&rkvdec_mmu { + status = "okay"; +}; + +&sdio { + bus-width = <4>; + cap-sd-highspeed; + cap-sdio-irq; + disable-wp; + keep-power-in-suspend; + mmc-pwrseq = <&sdio_pwrseq>; + non-removable; + num-slots = <1>; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc1_bus4 &sdmmc1_cmd &sdmmc1_clk>; + sd-uhs-sdr104; + supports-sdio; + status = "okay"; +}; + +&sdmmc { + bus-width = <4>; + cap-mmc-highspeed; + cap-sd-highspeed; + disable-wp; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc0_clk &sdmmc0_cmd &sdmmc0_dectn &sdmmc0_bus4>; + supports-sd; + vmmc-supply = <&vcc_sd>; + status = "okay"; +}; + +&spdif { + #sound-dai-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&spdifm0_tx>; + status = "okay"; +}; + +&threshold { + temperature = <80000>; /* millicelsius */ +}; + +&target { + temperature = <95000>; /* millicelsius */ +}; + +&soc_crit { + temperature = <100000>; /* millicelsius */ +}; + +&tsadc { + rockchip,hw-tshut-mode = <0>; + rockchip,hw-tshut-polarity = <0>; + rockchip,hw-tshut-temp = <110000>; + status = "okay"; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_xfer &uart0_cts>; + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&u2phy { + status = "okay"; +}; + +&u2phy_host { + phy-supply = <&vcc_host1_5v>; + status = "okay"; +}; + +&u2phy_otg { + phy-supply = <&vcc_otg_5v>; + status = "okay"; +}; + +&u3phy { + status = "okay"; +}; + +&u3phy_utmi { + phy-supply = <&vcc_host_5v>; + status = "okay"; +}; + +&u3phy_pipe { + phy-supply = <&vcc_host_5v>; + status = "okay"; +}; + +&usb20_otg { + dr_mode = "host"; + status = "okay"; +}; + +&usb_host0_ehci { + status = "okay"; +}; + +&usb_host0_ohci { + status = "okay"; +}; + +&usbdrd3 { + status = "okay"; +}; + +&usbdrd_dwc3 { + status = "okay"; +}; + +&vop { + status = "okay"; +}; + +&vop_mmu { + status = "okay"; +}; + +&vpu_service { + status = "okay"; +}; + +&vpu_mmu { + status = "okay"; +}; + +&vepu { + status = "okay"; +}; + +&vepu_mmu { + status = "okay"; +}; + +&venc_srv { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3328-dram-box-plus-timing.dtsi b/arch/arm64/boot/dts/rockchip/rk3328-dram-box-plus-timing.dtsi new file mode 100644 index 00000000..ac34cc7a --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3328-dram-box-plus-timing.dtsi @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2016 Fuzhou Rockchip Electronics Co., Ltd + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Or, alternatively, + * + * b) Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include +#include + +&ddr_timing { + ddr4_odt = ; + phy_ddr4_ca_drv = ; + phy_ddr4_ck_drv = ; + phy_ddr4_dq_drv = ; + phy_ddr4_odt = ; + + /* CA de-skew, one step is 47.8ps, range 0-15 */ + ddr3a1_ddr4a9_de-skew = <1>; + ddr3a0_ddr4a10_de-skew = <1>; + ddr3a3_ddr4a6_de-skew = <0>; + ddr3a2_ddr4a4_de-skew = <1>; + ddr3a5_ddr4a8_de-skew = <0>; + ddr3a4_ddr4a5_de-skew = <1>; + ddr3a7_ddr4a11_de-skew = <1>; + ddr3a6_ddr4a7_de-skew = <0>; + ddr3a9_ddr4a0_de-skew = <1>; + ddr3a8_ddr4a13_de-skew = <0>; + ddr3a11_ddr4a3_de-skew = <2>; + ddr3a10_ddr4cs0_de-skew = <3>; + ddr3a13_ddr4a2_de-skew = <1>; + ddr3a12_ddr4ba1_de-skew = <0>; + ddr3a15_ddr4odt0_de-skew = <3>; + ddr3a14_ddr4a1_de-skew = <2>; + ddr3ba1_ddr4a15_de-skew = <1>; + ddr3ba0_ddr4bg0_de-skew = <1>; + ddr3ras_ddr4cke_de-skew = <3>; + ddr3ba2_ddr4ba0_de-skew = <1>; + ddr3we_ddr4bg1_de-skew = <3>; + ddr3cas_ddr4a12_de-skew = <1>; + ddr3ckn_ddr4ckn_de-skew = <4>; + ddr3ckp_ddr4ckp_de-skew = <4>; + ddr3cke_ddr4a16_de-skew = <1>; + ddr3odt0_ddr4a14_de-skew = <1>; + ddr3cs0_ddr4act_de-skew = <2>; + ddr3reset_ddr4reset_de-skew = <3>; + ddr3cs1_ddr4cs1_de-skew = <2>; + ddr3odt1_ddr4odt1_de-skew = <2>; + + /* DATA de-skew + * RX one step is 25.1ps, range 0-15 + * TX one step is 47.8ps, range 0-15 + */ + cs0_dm0_rx_de-skew = <8>; + cs0_dm0_tx_de-skew = <9>; + cs0_dq0_rx_de-skew = <8>; + cs0_dq0_tx_de-skew = <9>; + cs0_dq1_rx_de-skew = <8>; + cs0_dq1_tx_de-skew = <9>; + cs0_dq2_rx_de-skew = <8>; + cs0_dq2_tx_de-skew = <9>; + cs0_dq3_rx_de-skew = <8>; + cs0_dq3_tx_de-skew = <9>; + cs0_dq4_rx_de-skew = <8>; + cs0_dq4_tx_de-skew = <9>; + cs0_dq5_rx_de-skew = <8>; + cs0_dq5_tx_de-skew = <9>; + cs0_dq6_rx_de-skew = <8>; + cs0_dq6_tx_de-skew = <9>; + cs0_dq7_rx_de-skew = <8>; + cs0_dq7_tx_de-skew = <9>; + cs0_dqs0_rx_de-skew = <7>; + cs0_dqs0p_tx_de-skew = <10>; + cs0_dqs0n_tx_de-skew = <10>; + + cs0_dm1_rx_de-skew = <8>; + cs0_dm1_tx_de-skew = <8>; + cs0_dq8_rx_de-skew = <8>; + cs0_dq8_tx_de-skew = <9>; + cs0_dq9_rx_de-skew = <8>; + cs0_dq9_tx_de-skew = <8>; + cs0_dq10_rx_de-skew = <8>; + cs0_dq10_tx_de-skew = <9>; + cs0_dq11_rx_de-skew = <8>; + cs0_dq11_tx_de-skew = <8>; + cs0_dq12_rx_de-skew = <8>; + cs0_dq12_tx_de-skew = <9>; + cs0_dq13_rx_de-skew = <8>; + cs0_dq13_tx_de-skew = <8>; + cs0_dq14_rx_de-skew = <8>; + cs0_dq14_tx_de-skew = <9>; + cs0_dq15_rx_de-skew = <8>; + cs0_dq15_tx_de-skew = <8>; + cs0_dqs1_rx_de-skew = <8>; + cs0_dqs1p_tx_de-skew = <10>; + cs0_dqs1n_tx_de-skew = <10>; + + cs0_dm2_rx_de-skew = <8>; + cs0_dm2_tx_de-skew = <9>; + cs0_dq16_rx_de-skew = <8>; + cs0_dq16_tx_de-skew = <9>; + cs0_dq17_rx_de-skew = <8>; + cs0_dq17_tx_de-skew = <9>; + cs0_dq18_rx_de-skew = <8>; + cs0_dq18_tx_de-skew = <9>; + cs0_dq19_rx_de-skew = <8>; + cs0_dq19_tx_de-skew = <9>; + cs0_dq20_rx_de-skew = <8>; + cs0_dq20_tx_de-skew = <9>; + cs0_dq21_rx_de-skew = <8>; + cs0_dq21_tx_de-skew = <9>; + cs0_dq22_rx_de-skew = <8>; + cs0_dq22_tx_de-skew = <9>; + cs0_dq23_rx_de-skew = <8>; + cs0_dq23_tx_de-skew = <9>; + cs0_dqs2_rx_de-skew = <7>; + cs0_dqs2p_tx_de-skew = <10>; + cs0_dqs2n_tx_de-skew = <10>; + + cs0_dm3_rx_de-skew = <8>; + cs0_dm3_tx_de-skew = <8>; + cs0_dq24_rx_de-skew = <8>; + cs0_dq24_tx_de-skew = <9>; + cs0_dq25_rx_de-skew = <8>; + cs0_dq25_tx_de-skew = <8>; + cs0_dq26_rx_de-skew = <8>; + cs0_dq26_tx_de-skew = <8>; + cs0_dq27_rx_de-skew = <8>; + cs0_dq27_tx_de-skew = <8>; + cs0_dq28_rx_de-skew = <8>; + cs0_dq28_tx_de-skew = <8>; + cs0_dq29_rx_de-skew = <8>; + cs0_dq29_tx_de-skew = <8>; + cs0_dq30_rx_de-skew = <8>; + cs0_dq30_tx_de-skew = <8>; + cs0_dq31_rx_de-skew = <8>; + cs0_dq31_tx_de-skew = <8>; + cs0_dqs3_rx_de-skew = <8>; + cs0_dqs3p_tx_de-skew = <10>; + cs0_dqs3n_tx_de-skew = <10>; + + cs1_dm0_rx_de-skew = <8>; + cs1_dm0_tx_de-skew = <9>; + cs1_dq0_rx_de-skew = <8>; + cs1_dq0_tx_de-skew = <9>; + cs1_dq1_rx_de-skew = <8>; + cs1_dq1_tx_de-skew = <9>; + cs1_dq2_rx_de-skew = <8>; + cs1_dq2_tx_de-skew = <9>; + cs1_dq3_rx_de-skew = <8>; + cs1_dq3_tx_de-skew = <9>; + cs1_dq4_rx_de-skew = <8>; + cs1_dq4_tx_de-skew = <9>; + cs1_dq5_rx_de-skew = <8>; + cs1_dq5_tx_de-skew = <9>; + cs1_dq6_rx_de-skew = <8>; + cs1_dq6_tx_de-skew = <9>; + cs1_dq7_rx_de-skew = <8>; + cs1_dq7_tx_de-skew = <9>; + cs1_dqs0_rx_de-skew = <7>; + cs1_dqs0p_tx_de-skew = <10>; + cs1_dqs0n_tx_de-skew = <10>; + + cs1_dm1_rx_de-skew = <8>; + cs1_dm1_tx_de-skew = <8>; + cs1_dq8_rx_de-skew = <8>; + cs1_dq8_tx_de-skew = <9>; + cs1_dq9_rx_de-skew = <8>; + cs1_dq9_tx_de-skew = <8>; + cs1_dq10_rx_de-skew = <8>; + cs1_dq10_tx_de-skew = <9>; + cs1_dq11_rx_de-skew = <8>; + cs1_dq11_tx_de-skew = <8>; + cs1_dq12_rx_de-skew = <8>; + cs1_dq12_tx_de-skew = <9>; + cs1_dq13_rx_de-skew = <8>; + cs1_dq13_tx_de-skew = <8>; + cs1_dq14_rx_de-skew = <8>; + cs1_dq14_tx_de-skew = <9>; + cs1_dq15_rx_de-skew = <8>; + cs1_dq15_tx_de-skew = <8>; + cs1_dqs1_rx_de-skew = <8>; + cs1_dqs1p_tx_de-skew = <10>; + cs1_dqs1n_tx_de-skew = <10>; + + cs1_dm2_rx_de-skew = <8>; + cs1_dm2_tx_de-skew = <9>; + cs1_dq16_rx_de-skew = <8>; + cs1_dq16_tx_de-skew = <9>; + cs1_dq17_rx_de-skew = <8>; + cs1_dq17_tx_de-skew = <9>; + cs1_dq18_rx_de-skew = <8>; + cs1_dq18_tx_de-skew = <9>; + cs1_dq19_rx_de-skew = <8>; + cs1_dq19_tx_de-skew = <9>; + cs1_dq20_rx_de-skew = <8>; + cs1_dq20_tx_de-skew = <9>; + cs1_dq21_rx_de-skew = <8>; + cs1_dq21_tx_de-skew = <9>; + cs1_dq22_rx_de-skew = <8>; + cs1_dq22_tx_de-skew = <9>; + cs1_dq23_rx_de-skew = <8>; + cs1_dq23_tx_de-skew = <9>; + cs1_dqs2_rx_de-skew = <7>; + cs1_dqs2p_tx_de-skew = <10>; + cs1_dqs2n_tx_de-skew = <10>; + + cs1_dm3_rx_de-skew = <8>; + cs1_dm3_tx_de-skew = <8>; + cs1_dq24_rx_de-skew = <8>; + cs1_dq24_tx_de-skew = <9>; + cs1_dq25_rx_de-skew = <8>; + cs1_dq25_tx_de-skew = <8>; + cs1_dq26_rx_de-skew = <8>; + cs1_dq26_tx_de-skew = <8>; + cs1_dq27_rx_de-skew = <8>; + cs1_dq27_tx_de-skew = <8>; + cs1_dq28_rx_de-skew = <8>; + cs1_dq28_tx_de-skew = <8>; + cs1_dq29_rx_de-skew = <8>; + cs1_dq29_tx_de-skew = <8>; + cs1_dq30_rx_de-skew = <8>; + cs1_dq30_tx_de-skew = <8>; + cs1_dq31_rx_de-skew = <8>; + cs1_dq31_tx_de-skew = <8>; + cs1_dqs3_rx_de-skew = <8>; + cs1_dqs3p_tx_de-skew = <10>; + cs1_dqs3n_tx_de-skew = <10>; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3328-roc-cc.dts b/arch/arm64/boot/dts/rockchip/rk3328-roc-cc.dts new file mode 100644 index 00000000..5df9b497 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3328-roc-cc.dts @@ -0,0 +1,597 @@ +/* + * Copyright (c) 2017 Fuzhou Rockchip Electronics Co., Ltd + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Or, alternatively, + * + * b) Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/dts-v1/; +#include "rk3328.dtsi" +#include "rk3328-dram-box-plus-timing.dtsi" + +/ { + model = "Firefly ROC-RK3328-CC Board"; + compatible = "firefly,roc-rk3328-cc", "rockchip,rk3328"; + + chosen { + bootargs = "earlyprintk=uart8250-32bit,0xff130000 swiotlb=1 kpti=0"; + stdout-path = "serial2:1500000n8"; + }; + + xin32k: xin32k { + compatible = "fixed-clock"; + clock-frequency = <32768>; + clock-output-names = "xin32k"; + #clock-cells = <0>; + }; + + gmac_clkin: external-gmac-clock { + compatible = "fixed-clock"; + clock-frequency = <125000000>; + clock-output-names = "gmac_clkin"; + #clock-cells = <0>; + }; + + vcc_phy: vcc-phy-regulator { + compatible = "regulator-fixed"; + regulator-name = "vcc_phy"; + regulator-always-on; + regulator-boot-on; + }; + + vcc_sys: vcc-sys { + compatible = "regulator-fixed"; + regulator-name = "vcc_sys"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + }; + + vcc_sd: sdmmc-regulator { + compatible = "regulator-fixed"; + gpio = <&gpio0 RK_PD6 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc0m1_gpio>; + regulator-name = "vcc_sd"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vcc_io>; + }; + + vccio_sd: sdmmcio-regulator { + compatible = "regulator-gpio"; + gpios = <&gpio0 RK_PD1 GPIO_ACTIVE_HIGH>; + states = <1800000 0x1 + 3300000 0x0>; + regulator-name = "vccio_sd"; + regulator-type = "voltage"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + }; + + vcc_host_5v: vcc_host1_5v: vcc_otg_5v: vcc-host-5v-regulator { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio1 RK_PD2 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&usb_host_drv>; + regulator-name = "vcc_host_5v"; + regulator-always-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&vcc_sys>; + }; + + leds { + compatible = "gpio-leds"; + + power { + gpios = <&rk805 1 GPIO_ACTIVE_LOW>; + linux,default-trigger = "heartbeat"; + }; + + user { + gpios = <&rk805 0 GPIO_ACTIVE_LOW>; + linux,default-trigger = "mmc0"; + }; + }; + + ir-receiver { + compatible = "gpio-ir-receiver"; + gpios = <&gpio2 RK_PA2 GPIO_ACTIVE_LOW>; + linux,rc-map-name = "rc-roc-cc"; + pinctrl-0 = <&ir_int>; + pinctrl-names = "default"; + }; + + hdmi-sound { + compatible = "simple-audio-card"; + simple-audio-card,format = "i2s"; + simple-audio-card,mclk-fs = <128>; + simple-audio-card,name = "HDMI"; + simple-audio-card,cpu { + sound-dai = <&i2s0>; + }; + simple-audio-card,codec { + sound-dai = <&hdmi>; + }; + }; + + sound { + compatible = "simple-audio-card"; + simple-audio-card,format = "i2s"; + simple-audio-card,mclk-fs = <256>; + simple-audio-card,name = "I2S"; + simple-audio-card,cpu { + sound-dai = <&i2s1>; + }; + simple-audio-card,codec { + sound-dai = <&codec>; + }; + }; +}; + +&codec { + #sound-dai-cells = <0>; + status = "okay"; +}; + +&cpu0 { + cpu-supply = <&vdd_arm>; +}; + +&cpu1 { + cpu-supply = <&vdd_arm>; +}; + +&cpu2 { + cpu-supply = <&vdd_arm>; +}; + +&cpu3 { + cpu-supply = <&vdd_arm>; +}; + +&dfi { + status = "okay"; +}; + +&dmc { + center-supply = <&vdd_logic>; + system-status-freq = < + /*system status freq(KHz)*/ + SYS_STATUS_NORMAL 1066000 + SYS_STATUS_REBOOT 1066000 + SYS_STATUS_SUSPEND 1066000 + SYS_STATUS_VIDEO_1080P 1066000 + SYS_STATUS_VIDEO_4K 1066000 + SYS_STATUS_VIDEO_4K_10B 1066000 + SYS_STATUS_PERFORMANCE 1066000 + SYS_STATUS_BOOST 1066000 + >; + status = "okay"; +}; + +&dmc_opp_table { + rockchip,leakage-voltage-sel = < + 1 8 0 + 9 254 0 + >; + opp-933000000 { + opp-hz = /bits/ 64 <933000000>; + opp-microvolt = <1150000>; + opp-microvolt-L0 = <1150000>; + opp-microvolt-L1 = <1100000>; + }; + opp-1066000000 { + opp-hz = /bits/ 64 <1066000000>; + opp-microvolt = <1200000>; + opp-microvolt-L0 = <1200000>; + opp-microvolt-L1 = <1175000>; + }; +}; + +&display_subsystem { + status = "okay"; +}; + +&emmc { + bus-width = <8>; + cap-mmc-highspeed; + mmc-hs200-1_8v; + non-removable; + pinctrl-names = "default"; + pinctrl-0 = <&emmc_clk &emmc_cmd &emmc_bus8>; + supports-emmc; + vmmc-supply = <&vcc_io>; + vqmmc-supply = <&vcc18_emmc>; + status = "okay"; +}; + +&gmac2io { + assigned-clocks = <&cru SCLK_MAC2IO>, <&cru SCLK_MAC2IO_EXT>; + assigned-clock-parents = <&gmac_clkin>, <&gmac_clkin>; + clock_in_out = "input"; + phy-supply = <&vcc_phy>; + phy-mode = "rgmii"; + pinctrl-names = "default"; + pinctrl-0 = <&rgmiim1_pins>; + snps,reset-gpio = <&gpio1 RK_PC2 GPIO_ACTIVE_LOW>; + snps,reset-active-low; + snps,reset-delays-us = <0 10000 50000>; + tx_delay = <0x25>; + rx_delay = <0x11>; + status = "okay"; +}; + +&gpu { + status = "okay"; + mali-supply = <&vdd_logic>; +}; + +&h265e { + status = "okay"; +}; + +&h265e_mmu { + status = "okay"; +}; + +&hdmi { + #sound-dai-cells = <0>; + ddc-i2c-scl-high-time-ns = <9625>; + ddc-i2c-scl-low-time-ns = <10000>; + status = "okay"; +}; + +&hdmiphy { + status = "okay"; +}; + +&i2c1 { + status = "okay"; + + rk805: rk805@18 { + compatible = "rockchip,rk805"; + status = "okay"; + reg = <0x18>; + interrupt-parent = <&gpio1>; + interrupts = <24 IRQ_TYPE_LEVEL_LOW>; + #clock-cells = <1>; + clock-output-names = "rk805-clkout1", "rk805-clkout2"; + pinctrl-names = "default"; + pinctrl-0 = <&pmic_int_l>; + rockchip,system-power-controller; + wakeup-source; + gpio-controller; + #gpio-cells = <2>; + + vcc1-supply = <&vcc_sys>; + vcc2-supply = <&vcc_sys>; + vcc3-supply = <&vcc_sys>; + vcc4-supply = <&vcc_sys>; + vcc5-supply = <&vcc_io>; + vcc6-supply = <&vcc_sys>; + + rtc { + status = "okay"; + }; + + pwrkey { + status = "okay"; + }; + + gpio { + status = "okay"; + }; + + regulators { + compatible = "rk805-regulator"; + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + vdd_logic: RK805_DCDC1 { + regulator-compatible = "RK805_DCDC1"; + regulator-name = "vdd_logic"; + regulator-min-microvolt = <712500>; + regulator-max-microvolt = <1450000>; + regulator-initial-mode = <0x1>; + regulator-ramp-delay = <12500>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-mode = <0x2>; + regulator-on-in-suspend; + regulator-suspend-microvolt = <1000000>; + }; + }; + + vdd_arm: RK805_DCDC2 { + regulator-compatible = "RK805_DCDC2"; + regulator-name = "vdd_arm"; + regulator-init-microvolt = <1225000>; + regulator-min-microvolt = <712500>; + regulator-max-microvolt = <1450000>; + regulator-initial-mode = <0x1>; + regulator-ramp-delay = <12500>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-mode = <0x2>; + regulator-on-in-suspend; + regulator-suspend-microvolt = <950000>; + }; + }; + + vcc_ddr: RK805_DCDC3 { + regulator-compatible = "RK805_DCDC3"; + regulator-name = "vcc_ddr"; + regulator-initial-mode = <0x1>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-mode = <0x2>; + regulator-on-in-suspend; + }; + }; + + vcc_io: RK805_DCDC4 { + regulator-compatible = "RK805_DCDC4"; + regulator-name = "vcc_io"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-initial-mode = <0x1>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-mode = <0x2>; + regulator-on-in-suspend; + regulator-suspend-microvolt = <3300000>; + }; + }; + + vcc_18: RK805_LDO1 { + regulator-compatible = "RK805_LDO1"; + regulator-name = "vcc_18"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vcc18_emmc: RK805_LDO2 { + regulator-compatible = "RK805_LDO2"; + regulator-name = "vcc18_emmc"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vdd_10: RK805_LDO3 { + regulator-compatible = "RK805_LDO3"; + regulator-name = "vdd_10"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1000000>; + }; + }; + }; + }; +}; + +&i2s0 { + #sound-dai-cells = <0>; + rockchip,bclk-fs = <128>; + status = "okay"; +}; + +&iep { + status = "okay"; +}; + +&iep_mmu { + status = "okay"; +}; + +&io_domains { + status = "okay"; + + vccio1-supply = <&vcc_io>; + vccio2-supply = <&vcc18_emmc>; + vccio3-supply = <&vccio_sd>; + vccio4-supply = <&vcc_io>; + vccio5-supply = <&vcc_io>; + vccio6-supply = <&vcc_io>; + pmuio-supply = <&vcc_io>; +}; + +&pinctrl { + ir { + ir_int: ir-int { + rockchip,pins = <2 RK_PA2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + pmic { + pmic_int_l: pmic-int-l { + rockchip,pins = <1 RK_PD0 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + + usb { + usb_host_drv: usb-host-drv { + rockchip,pins = <1 RK_PD2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; +}; + +&rkvdec { + status = "okay"; + vcodec-supply = <&vdd_logic>; +}; + +&rkvdec_mmu { + status = "okay"; +}; + +&sdmmc { + bus-width = <4>; + cap-mmc-highspeed; + cap-sd-highspeed; + disable-wp; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc0_clk &sdmmc0_cmd &sdmmc0_dectn &sdmmc0_bus4>; + supports-sd; + vmmc-supply = <&vcc_sd>; + status = "okay"; +}; + +&threshold { + temperature = <80000>; /* millicelsius */ +}; + +&target { + temperature = <95000>; /* millicelsius */ +}; + +&soc_crit { + temperature = <100000>; /* millicelsius */ +}; + +&tsadc { + rockchip,hw-tshut-mode = <0>; + rockchip,hw-tshut-polarity = <0>; + rockchip,hw-tshut-temp = <110000>; + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&u2phy { + status = "okay"; +}; + +&u2phy_host { + phy-supply = <&vcc_host1_5v>; + status = "okay"; +}; + +&u2phy_otg { + phy-supply = <&vcc_otg_5v>; + status = "okay"; +}; + +&u3phy { + status = "okay"; +}; + +&u3phy_utmi { + phy-supply = <&vcc_host_5v>; + status = "okay"; +}; + +&u3phy_pipe { + phy-supply = <&vcc_host_5v>; + status = "okay"; +}; + +&usb20_otg { + dr_mode = "host"; + status = "okay"; +}; + +&usb_host0_ehci { + status = "okay"; +}; + +&usb_host0_ohci { + status = "okay"; +}; + +&usbdrd3 { + status = "okay"; +}; + +&usbdrd_dwc3 { + status = "okay"; +}; + +&vop { + status = "okay"; +}; + +&vop_mmu { + status = "okay"; +}; + +&vpu_service { + status = "okay"; +}; + +&vpu_mmu { + status = "okay"; +}; + +&vepu { + status = "okay"; +}; + +&vepu_mmu { + status = "okay"; +}; + +&venc_srv { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3328-rock64.dts b/arch/arm64/boot/dts/rockchip/rk3328-rock64.dts index 4b2eef60..d47d572a 100644 --- a/arch/arm64/boot/dts/rockchip/rk3328-rock64.dts +++ b/arch/arm64/boot/dts/rockchip/rk3328-rock64.dts @@ -48,20 +48,15 @@ compatible = "pine64,rock64", "rockchip,rk3328"; chosen { - bootargs = "rockchip_jtag earlyprintk=uart8250-32bit,0xff130000 swiotlb=1 kpti=0"; + bootargs = "earlyprintk=uart8250-32bit,0xff130000 swiotlb=1 kpti=0"; stdout-path = "serial2:1500000n8"; }; - fiq-debugger { - compatible = "rockchip,fiq-debugger"; - rockchip,serial-id = <2>; - rockchip,signal-irq = <159>; - rockchip,wake-irq = <0>; - /* If enable uart uses irq instead of fiq */ - rockchip,irq-mode-enable = <0>; - rockchip,baudrate = <1500000>; /* Only 115200 and 1500000 */ - interrupts = ; - status = "okay"; + xin32k: xin32k { + compatible = "fixed-clock"; + clock-frequency = <32768>; + clock-output-names = "xin32k"; + #clock-cells = <0>; }; gmac_clkin: external-gmac-clock { @@ -71,87 +66,125 @@ #clock-cells = <0>; }; + vcc_phy: vcc-phy-regulator { + compatible = "regulator-fixed"; + regulator-name = "vcc_phy"; + regulator-always-on; + regulator-boot-on; + }; + + vcc_sys: vcc-sys { + compatible = "regulator-fixed"; + regulator-name = "vcc_sys"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + }; + vcc_sd: sdmmc-regulator { compatible = "regulator-fixed"; - gpio = <&gpio0 30 GPIO_ACTIVE_LOW>; + gpio = <&gpio0 RK_PD6 GPIO_ACTIVE_LOW>; pinctrl-names = "default"; pinctrl-0 = <&sdmmc0m1_gpio>; regulator-name = "vcc_sd"; + regulator-always-on; + regulator-boot-on; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; vin-supply = <&vcc_io>; }; - vcc_host_5v: vcc-host-5v-regulator { + vcc_host_5v: vcc_otg_5v: vcc-host-5v-regulator { compatible = "regulator-fixed"; - enable-active-high; - gpio = <&gpio0 0 GPIO_ACTIVE_HIGH>; + gpio = <&gpio0 RK_PA2 GPIO_ACTIVE_LOW>; pinctrl-names = "default"; - pinctrl-0 = <&usb30_host_drv>; + pinctrl-0 = <&usb_host_drv>; regulator-name = "vcc_host_5v"; - regulator-always-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; vin-supply = <&vcc_sys>; }; - vcc_host1_5v: vcc_otg_5v: vcc-host1-5v-regulator { - compatible = "regulator-fixed"; - enable-active-high; - gpio = <&gpio0 27 GPIO_ACTIVE_HIGH>; - pinctrl-names = "default"; - pinctrl-0 = <&usb20_host_drv>; - regulator-name = "vcc_host1_5v"; - regulator-always-on; - vin-supply = <&vcc_sys>; - }; + leds { + compatible = "gpio-leds"; - vcc_sys: vcc-sys { - compatible = "regulator-fixed"; - regulator-name = "vcc_sys"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - }; + standby-led { + gpios = <&rk805 0 GPIO_ACTIVE_LOW>; + linux,default-trigger = "heartbeat"; + }; - xin32k: xin32k { - compatible = "fixed-clock"; - clock-frequency = <32768>; - clock-output-names = "xin32k"; - #clock-cells = <0>; + power-led { + gpios = <&rk805 1 GPIO_ACTIVE_LOW>; + linux,default-trigger = "mmc0"; + }; }; ir-receiver { compatible = "gpio-ir-receiver"; + gpios = <&gpio2 RK_PA2 GPIO_ACTIVE_LOW>; + linux,rc-map-name = "rc-pine64"; pinctrl-0 = <&ir_int>; pinctrl-names = "default"; - status = "okay"; }; - sound { + dummy_codec: dummy-codec { + compatible = "linux,snd-soc-dummy"; + #sound-dai-cells = <0>; + }; + + hdmi-sound { compatible = "simple-audio-card"; simple-audio-card,format = "i2s"; - simple-audio-card,mclk-fs = <256>; - simple-audio-card,name = "rockchip,rk3328"; + simple-audio-card,mclk-fs = <128>; + simple-audio-card,name = "HDMI"; simple-audio-card,cpu { - sound-dai = <&i2s1>; + sound-dai = <&i2s0>; }; simple-audio-card,codec { - sound-dai = <&codec>; + sound-dai = <&hdmi>; }; }; - hdmi-sound { + sound { compatible = "simple-audio-card"; - simple-audio-card,format = "i2s"; - simple-audio-card,mclk-fs = <128>; - simple-audio-card,name = "rockchip,hdmi"; + simple-audio-card,mclk-fs = <256>; + simple-audio-card,name = "I2S"; + simple-audio-card,dai-link@0 { + format = "i2s"; + cpu { + sound-dai = <&i2s1>; + }; + codec { + sound-dai = <&codec>; + }; + }; + simple-audio-card,dai-link@1 { + format = "i2s"; + cpu { + sound-dai = <&i2s1>; + }; + codec { + sound-dai = <&dummy_codec>; + }; + }; + }; + + spdif-sound { + compatible = "simple-audio-card"; + simple-audio-card,name = "SPDIF"; simple-audio-card,cpu { - sound-dai = <&i2s0>; + sound-dai = <&spdif>; }; simple-audio-card,codec { - sound-dai = <&hdmi>; + sound-dai = <&spdif_out>; }; }; + + spdif_out: spdif-out { + compatible = "linux,spdif-dit"; + #sound-dai-cells = <0>; + }; }; &codec { @@ -175,6 +208,15 @@ cpu-supply = <&vdd_arm>; }; +&dfi { + status = "okay"; +}; + +&dmc { + center-supply = <&vdd_logic>; + status = "okay"; +}; + &display_subsystem { status = "okay"; }; @@ -184,30 +226,40 @@ cap-mmc-highspeed; mmc-hs200-1_8v; non-removable; - supports-emmc; pinctrl-names = "default"; pinctrl-0 = <&emmc_clk &emmc_cmd &emmc_bus8>; + supports-emmc; vmmc-supply = <&vcc_io>; vqmmc-supply = <&vcc18_emmc>; status = "okay"; }; &gmac2io { - phy-supply = <&vcc_io>; - phy-mode = "rgmii"; assigned-clocks = <&cru SCLK_MAC2IO>, <&cru SCLK_MAC2IO_EXT>; assigned-clock-parents = <&gmac_clkin>, <&gmac_clkin>; clock_in_out = "input"; - snps,reset-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>; - snps,reset-active-low; - snps,reset-delays-us = <0 10000 50000>; + phy-supply = <&vcc_phy>; + phy-mode = "rgmii"; pinctrl-names = "default"; pinctrl-0 = <&rgmiim1_pins>; - tx_delay = <0x26>; - rx_delay = <0x11>; + snps,reset-gpio = <&gpio1 RK_PC2 GPIO_ACTIVE_LOW>; + snps,reset-active-low; + snps,reset-delays-us = <0 10000 50000>; + tx_delay = <0x24>; + rx_delay = <0x18>; status = "okay"; }; +&gmac2phy { + phy-supply = <&vcc_phy>; + assigned-clocks = <&cru SCLK_MAC2PHY_SRC>; + assigned-clock-rate = <50000000>; + assigned-clocks = <&cru SCLK_MAC2PHY>; + assigned-clock-parents = <&cru SCLK_MAC2PHY_SRC>; + clock_in_out = "output"; + status = "disabled"; +}; + &gpu { status = "okay"; mali-supply = <&vdd_logic>; @@ -223,6 +275,8 @@ &hdmi { #sound-dai-cells = <0>; + ddc-i2c-scl-high-time-ns = <9625>; + ddc-i2c-scl-low-time-ns = <10000>; status = "okay"; }; @@ -239,14 +293,14 @@ reg = <0x18>; interrupt-parent = <&gpio2>; interrupts = <6 IRQ_TYPE_LEVEL_LOW>; + #clock-cells = <1>; + clock-output-names = "rk805-clkout1", "rk805-clkout2"; pinctrl-names = "default"; pinctrl-0 = <&pmic_int_l>; rockchip,system-power-controller; wakeup-source; gpio-controller; - clock-output-names = "rk805-clkout1", "rk805-clkout2"; #gpio-cells = <2>; - #clock-cells = <1>; vcc1-supply = <&vcc_sys>; vcc2-supply = <&vcc_sys>; @@ -256,11 +310,11 @@ vcc6-supply = <&vcc_sys>; rtc { - status = "disabled"; + status = "okay"; }; pwrkey { - status = "disabled"; + status = "okay"; }; gpio { @@ -280,8 +334,8 @@ regulator-max-microvolt = <1450000>; regulator-initial-mode = <0x1>; regulator-ramp-delay = <12500>; - regulator-boot-on; regulator-always-on; + regulator-boot-on; regulator-state-mem { regulator-mode = <0x2>; regulator-on-in-suspend; @@ -292,12 +346,13 @@ vdd_arm: RK805_DCDC2 { regulator-compatible = "RK805_DCDC2"; regulator-name = "vdd_arm"; + regulator-init-microvolt = <1225000>; regulator-min-microvolt = <712500>; regulator-max-microvolt = <1450000>; regulator-initial-mode = <0x1>; regulator-ramp-delay = <12500>; - regulator-boot-on; regulator-always-on; + regulator-boot-on; regulator-state-mem { regulator-mode = <0x2>; regulator-on-in-suspend; @@ -309,8 +364,8 @@ regulator-compatible = "RK805_DCDC3"; regulator-name = "vcc_ddr"; regulator-initial-mode = <0x1>; - regulator-boot-on; regulator-always-on; + regulator-boot-on; regulator-state-mem { regulator-mode = <0x2>; regulator-on-in-suspend; @@ -323,8 +378,8 @@ regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; regulator-initial-mode = <0x1>; - regulator-boot-on; regulator-always-on; + regulator-boot-on; regulator-state-mem { regulator-mode = <0x2>; regulator-on-in-suspend; @@ -332,13 +387,13 @@ }; }; - vdd_18: RK805_LDO1 { + vcc_18: RK805_LDO1 { regulator-compatible = "RK805_LDO1"; - regulator-name = "vdd_18"; + regulator-name = "vcc_18"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; - regulator-boot-on; regulator-always-on; + regulator-boot-on; regulator-state-mem { regulator-on-in-suspend; regulator-suspend-microvolt = <1800000>; @@ -350,8 +405,8 @@ regulator-name = "vcc18_emmc"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; - regulator-boot-on; regulator-always-on; + regulator-boot-on; regulator-state-mem { regulator-on-in-suspend; regulator-suspend-microvolt = <1800000>; @@ -363,8 +418,8 @@ regulator-name = "vdd_10"; regulator-min-microvolt = <1000000>; regulator-max-microvolt = <1000000>; - regulator-boot-on; regulator-always-on; + regulator-boot-on; regulator-state-mem { regulator-on-in-suspend; regulator-suspend-microvolt = <1000000>; @@ -381,17 +436,35 @@ }; &i2s1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2s1_mclk + &i2s1_sclk + &i2s1_lrcktx + &i2s1_lrckrx + &i2s1_sdo + &i2s1_sdi + &i2s1_sdio1 + &i2s1_sdio2 + &i2s1_sdio3>; #sound-dai-cells = <0>; status = "okay"; }; +&iep { + status = "okay"; +}; + +&iep_mmu { + status = "okay"; +}; + &io_domains { status = "okay"; vccio1-supply = <&vcc_io>; vccio2-supply = <&vcc18_emmc>; vccio3-supply = <&vcc_io>; - vccio4-supply = <&vdd_18>; + vccio4-supply = <&vcc_18>; vccio5-supply = <&vcc_io>; vccio6-supply = <&vcc_io>; pmuio-supply = <&vcc_io>; @@ -400,37 +473,26 @@ &pinctrl { ir { ir_int: ir-int { - rockchip,pins = <2 2 RK_FUNC_GPIO &pcfg_pull_none>; + rockchip,pins = <2 RK_PA2 RK_FUNC_GPIO &pcfg_pull_none>; }; }; pmic { pmic_int_l: pmic-int-l { - rockchip,pins = <2 6 RK_FUNC_GPIO &pcfg_pull_up>; + rockchip,pins = <2 RK_PA6 RK_FUNC_GPIO &pcfg_pull_up>; }; }; - sdio-pwrseq { - wifi_enable_h: wifi-enable-h { - rockchip,pins = <1 18 RK_FUNC_GPIO &pcfg_pull_none>; - }; - }; - - usb2 { - usb20_host_drv: usb20-host-drv { - rockchip,pins = <0 27 RK_FUNC_GPIO &pcfg_pull_none>; - }; - }; - - usb3 { - usb30_host_drv: usb30-host-drv { - rockchip,pins = <0 0 RK_FUNC_GPIO &pcfg_pull_none>; + usb { + usb_host_drv: usb-host-drv { + rockchip,pins = <0 RK_PA2 RK_FUNC_GPIO &pcfg_pull_none>; }; }; }; &rkvdec { status = "okay"; + vcodec-supply = <&vdd_logic>; }; &rkvdec_mmu { @@ -442,11 +504,17 @@ cap-mmc-highspeed; cap-sd-highspeed; disable-wp; - max-frequency = <150000000>; pinctrl-names = "default"; pinctrl-0 = <&sdmmc0_clk &sdmmc0_cmd &sdmmc0_dectn &sdmmc0_bus4>; - vmmc-supply = <&vcc_sd>; supports-sd; + vmmc-supply = <&vcc_sd>; + status = "okay"; +}; + +&spdif { + #sound-dai-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&spdifm0_tx>; status = "okay"; }; @@ -454,19 +522,43 @@ status = "okay"; flash@0 { - compatible = "gigadevice,gd25q128", "jedec,spi-nor"; + compatible = "jedec,spi-nor"; #address-cells = <1>; #size-cells = <1>; reg = <0>; m25p,fast-read; /* The max SCLK of the flash 104/80 MHZ */ spi-max-frequency = <50000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + loader@8000 { + label = "loader"; + reg = <0x8000 0x3F0000>; + }; + }; }; }; +&threshold { + temperature = <80000>; /* millicelsius */ +}; + +&target { + temperature = <95000>; /* millicelsius */ +}; + +&soc_crit { + temperature = <100000>; /* millicelsius */ +}; + &tsadc { rockchip,hw-tshut-mode = <0>; rockchip,hw-tshut-polarity = <0>; + rockchip,hw-tshut-temp = <110000>; status = "okay"; }; @@ -476,21 +568,19 @@ &u2phy { status = "okay"; - }; &u2phy_host { - phy-supply = <&vcc_host1_5v>; status = "okay"; }; &u2phy_otg { - phy-supply = <&vcc_otg_5v>; + vbus-supply = <&vcc_otg_5v>; status = "okay"; }; &u3phy { - phy-supply = <&vcc_host_5v>; + vbus-supply = <&vcc_host_5v>; status = "okay"; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3328-rockbox.dts b/arch/arm64/boot/dts/rockchip/rk3328-rockbox.dts new file mode 100644 index 00000000..4ba9b1e7 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3328-rockbox.dts @@ -0,0 +1,583 @@ +/* + * Copyright (c) 2017 Fuzhou Rockchip Electronics Co., Ltd + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Or, alternatively, + * + * b) Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/dts-v1/; +#include "rk3328.dtsi" + +/ { + model = "Pine64 RockBox"; + compatible = "pine64,rockbox", "rockchip,rk3328"; + + chosen { + bootargs = "earlyprintk=uart8250-32bit,0xff130000 swiotlb=1 kpti=0"; + stdout-path = "serial2:1500000n8"; + }; + + xin32k: xin32k { + compatible = "fixed-clock"; + clock-frequency = <32768>; + clock-output-names = "xin32k"; + #clock-cells = <0>; + }; + + vcc_phy: vcc-phy-regulator { + compatible = "regulator-fixed"; + regulator-name = "vcc_phy"; + regulator-always-on; + regulator-boot-on; + }; + + vcc_sys: vcc-sys { + compatible = "regulator-fixed"; + regulator-name = "vcc_sys"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + }; + + vcc_sd: sdmmc-regulator { + compatible = "regulator-fixed"; + gpio = <&gpio0 RK_PD6 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc0m1_gpio>; + regulator-name = "vcc_sd"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vcc_io>; + }; + + vcc_host_5v: vcc_host1_5v: vcc_otg_5v: vcc-host-5v-regulator { + compatible = "regulator-fixed"; + regulator-name = "vcc_host_5v"; + regulator-always-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + vin-supply = <&vcc_sys>; + }; + + leds { + compatible = "gpio-leds"; + + power { + gpios = <&rk805 0 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "default-on"; + default-state = "on"; + }; + }; + + ir-receiver { + compatible = "gpio-ir-receiver"; + gpios = <&gpio2 RK_PA2 GPIO_ACTIVE_LOW>; + linux,rc-map-name = "rc-pine64"; + pinctrl-0 = <&ir_int>; + pinctrl-names = "default"; + }; + + hdmi-sound { + compatible = "simple-audio-card"; + simple-audio-card,format = "i2s"; + simple-audio-card,mclk-fs = <128>; + simple-audio-card,name = "HDMI"; + simple-audio-card,cpu { + sound-dai = <&i2s0>; + }; + simple-audio-card,codec { + sound-dai = <&hdmi>; + }; + }; + + spdif-sound { + compatible = "simple-audio-card"; + simple-audio-card,name = "SPDIF"; + simple-audio-card,cpu { + sound-dai = <&spdif>; + }; + simple-audio-card,codec { + sound-dai = <&spdif_out>; + }; + }; + + spdif_out: spdif-out { + compatible = "linux,spdif-dit"; + #sound-dai-cells = <0>; + }; + + sdio_pwrseq: sdio-pwrseq { + compatible = "mmc-pwrseq-simple"; + pinctrl-names = "default"; + pinctrl-0 = <&wifi_enable_h>; + + /* + * On the module itself this is one of these (depending + * on the actual card populated): + * - SDIO_RESET_L_WL_REG_ON + * - PDN (power down when low) + */ + reset-gpios = <&gpio1 RK_PC2 GPIO_ACTIVE_LOW>; + }; + + wireless-wlan { + compatible = "wlan-platdata"; + rockchip,grf = <&grf>; + wifi_chip_type = "rtl8189fs"; + WIFI,host_wake_irq = <&gpio1 RK_PC3 GPIO_ACTIVE_HIGH>; + }; +}; + +&codec { + #sound-dai-cells = <0>; + status = "okay"; +}; + +&cpu0 { + cpu-supply = <&vdd_arm>; +}; + +&cpu1 { + cpu-supply = <&vdd_arm>; +}; + +&cpu2 { + cpu-supply = <&vdd_arm>; +}; + +&cpu3 { + cpu-supply = <&vdd_arm>; +}; + +&dfi { + status = "okay"; +}; + +&dmc { + center-supply = <&vdd_logic>; + status = "okay"; +}; + +&display_subsystem { + status = "okay"; +}; + +&emmc { + bus-width = <8>; + cap-mmc-highspeed; + mmc-hs200-1_8v; + non-removable; + pinctrl-names = "default"; + pinctrl-0 = <&emmc_clk &emmc_cmd &emmc_bus8>; + supports-emmc; + vmmc-supply = <&vcc_io>; + vqmmc-supply = <&vcc18_emmc>; + status = "okay"; +}; + +&gmac2phy { + phy-supply = <&vcc_phy>; + assigned-clocks = <&cru SCLK_MAC2PHY_SRC>; + assigned-clock-rate = <50000000>; + assigned-clocks = <&cru SCLK_MAC2PHY>; + assigned-clock-parents = <&cru SCLK_MAC2PHY_SRC>; + clock_in_out = "output"; + status = "okay"; +}; + +&gpu { + status = "okay"; + mali-supply = <&vdd_logic>; +}; + +&h265e { + status = "okay"; +}; + +&h265e_mmu { + status = "okay"; +}; + +&hdmi { + #sound-dai-cells = <0>; + ddc-i2c-scl-high-time-ns = <9625>; + ddc-i2c-scl-low-time-ns = <10000>; + status = "okay"; +}; + +&hdmiphy { + status = "okay"; +}; + +&i2c1 { + status = "okay"; + + rk805: rk805@18 { + compatible = "rockchip,rk805"; + status = "okay"; + reg = <0x18>; + interrupt-parent = <&gpio2>; + interrupts = <6 IRQ_TYPE_LEVEL_LOW>; + #clock-cells = <1>; + clock-output-names = "rk805-clkout1", "rk805-clkout2"; + pinctrl-names = "default"; + pinctrl-0 = <&pmic_int_l>; + rockchip,system-power-controller; + wakeup-source; + gpio-controller; + #gpio-cells = <2>; + + vcc1-supply = <&vcc_sys>; + vcc2-supply = <&vcc_sys>; + vcc3-supply = <&vcc_sys>; + vcc4-supply = <&vcc_sys>; + vcc5-supply = <&vcc_io>; + vcc6-supply = <&vcc_sys>; + + rtc { + status = "okay"; + }; + + pwrkey { + status = "okay"; + }; + + gpio { + status = "okay"; + }; + + regulators { + compatible = "rk805-regulator"; + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + vdd_logic: RK805_DCDC1 { + regulator-compatible = "RK805_DCDC1"; + regulator-name = "vdd_logic"; + regulator-min-microvolt = <712500>; + regulator-max-microvolt = <1450000>; + regulator-initial-mode = <0x1>; + regulator-ramp-delay = <12500>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-mode = <0x2>; + regulator-on-in-suspend; + regulator-suspend-microvolt = <1000000>; + }; + }; + + vdd_arm: RK805_DCDC2 { + regulator-compatible = "RK805_DCDC2"; + regulator-name = "vdd_arm"; + regulator-init-microvolt = <1225000>; + regulator-min-microvolt = <712500>; + regulator-max-microvolt = <1450000>; + regulator-initial-mode = <0x1>; + regulator-ramp-delay = <12500>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-mode = <0x2>; + regulator-on-in-suspend; + regulator-suspend-microvolt = <950000>; + }; + }; + + vcc_ddr: RK805_DCDC3 { + regulator-compatible = "RK805_DCDC3"; + regulator-name = "vcc_ddr"; + regulator-initial-mode = <0x1>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-mode = <0x2>; + regulator-on-in-suspend; + }; + }; + + vcc_io: RK805_DCDC4 { + regulator-compatible = "RK805_DCDC4"; + regulator-name = "vcc_io"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-initial-mode = <0x1>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-mode = <0x2>; + regulator-on-in-suspend; + regulator-suspend-microvolt = <3300000>; + }; + }; + + vcc_18: RK805_LDO1 { + regulator-compatible = "RK805_LDO1"; + regulator-name = "vcc_18"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vcc18_emmc: RK805_LDO2 { + regulator-compatible = "RK805_LDO2"; + regulator-name = "vcc18_emmc"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vdd_10: RK805_LDO3 { + regulator-compatible = "RK805_LDO3"; + regulator-name = "vdd_10"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1000000>; + }; + }; + }; + }; +}; + +&i2s0 { + #sound-dai-cells = <0>; + rockchip,bclk-fs = <128>; + status = "okay"; +}; + +&iep { + status = "okay"; +}; + +&iep_mmu { + status = "okay"; +}; + +&io_domains { + status = "okay"; + + vccio1-supply = <&vcc_io>; + vccio2-supply = <&vcc18_emmc>; + vccio3-supply = <&vcc_io>; + vccio4-supply = <&vcc_io>; + vccio5-supply = <&vcc_io>; + vccio6-supply = <&vcc_io>; + pmuio-supply = <&vcc_io>; +}; + +&pinctrl { + ir { + ir_int: ir-int { + rockchip,pins = <2 RK_PA2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + pmic { + pmic_int_l: pmic-int-l { + rockchip,pins = <2 RK_PA6 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + + sdio-pwrseq { + wifi_enable_h: wifi-enable-h { + rockchip,pins = <1 RK_PC2 RK_FUNC_GPIO &pcfg_pull_none_4ma>, + <1 RK_PC3 RK_FUNC_GPIO &pcfg_pull_none_4ma>; + }; + }; +}; + +&rkvdec { + status = "okay"; + vcodec-supply = <&vdd_logic>; +}; + +&rkvdec_mmu { + status = "okay"; +}; + +&sdio { + bus-width = <4>; + cap-sd-highspeed; + cap-sdio-irq; + disable-wp; + keep-power-in-suspend; + mmc-pwrseq = <&sdio_pwrseq>; + non-removable; + num-slots = <1>; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc1_bus4 &sdmmc1_cmd &sdmmc1_clk>; + supports-sdio; + status = "okay"; +}; + +&sdmmc { + bus-width = <4>; + cap-mmc-highspeed; + cap-sd-highspeed; + disable-wp; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc0_clk &sdmmc0_cmd &sdmmc0_dectn &sdmmc0_bus4>; + supports-sd; + vmmc-supply = <&vcc_sd>; + status = "okay"; +}; + +&spdif { + #sound-dai-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&spdifm0_tx>; + status = "okay"; +}; + +&threshold { + temperature = <80000>; /* millicelsius */ +}; + +&target { + temperature = <95000>; /* millicelsius */ +}; + +&soc_crit { + temperature = <100000>; /* millicelsius */ +}; + +&tsadc { + rockchip,hw-tshut-mode = <0>; + rockchip,hw-tshut-polarity = <0>; + rockchip,hw-tshut-temp = <110000>; + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&u2phy { + status = "okay"; +}; + +&u2phy_host { + phy-supply = <&vcc_host1_5v>; + status = "okay"; +}; + +&u2phy_otg { + phy-supply = <&vcc_otg_5v>; + status = "okay"; +}; + +&u3phy { + status = "okay"; +}; + +&u3phy_utmi { + phy-supply = <&vcc_host_5v>; + status = "okay"; +}; + +&u3phy_pipe { + phy-supply = <&vcc_host_5v>; + status = "okay"; +}; + +&usb20_otg { + dr_mode = "host"; + status = "okay"; +}; + +&usb_host0_ehci { + status = "okay"; +}; + +&usb_host0_ohci { + status = "okay"; +}; + +&usbdrd3 { + status = "okay"; +}; + +&usbdrd_dwc3 { + status = "okay"; +}; + +&vop { + status = "okay"; +}; + +&vop_mmu { + status = "okay"; +}; + +&vpu_service { + status = "okay"; +}; + +&vpu_mmu { + status = "okay"; +}; + +&vepu { + status = "okay"; +}; + +&vepu_mmu { + status = "okay"; +}; + +&venc_srv { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3328.dtsi b/arch/arm64/boot/dts/rockchip/rk3328.dtsi index 0d2251c9..b7b6f930 100644 --- a/arch/arm64/boot/dts/rockchip/rk3328.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3328.dtsi @@ -88,6 +88,8 @@ device_type = "cpu"; compatible = "arm,cortex-a53", "arm,armv8"; reg = <0x0 0x1>; + clocks = <&cru ARMCLK>; + dynamic-power-coefficient = <120>; enable-method = "psci"; operating-points-v2 = <&cpu0_opp_table>; }; @@ -95,6 +97,8 @@ device_type = "cpu"; compatible = "arm,cortex-a53", "arm,armv8"; reg = <0x0 0x2>; + clocks = <&cru ARMCLK>; + dynamic-power-coefficient = <120>; enable-method = "psci"; operating-points-v2 = <&cpu0_opp_table>; }; @@ -102,6 +106,8 @@ device_type = "cpu"; compatible = "arm,cortex-a53", "arm,armv8"; reg = <0x0 0x3>; + clocks = <&cru ARMCLK>; + dynamic-power-coefficient = <120>; enable-method = "psci"; operating-points-v2 = <&cpu0_opp_table>; }; @@ -161,6 +167,22 @@ opp-microvolt-L1 = <1300000 1300000 1350000>; clock-latency-ns = <40000>; }; + /* + opp-1392000000 { + opp-hz = /bits/ 64 <1392000000>; + opp-microvolt = <1350000 1350000 1350000>; + opp-microvolt-L0 = <1350000 1350000 1350000>; + opp-microvolt-L1 = <1325000 1325000 1350000>; + clock-latency-ns = <40000>; + }; + opp-1512000000 { + opp-hz = /bits/ 64 <1512000000>; + opp-microvolt = <1350000 1350000 1350000>; + opp-microvolt-L0 = <1350000 1350000 1350000>; + opp-microvolt-L1 = <1325000 1325000 1350000>; + clock-latency-ns = <40000>; + }; + */ }; arm-pmu { @@ -438,6 +460,7 @@ reg-shift = <2>; reg-io-width = <4>; dmas = <&dmac 2>, <&dmac 3>; + dma-names = "tx", "rx"; pinctrl-names = "default"; pinctrl-0 = <&uart0_xfer &uart0_cts &uart0_rts>; status = "disabled"; @@ -452,6 +475,7 @@ reg-shift = <2>; reg-io-width = <4>; dmas = <&dmac 4>, <&dmac 5>; + dma-names = "tx", "rx"; pinctrl-names = "default"; pinctrl-0 = <&uart1_xfer &uart1_cts &uart1_rts>; status = "disabled"; @@ -466,6 +490,7 @@ reg-shift = <2>; reg-io-width = <4>; dmas = <&dmac 6>, <&dmac 7>; + dma-names = "tx", "rx"; pinctrl-names = "default"; pinctrl-0 = <&uart2m1_xfer>; status = "disabled"; @@ -704,9 +729,9 @@ }; opp-300000000 { opp-hz = /bits/ 64 <300000000>; - opp-microvolt = <975000>; - opp-microvolt-L0 = <975000>; - opp-microvolt-L1 = <950000>; + opp-microvolt = <1050000>; + opp-microvolt-L0 = <1050000>; + opp-microvolt-L1 = <1025000>; }; opp-400000000 { opp-hz = /bits/ 64 <400000000>; @@ -720,6 +745,14 @@ opp-microvolt-L0 = <1150000>; opp-microvolt-L1 = <1100000>; }; + /* + opp-600000000 { + opp-hz = /bits/ 64 <600000000>; + opp-microvolt = <1150000>; + opp-microvolt-L0 = <1150000>; + opp-microvolt-L1 = <1125000>; + }; + */ }; vdpu: vpu_service@ff350000 { @@ -843,7 +876,7 @@ interrupts = ; interrupt-names = "rkvdec_mmu"; clocks = <&cru ACLK_RKVDEC>, <&cru HCLK_RKVDEC>; - clock-names = "aclk_vcodec", "hclk_vcodec"; + clock-names = "aclk", "hclk"; power-domains = <&power RK3328_PD_VIDEO>; #iommu-cells = <0>; }; @@ -921,6 +954,8 @@ vop: vop@ff370000 { compatible = "rockchip,rk3328-vop"; reg = <0x0 0xff370000 0x0 0x3efc>; + reg-names = "regs", "gamma_lut"; + rockchip,grf = <&grf>; interrupts = ; clocks = <&cru ACLK_VOP>, <&cru DCLK_LCDC>, <&cru HCLK_VOP>; clock-names = "aclk_vop", "dclk_vop", "hclk_vop"; @@ -1226,36 +1261,42 @@ sdmmc: dwmmc@ff500000 { compatible = "rockchip,rk3328-dw-mshc", "rockchip,rk3288-dw-mshc"; reg = <0x0 0xff500000 0x0 0x4000>; - clock-freq-min-max = <400000 150000000>; + max-frequency = <150000000>; clocks = <&cru HCLK_SDMMC>, <&cru SCLK_SDMMC>, <&cru SCLK_SDMMC_DRV>, <&cru SCLK_SDMMC_SAMPLE>; - clock-names = "biu", "ciu", "ciu-drv", "ciu-sample"; + clock-names = "biu", "ciu", "ciu-drive", "ciu-sample"; fifo-depth = <0x100>; interrupts = ; + resets = <&cru SRST_MMC0>; + reset-names = "reset"; status = "disabled"; }; sdio: dwmmc@ff510000 { compatible = "rockchip,rk3328-dw-mshc", "rockchip,rk3288-dw-mshc"; reg = <0x0 0xff510000 0x0 0x4000>; - clock-freq-min-max = <400000 150000000>; + max-frequency = <150000000>; clocks = <&cru HCLK_SDIO>, <&cru SCLK_SDIO>, <&cru SCLK_SDIO_DRV>, <&cru SCLK_SDIO_SAMPLE>; - clock-names = "biu", "ciu", "ciu-drv", "ciu-sample"; + clock-names = "biu", "ciu", "ciu-drive", "ciu-sample"; fifo-depth = <0x100>; interrupts = ; + resets = <&cru SRST_SDIO>; + reset-names = "reset"; status = "disabled"; }; emmc: dwmmc@ff520000 { compatible = "rockchip,rk3328-dw-mshc", "rockchip,rk3288-dw-mshc"; reg = <0x0 0xff520000 0x0 0x4000>; - clock-freq-min-max = <400000 150000000>; + max-frequency = <150000000>; clocks = <&cru HCLK_EMMC>, <&cru SCLK_EMMC>, <&cru SCLK_EMMC_DRV>, <&cru SCLK_EMMC_SAMPLE>; - clock-names = "biu", "ciu", "ciu-drv", "ciu-sample"; + clock-names = "biu", "ciu", "ciu-drive", "ciu-sample"; fifo-depth = <0x100>; interrupts = ; + resets = <&cru SRST_EMMC>; + reset-names = "reset"; status = "disabled"; }; @@ -1275,6 +1316,7 @@ "pclk_mac"; resets = <&cru SRST_GMAC2IO_A>; reset-names = "stmmaceth"; + snps,force_thresh_dma_mode; status = "disabled"; }; @@ -1345,12 +1387,14 @@ sdmmc_ext: dwmmc@ff5f0000 { compatible = "rockchip,rk3328-dw-mshc", "rockchip,rk3288-dw-mshc"; reg = <0x0 0xff5f0000 0x0 0x4000>; - clock-freq-min-max = <400000 150000000>; + max-frequency = <150000000>; clocks = <&cru HCLK_SDMMC_EXT>, <&cru SCLK_SDMMC_EXT>, <&cru SCLK_SDMMC_EXT_DRV>, <&cru SCLK_SDMMC_EXT_SAMPLE>; - clock-names = "biu", "ciu", "ciu-drv", "ciu-sample"; + clock-names = "biu", "ciu", "ciu-drive", "ciu-sample"; fifo-depth = <0x100>; interrupts = ; + resets = <&cru SRST_SDMMCEXT>; + reset-names = "reset"; status = "disabled"; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-khadas-edge.dts b/arch/arm64/boot/dts/rockchip/rk3399-khadas-edge.dts new file mode 100644 index 00000000..1381ba09 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3399-khadas-edge.dts @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018 Wesion Co., Ltd + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Or, alternatively, + * + * b) Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/dts-v1/; + +#include "rk3399-khadas-edge.dtsi" + +/ { + model = "Khadas Edge"; + compatible = "khadas,edge", "rockchip,rk3399"; +}; + +&gmac { + status = "okay"; +}; + +&fusb0 { + status = "okay"; + max-input-voltage = <13000000>; + max-input-current = <6000000>; +}; + +&fusb1 { + status = "okay"; + max-input-voltage = <13000000>; + max-input-current = <6000000>; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-khadas-edge.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-khadas-edge.dtsi new file mode 100644 index 00000000..a7c14f6c --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3399-khadas-edge.dtsi @@ -0,0 +1,1005 @@ +/* + * Copyright (c) 2018 Wesion Co., Ltd + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Or, alternatively, + * + * b) Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/dts-v1/; +#include +#include +#include "rk3399.dtsi" +#include "rk3399-linux.dtsi" +#include "rk3399-opp.dtsi" + +/ { + compatible = "khadas,edge", "rockchip,rk3399"; + + /* first 64k(0xff8c0000~0xff8d0000) for ddr and suspend */ + iram: sram@ff8d0000 { + compatible = "mmio-sram"; + reg = <0x0 0xff8d0000 0x0 0x20000>; /* 128k */ + }; + + gpio-keys { + compatible = "gpio-keys"; + #address-cells = <1>; + #size-cells = <0>; + autorepeat; + + pinctrl-names = "default"; + pinctrl-0 = <&pwrbtn>; + + button@0 { + gpios = <&gpio0 5 GPIO_ACTIVE_LOW>; + linux,code = ; + label = "GPIO Key Power"; + linux,input-type = <1>; + gpio-key,wakeup = <1>; + debounce-interval = <100>; + }; + }; + + vccadc_ref: vccadc-ref { + compatible = "regulator-fixed"; + regulator-name = "vcc1v8_sys"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + adc-keys { + compatible = "adc-keys"; + io-channels = <&saradc 1>; + io-channel-names = "buttons"; + poll-interval = <100>; + keyup-threshold-microvolt = <1800000>; + + button-up { + label = "Volume Up"; + linux,code = ; + press-threshold-microvolt = <100000>; + }; + + button-down { + label = "Volume Down"; + linux,code = ; + press-threshold-microvolt = <300000>; + }; + + back { + label = "Back"; + linux,code = ; + press-threshold-microvolt = <985000>; + }; + + menu { + label = "Menu"; + linux,code = ; + press-threshold-microvolt = <1314000>; + }; + }; + + clkin_gmac: external-gmac-clock { + compatible = "fixed-clock"; + clock-frequency = <125000000>; + clock-output-names = "clkin_gmac"; + #clock-cells = <0>; + }; + + dw_hdmi_audio: dw-hdmi-audio { + status = "disabled"; + compatible = "rockchip,dw-hdmi-audio"; + #sound-dai-cells = <0>; + }; + + hdmi-sound { + compatible = "simple-audio-card"; + simple-audio-card,format = "i2s"; + simple-audio-card,mclk-fs = <256>; + simple-audio-card,name = "HDMI"; + simple-audio-card,cpu { + sound-dai = <&i2s2>; + }; + simple-audio-card,codec { + sound-dai = <&hdmi>; + }; + }; + + hdmi-dp-sound { + compatible = "rockchip,rk3399-hdmi-dp"; + rockchip,cpu = <&i2s2>; + rockchip,codec = <&hdmi>, <&cdn_dp>; + status = "disabled"; + }; + + sdio_pwrseq: sdio-pwrseq { + compatible = "mmc-pwrseq-simple"; + clocks = <&rk808 1>; + clock-names = "ext_clock"; + pinctrl-names = "default"; + pinctrl-0 = <&wifi_enable_h>; + + /* + * On the module itself this is one of these (depending + * on the actual card populated): + * - SDIO_RESET_L_WL_REG_ON + * - PDN (power down when low) + */ + reset-gpios = <&gpio2 28 GPIO_ACTIVE_LOW>; /* GPIO2_D4 */ + }; + + wireless-wlan { + compatible = "wlan-platdata"; + rockchip,grf = <&grf>; + wifi_chip_type = "ap6354"; + sdio_vref = <1800>; + WIFI,host_wake_irq = <&gpio0 3 GPIO_ACTIVE_HIGH>; /* GPIO0_a3 */ + status = "okay"; + }; + + wireless-bluetooth { + compatible = "bluetooth-platdata"; + //wifi-bt-power-toggle; + uart_rts_gpios = <&gpio2 19 GPIO_ACTIVE_LOW>; /* GPIO2_C3 */ + pinctrl-names = "default", "rts_gpio"; + pinctrl-0 = <&uart0_rts>; + pinctrl-1 = <&uart0_gpios>; + //BT,power_gpio = <&gpio3 19 GPIO_ACTIVE_HIGH>; /* GPIOx_xx */ + BT,reset_gpio = <&gpio2 27 GPIO_ACTIVE_HIGH>; /* GPIO2_D3 */ + BT,wake_gpio = <&gpio2 26 GPIO_ACTIVE_HIGH>; /* GPIO2_D2 */ + BT,wake_host_irq = <&gpio0 4 GPIO_ACTIVE_HIGH>; /* GPIO0_A4 */ + status = "okay"; + }; + + leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + + sys_led { + gpios = <&gpio0 6 GPIO_ACTIVE_HIGH>; /* GPIO0_A6 */ + label = "sys_led"; + linux,default-trigger = "heartbeat"; + default-state = "on"; + }; + }; + + ir-receiver { + compatible = "gpio-ir-receiver"; + gpios = <&gpio1 RK_PB6 GPIO_ACTIVE_LOW>; + linux,rc-map-name = "rc-khadas"; + pinctrl-0 = <&ir_int>; + pinctrl-names = "default"; + }; + + vcc3v3_sys: vcc3v3-sys { + compatible = "regulator-fixed"; + regulator-name = "vcc3v3_sys"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + vcc5v0_host: vcc5v0-host-regulator { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio4 25 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&host_vbus_drv>; + regulator-name = "vcc5v0_host"; + regulator-always-on; + }; + + vcc5v0_sys: vcc5v0-sys { + compatible = "regulator-fixed"; + regulator-name = "vcc5v0_sys"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + }; + + vcc_sd: vcc-sd { + compatible = "regulator-fixed"; + enable-active-high; + regulator-boot-on; + regulator-always-on; + gpio = <&gpio0 1 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&vcc_sd_h>; + regulator-name = "vcc_sd"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + vcc_phy: vcc-phy-regulator { + compatible = "regulator-fixed"; + regulator-name = "vcc_phy"; + regulator-always-on; + regulator-boot-on; + }; + + vdd_log: vdd-log { + compatible = "pwm-regulator"; + pwms = <&pwm2 0 25000 1>; + regulator-name = "vdd_log"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1400000>; + regulator-always-on; + regulator-boot-on; + + /* for rockchip boot on */ + rockchip,pwm_id= <2>; + rockchip,pwm_voltage = <900000>; + }; + + xin32k: xin32k { + compatible = "fixed-clock"; + clock-frequency = <32768>; + clock-output-names = "xin32k"; + #clock-cells = <0>; + }; + + fan0: pwm-fan { + compatible = "pwm-fan"; + status = "okay"; + pwms = <&pwm0 0 40000 0>; /* 25kHz */ + cooling-min-state = <0>; + cooling-max-state = <3>; + #cooling-cells = <2>; + cooling-levels = <0 150 200 255>; + }; +}; + +&cdn_dp { + status = "okay"; + extcon = <&fusb0>; + phys = <&tcphy0_dp>; +}; + +&cpu_l0 { + cpu-supply = <&vdd_cpu_l>; +}; + +&cpu_l1 { + cpu-supply = <&vdd_cpu_l>; +}; + +&cpu_l2 { + cpu-supply = <&vdd_cpu_l>; +}; + +&cpu_l3 { + cpu-supply = <&vdd_cpu_l>; +}; + +&cpu_b0 { + cpu-supply = <&vdd_cpu_b>; +}; + +&cpu_b1 { + cpu-supply = <&vdd_cpu_b>; +}; + +&dfi { + status = "okay"; +}; + +&dmac_bus { + iram = <&iram>; +}; + +&dmc { + status = "okay"; + center-supply = <&vdd_center>; + system-status-freq = < + /*system status freq(KHz)*/ + SYS_STATUS_NORMAL 800000 + SYS_STATUS_REBOOT 400000 + SYS_STATUS_SUSPEND 400000 + SYS_STATUS_VIDEO_1080P 800000 + SYS_STATUS_VIDEO_4K 800000 + SYS_STATUS_VIDEO_4K_10B 800000 + SYS_STATUS_PERFORMANCE 800000 + SYS_STATUS_BOOST 800000 + SYS_STATUS_DUALVIEW 800000 + SYS_STATUS_ISP 800000 + >; + auto-min-freq = <400000>; + auto-freq-en = <0>; +}; + +&dmc_opp_table { + compatible = "operating-points-v2"; + + opp-200000000 { + opp-hz = /bits/ 64 <200000000>; + opp-microvolt = <825000>; + status = "disabled"; + }; + opp-300000000 { + opp-hz = /bits/ 64 <300000000>; + opp-microvolt = <850000>; + status = "disabled"; + }; + opp-400000000 { + opp-hz = /bits/ 64 <400000000>; + opp-microvolt = <900000>; + }; + opp-528000000 { + opp-hz = /bits/ 64 <528000000>; + opp-microvolt = <900000>; + status = "disabled"; + }; + opp-600000000 { + opp-hz = /bits/ 64 <600000000>; + opp-microvolt = <900000>; + status = "disabled"; + }; + opp-800000000 { + opp-hz = /bits/ 64 <800000000>; + opp-microvolt = <900000>; + }; + opp-928000000 { + opp-hz = /bits/ 64 <928000000>; + opp-microvolt = <900000>; + status = "disabled"; + }; + opp-1056000000 { + opp-hz = /bits/ 64 <1056000000>; + opp-microvolt = <900000>; + status = "disabled"; + }; +}; + +&display_subsystem { + status = "okay"; + + route { + route_hdmi: route-hdmi { + status = "okay"; + connect = <&vopb_out_hdmi>; + }; + + route_dp: route-dp { + connect = <&vopl_out_dp>; + }; + }; +}; + +&emmc_phy { + status = "okay"; +}; + +&i2c4 { + status = "okay"; + i2c-scl-rising-time-ns = <168>; + i2c-scl-falling-time-ns = <4>; + clock-frequency = <400000>; + + vdd_cpu_b: syr827@40 { + compatible = "silergy,syr827"; + reg = <0x40>; + vin-supply = <&vcc5v0_sys>; + regulator-compatible = "fan53555-reg"; + pinctrl-names = "default"; + pinctrl-0 = <&vsel1_gpio>; + vsel-gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; /* GPIO1_B5 */ + regulator-name = "vdd_cpu_b"; + regulator-min-microvolt = <712500>; + regulator-max-microvolt = <1500000>; + regulator-ramp-delay = <1000>; + fcs,suspend-voltage-selector = <1>; + regulator-always-on; + regulator-boot-on; + regulator-initial-state = <3>; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vdd_gpu: syr828@41 { + compatible = "silergy,syr828"; + reg = <0x41>; + vin-supply = <&vcc5v0_sys>; + regulator-compatible = "fan53555-reg"; + pinctrl-names = "default"; + pinctrl-0 = <&vsel2_gpio>; + sel-gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>;/* GPIO0_B5 */ + regulator-name = "vdd_gpu"; + regulator-min-microvolt = <712500>; + regulator-max-microvolt = <1500000>; + regulator-ramp-delay = <1000>; + fcs,suspend-voltage-selector = <1>; + regulator-always-on; + regulator-boot-on; + regulator-initial-state = <3>; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + rk808: pmic@1b { + compatible = "rockchip,rk808"; + reg = <0x1b>; + interrupt-parent = <&gpio1>; + interrupts = <22 IRQ_TYPE_LEVEL_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&pmic_int_l &pmic_dvs2>; + rockchip,system-power-controller; + wakeup-source; + #clock-cells = <1>; + clock-output-names = "rk808-clkout1", "rk808-clkout2"; + + vcc1-supply = <&vcc3v3_sys>; + vcc2-supply = <&vcc3v3_sys>; + vcc3-supply = <&vcc3v3_sys>; + vcc4-supply = <&vcc3v3_sys>; + vcc6-supply = <&vcc3v3_sys>; + vcc7-supply = <&vcc3v3_sys>; + vcc8-supply = <&vcc3v3_sys>; + vcc9-supply = <&vcc3v3_sys>; + vcc10-supply = <&vcc3v3_sys>; + vcc11-supply = <&vcc3v3_sys>; + vcc12-supply = <&vcc3v3_sys>; + vddio-supply = <&vcc1v8_pmu>; + + regulators { + vdd_center: DCDC_REG1 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <1350000>; + regulator-ramp-delay = <6001>; + regulator-name = "vdd_center"; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vdd_cpu_l: DCDC_REG2 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <1350000>; + regulator-ramp-delay = <6001>; + regulator-name = "vdd_cpu_l"; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcc_ddr: DCDC_REG3 { + regulator-always-on; + regulator-boot-on; + regulator-name = "vcc_ddr"; + regulator-state-mem { + regulator-on-in-suspend; + }; + }; + + vcc_1v8: DCDC_REG4 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "vcc_1v8"; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vcc1v8_apio2: LDO_REG1 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "vcc1v8_apio2"; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcc3v0_tp: LDO_REG2 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-name = "vcc3v0_tp"; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcc1v8_pmu: LDO_REG3 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "vcc1v8_pmu"; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vccio_sd: LDO_REG4 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3000000>; + regulator-name = "vccio_sd"; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <3000000>; + }; + }; + + vcc_vldo5: LDO_REG5 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-name = "vcc_vldo5"; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcc_1v5: LDO_REG6 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <1500000>; + regulator-name = "vcc_1v5"; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1500000>; + }; + }; + + vcca1v8_codec: LDO_REG7 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "vcca1v8_codec"; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcc_3v0: LDO_REG8 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-name = "vcc_3v0"; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <3000000>; + }; + }; + + vcc3v3_s3: SWITCH_REG1 { + regulator-always-on; + regulator-boot-on; + regulator-name = "vcc3v3_s3"; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcc3v3_s0: SWITCH_REG2 { + regulator-always-on; + regulator-boot-on; + regulator-name = "vcc3v3_s0"; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + }; + }; + + fusb1: fusb30x@22 { + compatible = "fairchild,fusb302"; + reg = <0x22>; + //fusb302,role="ROLE_MODE_UFP"; + pinctrl-names = "default"; + pinctrl-0 = <&fusb1_int>; + int-n-gpios = <&gpio4 30 GPIO_ACTIVE_HIGH>; + support-uboot-charge = <1>; + port-num = <1>; + status = "okay"; + }; +}; + +&i2c8 { + status = "okay"; + i2c-scl-rising-time-ns = <475>; + i2c-scl-falling-time-ns = <26>; + + fusb0: fusb30x@22 { + compatible = "fairchild,fusb302"; + reg = <0x22>; + pinctrl-names = "default"; + pinctrl-0 = <&fusb0_int>; + int-n-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>; + vbus-5v-gpios = <&gpio4 26 GPIO_ACTIVE_HIGH>; + status = "okay"; + }; +}; + +&i2s2 { + #sound-dai-cells = <0>; + rockchip,bclk-fs = <128>; + status = "okay"; +}; + +&gmac { + phy-supply = <&vcc_phy>; + phy-mode = "rgmii"; + clock_in_out = "input"; + snps,reset-gpio = <&gpio3 15 GPIO_ACTIVE_LOW>; + snps,reset-active-low; + snps,reset-delays-us = <0 20000 100000>; + assigned-clocks = <&cru SCLK_RMII_SRC>; + assigned-clock-parents = <&clkin_gmac>; + pinctrl-names = "default"; + pinctrl-0 = <&rgmii_pins>; + tx_delay = <0x28>; + rx_delay = <0x11>; + status = "disabled"; +}; + +&gpu { + status = "okay"; + mali-supply = <&vdd_gpu>; +}; + +&hdmi { + #address-cells = <1>; + #size-cells = <0>; + #sound-dai-cells = <0>; + status = "okay"; +}; + +&iep { + status = "okay"; +}; + +&iep_mmu { + status = "okay"; +}; + +&io_domains { + status = "okay"; + + bt656-supply = <&vcc1v8_apio2>; /* bt656_gpio2ab_ms */ + audio-supply = <&vcca1v8_codec>; /* audio_gpio3d4a_ms */ + sdmmc-supply = <&vccio_sd>; /* sdmmc_gpio4b_ms */ + gpio1830-supply = <&vcc_3v0>; /* gpio1833_gpio4cd_ms */ +}; + +&saradc { + status = "okay"; +}; + +&sdmmc { + clock-frequency = <150000000>; + max-frequency = <150000000>; + supports-sd; + bus-width = <4>; + cap-mmc-highspeed; + cap-sd-highspeed; + disable-wp; + num-slots = <1>; + sd-uhs-sdr12; + sd-uhs-sdr25; + sd-uhs-sdr50; + sd-uhs-sdr104; + vmmc-supply = <&vcc_sd>; + vqmmc-supply = <&vccio_sd>; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_cd &sdmmc_bus4>; + status = "okay"; +}; + +&sdio0 { + clock-frequency = <200000000>; + max-frequency = <200000000>; + supports-sdio; + bus-width = <4>; + disable-wp; + cap-sd-highspeed; + cap-sdio-irq; + keep-power-in-suspend; + mmc-pwrseq = <&sdio_pwrseq>; + non-removable; + num-slots = <1>; + pinctrl-names = "default"; + pinctrl-0 = <&sdio0_bus4 &sdio0_cmd &sdio0_clk>; + sd-uhs-sdr104; + status = "okay"; +}; + +&sdhci { + bus-width = <8>; + mmc-hs400-1_8v; + mmc-hs400-enhanced-strobe; + supports-emmc; + non-removable; + keep-power-in-suspend; + status = "okay"; +}; + +&tcphy0 { + extcon = <&fusb0>; + status = "okay"; +}; + +&tcphy1 { + status = "okay"; +}; + +&tsadc { + /* tshut mode 0:CRU 1:GPIO */ + rockchip,hw-tshut-mode = <1>; + /* tshut polarity 0:LOW 1:HIGH */ + rockchip,hw-tshut-polarity = <1>; + status = "okay"; +}; + +&u2phy0 { + status = "okay"; + extcon = <&fusb0>; + + u2phy0_otg: otg-port { + status = "okay"; + }; + + u2phy0_host: host-port { + phy-supply = <&vcc5v0_host>; + status = "okay"; + }; +}; + +&u2phy1 { + status = "okay"; + + u2phy1_otg: otg-port { + status = "okay"; + }; + + u2phy1_host: host-port { + phy-supply = <&vcc5v0_host>; + status = "okay"; + }; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_xfer &uart0_cts>; + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&usb_host0_ehci { + status = "okay"; +}; + +&usb_host0_ohci { + status = "okay"; +}; + +&usb_host1_ehci { + status = "okay"; +}; + +&usb_host1_ohci { + status = "okay"; +}; + +&usbdrd3_0 { + extcon = <&fusb0>; + status = "okay"; +}; + +&usbdrd_dwc3_0 { + status = "okay"; +}; + +&usbdrd3_1 { + status = "okay"; +}; + +&usbdrd_dwc3_1 { + dr_mode = "host"; + status = "okay"; +}; + +&pwm0 { + status = "okay"; +}; + +&pwm2 { + status = "okay"; + pinctrl-names = "active"; + pinctrl-0 = <&pwm2_pin_pull_down>; +}; + +&saradc { + vref-supply = <&vccadc_ref>; +}; + +&route_edp { + status = "disabled"; +}; + +&pinctrl { + ir { + ir_int: ir-int { + rockchip,pins = + <1 RK_PB6 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + pmic { + pmic_int_l: pmic-int-l { + rockchip,pins = + <1 22 RK_FUNC_GPIO &pcfg_pull_up>; + }; + + pmic_dvs2: pmic-dvs2 { + rockchip,pins = + <1 18 RK_FUNC_GPIO &pcfg_pull_down>; + }; + vsel1_gpio: vsel1-gpio { + rockchip,pins = + <1 13 RK_FUNC_GPIO &pcfg_pull_down>; + }; + vsel2_gpio: vsel2-gpio { + rockchip,pins = + <0 13 RK_FUNC_GPIO &pcfg_pull_down>; + }; + }; + + usb2 { + host_vbus_drv: host-vbus-drv { + rockchip,pins = + <4 25 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + vcc_sd { + vcc_sd_h: vcc-sd-h { + rockchip,pins = + <0 1 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + + fusb30x { + fusb0_int: fusb0-int { + rockchip,pins = <1 2 RK_FUNC_GPIO &pcfg_pull_up>; + }; + fusb1_int: fusb1-int { + rockchip,pins = <4 30 RK_FUNC_GPIO &pcfg_pull_up>; /* GPIO4_D6 */ + }; + }; + + buttons { + pwrbtn: pwrbtn { + rockchip,pins = <0 5 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + + lcd-panel { + lcd_panel_reset: lcd-panel-reset { + rockchip,pins = <4 30 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; + + sdio-pwrseq { + wifi_enable_h: wifi-enable-h { + rockchip,pins = + <2 28 RK_FUNC_GPIO &pcfg_pull_none>; /* GPIO2_D4 */ + }; + }; + + wireless-bluetooth { + uart0_gpios: uart0-gpios { + rockchip,pins = + <2 27 RK_FUNC_GPIO &pcfg_pull_none>; /* GPIO2_D3 */ + }; + }; +}; + +&pvtm { + status = "okay"; +}; + +&pmu_pvtm { + status = "okay"; +}; + +&pmu_io_domains { + status = "okay"; + pmu1830-supply = <&vcc_1v8>; +}; + +&rkvdec { + status = "okay"; +}; + +&vdec_mmu { + status = "okay"; +}; + +&vpu { + status = "okay"; +}; + +&vpu_mmu { + status = "okay"; +}; + +&vopb { + status = "okay"; + assigned-clocks = <&cru DCLK_VOP0_DIV>; + assigned-clock-parents = <&cru PLL_VPLL>; +}; + +&vopb_mmu { + status = "okay"; +}; + +&vopl { + status = "disabled"; + assigned-clocks = <&cru DCLK_VOP1_DIV>; + assigned-clock-parents = <&cru PLL_CPLL>; +}; + +&vopl_mmu { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-linux.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-linux.dtsi index 2851cd52..7be2af6b 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-linux.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-linux.dtsi @@ -47,18 +47,7 @@ compatible = "rockchip,linux", "rockchip,rk3399"; chosen { - bootargs = "earlycon=uart8250,mmio32,0xff1a0000 swiotlb=1 console=ttyFIQ0 rw root=PARTUUID=614e0000-0000 rootfstype=ext4 rootwait"; - }; - - reserved-memory { - #address-cells = <2>; - #size-cells = <2>; - ranges; - - drm_logo: drm-logo@00000000 { - compatible = "rockchip,drm-logo"; - reg = <0x0 0x0 0x0 0x0>; - }; + bootargs = "earlycon=uart8250,mmio32,0xff1a0000 swiotlb=1"; }; cif_isp0: cif_isp@ff910000 { @@ -120,7 +109,6 @@ status = "disabled"; ports = <&vopb_out>, <&vopl_out>; - logo-memory-region = <&drm_logo>; route { route_hdmi: route-hdmi { diff --git a/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dts b/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dts new file mode 100644 index 00000000..7b376aee --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dts @@ -0,0 +1,926 @@ +/* + * Copyright (c) 2016 Fuzhou Rockchip Electronics Co., Ltd + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Or, alternatively, + * + * b) Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/dts-v1/; +#include +#include +#include "rk3399.dtsi" +#include "rk3399-linux.dtsi" +#include "rk3399-opp.dtsi" + +/ { + model = "Radxa ROCK Pi 4"; + compatible = "radxa,rockpi4", "rockchip,rk3399"; + + xin32k: xin32k { + compatible = "fixed-clock"; + clock-frequency = <32768>; + clock-output-names = "xin32k"; + #clock-cells = <0>; + }; + + clkin_gmac: external-gmac-clock { + compatible = "fixed-clock"; + clock-frequency = <125000000>; + clock-output-names = "clkin_gmac"; + #clock-cells = <0>; + }; + + vcc1v8_s0: vcc1v8-s0 { + compatible = "regulator-fixed"; + regulator-name = "vcc1v8_s0"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + vcc_sys: vcc-sys { + compatible = "regulator-fixed"; + regulator-name = "vcc_sys"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + }; + + vcc_phy: vcc-phy-regulator { + compatible = "regulator-fixed"; + regulator-name = "vcc_phy"; + regulator-always-on; + regulator-boot-on; + }; + + vcc3v3_sys: vcc3v3-sys { + compatible = "regulator-fixed"; + regulator-name = "vcc3v3_sys"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + vin-supply = <&vcc_sys>; + }; + + vcc3v3_pcie: vcc3v3-pcie-regulator { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio2 RK_PD2 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&pcie_drv>; + regulator-boot-on; + regulator-always-on; + regulator-name = "vcc3v3_pcie"; + vin-supply = <&vcc3v3_sys>; + }; + + vcc5v0_host: vcc5v0-host-regulator { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio4 RK_PD1 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&host_vbus_drv>; + regulator-name = "vcc5v0_host"; + regulator-always-on; + }; + + vcc5v0_otg: vcc5v0-otg-regulator { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio1 RK_PA3 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&otg_vbus_drv>; + regulator-name = "vcc5v0_otg"; + regulator-always-on; + }; + + vdd_log: vdd-log { + compatible = "pwm-regulator"; + pwms = <&pwm2 0 25000 1>; + regulator-name = "vdd_log"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1400000>; + regulator-always-on; + regulator-boot-on; + + /* for rockchip boot on */ + rockchip,pwm_id= <2>; + rockchip,pwm_voltage = <900000>; + + vin-supply = <&vcc_sys>; + }; + + leds { + compatible = "gpio-leds"; + + user-led1 { + gpios=<&gpio3 RK_PD4 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "mmc0"; + }; + + user-led2 { + gpios=<&gpio3 RK_PD5 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + }; + }; + + hdmi-sound { + compatible = "simple-audio-card"; + simple-audio-card,format = "i2s"; + simple-audio-card,mclk-fs = <256>; + simple-audio-card,name = "HDMI"; + simple-audio-card,cpu { + sound-dai = <&i2s2>; + }; + simple-audio-card,codec { + sound-dai = <&hdmi>; + }; + }; + + es8316-sound { + compatible = "simple-audio-card"; + simple-audio-card,format = "i2s"; + simple-audio-card,name = "ES8316"; + simple-audio-card,mclk-fs = <256>; + simple-audio-card,widgets = + "Microphone", "Mic Jack", + "Headphone", "Headphone Jack"; + simple-audio-card,routing = + "Mic Jack", "MICBIAS1", + "IN1P", "Mic Jack", + "Headphone Jack", "HPOL", + "Headphone Jack", "HPOR"; + simple-audio-card,cpu { + sound-dai = <&i2s0>; + }; + simple-audio-card,codec { + sound-dai = <&es8316>; + }; + }; + + sdio_pwrseq: sdio-pwrseq { + compatible = "mmc-pwrseq-simple"; + clocks = <&rk808 1>; + clock-names = "ext_clock"; + pinctrl-names = "default"; + pinctrl-0 = <&wifi_enable_h>; + + /* + * On the module itself this is one of these (depending + * on the actual card populated): + * - SDIO_RESET_L_WL_REG_ON + * - PDN (power down when low) + */ + reset-gpios = <&gpio0 RK_PB2 GPIO_ACTIVE_LOW>; + }; + + wireless-wlan { + compatible = "wlan-platdata"; + rockchip,grf = <&grf>; + wifi_chip_type = "ap6256"; + sdio_vref = <1800>; + WIFI,host_wake_irq = <&gpio0 RK_PA3 GPIO_ACTIVE_HIGH>; + status = "okay"; + }; + + wireless-bluetooth { + compatible = "bluetooth-platdata"; + clocks = <&rk808 1>; + clock-names = "ext_clock"; + uart_rts_gpios = <&gpio2 RK_PC3 GPIO_ACTIVE_LOW>; + pinctrl-names = "default", "rts_gpio"; + pinctrl-0 = <&uart0_rts>; + pinctrl-1 = <&uart0_gpios>; + BT,reset_gpio = <&gpio0 RK_PB1 GPIO_ACTIVE_HIGH>; + BT,wake_gpio = <&gpio2 RK_PD3 GPIO_ACTIVE_HIGH>; + BT,wake_host_irq = <&gpio0 RK_PA4 GPIO_ACTIVE_HIGH>; + status = "okay"; + }; +}; + +&cpu_l0 { + cpu-supply = <&vdd_cpu_l>; +}; + +&cpu_l1 { + cpu-supply = <&vdd_cpu_l>; +}; + +&cpu_l2 { + cpu-supply = <&vdd_cpu_l>; +}; + +&cpu_l3 { + cpu-supply = <&vdd_cpu_l>; +}; + +&cpu_b0 { + cpu-supply = <&vdd_cpu_b>; +}; + +&cpu_b1 { + cpu-supply = <&vdd_cpu_b>; +}; + +&display_subsystem { + status = "okay"; + + route { + route_hdmi: route-hdmi { + status = "okay"; + connect = <&vopb_out_hdmi>; + }; + }; +}; + +&emmc_phy { + status = "okay"; +}; + +&i2c0 { + status = "okay"; + i2c-scl-rising-time-ns = <168>; + i2c-scl-falling-time-ns = <4>; + clock-frequency = <400000>; + + vdd_cpu_b: syr827@40 { + compatible = "silergy,syr827"; + reg = <0x40>; + regulator-compatible = "fan53555-reg"; + pinctrl-0 = <&vsel1_gpio>; + vsel-gpios = <&gpio1 RK_PC1 GPIO_ACTIVE_HIGH>; + regulator-name = "vdd_cpu_b"; + regulator-min-microvolt = <712500>; + regulator-max-microvolt = <1500000>; + regulator-ramp-delay = <1000>; + fcs,suspend-voltage-selector = <1>; + regulator-always-on; + regulator-boot-on; + vin-supply = <&vcc_sys>; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vdd_gpu: syr828@41 { + compatible = "silergy,syr828"; + reg = <0x41>; + regulator-compatible = "fan53555-reg"; + pinctrl-0 = <&vsel2_gpio>; + vsel-gpios = <&gpio1 RK_PB6 GPIO_ACTIVE_HIGH>; + regulator-name = "vdd_gpu"; + regulator-min-microvolt = <712500>; + regulator-max-microvolt = <1500000>; + regulator-ramp-delay = <1000>; + fcs,suspend-voltage-selector = <1>; + regulator-always-on; + regulator-boot-on; + vin-supply = <&vcc_sys>; + regulator-initial-mode = <1>; /* 1:force PWM 2:auto */ + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + rk808: pmic@1b { + compatible = "rockchip,rk808"; + reg = <0x1b>; + interrupt-parent = <&gpio1>; + interrupts = <21 IRQ_TYPE_LEVEL_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&pmic_int_l>; + rockchip,system-power-controller; + wakeup-source; + #clock-cells = <1>; + clock-output-names = "rk808-clkout1", "rk808-clkout2"; + + vcc1-supply = <&vcc_sys>; + vcc2-supply = <&vcc_sys>; + vcc3-supply = <&vcc_sys>; + vcc4-supply = <&vcc_sys>; + vcc6-supply = <&vcc_sys>; + vcc7-supply = <&vcc_sys>; + vcc8-supply = <&vcc3v3_sys>; + vcc9-supply = <&vcc_sys>; + vcc10-supply = <&vcc_sys>; + vcc11-supply = <&vcc_sys>; + vcc12-supply = <&vcc3v3_sys>; + vddio-supply = <&vcc_1v8>; + + regulators { + vdd_center: DCDC_REG1 { + regulator-name = "vdd_center"; + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <1350000>; + regulator-ramp-delay = <6001>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vdd_cpu_l: DCDC_REG2 { + regulator-name = "vdd_cpu_l"; + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <1350000>; + regulator-ramp-delay = <6001>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcc_ddr: DCDC_REG3 { + regulator-name = "vcc_ddr"; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + }; + }; + + vcc_1v8: DCDC_REG4 { + regulator-name = "vcc_1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vcc1v8_dvp: LDO_REG1 { + regulator-name = "vcc1v8_codec"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vcca1v8_hdmi: LDO_REG2 { + regulator-name = "vcca1v8_hdmi"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vcca_1v8: LDO_REG3 { + regulator-name = "vcca_1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vcc_sd: LDO_REG4 { + regulator-name = "vcc_sd"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3000000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <3000000>; + }; + }; + + vcc3v0_sd: LDO_REG5 { + regulator-name = "vcc3v0_sd"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <3000000>; + }; + }; + + vcc_1v5: LDO_REG6 { + regulator-name = "vcc_1v5"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <1500000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1500000>; + }; + }; + + vcca0v9_hdmi: LDO_REG7 { + regulator-name = "vcca0v9_hdmi"; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <900000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <900000>; + }; + }; + + vcc_3v0: LDO_REG8 { + regulator-name = "vcc_3v0"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <3000000>; + }; + }; + + vcc3v3_s3: SWITCH_REG1 { + regulator-name = "vcc3v3_s3"; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + }; + }; + + vcc3v3_s0: SWITCH_REG2 { + regulator-name = "vcc3v3_s0"; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + }; + }; + }; + }; +}; + +&i2c1 { + status = "okay"; + i2c-scl-rising-time-ns = <300>; + i2c-scl-falling-time-ns = <15>; + + es8316: es8316@11 { + #sound-dai-cells = <0>; + compatible = "everest,es8316"; + reg = <0x11>; + clocks = <&cru SCLK_I2S_8CH_OUT>; + clock-names = "mclk"; + pinctrl-names = "default"; + pinctrl-0 = <&i2s_8ch_mclk>; + }; +}; + +&i2s0 { + status = "okay"; + rockchip,i2s-broken-burst-len; + rockchip,playback-channels = <8>; + rockchip,capture-channels = <8>; + #sound-dai-cells = <0>; +}; + +&i2s2 { + #sound-dai-cells = <0>; + rockchip,bclk-fs = <128>; + status = "okay"; +}; + +&gmac { + phy-supply = <&vcc_phy>; + phy-mode = "rgmii"; + clock_in_out = "input"; + snps,reset-gpio = <&gpio3 RK_PB7 GPIO_ACTIVE_LOW>; + snps,reset-active-low; + snps,reset-delays-us = <0 10000 50000>; + assigned-clocks = <&cru SCLK_RMII_SRC>; + assigned-clock-parents = <&clkin_gmac>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&rgmii_pins>; + pinctrl-1 = <&rgmii_sleep_pins>; + tx_delay = <0x28>; + rx_delay = <0x11>; + status = "okay"; +}; + +&gpu { + status = "okay"; + mali-supply = <&vdd_gpu>; +}; + +&hdmi { + #address-cells = <1>; + #size-cells = <0>; + #sound-dai-cells = <0>; + status = "okay"; +}; + +&iep { + status = "okay"; +}; + +&iep_mmu { + status = "okay"; +}; + +&io_domains { + status = "okay"; + + bt656-supply = <&vcc_3v0>; /* bt656_gpio2ab_ms */ + audio-supply = <&vcc_3v0>; /* audio_gpio3d4a_ms */ + sdmmc-supply = <&vcc_sd>; /* sdmmc_gpio4b_ms */ + gpio1830-supply = <&vcc_3v0>; /* gpio1833_gpio4cd_ms */ +}; + +&saradc { + status = "okay"; +}; + +&sdmmc { + clock-frequency = <100000000>; + max-frequency = <100000000>; + supports-sd; + bus-width = <4>; + cap-mmc-highspeed; + cap-sd-highspeed; + disable-wp; + num-slots = <1>; + sd-uhs-sdr12; + sd-uhs-sdr25; + sd-uhs-sdr50; + sd-uhs-sdr104; + vqmmc-supply = <&vcc_sd>; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_cd &sdmmc_bus4>; + card-detect-delay = <800>; + status = "okay"; +}; + +&sdio0 { + clock-frequency = <100000000>; + max-frequency = <100000000>; + supports-sdio; + bus-width = <4>; + disable-wp; + cap-sd-highspeed; + cap-sdio-irq; + keep-power-in-suspend; + mmc-pwrseq = <&sdio_pwrseq>; + non-removable; + num-slots = <1>; + pinctrl-names = "default"; + pinctrl-0 = <&sdio0_bus4 &sdio0_cmd &sdio0_clk>; + sd-uhs-sdr104; + status = "okay"; +}; + +&sdhci { + bus-width = <8>; + mmc-hs400-1_8v; + mmc-hs400-enhanced-strobe; + supports-emmc; + non-removable; + status = "okay"; +}; + +&threshold { + temperature = <85000>; +}; + +&target { + temperature = <100000>; +}; + +&soc_crit { + temperature = <105000>; +}; + +&tcphy0 { + status = "okay"; +}; + +&tcphy1 { + status = "okay"; +}; + +&tsadc { + /* tshut mode 0:CRU 1:GPIO */ + rockchip,hw-tshut-mode = <1>; + /* tshut polarity 0:LOW 1:HIGH */ + rockchip,hw-tshut-polarity = <1>; + rockchip,hw-tshut-temp = <110000>; + status = "okay"; +}; + +&u2phy0 { + status = "okay"; + enable-active-high; + /* otg-vbus-gpios = <&gpio1 3 GPIO_ACTIVE_HIGH>;*/ + gpio = <&gpio1 3 GPIO_ACTIVE_HIGH>; + + u2phy0_otg: otg-port { + status = "okay"; + }; + + u2phy0_host: host-port { + phy-supply = <&vcc5v0_host>; + status = "okay"; + }; +}; + +&u2phy1 { + status = "okay"; + + u2phy1_otg: otg-port { + status = "okay"; + }; + + u2phy1_host: host-port { + phy-supply = <&vcc5v0_host>; + status = "okay"; + }; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_xfer &uart0_cts>; + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&uart4 { + status = "okay"; +}; + +&usb_host0_ehci { + status = "okay"; +}; + +&usb_host0_ohci { + status = "okay"; +}; + +&usb_host1_ehci { + status = "okay"; +}; + +&usb_host1_ohci { + status = "okay"; +}; + +&usbdrd3_0 { + extcon = <&u2phy0>; + status = "okay"; +}; + +&usbdrd_dwc3_0 { + dr_mode = "otg"; + status = "okay"; +}; + +&usbdrd3_1 { + status = "okay"; +}; + +&usbdrd_dwc3_1 { + dr_mode = "host"; + status = "okay"; +}; + +&pwm2 { + status = "okay"; +}; + +&pinctrl { + gmac { + rgmii_sleep_pins: rgmii-sleep-pins { + rockchip,pins = + <3 RK_PB7 RK_FUNC_GPIO &pcfg_output_low>; + }; + }; + + i2c4 { + i2c4_xfer: i2c4-xfer { + rockchip,pins = + <1 12 RK_FUNC_1 &pcfg_pull_up>, + <1 11 RK_FUNC_1 &pcfg_pull_up>; + }; + }; + + i2s0 { + i2s0_8ch_bus: i2s0-8ch-bus { + rockchip,pins = + <3 28 0 &pcfg_pull_none>, + <3 29 0 &pcfg_pull_none>; + }; + }; + + pcie { + pcie_drv: pcie-drv { + rockchip,pins = + <2 RK_PD2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + pmic { + pmic_int_l: pmic-int-l { + rockchip,pins = + <1 RK_PC5 RK_FUNC_GPIO &pcfg_pull_up>; + }; + + vsel1_gpio: vsel1-gpio { + rockchip,pins = + <1 RK_PC1 RK_FUNC_GPIO &pcfg_pull_down>; + }; + + vsel2_gpio: vsel2-gpio { + rockchip,pins = + <1 RK_PB6 RK_FUNC_GPIO &pcfg_pull_down>; + }; + }; + + sdio-pwrseq { + wifi_enable_h: wifi-enable-h { + rockchip,pins = + <0 RK_PB2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + sdio0 { + sdio0_bus1: sdio0-bus1 { + rockchip,pins = + <2 RK_PC4 RK_FUNC_1 &pcfg_pull_up_20ma>; + }; + + sdio0_bus4: sdio0-bus4 { + rockchip,pins = + <2 RK_PC4 RK_FUNC_1 &pcfg_pull_up_20ma>, + <2 RK_PC5 RK_FUNC_1 &pcfg_pull_up_20ma>, + <2 RK_PC6 RK_FUNC_1 &pcfg_pull_up_20ma>, + <2 RK_PC7 RK_FUNC_1 &pcfg_pull_up_20ma>; + }; + + sdio0_cmd: sdio0-cmd { + rockchip,pins = + <2 RK_PD0 RK_FUNC_1 &pcfg_pull_up_20ma>; + }; + + sdio0_clk: sdio0-clk { + rockchip,pins = + <2 RK_PD1 RK_FUNC_1 &pcfg_pull_none_20ma>; + }; + }; + + sdmmc { + sdmmc_bus1: sdmmc-bus1 { + rockchip,pins = + <4 RK_PB0 RK_FUNC_1 &pcfg_pull_up_8ma>; + }; + + sdmmc_bus4: sdmmc-bus4 { + rockchip,pins = + <4 RK_PB0 RK_FUNC_1 &pcfg_pull_up_8ma>, + <4 RK_PB1 RK_FUNC_1 &pcfg_pull_up_8ma>, + <4 RK_PB2 RK_FUNC_1 &pcfg_pull_up_8ma>, + <4 RK_PB3 RK_FUNC_1 &pcfg_pull_up_8ma>; + }; + + sdmmc_clk: sdmmc-clk { + rockchip,pins = + <4 RK_PB4 RK_FUNC_1 &pcfg_pull_none_18ma>; + }; + + sdmmc_cmd: sdmmc-cmd { + rockchip,pins = + <4 RK_PB5 RK_FUNC_1 &pcfg_pull_up_8ma>; + }; + }; + + usb2 { + host_vbus_drv: host-vbus-drv { + rockchip,pins = + <4 RK_PD1 RK_FUNC_GPIO &pcfg_pull_none>; + }; + + otg_vbus_drv: otg-vbus-drv { + rockchip,pins = + <1 3 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + wireless-bluetooth { + uart0_gpios: uart0-gpios { + rockchip,pins = + <2 RK_PC3 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; +}; + +&pvtm { + status = "okay"; +}; + +&pmu_pvtm { + status = "okay"; +}; + +&pmu_io_domains { + status = "okay"; + pmu1830-supply = <&vcc_3v0>; +}; + +&pcie_phy { + status = "okay"; +}; + +&pcie0 { + ep-gpios = <&gpio4 27 GPIO_ACTIVE_HIGH>; + num-lanes = <4>; + pinctrl-names = "default"; + pinctrl-0 = <&pcie_clkreqnb_cpm>; + status = "okay"; +}; + +&rkvdec { + status = "okay"; +}; + +&vdec_mmu { + status = "okay"; +}; + +&vpu { + status = "okay"; +}; + +&vpu_mmu { + status = "okay"; +}; + +&vopb { + status = "okay"; +}; + +&vopb_mmu { + status = "okay"; +}; + +&vopl { + status = "disabled"; +}; + +&vopl_mmu { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-rock960.dts b/arch/arm64/boot/dts/rockchip/rk3399-rock960.dts new file mode 100644 index 00000000..865a1da9 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3399-rock960.dts @@ -0,0 +1,1003 @@ +/* + * Copyright (c) 2016 Fuzhou Rockchip Electronics Co., Ltd + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Or, alternatively, + * + * b) Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/dts-v1/; + +#include +#include +#include "rk3399.dtsi" +#include "rk3399-linux.dtsi" +#include "rk3399-opp.dtsi" + + +/ { + model = "ROCK960"; + compatible = "96rocks,rock960", "rockchip,rk3399"; + + vcc1v8_s0: vcc1v8-s0 { + compatible = "regulator-fixed"; + regulator-name = "vcc1v8_s0"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + vcc_sys: vcc-sys { + compatible = "regulator-fixed"; + regulator-name = "vcc_sys"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + }; + + vcc_phy: vcc-phy-regulator { + compatible = "regulator-fixed"; + regulator-name = "vcc_phy"; + regulator-always-on; + regulator-boot-on; + }; + + vcc3v3_sys: vcc3v3-sys { + compatible = "regulator-fixed"; + regulator-name = "vcc3v3_sys"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + vin-supply = <&vcc_sys>; + }; + + vcc3v3_pcie: vcc3v3-pcie-regulator { + compatible = "regulator-fixed"; + gpio = <&gpio3 11 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&pcie_drv>; + regulator-boot-on; + regulator-always-on; + regulator-name = "vcc3v3_pcie"; + vin-supply = <&vcc3v3_sys>; + }; + + vcc5v0_host: vcc5v0-host-regulator { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio4 25 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&host_vbus_drv>; + regulator-name = "vcc5v0_host"; + regulator-always-on; + }; + + vdd_log: vdd-log { + compatible = "pwm-regulator"; + pwms = <&pwm2 0 25000 1>; + regulator-name = "vdd_log"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1400000>; + regulator-always-on; + regulator-boot-on; + + /* for rockchip boot on */ + rockchip,pwm_id= <2>; + rockchip,pwm_voltage = <900000>; + + vin-supply = <&vcc_sys>; + }; + + clkin_gmac: external-gmac-clock { + compatible = "fixed-clock"; + clock-frequency = <125000000>; + clock-output-names = "clkin_gmac"; + #clock-cells = <0>; + }; + + hdmi-sound { + compatible = "simple-audio-card"; + simple-audio-card,format = "i2s"; + simple-audio-card,mclk-fs = <256>; + simple-audio-card,name = "HDMI"; + simple-audio-card,cpu { + sound-dai = <&i2s2>; + }; + simple-audio-card,codec { + sound-dai = <&hdmi>; + }; + }; + + spdif-sound { + compatible = "simple-audio-card"; + simple-audio-card,name = "SPDIF"; + simple-audio-card,cpu { + sound-dai = <&spdif>; + }; + simple-audio-card,codec { + sound-dai = <&spdif_out>; + }; + }; + + spdif_out: spdif-out { + compatible = "linux,spdif-dit"; + #sound-dai-cells = <0>; + }; + + sdio_pwrseq: sdio-pwrseq { + compatible = "mmc-pwrseq-simple"; + clocks = <&rk808 1>; + clock-names = "ext_clock"; + pinctrl-names = "default"; + pinctrl-0 = <&wifi_enable_h>; + post-power-on-delay-ms = <200>; + power-off-delay-us = <10>; + + /* + * On the module itself this is one of these (depending + * on the actual card populated): + * - SDIO_RESET_L_WL_REG_ON + * - PDN (power down when low) + */ + reset-gpios = <&gpio0 10 GPIO_ACTIVE_LOW>; + }; + + wireless-wlan { + compatible = "wlan-platdata"; + rockchip,grf = <&grf>; + wifi_chip_type = "ap6354"; + sdio_vref = <1800>; + WIFI,host_wake_irq = <&gpio0 3 GPIO_ACTIVE_HIGH>; + status = "okay"; + }; + + wireless-bluetooth { + compatible = "bluetooth-platdata"; + clocks = <&rk808 1>; + clock-names = "ext_clock"; + /* wifi-bt-power-toggle; */ + uart_rts_gpios = <&gpio2 19 GPIO_ACTIVE_LOW>; + pinctrl-names = "default", "rts_gpio"; + pinctrl-0 = <&uart0_rts>; + pinctrl-1 = <&uart0_gpios>; + /* BT,power_gpio = <&gpio3 19 GPIO_ACTIVE_HIGH>; */ + BT,reset_gpio = <&gpio0 9 GPIO_ACTIVE_HIGH>; + BT,wake_gpio = <&gpio2 27 GPIO_ACTIVE_HIGH>; + BT,wake_host_irq = <&gpio0 4 GPIO_ACTIVE_HIGH>; + status = "okay"; + }; + + test-power { + status = "okay"; + }; +}; + +&hdmi { + #address-cells = <1>; + #size-cells = <0>; + #sound-dai-cells = <0>; + status = "okay"; +}; + +&sdmmc { + clock-frequency = <100000000>; + max-frequency = <100000000>; + supports-sd; + bus-width = <4>; + cap-mmc-highspeed; + cap-sd-highspeed; + disable-wp; + num-slots = <1>; + sd-uhs-sdr12; + sd-uhs-sdr25; + sd-uhs-sdr50; + sd-uhs-sdr104; + vqmmc-supply = <&vcc_sd>; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_cd &sdmmc_bus4>; + card-detect-delay = <800>; + status = "okay"; +}; + +&sdio0 { + clock-frequency = <100000000>; + max-frequency = <100000000>; + supports-sdio; + bus-width = <4>; + disable-wp; + cap-sd-highspeed; + cap-sdio-irq; + keep-power-in-suspend; + mmc-pwrseq = <&sdio_pwrseq>; + non-removable; + num-slots = <1>; + pinctrl-names = "default"; + pinctrl-0 = <&sdio0_bus4 &sdio0_cmd &sdio0_clk>; + sd-uhs-sdr104; + status = "okay"; +}; + +&emmc_phy { + status = "okay"; +}; + +&sdhci { + bus-width = <8>; + mmc-hs400-1_8v; + supports-emmc; + non-removable; + mmc-hs400-enhanced-strobe; + status = "okay"; +}; + +&i2s0 { + status = "okay"; + rockchip,i2s-broken-burst-len; + rockchip,playback-channels = <8>; + rockchip,capture-channels = <8>; + #sound-dai-cells = <0>; +}; + +&i2s2 { + #sound-dai-cells = <0>; + rockchip,bclk-fs = <128>; + status = "okay"; +}; + +&spdif { + pinctrl-0 = <&spdif_bus_1>; + status = "okay"; + #sound-dai-cells = <0>; +}; + +&i2c0 { + status = "okay"; + i2c-scl-rising-time-ns = <168>; + i2c-scl-falling-time-ns = <4>; + clock-frequency = <400000>; + + vdd_cpu_b: syr827@40 { + compatible = "silergy,syr827"; + reg = <0x40>; + regulator-compatible = "fan53555-reg"; + pinctrl-0 = <&vsel1_gpio>; + vsel-gpios = <&gpio1 17 GPIO_ACTIVE_HIGH>; + regulator-name = "vdd_cpu_b"; + regulator-min-microvolt = <712500>; + regulator-max-microvolt = <1500000>; + regulator-ramp-delay = <1000>; + fcs,suspend-voltage-selector = <1>; + regulator-always-on; + regulator-boot-on; + vin-supply = <&vcc_sys>; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vdd_gpu: syr828@41 { + compatible = "silergy,syr828"; + reg = <0x41>; + regulator-compatible = "fan53555-reg"; + pinctrl-0 = <&vsel2_gpio>; + vsel-gpios = <&gpio1 14 GPIO_ACTIVE_HIGH>; + regulator-name = "vdd_gpu"; + regulator-min-microvolt = <712500>; + regulator-max-microvolt = <1500000>; + regulator-ramp-delay = <1000>; + fcs,suspend-voltage-selector = <1>; + regulator-always-on; + regulator-boot-on; + vin-supply = <&vcc_sys>; + regulator-initial-mode = <1>; /* 1:force PWM 2:auto */ + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + rk808: pmic@1b { + compatible = "rockchip,rk808"; + reg = <0x1b>; + interrupt-parent = <&gpio1>; + interrupts = <21 IRQ_TYPE_LEVEL_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&pmic_int_l>; + rockchip,system-power-controller; + wakeup-source; + #clock-cells = <1>; + clock-output-names = "xin32k", "rk808-clkout2"; + + vcc1-supply = <&vcc_sys>; + vcc2-supply = <&vcc_sys>; + vcc3-supply = <&vcc_sys>; + vcc4-supply = <&vcc_sys>; + vcc6-supply = <&vcc_sys>; + vcc7-supply = <&vcc_sys>; + vcc8-supply = <&vcc3v3_sys>; + vcc9-supply = <&vcc_sys>; + vcc10-supply = <&vcc_sys>; + vcc11-supply = <&vcc_sys>; + vcc12-supply = <&vcc3v3_sys>; + vddio-supply = <&vcc_1v8>; + + regulators { + vdd_center: DCDC_REG1 { + regulator-name = "vdd_center"; + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <1350000>; + regulator-ramp-delay = <6001>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vdd_cpu_l: DCDC_REG2 { + regulator-name = "vdd_cpu_l"; + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <1350000>; + regulator-ramp-delay = <6001>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vcc_ddr: DCDC_REG3 { + regulator-name = "vcc_ddr"; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + }; + }; + + vcc_1v8: DCDC_REG4 { + regulator-name = "vcc_1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vcc1v8_dvp: LDO_REG1 { + regulator-name = "vcc1v8_dvp"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vcca1v8_hdmi: LDO_REG2 { + regulator-name = "vcca1v8_hdmi"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vcca_1v8: LDO_REG3 { + regulator-name = "vcca_1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + }; + }; + + vcc_sd: LDO_REG4 { + regulator-name = "vcc_sd"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3000000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <3000000>; + }; + }; + + vcc3v0_sd: LDO_REG5 { + regulator-name = "vcc3v0_sd"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <3000000>; + }; + }; + + vcc_1v5: LDO_REG6 { + regulator-name = "vcc_1v5"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <1500000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1500000>; + }; + }; + + vcca0v9_hdmi: LDO_REG7 { + regulator-name = "vcca0v9_hdmi"; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <900000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <900000>; + }; + }; + + vcc_3v0: LDO_REG8 { + regulator-name = "vcc_3v0"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <3000000>; + }; + }; + + vcc3v3_s3: SWITCH_REG1 { + regulator-name = "vcc3v3_s3"; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + }; + }; + + vcc3v3_s0: SWITCH_REG2 { + regulator-name = "vcc3v3_s0"; + regulator-always-on; + regulator-boot-on; + regulator-state-mem { + regulator-on-in-suspend; + }; + }; + }; + }; +}; + +&i2c1 { + status = "okay"; +}; + +&i2c6 { + status = "okay"; +}; + +&i2c4 { + status = "okay"; + fusb0: fusb30x@22 { + compatible = "fairchild,fusb302"; + reg = <0x22>; + pinctrl-names = "default"; + pinctrl-0 = <&fusb0_int>; + vbus-5v-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>; + int-n-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>; + status = "okay"; + }; +}; + +&i2c2 { + status = "okay"; + camera0: camera-module@10 { + status = "disabled"; + compatible = "omnivision,ov13850-v4l2-i2c-subdev"; + reg = < 0x10 >; + device_type = "v4l2-i2c-subdev"; + clocks = <&cru SCLK_CIF_OUT>; + clock-names = "clk_cif_out"; + pinctrl-names = "rockchip,camera_default", + "rockchip,camera_sleep"; + pinctrl-0 = <&cam0_default_pins>; + pinctrl-1 = <&cam0_sleep_pins>; + //rockchip,pd-gpio = <&gpio4 4 GPIO_ACTIVE_LOW>; + rockchip,pwr-gpio = <&gpio4 4 GPIO_ACTIVE_HIGH>; + rockchip,rst-gpio = <&gpio3 29 GPIO_ACTIVE_LOW>; + rockchip,camera-module-mclk-name = "clk_cif_out"; + rockchip,camera-module-facing = "back"; + rockchip,camera-module-name = "cmk-cb0695-fv1"; + rockchip,camera-module-len-name = "lg9569a2"; + rockchip,camera-module-fov-h = "66.0"; + rockchip,camera-module-fov-v = "50.1"; + rockchip,camera-module-orientation = <0>; + rockchip,camera-module-iq-flip = <0>; + rockchip,camera-module-iq-mirror = <0>; + rockchip,camera-module-flip = <1>; + rockchip,camera-module-mirror = <0>; + + rockchip,camera-module-defrect0 = <2112 1568 0 0 2112 1568>; + rockchip,camera-module-defrect1 = <4224 3136 0 0 4224 3136>; + rockchip,camera-module-defrect3 = <3264 2448 0 0 3264 2448>; + rockchip,camera-module-flash-support = <1>; + rockchip,camera-module-mipi-dphy-index = <0>; + }; + + camera1: camera-module@36 { + status = "disabled"; + compatible = "omnivision,ov4690-v4l2-i2c-subdev"; + reg = <0x36>; + device_type = "v4l2-i2c-subdev"; + clocks = <&cru SCLK_CIF_OUT>; + clock-names = "clk_cif_out"; + pinctrl-names = "rockchip,camera_default", + "rockchip,camera_sleep"; + pinctrl-0 = <&cam0_default_pins>; + pinctrl-1 = <&cam0_sleep_pins>; + rockchip,pd-gpio = <&gpio3 4 GPIO_ACTIVE_LOW>; + //rockchip,pwr-gpio = <&gpio3 13 0>; + rockchip,rst-gpio = <&gpio2 10 GPIO_ACTIVE_LOW>; + rockchip,camera-module-mclk-name = "clk_cif_out"; + rockchip,camera-module-facing = "back"; + rockchip,camera-module-name = "LA6111PA"; + rockchip,camera-module-len-name = "YM6011P"; + rockchip,camera-module-fov-h = "116"; + rockchip,camera-module-fov-v = "61"; + rockchip,camera-module-orientation = <0>; + rockchip,camera-module-iq-flip = <0>; + rockchip,camera-module-iq-mirror = <0>; + rockchip,camera-module-flip = <0>; + rockchip,camera-module-mirror = <1>; + + rockchip,camera-module-defrect0 = <2688 1520 0 0 2688 1520>; + rockchip,camera-module-flash-support = <0>; + rockchip,camera-module-mipi-dphy-index = <0>; + }; + +}; + +&cpu_l0 { + cpu-supply = <&vdd_cpu_l>; +}; + +&cpu_l1 { + cpu-supply = <&vdd_cpu_l>; +}; + +&cpu_l2 { + cpu-supply = <&vdd_cpu_l>; +}; + +&cpu_l3 { + cpu-supply = <&vdd_cpu_l>; +}; + +&cpu_b0 { + cpu-supply = <&vdd_cpu_b>; +}; + +&cpu_b1 { + cpu-supply = <&vdd_cpu_b>; +}; + +&gpu { + status = "okay"; + mali-supply = <&vdd_gpu>; +}; + +&threshold { + temperature = <85000>; +}; + +&target { + temperature = <100000>; +}; + +&soc_crit { + temperature = <105000>; +}; + +&tcphy0 { + extcon = <&fusb0>; + status = "okay"; +}; + +&tcphy1 { + status = "okay"; +}; + +&tsadc { + /* tshut mode 0:CRU 1:GPIO */ + rockchip,hw-tshut-mode = <1>; + /* tshut polarity 0:LOW 1:HIGH */ + rockchip,hw-tshut-polarity = <1>; + rockchip,hw-tshut-temp = <110000>; + status = "okay"; +}; + +&u2phy0 { + status = "okay"; + extcon = <&fusb0>; + + u2phy0_otg: otg-port { + status = "okay"; + }; + + u2phy0_host: host-port { + phy-supply = <&vcc5v0_host>; + status = "okay"; + }; +}; + +&u2phy1 { + status = "okay"; + + u2phy1_otg: otg-port { + status = "okay"; + }; + + u2phy1_host: host-port { + phy-supply = <&vcc5v0_host>; + status = "okay"; + }; +}; + +&uart0 { + dmas = <&dmac_peri 0>, <&dmac_peri 1>; + dma-names = "tx", "rx"; + pinctrl-names = "default"; + pinctrl-0 = <&uart0_xfer &uart0_cts>; + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&uart3 { + dmas = <&dmac_peri 6>, <&dmac_peri 7>; + dma-names = "tx", "rx"; + status = "okay"; +}; + +&uart4 { + dmas = <&dmac_peri 8>, <&dmac_peri 9>; + dma-names = "tx", "rx"; + status = "okay"; +}; + +&usb_host0_ehci { + status = "okay"; +}; + +&usb_host0_ohci { + status = "okay"; +}; + +&usb_host1_ehci { + status = "okay"; +}; + +&usb_host1_ohci { + status = "okay"; +}; + +&usbdrd3_0 { + extcon = <&fusb0>; + status = "okay"; +}; + +&usbdrd_dwc3_0 { + dr_mode = "otg"; + status = "okay"; +}; + +&usbdrd3_1 { + status = "okay"; +}; + +&usbdrd_dwc3_1 { + dr_mode = "host"; + status = "okay"; +}; + +&pwm2 { + status = "okay"; +}; + +&gmac { + phy-supply = <&vcc_phy>; + phy-mode = "rgmii"; + clock_in_out = "input"; + snps,reset-gpio = <&gpio3 15 GPIO_ACTIVE_LOW>; + snps,reset-active-low; + snps,reset-delays-us = <0 10000 50000>; + assigned-clocks = <&cru SCLK_RMII_SRC>; + assigned-clock-parents = <&clkin_gmac>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&rgmii_pins>; + pinctrl-1 = <&rgmii_sleep_pins>; + tx_delay = <0x28>; + rx_delay = <0x11>; + status = "disabled"; +}; + +&saradc { + status = "okay"; +}; + +&iep { + status = "okay"; +}; + +&iep_mmu { + status = "okay"; +}; + +&io_domains { + status = "okay"; + + bt656-supply = <&vcc1v8_s0>; /* bt656_gpio2ab_ms */ + audio-supply = <&vcc1v8_s0>; /* audio_gpio3d4a_ms */ + sdmmc-supply = <&vcc_sd>; /* sdmmc_gpio4b_ms */ + gpio1830-supply = <&vcc_3v0>; /* gpio1833_gpio4cd_ms */ +}; + +&pcie_phy { + status = "okay"; +}; + +&pcie0 { + ep-gpios = <&gpio3 9 GPIO_ACTIVE_HIGH>; + num-lanes = <4>; + pinctrl-names = "default"; + pinctrl-0 = <&pcie_clkreqn_cpm>; + status = "okay"; +}; + +&pinctrl { + + sdio0 { + sdio0_bus1: sdio0-bus1 { + rockchip,pins = + <2 20 RK_FUNC_1 &pcfg_pull_up_20ma>; + }; + + sdio0_bus4: sdio0-bus4 { + rockchip,pins = + <2 20 RK_FUNC_1 &pcfg_pull_up_20ma>, + <2 21 RK_FUNC_1 &pcfg_pull_up_20ma>, + <2 22 RK_FUNC_1 &pcfg_pull_up_20ma>, + <2 23 RK_FUNC_1 &pcfg_pull_up_20ma>; + }; + + sdio0_cmd: sdio0-cmd { + rockchip,pins = + <2 24 RK_FUNC_1 &pcfg_pull_up_20ma>; + }; + + sdio0_clk: sdio0-clk { + rockchip,pins = + <2 25 RK_FUNC_1 &pcfg_pull_none_20ma>; + }; + }; + + sdmmc { + sdmmc_bus1: sdmmc-bus1 { + rockchip,pins = + <4 8 RK_FUNC_1 &pcfg_pull_up_8ma>; + }; + + sdmmc_bus4: sdmmc-bus4 { + rockchip,pins = + <4 8 RK_FUNC_1 &pcfg_pull_up_8ma>, + <4 9 RK_FUNC_1 &pcfg_pull_up_8ma>, + <4 10 RK_FUNC_1 &pcfg_pull_up_8ma>, + <4 11 RK_FUNC_1 &pcfg_pull_up_8ma>; + }; + + sdmmc_clk: sdmmc-clk { + rockchip,pins = + <4 12 RK_FUNC_1 &pcfg_pull_none_18ma>; + }; + + sdmmc_cmd: sdmmc-cmd { + rockchip,pins = + <4 13 RK_FUNC_1 &pcfg_pull_up_8ma>; + }; + }; + + sdio-pwrseq { + wifi_enable_h: wifi-enable-h { + rockchip,pins = + <0 9 RK_FUNC_GPIO &pcfg_pull_none>, + <0 10 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + wireless-bluetooth { + uart0_gpios: uart0-gpios { + rockchip,pins = + <2 19 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + usb2 { + host_vbus_drv: host-vbus-drv { + rockchip,pins = + <4 25 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + pcie { + pcie_drv: pcie-drv { + rockchip,pins = + <3 11 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + pmic { + pmic_int_l: pmic-int-l { + rockchip,pins = + <1 21 RK_FUNC_GPIO &pcfg_pull_up>; + }; + + vsel1_gpio: vsel1-gpio { + rockchip,pins = + <1 17 RK_FUNC_GPIO &pcfg_pull_down>; + }; + + vsel2_gpio: vsel2-gpio { + rockchip,pins = + <1 14 RK_FUNC_GPIO &pcfg_pull_down>; + }; + }; + + gmac { + rgmii_sleep_pins: rgmii-sleep-pins { + rockchip,pins = + <3 15 RK_FUNC_GPIO &pcfg_output_low>; + }; + }; + + fusb30x { + fusb0_int: fusb0-int { + rockchip,pins = + <1 2 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; +}; + +&pvtm { + status = "okay"; +}; + +&pmu_pvtm { + status = "okay"; +}; + +&pmu_io_domains { + status = "okay"; + pmu1830-supply = <&vcc_1v8>; +}; + +&rockchip_suspend { + status = "okay"; + rockchip,sleep-debug-en = <0>; + rockchip,sleep-mode-config = < + (0 + | RKPM_SLP_ARMPD + | RKPM_SLP_PERILPPD + | RKPM_SLP_DDR_RET + | RKPM_SLP_PLLPD + | RKPM_SLP_CENTER_PD + | RKPM_SLP_AP_PWROFF + ) + >; + rockchip,wakeup-config = < + (0 + | RKPM_GPIO_WKUP_EN + | RKPM_PWM_WKUP_EN + ) + >; + rockchip,pwm-regulator-config = < + (0 + | PWM2_REGULATOR_EN + ) + >; + rockchip,power-ctrl = + <&gpio1 17 GPIO_ACTIVE_HIGH>, + <&gpio1 14 GPIO_ACTIVE_HIGH>; +}; + +&vopb { + status = "okay"; +}; + +&vopb_mmu { + status = "okay"; +}; + +&cif_isp0 { + rockchip,camera-modules-attached = <&camera0>; + status = "okay"; +}; + +&isp0_mmu { + status = "okay"; +}; + +&cif_isp1 { + rockchip,camera-modules-attached = <&camera1>; + status = "disabled"; +}; + +&isp1_mmu { + status = "okay"; +}; + +&vpu { + status = "okay"; + /* 0 means ion, 1 means drm */ + //allocator = <0>; +}; + +&rkvdec { + status = "okay"; + /* 0 means ion, 1 means drm */ + //allocator = <0>; +}; + +&display_subsystem { + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dts b/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dts index 02b8ba7d..aa1aee54 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dts +++ b/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dts @@ -8,23 +8,33 @@ #include #include #include "rk3399.dtsi" +#include "rk3399-linux.dtsi" #include "rk3399-opp.dtsi" / { model = "Pine64 RockPro64"; compatible = "pine64,rockpro64", "rockchip,rk3399"; - chosen { - bootargs = "earlyprintk=uart8250,mmio32,0xff1a0000 swiotlb=1"; - stdout-path = "serial2:1500000n8"; - }; - /* first 64k(0xff8c0000~0xff8d0000) for ddr and suspend */ iram: sram@ff8d0000 { compatible = "mmio-sram"; reg = <0x0 0xff8d0000 0x0 0x20000>; /* 128k */ }; + xin32k: xin32k { + compatible = "fixed-clock"; + clock-frequency = <32768>; + clock-output-names = "xin32k"; + #clock-cells = <0>; + }; + + clkin_gmac: external-gmac-clock { + compatible = "fixed-clock"; + clock-frequency = <125000000>; + clock-output-names = "clkin_gmac"; + #clock-cells = <0>; + }; + dc_12v: dc-12v { compatible = "regulator-fixed"; regulator-name = "dc_12v"; @@ -92,6 +102,7 @@ vdd_log: vdd-log { compatible = "pwm-regulator"; pwms = <&pwm2 0 25000 1>; + pwm-supply = <&vcc_sys>; regulator-name = "vdd_log"; regulator-min-microvolt = <800000>; regulator-max-microvolt = <1400000>; @@ -101,56 +112,54 @@ /* for rockchip boot on */ rockchip,pwm_id= <2>; rockchip,pwm_voltage = <900000>; + }; - vin-supply = <&vcc_sys>; + leds { + compatible = "gpio-leds"; + + work-led { + gpios = <&gpio0 RK_PB3 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "mmc0"; + }; + + diy-led { + gpios = <&gpio0 RK_PA2 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + }; }; - clkin_gmac: external-gmac-clock { - compatible = "fixed-clock"; - clock-frequency = <125000000>; - clock-output-names = "clkin_gmac"; - #clock-cells = <0>; + ir-receiver { + compatible = "gpio-ir-receiver"; + gpios = <&gpio0 RK_PA6 GPIO_ACTIVE_LOW>; + linux,rc-map-name = "rc-pine64"; + pinctrl-0 = <&ir_int>; + pinctrl-names = "default"; }; - spdif-sound { - status = "disabled"; + hdmi-sound { compatible = "simple-audio-card"; - simple-audio-card,name = "ROCKCHIP,SPDIF"; + simple-audio-card,format = "i2s"; + simple-audio-card,mclk-fs = <256>; + simple-audio-card,name = "HDMI"; simple-audio-card,cpu { - sound-dai = <&spdif>; + sound-dai = <&i2s2>; }; simple-audio-card,codec { - sound-dai = <&spdif_out>; + sound-dai = <&hdmi>; }; }; - spdif_out: spdif-out { + hdmi-dp-sound { + compatible = "rockchip,rk3399-hdmi-dp"; + rockchip,cpu = <&i2s2>; + rockchip,codec = <&hdmi>, <&cdn_dp>; status = "disabled"; - compatible = "linux,spdif-dit"; - #sound-dai-cells = <0>; - }; - - sdio_pwrseq: sdio-pwrseq { - compatible = "mmc-pwrseq-simple"; - clocks = <&rk808 1>; - clock-names = "ext_clock"; - pinctrl-names = "default"; - pinctrl-0 = <&wifi_enable_h>; - - /* - * On the module itself this is one of these (depending - * on the actual card populated): - * - SDIO_RESET_L_WL_REG_ON - * - PDN (power down when low) - */ - reset-gpios = <&gpio0 RK_PB2 GPIO_ACTIVE_LOW>; }; es8316-sound { - status = "okay"; compatible = "simple-audio-card"; simple-audio-card,format = "i2s"; - simple-audio-card,name = "rockchip,es8316-codec"; + simple-audio-card,name = "ES8316"; simple-audio-card,mclk-fs = <256>; simple-audio-card,widgets = "Microphone", "Mic Jack", @@ -168,40 +177,59 @@ }; }; - leds { - status = "okay"; - compatible = "gpio-leds"; - work-led { - gpios = <&gpio0 RK_PB3 GPIO_ACTIVE_HIGH>; - linux,default-trigger = "heartbeat"; - default-state = "on"; + spdif-sound { + compatible = "simple-audio-card"; + simple-audio-card,name = "SPDIF"; + simple-audio-card,cpu { + sound-dai = <&spdif>; }; - diy-led { - gpios = <&gpio0 RK_PA2 GPIO_ACTIVE_HIGH>; - linux,default-trigger = "none"; - default-state = "off"; + simple-audio-card,codec { + sound-dai = <&spdif_out>; }; }; - rk_key: rockchip-key { - compatible = "rockchip,key"; - status = "okay"; + spdif_out: spdif-out { + compatible = "linux,spdif-dit"; + #sound-dai-cells = <0>; + }; - io-channels = <&saradc 1>; + sdio_pwrseq: sdio-pwrseq { + compatible = "mmc-pwrseq-simple"; + clocks = <&rk808 1>; + clock-names = "ext_clock"; + pinctrl-names = "default"; + pinctrl-0 = <&wifi_enable_h>; - power-key { - gpios = <&gpio0 RK_PA5 GPIO_ACTIVE_LOW>; - linux,code = <116>; - label = "power"; - gpio-key,wakeup; - }; + /* + * On the module itself this is one of these (depending + * on the actual card populated): + * - SDIO_RESET_L_WL_REG_ON + * - PDN (power down when low) + */ + reset-gpios = <&gpio0 RK_PB2 GPIO_ACTIVE_LOW>; }; - hdmi_dp_sound: hdmi-dp-sound { - status = "okay"; - compatible = "rockchip,rk3399-hdmi-dp"; - rockchip,cpu = <&i2s2>; - rockchip,codec = <&hdmi>, <&cdn_dp>; + wireless-wlan { + compatible = "wlan-platdata"; + rockchip,grf = <&grf>; + wifi_chip_type = "ap6354"; + sdio_vref = <1800>; + WIFI,host_wake_irq = <&gpio0 RK_PA3 GPIO_ACTIVE_HIGH>; + status = "disabled"; + }; + + wireless-bluetooth { + compatible = "bluetooth-platdata"; + clocks = <&rk808 1>; + clock-names = "ext_clock"; + uart_rts_gpios = <&gpio2 RK_PC3 GPIO_ACTIVE_LOW>; + pinctrl-names = "default", "rts_gpio"; + pinctrl-0 = <&uart0_rts>; + pinctrl-1 = <&uart0_gpios>; + BT,reset_gpio = <&gpio0 RK_PB1 GPIO_ACTIVE_HIGH>; + BT,wake_gpio = <&gpio2 RK_PD3 GPIO_ACTIVE_HIGH>; + BT,wake_host_irq = <&gpio0 RK_PA4 GPIO_ACTIVE_HIGH>; + status = "disabled"; }; }; @@ -253,30 +281,21 @@ &dmc { status = "okay"; center-supply = <&vdd_center>; - upthreshold = <40>; - downdifferential = <20>; system-status-freq = < /*system status freq(KHz)*/ SYS_STATUS_NORMAL 800000 SYS_STATUS_REBOOT 400000 SYS_STATUS_SUSPEND 400000 - SYS_STATUS_VIDEO_1080P 400000 + SYS_STATUS_VIDEO_1080P 800000 SYS_STATUS_VIDEO_4K 800000 SYS_STATUS_VIDEO_4K_10B 800000 SYS_STATUS_PERFORMANCE 800000 - SYS_STATUS_BOOST 400000 + SYS_STATUS_BOOST 800000 SYS_STATUS_DUALVIEW 800000 SYS_STATUS_ISP 800000 >; - vop-bw-dmc-freq = < - /* min_bw(MB/s) max_bw(MB/s) freq(KHz) */ - 0 577 200000 - 578 1701 300000 - 1702 99999 400000 - >; - auto-min-freq = <400000>; + auto-min-freq = <800000>; auto-freq-en = <0>; - }; &dmc_opp_table { @@ -325,42 +344,26 @@ &display_subsystem { status = "okay"; - ports = <&vopb_out>; - route { route_hdmi: route-hdmi { status = "okay"; connect = <&vopb_out_hdmi>; }; - route_dsi: route-dsi { - status = "disabled"; - connect = <&vopb_out_dsi>; - }; - - route_edp: route-edp { - status = "disabled"; - connect = <&vopb_out_edp>; + route_dp: route-dp { + connect = <&vopl_out_dp>; }; }; }; -&dp_in_vopb { - status = "disabled"; -}; - -&edp { - /delete-node/ pinctrl-0; -}; - &emmc_phy { status = "okay"; }; &i2c0 { status = "okay"; - i2c-scl-rising-time-ns = <168>; - i2c-scl-falling-time-ns = <4>; + i2c-scl-rising-time-ns = <180>; + i2c-scl-falling-time-ns = <30>; clock-frequency = <400000>; vdd_cpu_b: syr827@40 { @@ -393,6 +396,7 @@ regulator-max-microvolt = <1500000>; regulator-ramp-delay = <1000>; fcs,suspend-voltage-selector = <1>; + regulator-always-on; regulator-boot-on; vin-supply = <&vcc_sys>; regulator-initial-mode = <1>; /* 1:force PWM 2:auto */ @@ -411,7 +415,7 @@ rockchip,system-power-controller; wakeup-source; #clock-cells = <1>; - clock-output-names = "xin32k", "rk808-clkout2"; + clock-output-names = "rk808-clkout1", "rk808-clkout2"; vcc1-supply = <&vcc_sys>; vcc2-supply = <&vcc_sys>; @@ -429,8 +433,8 @@ regulators { vdd_center: DCDC_REG1 { regulator-name = "vdd_center"; - regulator-min-microvolt = <750000>; - regulator-max-microvolt = <1350000>; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <900000>; regulator-ramp-delay = <6001>; regulator-always-on; regulator-boot-on; @@ -476,6 +480,7 @@ regulator-name = "vcc1v8_dvp"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; + regulator-always-on; regulator-boot-on; regulator-state-mem { regulator-on-in-suspend; @@ -487,6 +492,7 @@ regulator-name = "vcc3v0_touch"; regulator-min-microvolt = <3000000>; regulator-max-microvolt = <3000000>; + regulator-always-on; regulator-boot-on; regulator-state-mem { regulator-on-in-suspend; @@ -510,6 +516,7 @@ regulator-name = "vcc_sd"; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <3300000>; + regulator-always-on; regulator-boot-on; regulator-state-mem { regulator-on-in-suspend; @@ -586,14 +593,6 @@ }; }; -&i2s0 { - status = "okay"; - rockchip,i2s-broken-burst-len; - rockchip,playback-channels = <8>; - rockchip,capture-channels = <8>; - #sound-dai-cells = <0>; -}; - &i2c1 { status = "okay"; i2c-scl-rising-time-ns = <168>; @@ -624,6 +623,14 @@ }; }; +&i2s0 { + status = "okay"; + rockchip,i2s-broken-burst-len; + rockchip,playback-channels = <8>; + rockchip,capture-channels = <8>; + #sound-dai-cells = <0>; +}; + &i2s1 { status = "okay"; rockchip,i2s-broken-burst-len; @@ -634,6 +641,7 @@ &i2s2 { #sound-dai-cells = <0>; + rockchip,bclk-fs = <128>; status = "okay"; }; @@ -641,6 +649,7 @@ phy-supply = <&vcc_phy>; phy-mode = "rgmii"; clock_in_out = "input"; + snps,force_thresh_dma_mode; snps,reset-gpio = <&gpio3 RK_PB7 GPIO_ACTIVE_LOW>; snps,reset-active-low; snps,reset-delays-us = <0 10000 50000>; @@ -660,6 +669,17 @@ }; &hdmi { + #address-cells = <1>; + #size-cells = <0>; + #sound-dai-cells = <0>; + status = "okay"; +}; + +&iep { + status = "okay"; +}; + +&iep_mmu { status = "okay"; }; @@ -678,13 +698,17 @@ &sdmmc { clock-frequency = <50000000>; - clock-freq-min-max = <400000 150000000>; + max-frequency = <150000000>; supports-sd; bus-width = <4>; cap-mmc-highspeed; cap-sd-highspeed; disable-wp; num-slots = <1>; + sd-uhs-sdr12; + sd-uhs-sdr25; + sd-uhs-sdr50; + sd-uhs-sdr104; vqmmc-supply = <&vcc_sd>; pinctrl-names = "default"; pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_cd &sdmmc_bus4>; @@ -694,7 +718,7 @@ &sdio0 { clock-frequency = <50000000>; - clock-freq-min-max = <200000 50000000>; + max-frequency = <50000000>; supports-sdio; bus-width = <4>; disable-wp; @@ -707,35 +731,50 @@ pinctrl-names = "default"; pinctrl-0 = <&sdio0_bus4 &sdio0_cmd &sdio0_clk>; sd-uhs-sdr104; - status = "okay"; + status = "disabled"; }; &sdhci { bus-width = <8>; - mmc-hs200-1_8v; mmc-hs400-1_8v; + mmc-hs400-enhanced-strobe; + supports-emmc; + non-removable; keep-power-in-suspend; status = "okay"; }; &spdif { - status = "disabled"; + status = "okay"; pinctrl-0 = <&spdif_bus_1>; #sound-dai-cells = <0>; }; &spi1 { status = "okay"; + max-freq = <10000000>; + flash@0 { - compatible = "gigadevice,gd25q128", "jedec,spi-nor"; + compatible = "jedec,spi-nor"; #address-cells = <1>; #size-cells = <1>; reg = <0>; - m25p,fast-read; - spi-max-frequency = <24000000>; + spi-max-frequency = <10000000>; }; }; +&threshold { + temperature = <80000>; +}; + +&target { + temperature = <95000>; +}; + +&soc_crit { + temperature = <100000>; +}; + &tcphy0 { extcon = <&fusb0>; status = "okay"; @@ -826,114 +865,15 @@ status = "okay"; }; -&pwm2 { +&pwm1 { status = "okay"; pinctrl-names = "active"; - pinctrl-0 = <&pwm2_pin_pull_down>; }; -&pwm3 { +&pwm2 { status = "okay"; - - interrupts = ; - compatible = "rockchip,remotectl-pwm"; - remote_pwm_id = <3>; - handle_cpu_id = <1>; - remote_support_psci = <1>; - - ir_key1 { - rockchip,usercode = <0x4040>; - rockchip,key_table = - <0xf2 KEY_REPLY>, - <0xba KEY_BACK>, - <0xf4 KEY_UP>, - <0xf1 KEY_DOWN>, - <0xef KEY_LEFT>, - <0xee KEY_RIGHT>, - <0xbd KEY_HOME>, - <0xea KEY_VOLUMEUP>, - <0xe3 KEY_VOLUMEDOWN>, - <0xe2 KEY_SEARCH>, - <0xb2 KEY_POWER>, - <0xbc KEY_MUTE>, - <0xec KEY_MENU>, - <0xbf 0x190>, - <0xe0 0x191>, - <0xe1 0x192>, - <0xe9 183>, - <0xe6 248>, - <0xe8 185>, - <0xe7 186>, - <0xf0 388>, - <0xbe 0x175>; - }; - - ir_key2 { - rockchip,usercode = <0xff00>; - rockchip,key_table = - <0xf9 KEY_HOME>, - <0xbf KEY_BACK>, - <0xfb KEY_MENU>, - <0xaa KEY_REPLY>, - <0xb9 KEY_UP>, - <0xe9 KEY_DOWN>, - <0xb8 KEY_LEFT>, - <0xea KEY_RIGHT>, - <0xeb KEY_VOLUMEDOWN>, - <0xef KEY_VOLUMEUP>, - <0xf7 KEY_MUTE>, - <0xe7 KEY_POWER>, - <0xfc KEY_POWER>, - <0xa9 KEY_VOLUMEDOWN>, - <0xa8 KEY_VOLUMEDOWN>, - <0xe0 KEY_VOLUMEDOWN>, - <0xa5 KEY_VOLUMEDOWN>, - <0xab 183>, - <0xb7 388>, - <0xe8 388>, - <0xf8 184>, - <0xaf 185>, - <0xed KEY_VOLUMEDOWN>, - <0xee 186>, - <0xb3 KEY_VOLUMEDOWN>, - <0xf1 KEY_VOLUMEDOWN>, - <0xf2 KEY_VOLUMEDOWN>, - <0xf3 KEY_SEARCH>, - <0xb4 KEY_VOLUMEDOWN>, - <0xbe KEY_SEARCH>; - }; - - ir_key3 { - rockchip,usercode = <0x1dcc>; - rockchip,key_table = - <0xee KEY_REPLY>, - <0xf0 KEY_BACK>, - <0xf8 KEY_UP>, - <0xbb KEY_DOWN>, - <0xef KEY_LEFT>, - <0xed KEY_RIGHT>, - <0xfc KEY_HOME>, - <0xf1 KEY_VOLUMEUP>, - <0xfd KEY_VOLUMEDOWN>, - <0xb7 KEY_SEARCH>, - <0xff KEY_POWER>, - <0xf3 KEY_MUTE>, - <0xbf KEY_MENU>, - <0xf9 0x191>, - <0xf5 0x192>, - <0xb3 388>, - <0xbe KEY_1>, - <0xba KEY_2>, - <0xb2 KEY_3>, - <0xbd KEY_4>, - <0xf9 KEY_5>, - <0xb1 KEY_6>, - <0xfc KEY_7>, - <0xf8 KEY_8>, - <0xb0 KEY_9>, - <0xb6 KEY_0>, - <0xb5 KEY_BACKSPACE>; - }; + pinctrl-names = "active"; + pinctrl-0 = <&pwm2_pin_pull_down>; }; &pinctrl { @@ -951,6 +891,13 @@ }; }; + ir { + ir_int: ir-int { + rockchip,pins = + <0 RK_PA6 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + pcie { pcie_pwr_en: pcie-pwr-en { rockchip,pins = @@ -982,6 +929,7 @@ sdio-pwrseq { wifi_enable_h: wifi-enable-h { rockchip,pins = + <0 RK_PB1 RK_FUNC_GPIO &pcfg_pull_none>, <0 RK_PB2 RK_FUNC_GPIO &pcfg_pull_none>; }; }; @@ -1085,35 +1033,6 @@ status = "okay"; }; -&rockchip_suspend { - status = "okay"; - rockchip,sleep-debug-en = <0>; - rockchip,sleep-mode-config = < - (0 - | RKPM_SLP_ARMPD - | RKPM_SLP_PERILPPD - | RKPM_SLP_DDR_RET - | RKPM_SLP_PLLPD - | RKPM_SLP_CENTER_PD - | RKPM_SLP_AP_PWROFF - ) - >; - rockchip,wakeup-config = < - (0 - | RKPM_GPIO_WKUP_EN - | RKPM_PWM_WKUP_EN - ) - >; - rockchip,pwm-regulator-config = < - (0 - | PWM2_REGULATOR_EN - ) - >; - rockchip,power-ctrl = - <&gpio1 RK_PC1 GPIO_ACTIVE_HIGH>, - <&gpio1 RK_PB6 GPIO_ACTIVE_HIGH>; -}; - &vdec_mmu { status = "okay"; }; @@ -1135,3 +1054,13 @@ &vopb_mmu { status = "okay"; }; + +&vopl { + status = "disabled"; + assigned-clocks = <&cru DCLK_VOP1_DIV>; + assigned-clock-parents = <&cru PLL_CPLL>; +}; + +&vopl_mmu { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dts b/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dts new file mode 100644 index 00000000..8706dc7d --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dts @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2016 Fuzhou Rockchip Electronics Co., Ltd + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Or, alternatively, + * + * b) Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/dts-v1/; + +#include "rk3399-sapphire.dtsi" +#include "rk3399-linux.dtsi" +#include + +/ { + model = "Rockchip RK3399 Sapphire Board"; + compatible = "rockchip,rk3399-sapphire", "rockchip,rk3399"; + + gpio-keys { + compatible = "gpio-keys"; + #address-cells = <1>; + #size-cells = <0>; + autorepeat; + + pinctrl-names = "default"; + pinctrl-0 = <&pwrbtn>; + + button@0 { + gpios = <&gpio0 5 GPIO_ACTIVE_LOW>; + linux,code = ; + label = "GPIO Key Power"; + linux,input-type = <1>; + gpio-key,wakeup = <1>; + debounce-interval = <100>; + }; + }; + + vccadc_ref: vccadc-ref { + compatible = "regulator-fixed"; + regulator-name = "vcc1v8_sys"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; +}; + +&hdmi_sound { + simple-audio-card,name = "HDMI"; + status = "okay"; +}; + +&i2s2 { + #sound-dai-cells = <0>; + rockchip,bclk-fs = <128>; + status = "okay"; +}; + +&iep { + status = "okay"; +}; + +&iep_mmu { + status = "okay"; +}; + +&saradc { + vref-supply = <&vccadc_ref>; +}; + +&vpu { + status = "okay"; +}; + +&rkvdec { + status = "okay"; +}; + +&display_subsystem { + ports = <&vopb_out>; + status = "okay"; +}; + +&route_hdmi { + status = "okay"; +}; + +&cdn_dp { + status = "disabled"; +}; + +&dp_in_vopb { + status = "disabled"; +}; + +&hdmi { + #address-cells = <1>; + #size-cells = <0>; + #sound-dai-cells = <0>; + status = "okay"; +}; + +&pcie_phy { + status = "disabled"; +}; + +&pcie0 { + status = "disabled"; +}; + +&sdio0 { + status = "disabled"; +}; + +&vopb { + status = "okay"; +}; + +&vopb_mmu { + status = "okay"; +}; + +&pinctrl { + sdio-pwrseq { + wifi_enable_h: wifi-enable-h { + rockchip,pins = + <0 RK_PB2 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + buttons { + pwrbtn: pwrbtn { + rockchip,pins = <0 5 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; +}; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi index 3d76b973..62ba4281 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-sapphire.dtsi @@ -536,6 +536,14 @@ status = "okay"; }; +&pvtm { + status = "okay"; +}; + +&pmu_pvtm { + status = "okay"; +}; + &pmu_io_domains { status = "okay"; pmu1830-supply = <&vcc_3v0>; @@ -563,7 +571,7 @@ &sdio0 { clock-frequency = <50000000>; - clock-freq-min-max = <200000 50000000>; + max-frequency = <50000000>; supports-sdio; bus-width = <4>; disable-wp; @@ -581,14 +589,17 @@ &sdmmc { clock-frequency = <150000000>; - clock-freq-min-max = <100000 150000000>; + max-frequency = <150000000>; supports-sd; bus-width = <4>; cap-mmc-highspeed; cap-sd-highspeed; disable-wp; num-slots = <1>; - //sd-uhs-sdr104; + sd-uhs-sdr12; + sd-uhs-sdr25; + sd-uhs-sdr50; + sd-uhs-sdr104; vmmc-supply = <&vcc_sd>; vqmmc-supply = <&vccio_sd>; pinctrl-names = "default"; diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi index 815a8c13..fbe3d0ed 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi @@ -646,6 +646,8 @@ uart0: serial@ff180000 { compatible = "rockchip,rk3399-uart", "snps,dw-apb-uart"; reg = <0x0 0xff180000 0x0 0x100>; + dmas = <&dmac_peri 0>, <&dmac_peri 1>; + dma-names = "tx", "rx"; clocks = <&cru SCLK_UART0>, <&cru PCLK_UART0>; clock-names = "baudclk", "apb_pclk"; interrupts = ; @@ -659,6 +661,8 @@ uart1: serial@ff190000 { compatible = "rockchip,rk3399-uart", "snps,dw-apb-uart"; reg = <0x0 0xff190000 0x0 0x100>; + dmas = <&dmac_peri 2>, <&dmac_peri 3>; + dma-names = "tx", "rx"; clocks = <&cru SCLK_UART1>, <&cru PCLK_UART1>; clock-names = "baudclk", "apb_pclk"; interrupts = ; @@ -672,6 +676,8 @@ uart2: serial@ff1a0000 { compatible = "rockchip,rk3399-uart", "snps,dw-apb-uart"; reg = <0x0 0xff1a0000 0x0 0x100>; + dmas = <&dmac_peri 4>, <&dmac_peri 5>; + dma-names = "tx", "rx"; clocks = <&cru SCLK_UART2>, <&cru PCLK_UART2>; clock-names = "baudclk", "apb_pclk"; interrupts = ; @@ -685,6 +691,8 @@ uart3: serial@ff1b0000 { compatible = "rockchip,rk3399-uart", "snps,dw-apb-uart"; reg = <0x0 0xff1b0000 0x0 0x100>; + dmas = <&dmac_peri 6>, <&dmac_peri 7>; + dma-names = "tx", "rx"; clocks = <&cru SCLK_UART3>, <&cru PCLK_UART3>; clock-names = "baudclk", "apb_pclk"; interrupts = ; @@ -698,6 +706,8 @@ spi0: spi@ff1c0000 { compatible = "rockchip,rk3399-spi", "rockchip,rk3066-spi"; reg = <0x0 0xff1c0000 0x0 0x1000>; + dmas = <&dmac_peri 10>, <&dmac_peri 11>; + dma-names = "tx", "rx"; clocks = <&cru SCLK_SPI0>, <&cru PCLK_SPI0>; clock-names = "spiclk", "apb_pclk"; interrupts = ; @@ -711,6 +721,8 @@ spi1: spi@ff1d0000 { compatible = "rockchip,rk3399-spi", "rockchip,rk3066-spi"; reg = <0x0 0xff1d0000 0x0 0x1000>; + dmas = <&dmac_peri 12>, <&dmac_peri 13>; + dma-names = "tx", "rx"; clocks = <&cru SCLK_SPI1>, <&cru PCLK_SPI1>; clock-names = "spiclk", "apb_pclk"; interrupts = ; @@ -724,6 +736,8 @@ spi2: spi@ff1e0000 { compatible = "rockchip,rk3399-spi", "rockchip,rk3066-spi"; reg = <0x0 0xff1e0000 0x0 0x1000>; + dmas = <&dmac_peri 14>, <&dmac_peri 15>; + dma-names = "tx", "rx"; clocks = <&cru SCLK_SPI2>, <&cru PCLK_SPI2>; clock-names = "spiclk", "apb_pclk"; interrupts = ; @@ -737,6 +751,8 @@ spi4: spi@ff1f0000 { compatible = "rockchip,rk3399-spi", "rockchip,rk3066-spi"; reg = <0x0 0xff1f0000 0x0 0x1000>; + dmas = <&dmac_peri 18>, <&dmac_peri 19>; + dma-names = "tx", "rx"; clocks = <&cru SCLK_SPI4>, <&cru PCLK_SPI4>; clock-names = "spiclk", "apb_pclk"; interrupts = ; @@ -750,6 +766,8 @@ spi5: spi@ff200000 { compatible = "rockchip,rk3399-spi", "rockchip,rk3066-spi"; reg = <0x0 0xff200000 0x0 0x1000>; + dmas = <&dmac_bus 8>, <&dmac_bus 9>; + dma-names = "tx", "rx"; clocks = <&cru SCLK_SPI5>, <&cru PCLK_SPI5>; clock-names = "spiclk", "apb_pclk"; interrupts = ; @@ -1152,6 +1170,8 @@ spi3: spi@ff350000 { compatible = "rockchip,rk3399-spi", "rockchip,rk3066-spi"; reg = <0x0 0xff350000 0x0 0x1000>; + dmas = <&dmac_peri 16>, <&dmac_peri 17>; + dma-names = "tx", "rx"; clocks = <&pmucru SCLK_SPI3_PMU>, <&pmucru PCLK_SPI3_PMU>; clock-names = "spiclk", "apb_pclk"; interrupts = ; @@ -1165,6 +1185,8 @@ uart4: serial@ff370000 { compatible = "rockchip,rk3399-uart", "snps,dw-apb-uart"; reg = <0x0 0xff370000 0x0 0x100>; + dmas = <&dmac_peri 8>, <&dmac_peri 9>; + dma-names = "tx", "rx"; clocks = <&pmucru SCLK_UART4_PMU>, <&pmucru PCLK_UART4_PMU>; clock-names = "baudclk", "apb_pclk"; interrupts = ; @@ -1790,6 +1812,7 @@ clocks = <&cru ACLK_VOP1>, <&cru DCLK_VOP1>, <&cru HCLK_VOP1>, <&cru DCLK_VOP1_DIV>; clock-names = "aclk_vop", "dclk_vop", "hclk_vop", "dclk_source"; iommus = <&vopl_mmu>; + rockchip,grf = <&grf>; power-domains = <&power RK3399_PD_VOPL>; resets = <&cru SRST_A_VOP1>, <&cru SRST_H_VOP1>, <&cru SRST_D_VOP1>; reset-names = "axi", "ahb", "dclk"; @@ -1860,6 +1883,7 @@ clock-names = "aclk_vop", "dclk_vop", "hclk_vop", "dclk_source"; resets = <&cru SRST_A_VOP0>, <&cru SRST_H_VOP0>, <&cru SRST_D_VOP0>; reset-names = "axi", "ahb", "dclk"; + rockchip,grf = <&grf>; power-domains = <&power RK3399_PD_VOPB>; iommus = <&vopb_mmu>; status = "disabled"; @@ -1982,7 +2006,7 @@ compatible = "rockchip,rk3399-dw-hdmi"; reg = <0x0 0xff940000 0x0 0x20000>; pinctrl-names = "default"; - pinctrl-0 = <&hdmi_i2c_xfer>; + pinctrl-0 = <&hdmi_i2c_xfer>, <&hdmi_cec>; interrupts = ; clocks = <&cru PCLK_HDMI_CTRL>, <&cru SCLK_HDMI_SFR>, diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c index 2b05653e..2ad8515c 100644 --- a/arch/arm64/mm/dma-mapping.c +++ b/arch/arm64/mm/dma-mapping.c @@ -32,7 +32,7 @@ static struct gen_pool *atomic_pool; -#define DEFAULT_DMA_COHERENT_POOL_SIZE SZ_256K +#define DEFAULT_DMA_COHERENT_POOL_SIZE SZ_2M static size_t atomic_pool_size __initdata = DEFAULT_DMA_COHERENT_POOL_SIZE; static int __init early_coherent_pool(char *p) diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c index 0a9f31f2..183114d8 100644 --- a/drivers/clk/rockchip/clk-pll.c +++ b/drivers/clk/rockchip/clk-pll.c @@ -364,6 +364,17 @@ static const struct rockchip_pll_rate_table *rockchip_get_pll_settings( static long rockchip_pll_round_rate(struct clk_hw *hw, unsigned long drate, unsigned long *prate) { + struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw); + const struct rockchip_pll_rate_table *rate; + + /* Get required rate settings from table */ + rate = rockchip_get_pll_settings(pll, drate); + if (!rate) { + pr_debug("%s: Invalid rate : %lu for pll clk %s\n", __func__, + drate, __clk_get_name(hw->clk)); + return -EINVAL; + } + return drate; } diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c index ca6c2ad3..d72c02af 100644 --- a/drivers/clk/rockchip/clk-rk3288.c +++ b/drivers/clk/rockchip/clk-rk3288.c @@ -105,6 +105,43 @@ static struct rockchip_pll_rate_table rk3288_pll_rates[] = { { /* sentinel */ }, }; +static struct rockchip_pll_rate_table rk3288_npll_rates[] = { + RK3066_PLL_RATE_NB(594000000, 1, 99, 4, 32), + RK3066_PLL_RATE_NB(585000000, 6, 585, 4, 32), + RK3066_PLL_RATE_NB(432000000, 3, 216, 4, 32), + RK3066_PLL_RATE_NB(426000000, 3, 213, 4, 32), + RK3066_PLL_RATE_NB(400000000, 1, 100, 6, 32), + RK3066_PLL_RATE(348500000, 8, 697, 6), + RK3066_PLL_RATE_NB(342000000, 3, 171, 4, 32), + RK3066_PLL_RATE_NB(297000000, 2, 198, 8, 16), + RK3066_PLL_RATE_NB(270000000, 1, 135, 12, 32), + RK3066_PLL_RATE_NB(260000000, 1, 130, 12, 32), + RK3066_PLL_RATE(241500000, 2, 161, 8), + RK3066_PLL_RATE(162000000, 1, 81, 12), + RK3066_PLL_RATE(154000000, 6, 539, 14), + RK3066_PLL_RATE_NB(148500000, 1, 99, 16, 32), + RK3066_PLL_RATE(148352000, 13, 1125, 14), + RK3066_PLL_RATE_NB(146250000, 6, 585, 16, 32), + RK3066_PLL_RATE(121750000, 6, 487, 16), + RK3066_PLL_RATE(119000000, 3, 238, 16), + RK3066_PLL_RATE_NB(108000000, 1, 54, 12, 32), + RK3066_PLL_RATE_NB(106500000, 4, 213, 12, 32), + RK3066_PLL_RATE(101000000, 3, 202, 16), + RK3066_PLL_RATE(88750000, 6, 355, 16), + RK3066_PLL_RATE_NB(85500000, 4, 171, 12, 32), + RK3066_PLL_RATE(83500000, 3, 167, 16), + RK3066_PLL_RATE(79500000, 1, 53, 16), + RK3066_PLL_RATE_NB(74250000, 4, 198, 16, 32), + RK3066_PLL_RATE(74176000, 26, 1125, 14), + RK3066_PLL_RATE(72000000, 1, 48, 16), + RK3066_PLL_RATE(71000000, 3, 142, 16), + RK3066_PLL_RATE(68250000, 2, 91, 16), + RK3066_PLL_RATE(65000000, 3, 130, 16), + RK3066_PLL_RATE(40000000, 3, 80, 16), + RK3066_PLL_RATE(33750000, 2, 45, 16), + { /* sentinel */ }, +}; + #define RK3288_DIV_ACLK_CORE_M0_MASK 0xf #define RK3288_DIV_ACLK_CORE_M0_SHIFT 0 #define RK3288_DIV_ACLK_CORE_MP_MASK 0xf @@ -214,7 +251,7 @@ static struct rockchip_pll_clock rk3288_pll_clks[] __initdata = { [gpll] = PLL(pll_rk3066, PLL_GPLL, "gpll", mux_pll_p, 0, RK3288_PLL_CON(12), RK3288_MODE_CON, 12, 8, 0, rk3288_pll_rates), [npll] = PLL(pll_rk3066, PLL_NPLL, "npll", mux_pll_p, 0, RK3288_PLL_CON(16), - RK3288_MODE_CON, 14, 9, ROCKCHIP_PLL_SYNC_RATE, rk3288_pll_rates), + RK3288_MODE_CON, 14, 9, 0, rk3288_npll_rates), }; static struct clk_div_table div_hclk_cpu_t[] = { @@ -429,7 +466,7 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { RK3288_CLKSEL_CON(30), 14, 2, MFLAGS, 8, 5, DFLAGS, RK3288_CLKGATE_CON(3), 4, GFLAGS), - COMPOSITE(DCLK_VOP0, "dclk_vop0", mux_pll_src_cpll_gpll_npll_p, 0, + COMPOSITE(DCLK_VOP0, "dclk_vop0", mux_pll_src_cpll_gpll_npll_p, CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, RK3288_CLKSEL_CON(27), 0, 2, MFLAGS, 8, 8, DFLAGS, RK3288_CLKGATE_CON(3), 1, GFLAGS), COMPOSITE(DCLK_VOP1, "dclk_vop1", mux_pll_src_cpll_gpll_npll_p, 0, diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index b14f5c22..5b0aef7d 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -22,12 +22,12 @@ #include #include #include -#include #include #include #include #include #include +#include #include "dmaengine.h" #define PL330_MAX_CHAN 8 @@ -240,7 +240,6 @@ enum pl330_byteswap { #define BYTE_TO_BURST(b, ccr) ((b) / BRST_SIZE(ccr) / BRST_LEN(ccr)) #define BURST_TO_BYTE(c, ccr) ((c) * BRST_SIZE(ccr) * BRST_LEN(ccr)) -#define BYTE_MOD_BURST_LEN(b, ccr) (((b) / BRST_SIZE(ccr)) % BRST_LEN(ccr)) /* * With 256 bytes, we can do more than 2.5MB and 5MB xfers per req @@ -447,9 +446,8 @@ struct dma_pl330_chan { int burst_len; /* the number of burst */ dma_addr_t fifo_addr; - /* interlace size */ - unsigned int src_interlace_size; - unsigned int dst_interlace_size; + /* for cyclic capability */ + bool cyclic; /* for runtime pm tracking */ bool active; @@ -496,8 +494,6 @@ struct pl330_dmac { /* Peripheral channels connected to this DMAC */ unsigned int num_peripherals; struct dma_pl330_chan *peripherals; /* keep at end */ - /* set peripherals request type according to soc config*/ - enum pl330_cond peripherals_req_type; int quirks; }; @@ -536,13 +532,6 @@ struct dma_pl330_desc { unsigned peri:5; /* Hook to attach to DMAC's list of reqs with due callback */ struct list_head rqd; - - /* For cyclic capability */ - bool cyclic; - size_t num_periods; - /* interlace size */ - unsigned int src_interlace_size; - unsigned int dst_interlace_size; }; struct _xfer_spec { @@ -550,11 +539,6 @@ struct _xfer_spec { struct dma_pl330_desc *desc; }; -static inline bool _queue_empty(struct pl330_thread *thrd) -{ - return thrd->req[0].desc == NULL && thrd->req[1].desc == NULL; -} - static inline bool _queue_full(struct pl330_thread *thrd) { return thrd->req[0].desc != NULL && thrd->req[1].desc != NULL; @@ -576,22 +560,6 @@ static inline u32 get_revision(u32 periph_id) return (periph_id >> PERIPH_REV_SHIFT) & PERIPH_REV_MASK; } -static inline u32 _emit_ADDH(unsigned dry_run, u8 buf[], - enum pl330_dst da, u16 val) -{ - if (dry_run) - return SZ_DMAADDH; - - buf[0] = CMD_DMAADDH; - buf[0] |= (da << 1); - *((__le16 *)&buf[1]) = cpu_to_le16(val); - - PL330_DBGCMD_DUMP(SZ_DMAADDH, "\tDMAADDH %s %u\n", - da == 1 ? "DA" : "SA", val); - - return SZ_DMAADDH; -} - static inline u32 _emit_END(unsigned dry_run, u8 buf[]) { if (dry_run) @@ -738,7 +706,10 @@ static inline u32 _emit_MOV(unsigned dry_run, u8 buf[], buf[0] = CMD_DMAMOV; buf[1] = dst; - *((__le32 *)&buf[2]) = cpu_to_le32(val); + buf[2] = val; + buf[3] = val >> 8; + buf[4] = val >> 16; + buf[5] = val >> 24; PL330_DBGCMD_DUMP(SZ_DMAMOV, "\tDMAMOV %s 0x%x\n", dst == SAR ? "SAR" : (dst == DAR ? "DAR" : "CCR"), val); @@ -746,18 +717,6 @@ static inline u32 _emit_MOV(unsigned dry_run, u8 buf[], return SZ_DMAMOV; } -static inline u32 _emit_NOP(unsigned dry_run, u8 buf[]) -{ - if (dry_run) - return SZ_DMANOP; - - buf[0] = CMD_DMANOP; - - PL330_DBGCMD_DUMP(SZ_DMANOP, "\tDMANOP\n"); - - return SZ_DMANOP; -} - static inline u32 _emit_RMB(unsigned dry_run, u8 buf[]) { if (dry_run) @@ -825,39 +784,6 @@ static inline u32 _emit_STP(unsigned dry_run, u8 buf[], return SZ_DMASTP; } -static inline u32 _emit_STZ(unsigned dry_run, u8 buf[]) -{ - if (dry_run) - return SZ_DMASTZ; - - buf[0] = CMD_DMASTZ; - - PL330_DBGCMD_DUMP(SZ_DMASTZ, "\tDMASTZ\n"); - - return SZ_DMASTZ; -} - -static inline u32 _emit_WFE(unsigned dry_run, u8 buf[], u8 ev, - unsigned invalidate) -{ - if (dry_run) - return SZ_DMAWFE; - - buf[0] = CMD_DMAWFE; - - ev &= 0x1f; - ev <<= 3; - buf[1] = ev; - - if (invalidate) - buf[1] |= (1 << 1); - - PL330_DBGCMD_DUMP(SZ_DMAWFE, "\tDMAWFE %u%s\n", - ev >> 3, invalidate ? ", I" : ""); - - return SZ_DMAWFE; -} - static inline u32 _emit_WFP(unsigned dry_run, u8 buf[], enum pl330_cond cond, u8 peri) { @@ -913,10 +839,11 @@ static inline u32 _emit_GO(unsigned dry_run, u8 buf[], buf[0] = CMD_DMAGO; buf[0] |= (ns << 1); - buf[1] = chan & 0x7; - - *((__le32 *)&buf[2]) = cpu_to_le32(addr); + buf[2] = addr; + buf[3] = addr >> 8; + buf[4] = addr >> 16; + buf[5] = addr >> 24; return SZ_DMAGO; } @@ -1120,13 +1047,16 @@ static bool _start(struct pl330_thread *thrd) if (_state(thrd) == PL330_STATE_KILLING) UNTIL(thrd, PL330_STATE_STOPPED) + /* fall through */ case PL330_STATE_FAULTING: _stop(thrd); + /* fall through */ case PL330_STATE_KILLING: case PL330_STATE_COMPLETING: UNTIL(thrd, PL330_STATE_STOPPED) + /* fall through */ case PL330_STATE_STOPPED: return _trigger(thrd); @@ -1169,68 +1099,96 @@ static inline int _ldst_memtomem(unsigned dry_run, u8 buf[], return off; } -static inline int _ldst_devtomem(struct pl330_dmac *pl330, unsigned dry_run, - u8 buf[], const struct _xfer_spec *pxs, - int cyc) +static u32 _emit_load(unsigned int dry_run, u8 buf[], + enum pl330_cond cond, enum dma_transfer_direction direction, + u8 peri) { int off = 0; - enum pl330_cond cond = pl330->peripherals_req_type; - while (cyc--) { - off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri); - off += _emit_LDP(dry_run, &buf[off], cond, pxs->desc->peri); - off += _emit_ST(dry_run, &buf[off], ALWAYS); -#ifdef CONFIG_ARCH_ROCKCHIP - /* - * Make suree dma has finish transmission, or later flush may - * cause dma second transmission,and fifo is overrun. - */ - off += _emit_WMB(dry_run, &buf[off]); - off += _emit_NOP(dry_run, &buf[off]); - off += _emit_WMB(dry_run, &buf[off]); - off += _emit_NOP(dry_run, &buf[off]); -#endif + switch (direction) { + case DMA_MEM_TO_MEM: + /* fall through */ + case DMA_MEM_TO_DEV: + off += _emit_LD(dry_run, &buf[off], cond); + break; + + case DMA_DEV_TO_MEM: + if (cond == ALWAYS) { + off += _emit_LDP(dry_run, &buf[off], SINGLE, + peri); + off += _emit_LDP(dry_run, &buf[off], BURST, + peri); + } else { + off += _emit_LDP(dry_run, &buf[off], cond, + peri); + } + break; + + default: + /* this code should be unreachable */ + WARN_ON(1); + break; + } + + return off; +} + +static inline u32 _emit_store(unsigned int dry_run, u8 buf[], + enum pl330_cond cond, enum dma_transfer_direction direction, + u8 peri) +{ + int off = 0; + + switch (direction) { + case DMA_MEM_TO_MEM: + /* fall through */ + case DMA_DEV_TO_MEM: + off += _emit_ST(dry_run, &buf[off], cond); + break; - if (!(pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP)) - off += _emit_FLUSHP(dry_run, &buf[off], - pxs->desc->peri); - if (pxs->desc->dst_interlace_size) { - off += _emit_ADDH(dry_run, &buf[off], DST, - pxs->desc->dst_interlace_size); + case DMA_MEM_TO_DEV: + if (cond == ALWAYS) { + off += _emit_STP(dry_run, &buf[off], SINGLE, + peri); + off += _emit_STP(dry_run, &buf[off], BURST, + peri); + } else { + off += _emit_STP(dry_run, &buf[off], cond, + peri); } + break; + + default: + /* this code should be unreachable */ + WARN_ON(1); + break; } return off; } -static inline int _ldst_memtodev(struct pl330_dmac *pl330, +static inline int _ldst_peripheral(struct pl330_dmac *pl330, unsigned dry_run, u8 buf[], - const struct _xfer_spec *pxs, int cyc) + const struct _xfer_spec *pxs, int cyc, + enum pl330_cond cond) { int off = 0; - enum pl330_cond cond = pl330->peripherals_req_type; + if (pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP) + cond = BURST; + + /* + * do FLUSHP at beginning to clear any stale dma requests before the + * first WFP. + */ + if (!(pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP)) + off += _emit_FLUSHP(dry_run, &buf[off], pxs->desc->peri); while (cyc--) { off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri); - off += _emit_LD(dry_run, &buf[off], ALWAYS); - off += _emit_STP(dry_run, &buf[off], cond, pxs->desc->peri); -#ifdef CONFIG_ARCH_ROCKCHIP - /* - * Make suree dma has finish transmission, or later flush may - * cause dma second transmission,and fifo is overrun. - */ - off += _emit_WMB(dry_run, &buf[off]); - off += _emit_NOP(dry_run, &buf[off]); - off += _emit_WMB(dry_run, &buf[off]); - off += _emit_NOP(dry_run, &buf[off]); -#endif - - if (!(pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP)) - off += _emit_FLUSHP(dry_run, &buf[off], - pxs->desc->peri); - if (pxs->desc->src_interlace_size) - off += _emit_ADDH(dry_run, &buf[off], SRC, - pxs->desc->src_interlace_size); + off += _emit_load(dry_run, &buf[off], cond, pxs->desc->rqtype, + pxs->desc->peri); + off += _emit_store(dry_run, &buf[off], cond, pxs->desc->rqtype, + pxs->desc->peri); } return off; @@ -1240,19 +1198,65 @@ static int _bursts(struct pl330_dmac *pl330, unsigned dry_run, u8 buf[], const struct _xfer_spec *pxs, int cyc) { int off = 0; + enum pl330_cond cond = BRST_LEN(pxs->ccr) > 1 ? BURST : SINGLE; switch (pxs->desc->rqtype) { case DMA_MEM_TO_DEV: - off += _ldst_memtodev(pl330, dry_run, &buf[off], pxs, cyc); - break; + /* fall through */ case DMA_DEV_TO_MEM: - off += _ldst_devtomem(pl330, dry_run, &buf[off], pxs, cyc); + off += _ldst_peripheral(pl330, dry_run, &buf[off], pxs, cyc, + cond); break; + case DMA_MEM_TO_MEM: off += _ldst_memtomem(dry_run, &buf[off], pxs, cyc); break; + + default: + /* this code should be unreachable */ + WARN_ON(1); + break; + } + + return off; +} + +/* + * transfer dregs with single transfers to peripheral, or a reduced size burst + * for mem-to-mem. + */ +static int _dregs(struct pl330_dmac *pl330, unsigned int dry_run, u8 buf[], + const struct _xfer_spec *pxs, int transfer_length) +{ + int off = 0; + int dregs_ccr; + + if (transfer_length == 0) + return off; + + switch (pxs->desc->rqtype) { + case DMA_MEM_TO_DEV: + /* fall through */ + case DMA_DEV_TO_MEM: + off += _ldst_peripheral(pl330, dry_run, &buf[off], pxs, + transfer_length, SINGLE); + break; + + case DMA_MEM_TO_MEM: + dregs_ccr = pxs->ccr; + dregs_ccr &= ~((0xf << CC_SRCBRSTLEN_SHFT) | + (0xf << CC_DSTBRSTLEN_SHFT)); + dregs_ccr |= (((transfer_length - 1) & 0xf) << + CC_SRCBRSTLEN_SHFT); + dregs_ccr |= (((transfer_length - 1) & 0xf) << + CC_DSTBRSTLEN_SHFT); + off += _emit_MOV(dry_run, &buf[off], CCR, dregs_ccr); + off += _ldst_memtomem(dry_run, &buf[off], pxs, 1); + break; + default: - off += 0x40000000; /* Scare off the Client */ + /* this code should be unreachable */ + WARN_ON(1); break; } @@ -1341,129 +1345,6 @@ static inline int _loop(struct pl330_dmac *pl330, unsigned dry_run, u8 buf[], return off; } -static int _period(struct pl330_dmac *pl330, unsigned int dry_run, u8 buf[], - unsigned long bursts, const struct _xfer_spec *pxs, int ev) -{ - unsigned int lcnt1, ljmp1; - int cyc, off = 0; - struct _arg_LPEND lpend; - struct pl330_xfer *x = &pxs->desc->px; - - if (bursts > 256) { - lcnt1 = 256; - cyc = bursts / 256; - } else { - lcnt1 = bursts; - cyc = 1; - } - - /* loop1 */ - off += _emit_LP(dry_run, &buf[off], 1, lcnt1); - ljmp1 = off; - off += _bursts(pl330, dry_run, &buf[off], pxs, cyc); - lpend.cond = ALWAYS; - lpend.forever = false; - lpend.loop = 1; - lpend.bjump = off - ljmp1; - off += _emit_LPEND(dry_run, &buf[off], &lpend); - - /* remainder */ - lcnt1 = bursts - (lcnt1 * cyc); - - if (lcnt1) { - off += _emit_LP(dry_run, &buf[off], 1, lcnt1); - ljmp1 = off; - off += _bursts(pl330, dry_run, &buf[off], pxs, 1); - lpend.cond = ALWAYS; - lpend.forever = false; - lpend.loop = 1; - lpend.bjump = off - ljmp1; - off += _emit_LPEND(dry_run, &buf[off], &lpend); - } - - if (pxs->desc->src_interlace_size == 0 && - pxs->desc->dst_interlace_size == 0 && - pl330->peripherals_req_type == BURST) { - unsigned int ccr = pxs->ccr; - unsigned long c = 0; - - c = BYTE_MOD_BURST_LEN(x->bytes, pxs->ccr); - - if (c) { - ccr &= ~(0xf << CC_SRCBRSTLEN_SHFT); - ccr &= ~(0xf << CC_DSTBRSTLEN_SHFT); - off += _emit_MOV(dry_run, &buf[off], CCR, ccr); - off += _emit_LP(dry_run, &buf[off], 1, c); - ljmp1 = off; - off += _bursts(pl330, dry_run, &buf[off], pxs, 1); - lpend.cond = ALWAYS; - lpend.forever = false; - lpend.loop = 1; - lpend.bjump = off - ljmp1; - off += _emit_LPEND(dry_run, &buf[off], &lpend); - off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr); - } - } - - off += _emit_SEV(dry_run, &buf[off], ev); - - return off; -} - -/* Returns bytes consumed */ -static inline int _loop_cyclic(struct pl330_dmac *pl330, unsigned int dry_run, - u8 buf[], unsigned long bursts, const struct _xfer_spec *pxs, int ev) -{ - int off, periods, residue, i; - unsigned int lcnt0, ljmp0, ljmpfe; - struct _arg_LPEND lpend; - struct pl330_xfer *x = &pxs->desc->px; - - off = 0; - ljmpfe = off; - lcnt0 = pxs->desc->num_periods; - periods = 1; - - while (lcnt0 > 256) { - periods++; - lcnt0 = pxs->desc->num_periods / periods; - } - - residue = pxs->desc->num_periods % periods; - - /* forever loop */ - off += _emit_MOV(dry_run, &buf[off], SAR, x->src_addr); - off += _emit_MOV(dry_run, &buf[off], DAR, x->dst_addr); -#ifdef CONFIG_ARCH_ROCKCHIP - if (!(pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP)) - off += _emit_FLUSHP(dry_run, &buf[off], - pxs->desc->peri); -#endif - /* loop0 */ - off += _emit_LP(dry_run, &buf[off], 0, lcnt0); - ljmp0 = off; - - for (i = 0; i < periods; i++) - off += _period(pl330, dry_run, &buf[off], bursts, pxs, ev); - - lpend.cond = ALWAYS; - lpend.forever = false; - lpend.loop = 0; - lpend.bjump = off - ljmp0; - off += _emit_LPEND(dry_run, &buf[off], &lpend); - - for (i = 0; i < residue; i++) - off += _period(pl330, dry_run, &buf[off], bursts, pxs, ev); - - lpend.cond = ALWAYS; - lpend.forever = true; - lpend.loop = 1; - lpend.bjump = off - ljmpfe; - off += _emit_LPEND(dry_run, &buf[off], &lpend); - - return off; -} - static inline int _setup_loops(struct pl330_dmac *pl330, unsigned dry_run, u8 buf[], const struct _xfer_spec *pxs) @@ -1471,23 +1352,16 @@ static inline int _setup_loops(struct pl330_dmac *pl330, struct pl330_xfer *x = &pxs->desc->px; u32 ccr = pxs->ccr; unsigned long c, bursts = BYTE_TO_BURST(x->bytes, ccr); + int num_dregs = (x->bytes - BURST_TO_BYTE(bursts, ccr)) / + BRST_SIZE(ccr); int off = 0; -#ifdef CONFIG_ARCH_ROCKCHIP - if (!(pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP)) - off += _emit_FLUSHP(dry_run, &buf[off], - pxs->desc->peri); -#endif - if (pxs->desc->rqtype == DMA_DEV_TO_MEM) - bursts = x->bytes / (BRST_SIZE(ccr) * BRST_LEN(ccr) + - pxs->desc->dst_interlace_size); - else if (pxs->desc->rqtype == DMA_MEM_TO_DEV) - bursts = x->bytes / (BRST_SIZE(ccr) * BRST_LEN(ccr) + - pxs->desc->src_interlace_size); + while (bursts) { c = bursts; off += _loop(pl330, dry_run, &buf[off], &c, pxs); bursts -= c; } + off += _dregs(pl330, dry_run, &buf[off], pxs, num_dregs); return off; } @@ -1507,42 +1381,6 @@ static inline int _setup_xfer(struct pl330_dmac *pl330, /* Setup Loop(s) */ off += _setup_loops(pl330, dry_run, &buf[off], pxs); - if (pxs->desc->src_interlace_size == 0 && - pxs->desc->dst_interlace_size == 0 && - pl330->peripherals_req_type == BURST) { - unsigned int ccr = pxs->ccr; - unsigned long c = 0; - - c = BYTE_MOD_BURST_LEN(x->bytes, pxs->ccr); - - if (c) { - ccr &= ~(0xf << CC_SRCBRSTLEN_SHFT); - ccr &= ~(0xf << CC_DSTBRSTLEN_SHFT); - off += _emit_MOV(dry_run, &buf[off], CCR, ccr); - off += _loop(pl330, dry_run, &buf[off], &c, pxs); - } - } - - return off; -} - -static inline int _setup_xfer_cyclic(struct pl330_dmac *pl330, unsigned dry_run, - u8 buf[], const struct _xfer_spec *pxs, int ev) -{ - struct pl330_xfer *x = &pxs->desc->px; - u32 ccr = pxs->ccr; - unsigned long bursts = BYTE_TO_BURST(x->bytes, ccr); - int off = 0; - - if (pxs->desc->rqtype == DMA_DEV_TO_MEM) - bursts = x->bytes / (BRST_SIZE(ccr) * BRST_LEN(ccr) - + pxs->desc->dst_interlace_size); - else if (pxs->desc->rqtype == DMA_MEM_TO_DEV) - bursts = x->bytes / (BRST_SIZE(ccr) * BRST_LEN(ccr) - + pxs->desc->src_interlace_size); - /* Setup Loop(s) */ - off += _loop_cyclic(pl330, dry_run, &buf[off], bursts, pxs, ev); - return off; } @@ -1555,7 +1393,6 @@ static int _setup_req(struct pl330_dmac *pl330, unsigned dry_run, struct _xfer_spec *pxs) { struct _pl330_req *req = &thrd->req[index]; - struct pl330_xfer *x; u8 *buf = req->mc_cpu; int off = 0; @@ -1564,25 +1401,12 @@ static int _setup_req(struct pl330_dmac *pl330, unsigned dry_run, /* DMAMOV CCR, ccr */ off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr); - x = &pxs->desc->px; + off += _setup_xfer(pl330, dry_run, &buf[off], pxs); - if (pl330->peripherals_req_type != BURST) { - /* Error if xfer length is not aligned at burst size */ - if (x->bytes % (BRST_SIZE(pxs->ccr) * BRST_LEN(pxs->ccr))) - return -EINVAL; - } - - if (!pxs->desc->cyclic) { - off += _setup_xfer(pl330, dry_run, &buf[off], pxs); - - /* DMASEV peripheral/event */ - off += _emit_SEV(dry_run, &buf[off], thrd->ev); - /* DMAEND */ - off += _emit_END(dry_run, &buf[off]); - } else { - off += _setup_xfer_cyclic(pl330, dry_run, &buf[off], - pxs, thrd->ev); - } + /* DMASEV peripheral/event */ + off += _emit_SEV(dry_run, &buf[off], thrd->ev); + /* DMAEND */ + off += _emit_END(dry_run, &buf[off]); return off; } @@ -1634,6 +1458,20 @@ static int pl330_submit_req(struct pl330_thread *thrd, u32 ccr; int ret = 0; + switch (desc->rqtype) { + case DMA_MEM_TO_DEV: + break; + + case DMA_DEV_TO_MEM: + break; + + case DMA_MEM_TO_MEM: + break; + + default: + return -ENOTSUPP; + } + if (pl330->state == DYING || pl330->dmac_tbd.reset_chan & (1 << thrd->id)) { dev_info(thrd->dmac->ddma.dev, "%s:%d\n", @@ -1816,8 +1654,8 @@ static int pl330_update(struct pl330_dmac *pl330) if (pl330->pcfg.num_events < 32 && val & ~((1 << pl330->pcfg.num_events) - 1)) { pl330->dmac_tbd.reset_dmac = true; - dev_err_ratelimited(pl330->ddma.dev, "%s:%d Unexpected!\n", - __func__, __LINE__); + dev_err(pl330->ddma.dev, "%s:%d Unexpected!\n", __func__, + __LINE__); ret = 1; goto updt_exit; } @@ -1844,17 +1682,15 @@ static int pl330_update(struct pl330_dmac *pl330) /* Detach the req */ descdone = thrd->req[active].desc; - if (descdone) { - if (!descdone->cyclic) { - thrd->req[active].desc = NULL; - thrd->req_running = -1; - /* Get going again ASAP */ - _start(thrd); - } - - /* For now, just make a list of callbacks to be done */ - list_add_tail(&descdone->rqd, &pl330->req_done); - } + thrd->req[active].desc = NULL; + + thrd->req_running = -1; + + /* Get going again ASAP */ + _start(thrd); + + /* For now, just make a list of callbacks to be done */ + list_add_tail(&descdone->rqd, &pl330->req_done); } } @@ -1947,8 +1783,6 @@ static inline void _free_event(struct pl330_thread *thrd, int ev) static void pl330_release_channel(struct pl330_thread *thrd) { - struct pl330_dmac *pl330; - if (!thrd || thrd->free) return; @@ -1957,8 +1791,6 @@ static void pl330_release_channel(struct pl330_thread *thrd) dma_pl330_rqcb(thrd->req[1 - thrd->lstenq].desc, PL330_ERR_ABORT); dma_pl330_rqcb(thrd->req[thrd->lstenq].desc, PL330_ERR_ABORT); - pl330 = thrd->dmac; - _free_event(thrd, thrd->ev); thrd->free = true; } @@ -2091,11 +1923,8 @@ static int dmac_alloc_resources(struct pl330_dmac *pl330) static int pl330_add(struct pl330_dmac *pl330) { - void __iomem *regs; int i, ret; - regs = pl330->base; - /* Check if we can handle this DMAC */ if ((pl330->pcfg.periph_id & 0xfffff) != PERIPH_ID_VAL) { dev_err(pl330->ddma.dev, "PERIPH_ID 0x%x !\n", @@ -2224,27 +2053,12 @@ static void pl330_tasklet(unsigned long data) spin_lock_irqsave(&pch->lock, flags); /* Pick up ripe tomatoes */ - list_for_each_entry_safe(desc, _dt, &pch->work_list, node) { + list_for_each_entry_safe(desc, _dt, &pch->work_list, node) if (desc->status == DONE) { - if (!desc->cyclic) { + if (!pch->cyclic) dma_cookie_complete(&desc->txd); - list_move_tail(&desc->node, &pch->completed_list); - } else { - dma_async_tx_callback callback; - void *callback_param; - - desc->status = BUSY; - callback = desc->txd.callback; - callback_param = desc->txd.callback_param; - - if (callback) { - spin_unlock_irqrestore(&pch->lock, flags); - callback(callback_param); - spin_lock_irqsave(&pch->lock, flags); - } - } + list_move_tail(&desc->node, &pch->completed_list); } - } /* Try to submit a req imm. next to the last completed cookie */ fill_queue(pch); @@ -2253,7 +2067,7 @@ static void pl330_tasklet(unsigned long data) spin_lock(&pch->thread->dmac->lock); _stop(pch->thread); spin_unlock(&pch->thread->dmac->lock); - power_down = pch->active; + power_down = true; pch->active = false; } else { /* Make sure the PL330 Channel thread is active */ @@ -2272,8 +2086,20 @@ static void pl330_tasklet(unsigned long data) callback = desc->txd.callback; callback_param = desc->txd.callback_param; - desc->status = FREE; - list_move_tail(&desc->node, &pch->dmac->desc_pool); + if (pch->cyclic) { + desc->status = PREP; + list_move_tail(&desc->node, &pch->work_list); + if (power_down) { + pch->active = true; + spin_lock(&pch->thread->dmac->lock); + _start(pch->thread); + spin_unlock(&pch->thread->dmac->lock); + power_down = false; + } + } else { + desc->status = FREE; + list_move_tail(&desc->node, &pch->dmac->desc_pool); + } dma_descriptor_unmap(&desc->txd); @@ -2292,18 +2118,6 @@ static void pl330_tasklet(unsigned long data) } } -bool pl330_filter(struct dma_chan *chan, void *param) -{ - u8 *peri_id; - - if (chan->device->dev->driver != &pl330_driver.drv) - return false; - - peri_id = chan->private; - return *peri_id == (unsigned long)param; -} -EXPORT_SYMBOL(pl330_filter); - static struct dma_chan *of_dma_pl330_xlate(struct of_phandle_args *dma_spec, struct of_dma *ofdma) { @@ -2333,6 +2147,7 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan) spin_lock_irqsave(&pl330->lock, flags); dma_cookie_init(chan); + pch->cyclic = false; pch->thread = pl330_request_channel(pl330); if (!pch->thread) { @@ -2347,6 +2162,18 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan) return 1; } +static int fixup_burst_len(int max_burst_len, int quirks) +{ + if (quirks & PL330_QUIRK_BROKEN_NO_FLUSHP) + return 1; + else if (max_burst_len > PL330_MAX_BURST) + return PL330_MAX_BURST; + else if (max_burst_len < 1) + return 1; + else + return max_burst_len; +} + static int pl330_config(struct dma_chan *chan, struct dma_slave_config *slave_config) { @@ -2357,19 +2184,15 @@ static int pl330_config(struct dma_chan *chan, pch->fifo_addr = slave_config->dst_addr; if (slave_config->dst_addr_width) pch->burst_sz = __ffs(slave_config->dst_addr_width); - if (slave_config->dst_maxburst) - pch->burst_len = slave_config->dst_maxburst; - if (slave_config->src_interlace_size) - pch->src_interlace_size = slave_config->src_interlace_size; + pch->burst_len = fixup_burst_len(slave_config->dst_maxburst, + pch->dmac->quirks); } else if (slave_config->direction == DMA_DEV_TO_MEM) { if (slave_config->src_addr) pch->fifo_addr = slave_config->src_addr; if (slave_config->src_addr_width) pch->burst_sz = __ffs(slave_config->src_addr_width); - if (slave_config->src_maxburst) - pch->burst_len = slave_config->src_maxburst; - if (slave_config->dst_interlace_size) - pch->dst_interlace_size = slave_config->dst_interlace_size; + pch->burst_len = fixup_burst_len(slave_config->src_maxburst, + pch->dmac->quirks); } return 0; @@ -2386,13 +2209,14 @@ static int pl330_terminate_all(struct dma_chan *chan) pm_runtime_get_sync(pl330->ddma.dev); spin_lock_irqsave(&pch->lock, flags); + spin_lock(&pl330->lock); _stop(pch->thread); - spin_unlock(&pl330->lock); - pch->thread->req[0].desc = NULL; pch->thread->req[1].desc = NULL; pch->thread->req_running = -1; + spin_unlock(&pl330->lock); + power_down = pch->active; pch->active = false; @@ -2460,7 +2284,8 @@ static void pl330_free_chan_resources(struct dma_chan *chan) pl330_release_channel(pch->thread); pch->thread = NULL; - list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool); + if (pch->cyclic) + list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool); spin_unlock_irqrestore(&pl330->lock, flags); pm_runtime_mark_last_busy(pch->dmac->ddma.dev); @@ -2486,6 +2311,11 @@ static int pl330_get_current_xferred_count(struct dma_pl330_chan *pch, } pm_runtime_mark_last_busy(pch->dmac->ddma.dev); pm_runtime_put_autosuspend(pl330->ddma.dev); + + /* If DMAMOV hasn't finished yet, SAR/DAR can be zero */ + if (!val) + return 0; + return val - addr; } @@ -2495,7 +2325,7 @@ pl330_tx_status(struct dma_chan *chan, dma_cookie_t cookie, { enum dma_status ret; unsigned long flags; - struct dma_pl330_desc *desc, *running = NULL; + struct dma_pl330_desc *desc, *running = NULL, *last_enq = NULL; struct dma_pl330_chan *pch = to_pchan(chan); unsigned int transferred, residual = 0; @@ -2508,17 +2338,29 @@ pl330_tx_status(struct dma_chan *chan, dma_cookie_t cookie, goto out; spin_lock_irqsave(&pch->lock, flags); + spin_lock(&pch->thread->dmac->lock); if (pch->thread->req_running != -1) running = pch->thread->req[pch->thread->req_running].desc; + last_enq = pch->thread->req[pch->thread->lstenq].desc; + /* Check in pending list */ list_for_each_entry(desc, &pch->work_list, node) { - if (desc->status == DONE && !desc->cyclic) + if (desc->status == DONE) transferred = desc->bytes_requested; else if (running && desc == running) transferred = pl330_get_current_xferred_count(pch, desc); + else if (desc->status == BUSY) + /* + * Busy but not running means either just enqueued, + * or finished and not yet marked done + */ + if (desc == last_enq) + transferred = 0; + else + transferred = desc->bytes_requested; else transferred = 0; residual += desc->bytes_requested - transferred; @@ -2539,6 +2381,7 @@ pl330_tx_status(struct dma_chan *chan, dma_cookie_t cookie, if (desc->last) residual = 0; } + spin_unlock(&pch->thread->dmac->lock); spin_unlock_irqrestore(&pch->lock, flags); out: @@ -2586,8 +2429,12 @@ static dma_cookie_t pl330_tx_submit(struct dma_async_tx_descriptor *tx) /* Assign cookies to all nodes */ while (!list_empty(&last->node)) { desc = list_entry(last->node.next, struct dma_pl330_desc, node); - + if (pch->cyclic) { + desc->txd.callback = last->txd.callback; + desc->txd.callback_param = last->txd.callback_param; + } desc->last = false; + dma_cookie_assign(&desc->txd); list_move_tail(&desc->node, &pch->submitted_list); @@ -2612,7 +2459,8 @@ static inline void _init_desc(struct dma_pl330_desc *desc) } /* Returns the number of descriptors added to the DMAC pool */ -static int add_desc(struct pl330_dmac *pl330, gfp_t flg, int count) +static int add_desc(struct list_head *pool, spinlock_t *lock, + gfp_t flg, int count) { struct dma_pl330_desc *desc; unsigned long flags; @@ -2622,27 +2470,28 @@ static int add_desc(struct pl330_dmac *pl330, gfp_t flg, int count) if (!desc) return 0; - spin_lock_irqsave(&pl330->pool_lock, flags); + spin_lock_irqsave(lock, flags); for (i = 0; i < count; i++) { _init_desc(&desc[i]); - list_add_tail(&desc[i].node, &pl330->desc_pool); + list_add_tail(&desc[i].node, pool); } - spin_unlock_irqrestore(&pl330->pool_lock, flags); + spin_unlock_irqrestore(lock, flags); return count; } -static struct dma_pl330_desc *pluck_desc(struct pl330_dmac *pl330) +static struct dma_pl330_desc *pluck_desc(struct list_head *pool, + spinlock_t *lock) { struct dma_pl330_desc *desc = NULL; unsigned long flags; - spin_lock_irqsave(&pl330->pool_lock, flags); + spin_lock_irqsave(lock, flags); - if (!list_empty(&pl330->desc_pool)) { - desc = list_entry(pl330->desc_pool.next, + if (!list_empty(pool)) { + desc = list_entry(pool->next, struct dma_pl330_desc, node); list_del_init(&desc->node); @@ -2651,7 +2500,7 @@ static struct dma_pl330_desc *pluck_desc(struct pl330_dmac *pl330) desc->txd.callback = NULL; } - spin_unlock_irqrestore(&pl330->pool_lock, flags); + spin_unlock_irqrestore(lock, flags); return desc; } @@ -2663,20 +2512,18 @@ static struct dma_pl330_desc *pl330_get_desc(struct dma_pl330_chan *pch) struct dma_pl330_desc *desc; /* Pluck one desc from the pool of DMAC */ - desc = pluck_desc(pl330); + desc = pluck_desc(&pl330->desc_pool, &pl330->pool_lock); /* If the DMAC pool is empty, alloc new */ if (!desc) { - if (!add_desc(pl330, GFP_ATOMIC, 1)) - return NULL; + DEFINE_SPINLOCK(lock); + LIST_HEAD(pool); - /* Try again */ - desc = pluck_desc(pl330); - if (!desc) { - dev_err(pch->dmac->ddma.dev, - "%s:%d ALERT!\n", __func__, __LINE__); + if (!add_desc(&pool, &lock, GFP_ATOMIC, 1)) return NULL; - } + + desc = pluck_desc(&pool, &lock); + WARN_ON(!desc || !list_empty(&pool)); } /* Initialize the descriptor */ @@ -2687,9 +2534,6 @@ static struct dma_pl330_desc *pl330_get_desc(struct dma_pl330_chan *pch) desc->peri = peri_id ? pch->chan.chan_id : 0; desc->rqcfg.pcfg = &pch->dmac->pcfg; - desc->cyclic = false; - desc->num_periods = 1; - dma_async_tx_descriptor_init(&desc->txd, &pch->chan); return desc; @@ -2742,14 +2586,8 @@ static inline int get_burst_len(struct dma_pl330_desc *desc, size_t len) burst_len >>= desc->rqcfg.brst_size; /* src/dst_burst_len can't be more than 16 */ - if (burst_len > 16) - burst_len = 16; - - while (burst_len > 1) { - if (!(len % (burst_len << desc->rqcfg.brst_size))) - break; - burst_len--; - } + if (burst_len > PL330_MAX_BURST) + burst_len = PL330_MAX_BURST; return burst_len; } @@ -2759,9 +2597,10 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( size_t period_len, enum dma_transfer_direction direction, unsigned long flags) { - struct dma_pl330_desc *desc = NULL; + struct dma_pl330_desc *desc = NULL, *first = NULL; struct dma_pl330_chan *pch = to_pchan(chan); struct pl330_dmac *pl330 = pch->dmac; + unsigned int i; dma_addr_t dst; dma_addr_t src; @@ -2774,46 +2613,67 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( return NULL; } - desc = pl330_get_desc(pch); - if (!desc) { - dev_err(pch->dmac->ddma.dev, "%s:%d Unable to fetch desc\n", - __func__, __LINE__); - return NULL; - } + for (i = 0; i < len / period_len; i++) { + desc = pl330_get_desc(pch); + if (!desc) { + dev_err(pch->dmac->ddma.dev, "%s:%d Unable to fetch desc\n", + __func__, __LINE__); - switch (direction) { - case DMA_MEM_TO_DEV: - desc->rqcfg.src_inc = 1; - desc->rqcfg.dst_inc = 0; - src = dma_addr; - dst = pch->fifo_addr; - break; - case DMA_DEV_TO_MEM: - desc->rqcfg.src_inc = 0; - desc->rqcfg.dst_inc = 1; - src = pch->fifo_addr; - dst = dma_addr; - break; - default: - break; - } + if (!first) + return NULL; + + spin_lock_irqsave(&pl330->pool_lock, flags); + + while (!list_empty(&first->node)) { + desc = list_entry(first->node.next, + struct dma_pl330_desc, node); + list_move_tail(&desc->node, &pl330->desc_pool); + } - desc->rqtype = direction; - desc->rqcfg.brst_size = pch->burst_sz; + list_move_tail(&first->node, &pl330->desc_pool); - if (pl330->peripherals_req_type == BURST) + spin_unlock_irqrestore(&pl330->pool_lock, flags); + + return NULL; + } + + switch (direction) { + case DMA_MEM_TO_DEV: + desc->rqcfg.src_inc = 1; + desc->rqcfg.dst_inc = 0; + src = dma_addr; + dst = pch->fifo_addr; + break; + case DMA_DEV_TO_MEM: + desc->rqcfg.src_inc = 0; + desc->rqcfg.dst_inc = 1; + src = pch->fifo_addr; + dst = dma_addr; + break; + default: + break; + } + + desc->rqtype = direction; + desc->rqcfg.brst_size = pch->burst_sz; desc->rqcfg.brst_len = pch->burst_len; - else - desc->rqcfg.brst_len = 1; + desc->bytes_requested = period_len; + fill_px(&desc->px, dst, src, period_len); - desc->bytes_requested = len; - fill_px(&desc->px, dst, src, period_len); + if (!first) + first = desc; + else + list_add_tail(&desc->node, &first->node); - desc->cyclic = true; - desc->num_periods = len / period_len; + dma_addr += period_len; + } + + if (!desc) + return NULL; + + pch->cyclic = true; desc->txd.flags = flags; - desc->src_interlace_size = pch->src_interlace_size; - desc->dst_interlace_size = pch->dst_interlace_size; + return &desc->txd; } @@ -2898,7 +2758,6 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, { struct dma_pl330_desc *first, *desc = NULL; struct dma_pl330_chan *pch = to_pchan(chan); - struct pl330_dmac *pl330 = pch->dmac; struct scatterlist *sg; int i; dma_addr_t addr; @@ -2942,16 +2801,9 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, } desc->rqcfg.brst_size = pch->burst_sz; - - if (pl330->peripherals_req_type == BURST) - desc->rqcfg.brst_len = pch->burst_len; - else - desc->rqcfg.brst_len = 1; - + desc->rqcfg.brst_len = pch->burst_len; desc->rqtype = direction; desc->bytes_requested = sg_dma_len(sg); - desc->src_interlace_size = pch->src_interlace_size; - desc->dst_interlace_size = pch->dst_interlace_size; } /* Return the last desc in the chain */ @@ -3017,7 +2869,6 @@ static SIMPLE_DEV_PM_OPS(pl330_pm, pl330_suspend, pl330_resume); static int pl330_probe(struct amba_device *adev, const struct amba_id *id) { - struct dma_pl330_platdata *pdat; struct pl330_config *pcfg; struct pl330_dmac *pl330; struct dma_pl330_chan *pch, *_p; @@ -3027,8 +2878,6 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) int num_chan; struct device_node *np = adev->dev.of_node; - pdat = dev_get_platdata(&adev->dev); - ret = dma_set_mask_and_coherent(&adev->dev, DMA_BIT_MASK(32)); if (ret) return ret; @@ -3043,12 +2892,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pd = &pl330->ddma; pd->dev = &adev->dev; - pl330->mcbufsz = pdat ? pdat->mcbuf_sz : 0; - - if (of_find_property(np, "peripherals-req-type-burst", NULL)) - pl330->peripherals_req_type = BURST; - else - pl330->peripherals_req_type = SINGLE; + pl330->mcbufsz = 0; /* get quirk */ for (i = 0; i < ARRAY_SIZE(of_quirks); i++) @@ -3086,16 +2930,14 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) spin_lock_init(&pl330->pool_lock); /* Create a descriptor pool of default size */ - if (!add_desc(pl330, GFP_KERNEL, NR_DEFAULT_DESC)) + if (!add_desc(&pl330->desc_pool, &pl330->pool_lock, + GFP_KERNEL, NR_DEFAULT_DESC)) dev_warn(&adev->dev, "unable to allocate desc\n"); INIT_LIST_HEAD(&pd->channels); /* Initialize channel parameters */ - if (pdat) - num_chan = max_t(int, pdat->nr_valid_peri, pcfg->num_chan); - else - num_chan = max_t(int, pcfg->num_peri, pcfg->num_chan); + num_chan = max_t(int, pcfg->num_peri, pcfg->num_chan); pl330->num_peripherals = num_chan; @@ -3108,11 +2950,8 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) for (i = 0; i < num_chan; i++) { pch = &pl330->peripherals[i]; - if (!adev->dev.of_node) - pch->chan.private = pdat ? &pdat->peri_id[i] : NULL; - else - pch->chan.private = adev->dev.of_node; + pch->chan.private = adev->dev.of_node; INIT_LIST_HEAD(&pch->submitted_list); INIT_LIST_HEAD(&pch->work_list); INIT_LIST_HEAD(&pch->completed_list); @@ -3125,15 +2964,11 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) list_add_tail(&pch->chan.device_node, &pd->channels); } - if (pdat) { - pd->cap_mask = pdat->cap_mask; - } else { - dma_cap_set(DMA_MEMCPY, pd->cap_mask); - if (pcfg->num_peri) { - dma_cap_set(DMA_SLAVE, pd->cap_mask); - dma_cap_set(DMA_CYCLIC, pd->cap_mask); - dma_cap_set(DMA_PRIVATE, pd->cap_mask); - } + dma_cap_set(DMA_MEMCPY, pd->cap_mask); + if (pcfg->num_peri) { + dma_cap_set(DMA_SLAVE, pd->cap_mask); + dma_cap_set(DMA_CYCLIC, pd->cap_mask); + dma_cap_set(DMA_PRIVATE, pd->cap_mask); } pd->device_alloc_chan_resources = pl330_alloc_chan_resources; @@ -3149,7 +2984,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pd->src_addr_widths = PL330_DMA_BUSWIDTHS; pd->dst_addr_widths = PL330_DMA_BUSWIDTHS; pd->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); - pd->residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT; + pd->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; pd->max_burst = ((pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP) ? 1 : PL330_MAX_BURST); @@ -3217,12 +3052,19 @@ static int pl330_remove(struct amba_device *adev) { struct pl330_dmac *pl330 = amba_get_drvdata(adev); struct dma_pl330_chan *pch, *_p; + int i, irq; pm_runtime_get_noresume(pl330->ddma.dev); if (adev->dev.of_node) of_dma_controller_free(adev->dev.of_node); + for (i = 0; i < AMBA_NR_IRQS; i++) { + irq = adev->irq[i]; + if (irq) + devm_free_irq(&adev->dev, irq, pl330); + } + dma_async_device_unregister(&pl330->ddma); /* Idle the DMAC */ @@ -3244,7 +3086,7 @@ static int pl330_remove(struct amba_device *adev) return 0; } -static struct amba_id pl330_ids[] = { +static const struct amba_id pl330_ids[] = { { .id = 0x00041330, .mask = 0x000fffff, diff --git a/drivers/gpu/arm/mali400/mali/linux/mali_devfreq.c b/drivers/gpu/arm/mali400/mali/linux/mali_devfreq.c index c9b8652f..6c97c530 100644 --- a/drivers/gpu/arm/mali400/mali/linux/mali_devfreq.c +++ b/drivers/gpu/arm/mali400/mali/linux/mali_devfreq.c @@ -259,7 +259,7 @@ int mali_devfreq_init(struct mali_device *mdev) return -EFAULT; mdev->devfreq = devfreq_add_device(mdev->dev, dp, - "simple_ondemand", NULL); + "performance", NULL); if (IS_ERR(mdev->devfreq)) { mali_devfreq_term_freq_table(mdev); return PTR_ERR(mdev->devfreq); diff --git a/drivers/gpu/arm/midgard/backend/gpu/mali_kbase_devfreq.c b/drivers/gpu/arm/midgard/backend/gpu/mali_kbase_devfreq.c index 1495f06c..a6d2e012 100644 --- a/drivers/gpu/arm/midgard/backend/gpu/mali_kbase_devfreq.c +++ b/drivers/gpu/arm/midgard/backend/gpu/mali_kbase_devfreq.c @@ -348,8 +348,7 @@ int kbase_devfreq_init(struct kbase_device *kbdev) dp = &kbdev->devfreq_profile; dp->initial_freq = kbdev->current_freq; - /* .KP : set devfreq_dvfs_interval_in_ms */ - dp->polling_ms = 20; + dp->polling_ms = 100; dp->target = kbase_devfreq_target; dp->get_dev_status = kbase_devfreq_status; dp->get_cur_freq = kbase_devfreq_cur_freq; @@ -363,7 +362,7 @@ int kbase_devfreq_init(struct kbase_device *kbdev) return err; kbdev->devfreq = devfreq_add_device(kbdev->dev, dp, - "simple_ondemand", NULL); + "performance", NULL); if (IS_ERR(kbdev->devfreq)) { kbase_devfreq_term_freq_table(kbdev); return PTR_ERR(kbdev->devfreq); diff --git a/drivers/gpu/arm/midgard/mali_kbase_config_defaults.h b/drivers/gpu/arm/midgard/mali_kbase_config_defaults.h index 1cf44b35..a6a1a52f 100644 --- a/drivers/gpu/arm/midgard/mali_kbase_config_defaults.h +++ b/drivers/gpu/arm/midgard/mali_kbase_config_defaults.h @@ -109,8 +109,7 @@ enum { /* * Default period for DVFS sampling */ -// #define DEFAULT_PM_DVFS_PERIOD 100 /* 100ms */ -#define DEFAULT_PM_DVFS_PERIOD 20 /* 20 ms */ +#define DEFAULT_PM_DVFS_PERIOD 100 /* 100ms */ /* * Power Management poweroff tick granuality. This is in nanoseconds to diff --git a/drivers/gpu/arm/midgard/mali_kbase_core_linux.c b/drivers/gpu/arm/midgard/mali_kbase_core_linux.c index 24bafe2b..65bc5ad2 100644 --- a/drivers/gpu/arm/midgard/mali_kbase_core_linux.c +++ b/drivers/gpu/arm/midgard/mali_kbase_core_linux.c @@ -515,14 +515,20 @@ static int kbase_legacy_dispatch(struct kbase_context *kctx, case KBASE_FUNC_JOB_SUBMIT: { struct kbase_uk_job_submit *job = args; + void __user *user_addr = NULL; if (sizeof(*job) != args_size) goto bad_size; - if (kbase_jd_submit(kctx, job->addr.value, - job->nr_atoms, - job->stride, - false) != 0) +#ifdef CONFIG_COMPAT + if (kbase_ctx_flag(kctx, KCTX_COMPAT)) + user_addr = compat_ptr(job->addr.compat_value); + else +#endif + user_addr = job->addr.value; + + if (kbase_jd_submit(kctx, user_addr, job->nr_atoms, + job->stride, false) != 0) ukh->ret = MALI_ERROR_FUNCTION_FAILED; break; } @@ -531,14 +537,20 @@ static int kbase_legacy_dispatch(struct kbase_context *kctx, case KBASE_FUNC_JOB_SUBMIT_UK6: { struct kbase_uk_job_submit *job = args; + void __user *user_addr = NULL; if (sizeof(*job) != args_size) goto bad_size; - if (kbase_jd_submit(kctx, job->addr.value, - job->nr_atoms, - job->stride, - true) != 0) +#ifdef CONFIG_COMPAT + if (kbase_ctx_flag(kctx, KCTX_COMPAT)) + user_addr = compat_ptr(job->addr.compat_value); + else +#endif + user_addr = job->addr.value; + + if (kbase_jd_submit(kctx, user_addr, job->nr_atoms, + job->stride, true) != 0) ukh->ret = MALI_ERROR_FUNCTION_FAILED; break; } @@ -646,7 +658,8 @@ static int kbase_legacy_dispatch(struct kbase_context *kctx, goto bad_size; if (find->gpu_addr & ~PAGE_MASK) { - dev_warn(kbdev->dev, "kbase_legacy_dispatch case KBASE_FUNC_FIND_CPU_OFFSET: find->gpu_addr: passed parameter is invalid"); + dev_warn(kbdev->dev, + "kbase_legacy_dispatch case KBASE_FUNC_FIND_CPU_OFFSET: find->gpu_addr: passed parameter is invalid"); goto out_bad; } @@ -674,8 +687,11 @@ static int kbase_legacy_dispatch(struct kbase_context *kctx, goto bad_size; /* version buffer size check is made in compile time assert */ - memcpy(get_version->version_buffer, KERNEL_SIDE_DDK_VERSION_STRING, sizeof(KERNEL_SIDE_DDK_VERSION_STRING)); - get_version->version_string_size = sizeof(KERNEL_SIDE_DDK_VERSION_STRING); + memcpy(get_version->version_buffer, + KERNEL_SIDE_DDK_VERSION_STRING, + sizeof(KERNEL_SIDE_DDK_VERSION_STRING)); + get_version->version_string_size = + sizeof(KERNEL_SIDE_DDK_VERSION_STRING); get_version->rk_version = ROCKCHIP_VERSION; break; } @@ -828,7 +844,8 @@ static int kbase_legacy_dispatch(struct kbase_context *kctx, #ifdef CONFIG_COMPAT if (kbase_ctx_flag(kctx, KCTX_COMPAT)) - user_buf = compat_ptr(add_data->buf.compat_value); + user_buf = + compat_ptr(add_data->buf.compat_value); else #endif user_buf = add_data->buf.value; @@ -977,9 +994,9 @@ static int kbase_legacy_dispatch(struct kbase_context *kctx, return ret; - bad_size: +bad_size: dev_err(kbdev->dev, "Wrong syscall size (%d) for %08x\n", args_size, id); - out_bad: +out_bad: return -EINVAL; } @@ -1317,7 +1334,16 @@ static int kbase_api_set_flags(struct kbase_context *kctx, static int kbase_api_job_submit(struct kbase_context *kctx, struct kbase_ioctl_job_submit *submit) { - return kbase_jd_submit(kctx, submit->addr.value, submit->nr_atoms, + void __user *user_addr = NULL; + +#ifdef CONFIG_COMPAT + if (kbase_ctx_flag(kctx, KCTX_COMPAT)) + user_addr = compat_ptr(submit->addr.compat_value); + else +#endif + user_addr = submit->addr.value; + + return kbase_jd_submit(kctx, user_addr, submit->nr_atoms, submit->stride, false); } @@ -1548,6 +1574,7 @@ static int kbase_api_mem_alias(struct kbase_context *kctx, union kbase_ioctl_mem_alias *alias) { struct base_mem_aliasing_info *ai; + void __user *user_addr = NULL; u64 flags; int err; @@ -1558,8 +1585,15 @@ static int kbase_api_mem_alias(struct kbase_context *kctx, if (!ai) return -ENOMEM; - err = copy_from_user(ai, alias->in.aliasing_info.value, - sizeof(*ai) * alias->in.nents); +#ifdef CONFIG_COMPAT + if (kbase_ctx_flag(kctx, KCTX_COMPAT)) + user_addr = + compat_ptr(alias->in.aliasing_info.compat_value); + else +#endif + user_addr = alias->in.aliasing_info.value; + + err = copy_from_user(ai, user_addr, sizeof(*ai) * alias->in.nents); if (err) { vfree(ai); return err; @@ -1586,10 +1620,18 @@ static int kbase_api_mem_import(struct kbase_context *kctx, { int ret; u64 flags = import->in.flags; + void __user *phandle; + +#ifdef CONFIG_COMPAT + if (kbase_ctx_flag(kctx, KCTX_COMPAT)) + phandle = compat_ptr(import->in.phandle.compat_value); + else +#endif + phandle = import->in.phandle.value; ret = kbase_mem_import(kctx, import->in.type, - import->in.phandle.value, + phandle, import->in.padding, &import->out.gpu_va, &import->out.va_pages, @@ -1654,6 +1696,7 @@ static int kbase_api_get_profiling_controls(struct kbase_context *kctx, static int kbase_api_mem_profile_add(struct kbase_context *kctx, struct kbase_ioctl_mem_profile_add *data) { + char __user *user_buf; char *buf; int err; @@ -1666,7 +1709,14 @@ static int kbase_api_mem_profile_add(struct kbase_context *kctx, if (ZERO_OR_NULL_PTR(buf)) return -ENOMEM; - err = copy_from_user(buf, data->buffer.value, data->len); +#ifdef CONFIG_COMPAT + if (kbase_ctx_flag(kctx, KCTX_COMPAT)) + user_buf = compat_ptr(data->buffer.compat_value); + else +#endif + user_buf = data->buffer.value; + + err = copy_from_user(buf, user_buf, data->len); if (err) { kfree(buf); return err; diff --git a/drivers/gpu/drm/armada/armada_gem.c b/drivers/gpu/drm/armada/armada_gem.c index 60a688ef..cd5bb991 100644 --- a/drivers/gpu/drm/armada/armada_gem.c +++ b/drivers/gpu/drm/armada/armada_gem.c @@ -546,7 +546,7 @@ armada_gem_prime_export(struct drm_device *dev, struct drm_gem_object *obj, exp_info.flags = O_RDWR; exp_info.priv = obj; - return dma_buf_export(&exp_info); + return drm_gem_dmabuf_export(dev, &exp_info); } struct drm_gem_object * diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h index 3930ba04..c5ace780 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h @@ -14,7 +14,7 @@ struct dw_hdmi_audio_data { struct dw_hdmi_i2s_audio_data { struct dw_hdmi *hdmi; - struct platform_device *pdev; + u8 *eld; void (*write)(struct dw_hdmi *hdmi, u8 val, int offset); u8 (*read)(struct dw_hdmi *hdmi, int offset); diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c index f1f62d8c..d0904f6b 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c @@ -11,12 +11,15 @@ #include +#include /* This is only to get MAX_ELD_BYTES */ + #include "dw-hdmi.h" #include "dw-hdmi-audio.h" #define DRIVER_NAME "dw-hdmi-i2s-audio" -static inline void hdmi_write(struct dw_hdmi_i2s_audio_data *audio, u8 val, int offset) +static inline void hdmi_write(struct dw_hdmi_i2s_audio_data *audio, + u8 val, int offset) { struct dw_hdmi *hdmi = audio->hdmi; @@ -107,8 +110,7 @@ static int dw_hdmi_i2s_hw_params(struct device *dev, void *data, HDMI_AUD_INT_FIFO_FULL_MSK, HDMI_AUD_INT); hdmi_update_bits(audio, HDMI_AUD_CONF0_SW_RESET, HDMI_AUD_CONF0_SW_RESET, HDMI_AUD_CONF0); - hdmi_update_bits(audio, HDMI_MC_SWRSTZ_I2S_RESET_MSK, - HDMI_MC_SWRSTZ_I2S_RESET_MSK, HDMI_MC_SWRSTZ); + hdmi_write(audio, (u8)~HDMI_MC_SWRSTZ_I2S_RESET_MSK, HDMI_MC_SWRSTZ); switch (hparms->mode) { case NLPCM: @@ -185,16 +187,11 @@ static int dw_hdmi_i2s_hw_params(struct device *dev, void *data, hdmi_write(audio, 0x00, HDMI_FC_AUDICONF1); /* Set Channel Allocation */ - hdmi_write(audio, 0x00, HDMI_FC_AUDICONF2); + hdmi_write(audio, hparms->cea.channel_allocation, HDMI_FC_AUDICONF2); /* Set LFEPBLDOWN-MIX INH and LSV */ hdmi_write(audio, 0x00, HDMI_FC_AUDICONF3); - hdmi_update_bits(audio, HDMI_AUD_CONF0_SW_RESET, - HDMI_AUD_CONF0_SW_RESET, HDMI_AUD_CONF0); - hdmi_update_bits(audio, HDMI_MC_SWRSTZ_I2S_RESET_MSK, - HDMI_MC_SWRSTZ_I2S_RESET_MSK, HDMI_MC_SWRSTZ); - dw_hdmi_audio_enable(hdmi); return 0; @@ -208,11 +205,22 @@ static void dw_hdmi_i2s_audio_shutdown(struct device *dev, void *data) dw_hdmi_audio_disable(hdmi); hdmi_write(audio, HDMI_AUD_CONF0_SW_RESET, HDMI_AUD_CONF0); + hdmi_write(audio, (u8)~HDMI_MC_SWRSTZ_I2S_RESET_MSK, HDMI_MC_SWRSTZ); +} + +static int dw_hdmi_i2s_get_eld(struct device *dev, void *data, u8 *buf, size_t len) +{ + struct dw_hdmi_i2s_audio_data *audio = data; + + memcpy(buf, audio->eld, min(len, (size_t)MAX_ELD_BYTES)); + + return 0; } static struct hdmi_codec_ops dw_hdmi_i2s_ops = { .hw_params = dw_hdmi_i2s_hw_params, .audio_shutdown = dw_hdmi_i2s_audio_shutdown, + .get_eld = dw_hdmi_i2s_get_eld, }; static int snd_dw_hdmi_probe(struct platform_device *pdev) @@ -220,6 +228,7 @@ static int snd_dw_hdmi_probe(struct platform_device *pdev) struct dw_hdmi_i2s_audio_data *audio = pdev->dev.platform_data; struct platform_device_info pdevinfo; struct hdmi_codec_pdata pdata; + struct platform_device *platform; pdata.ops = &dw_hdmi_i2s_ops; pdata.i2s = 1; @@ -234,23 +243,27 @@ static int snd_dw_hdmi_probe(struct platform_device *pdev) pdevinfo.size_data = sizeof(pdata); pdevinfo.dma_mask = DMA_BIT_MASK(32); - audio->pdev = platform_device_register_full(&pdevinfo); - return IS_ERR_OR_NULL(audio->pdev); + platform = platform_device_register_full(&pdevinfo); + if (IS_ERR(platform)) + return PTR_ERR(platform); + + dev_set_drvdata(&pdev->dev, platform); + + return 0; } static int snd_dw_hdmi_remove(struct platform_device *pdev) { - struct dw_hdmi_i2s_audio_data *audio = pdev->dev.platform_data; + struct platform_device *platform = dev_get_drvdata(&pdev->dev); - if (!IS_ERR_OR_NULL(audio->pdev)) - platform_device_unregister(audio->pdev); + platform_device_unregister(platform); return 0; } static struct platform_driver snd_dw_hdmi_driver = { .probe = snd_dw_hdmi_probe, - .remove = snd_dw_hdmi_remove, + .remove = snd_dw_hdmi_remove, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 8cb2cb4e..3171f7c6 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -89,6 +89,7 @@ static const struct dw_hdmi_audio_tmds_n common_tmds_n_table[] = { { .tmds = 71000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, { .tmds = 72000000, .n_32k = 4096, .n_44k1 = 5635, .n_48k = 6144, }, { .tmds = 73250000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, }, + { .tmds = 74176000, .n_32k = 11648, .n_44k1 = 17836, .n_48k = 11648, }, { .tmds = 74250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, { .tmds = 75000000, .n_32k = 4096, .n_44k1 = 5880, .n_48k = 6144, }, { .tmds = 78750000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, }, @@ -105,13 +106,16 @@ static const struct dw_hdmi_audio_tmds_n common_tmds_n_table[] = { { .tmds = 119000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, }, { .tmds = 135000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, { .tmds = 146250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, - { .tmds = 148500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, + { .tmds = 148352000, .n_32k = 11648, .n_44k1 = 8918, .n_48k = 5824, }, + { .tmds = 148500000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, { .tmds = 154000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, }, { .tmds = 162000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, /* For 297 MHz+ HDMI spec have some other rule for setting N */ - { .tmds = 297000000, .n_32k = 3073, .n_44k1 = 4704, .n_48k = 5120, }, - { .tmds = 594000000, .n_32k = 3073, .n_44k1 = 9408, .n_48k = 10240, }, + { .tmds = 296703000, .n_32k = 5824, .n_44k1 = 4459, .n_48k = 5824, }, + { .tmds = 297000000, .n_32k = 3072, .n_44k1 = 4704, .n_48k = 5120, }, + { .tmds = 593407000, .n_32k = 5824, .n_44k1 = 8918, .n_48k = 5824, }, + { .tmds = 594000000, .n_32k = 3072, .n_44k1 = 9408, .n_48k = 6144, }, /* End of table */ { .tmds = 0, .n_32k = 0, .n_44k1 = 0, .n_48k = 0, }, @@ -263,6 +267,9 @@ struct dw_hdmi { u8 (*read)(struct dw_hdmi *hdmi, int offset); bool initialized; /* hdmi is enabled before bind */ + + void (*enable_audio)(struct dw_hdmi *hdmi); + void (*disable_audio)(struct dw_hdmi *hdmi); }; #define HDMI_IH_PHY_STAT0_RX_SENSE \ @@ -636,14 +643,18 @@ static struct i2c_adapter *dw_hdmi_i2c_adapter(struct dw_hdmi *hdmi) static void hdmi_set_cts_n(struct dw_hdmi *hdmi, unsigned int cts, unsigned int n) { - /* Must be set/cleared first */ - hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3); + /* Use Auto CTS mode with CTS is unknown */ + if (cts) { + /* Must be set/cleared first */ + hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3); - /* nshift factor = 0 */ - hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3); + /* nshift factor = 0 */ + hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3); - hdmi_writeb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) | - HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3); + hdmi_writeb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) | + HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3); + } else + hdmi_writeb(hdmi, 0, HDMI_AUD_CTS3); hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2); hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1); @@ -770,24 +781,30 @@ static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi, { unsigned long ftdms = pixel_clk; unsigned int n, cts; + u8 config3; u64 tmp; n = hdmi_find_n(hdmi, pixel_clk, sample_rate); - /* - * Compute the CTS value from the N value. Note that CTS and N - * can be up to 20 bits in total, so we need 64-bit math. Also - * note that our TDMS clock is not fully accurate; it is accurate - * to kHz. This can introduce an unnecessary remainder in the - * calculation below, so we don't try to warn about that. - */ - tmp = (u64)ftdms * n; - do_div(tmp, 128 * sample_rate); - cts = tmp; + config3 = hdmi_readb(hdmi, HDMI_CONFIG3_ID); - dev_dbg(hdmi->dev, "%s: fs=%uHz ftdms=%lu.%03luMHz N=%d cts=%d\n", - __func__, sample_rate, ftdms / 1000000, (ftdms / 1000) % 1000, - n, cts); + if (config3 & HDMI_CONFIG3_AHBAUDDMA) { + /* + * Compute the CTS value from the N value. Note that CTS and N + * can be up to 20 bits in total, so we need 64-bit math. Also + * note that our TDMS clock is not fully accurate; it is accurate + * to kHz. This can introduce an unnecessary remainder in the + * calculation below, so we don't try to warn about that. + */ + tmp = (u64)ftdms * n; + do_div(tmp, 128 * sample_rate); + cts = tmp; + + dev_dbg(hdmi->dev, "%s: fs=%uHz ftdms=%lu.%03luMHz N=%d cts=%d\n", + __func__, sample_rate, ftdms / 1000000, (ftdms / 1000) % 1000, + n, cts); + } else + cts = 0; spin_lock_irq(&hdmi->audio_lock); hdmi->audio_n = n; @@ -821,13 +838,49 @@ void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate) } EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_rate); +static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi, bool enable) +{ + if (enable) + hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_AUDCLK_DISABLE; + else + hdmi->mc_clkdis |= HDMI_MC_CLKDIS_AUDCLK_DISABLE; + hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); + + if (enable) { + hdmi_set_cts_n(hdmi, 0, 0); + hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n); + } +} + +static void dw_hdmi_ahb_audio_enable(struct dw_hdmi *hdmi) +{ + hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n); +} + +static void dw_hdmi_ahb_audio_disable(struct dw_hdmi *hdmi) +{ + hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0); +} + +static void dw_hdmi_i2s_audio_enable(struct dw_hdmi *hdmi) +{ + hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n); + hdmi_enable_audio_clk(hdmi, true); +} + +static void dw_hdmi_i2s_audio_disable(struct dw_hdmi *hdmi) +{ + hdmi_enable_audio_clk(hdmi, false); +} + void dw_hdmi_audio_enable(struct dw_hdmi *hdmi) { unsigned long flags; spin_lock_irqsave(&hdmi->audio_lock, flags); hdmi->audio_enable = true; - hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n); + if (hdmi->enable_audio) + hdmi->enable_audio(hdmi); spin_unlock_irqrestore(&hdmi->audio_lock, flags); } EXPORT_SYMBOL_GPL(dw_hdmi_audio_enable); @@ -838,7 +891,8 @@ void dw_hdmi_audio_disable(struct dw_hdmi *hdmi) spin_lock_irqsave(&hdmi->audio_lock, flags); hdmi->audio_enable = false; - hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0); + if (hdmi->disable_audio) + hdmi->disable_audio(hdmi); spin_unlock_irqrestore(&hdmi->audio_lock, flags); } EXPORT_SYMBOL_GPL(dw_hdmi_audio_disable); @@ -1691,7 +1745,21 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode) break; } - frame.scan_mode = HDMI_SCAN_MODE_NONE; + frame.scan_mode = HDMI_SCAN_MODE_UNDERSCAN; + frame.content_type = HDMI_CONTENT_TYPE_GRAPHICS; + frame.itc = true; + + if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) { + frame.colorimetry = HDMI_COLORIMETRY_NONE; + frame.extended_colorimetry = 0; + frame.quantization_range = HDMI_QUANTIZATION_RANGE_FULL; + frame.ycc_quantization_range = HDMI_YCC_QUANTIZATION_RANGE_FULL; + } else { + frame.quantization_range = HDMI_QUANTIZATION_RANGE_LIMITED; + frame.ycc_quantization_range = HDMI_YCC_QUANTIZATION_RANGE_LIMITED; + } + + hdmi_infoframe_log(KERN_INFO, hdmi->dev, &frame); /* * The Designware IP uses a different byte format from standard @@ -1786,6 +1854,8 @@ static void hdmi_config_vendor_specific_infoframe(struct dw_hdmi *hdmi, return; } + hdmi_infoframe_log(KERN_INFO, hdmi->dev, &frame); + /* Set the length of HDMI vendor specific InfoFrame payload */ hdmi_writeb(hdmi, buffer[2], HDMI_FC_VSDSIZE); @@ -1826,7 +1896,7 @@ static void hdmi_config_hdr_infoframe(struct dw_hdmi *hdmi) /* Dynamic Range and Mastering Infoframe is introduced in v2.11a. */ if (hdmi->version < 0x211a) { - DRM_ERROR("Not support DRM Infoframe\n"); + DRM_DEBUG("Not support DRM Infoframe\n"); return; } @@ -1857,7 +1927,9 @@ static void hdmi_config_hdr_infoframe(struct dw_hdmi *hdmi) return; } - hdmi_writeb(hdmi, 1, HDMI_FC_DRM_HB0); + hdmi_infoframe_log(KERN_INFO, hdmi->dev, &frame); + + hdmi_writeb(hdmi, frame.version, HDMI_FC_DRM_HB0); hdmi_writeb(hdmi, frame.length, HDMI_FC_DRM_HB1); hdmi_writeb(hdmi, frame.eotf, HDMI_FC_DRM_PB0); hdmi_writeb(hdmi, frame.metadata_type, HDMI_FC_DRM_PB1); @@ -1991,10 +2063,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, HDMI_FC_INVIDCONF_IN_I_P_INTERLACED : HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE; - inv_val |= hdmi->sink_is_hdmi ? - HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE : - HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE; - hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF); hdisplay = mode->hdisplay; @@ -2129,12 +2197,6 @@ static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi) HDMI_MC_FLOWCTRL); } -static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi) -{ - hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_AUDCLK_DISABLE; - hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS); -} - /* Workaround to clear the overflow condition */ static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi) { @@ -2286,12 +2348,15 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode) /* HDMI Initialization Step E - Configure audio */ hdmi_clk_regenerator_update_pixel_clock(hdmi); - hdmi_enable_audio_clk(hdmi); + hdmi_enable_audio_clk(hdmi, true); } /* not for DVI mode */ if (hdmi->sink_is_hdmi) { dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__); + hdmi_modb(hdmi, HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE, + HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE, + HDMI_FC_INVIDCONF); /* HDMI Initialization Step F - Configure AVI InfoFrame */ hdmi_config_AVI(hdmi, mode); @@ -2479,6 +2544,7 @@ static void dw_hdmi_bridge_nop(struct drm_bridge *bridge) static enum drm_connector_status dw_hdmi_connector_detect(struct drm_connector *connector, bool force) { + enum drm_connector_status status; struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, connector); @@ -2488,7 +2554,24 @@ dw_hdmi_connector_detect(struct drm_connector *connector, bool force) dw_hdmi_update_phy_mask(hdmi); mutex_unlock(&hdmi->mutex); - return hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data); + status = hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data); + + if (status == connector_status_connected && hdmi->ddc) { + struct edid *edid = drm_get_edid(connector, hdmi->ddc); + if (edid) { + dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n", + edid->width_cm, edid->height_cm); + + hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid); + hdmi->sink_has_audio = drm_detect_monitor_audio(edid); + drm_mode_connector_update_edid_property(connector, edid); + cec_notifier_set_phys_addr_from_edid(hdmi->cec_notifier, edid); + drm_edid_to_eld(connector, edid); + kfree(edid); + } + } + + return status; } static int dw_hdmi_connector_get_modes(struct drm_connector *connector) @@ -2891,9 +2974,6 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) dw_hdmi_update_phy_mask(hdmi); } mutex_unlock(&hdmi->mutex); - if (!(phy_stat & (HDMI_PHY_RX_SENSE | HDMI_PHY_HPD))) - cec_notifier_set_phys_addr(hdmi->cec_notifier, - CEC_PHYS_ADDR_INVALID); } check_hdmi_irq(hdmi, intr_stat, phy_int_pol); @@ -3706,6 +3786,8 @@ int dw_hdmi_bind(struct device *dev, struct device *master, audio.irq = irq; audio.hdmi = hdmi; audio.eld = hdmi->connector.eld; + hdmi->enable_audio = dw_hdmi_ahb_audio_enable; + hdmi->disable_audio = dw_hdmi_ahb_audio_disable; pdevinfo.name = "dw-hdmi-ahb-audio"; pdevinfo.data = &audio; @@ -3719,6 +3801,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master, audio.write = hdmi_writeb; audio.read = hdmi_readb; audio.mod = hdmi_modb; + audio.eld = hdmi->connector.eld; + hdmi->enable_audio = dw_hdmi_i2s_audio_enable; + hdmi->disable_audio = dw_hdmi_i2s_audio_disable; pdevinfo.name = "dw-hdmi-i2s-audio"; pdevinfo.data = &audio; diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index f77d4aa1..4da489b5 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1563,15 +1563,15 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev, for_each_connector_in_state(old_state, connector, old_conn_state, i) { const struct drm_connector_helper_funcs *funcs; - if (!connector->state->crtc) - continue; + funcs = connector->helper_private; - if (!connector->state->crtc->state->active) + if (!funcs || !funcs->atomic_begin) continue; - funcs = connector->helper_private; + if (!connector->state->crtc) + continue; - if (!funcs || !funcs->atomic_begin) + if (active_only && !connector->state->crtc->state->active) continue; DRM_DEBUG_ATOMIC("flush beginning [CONNECTOR:%d:%s]\n", @@ -1645,15 +1645,15 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev, for_each_connector_in_state(old_state, connector, old_conn_state, i) { const struct drm_connector_helper_funcs *funcs; - if (!connector->state->crtc) - continue; + funcs = connector->helper_private; - if (!connector->state->crtc->state->active) + if (!funcs || !funcs->atomic_flush) continue; - funcs = connector->helper_private; + if (!connector->state->crtc) + continue; - if (!funcs || !funcs->atomic_flush) + if (active_only && !connector->state->crtc->state->active) continue; DRM_DEBUG_ATOMIC("flushing [CONNECTOR:%d:%s]\n", diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index bfe67107..69a1eb4e 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1233,25 +1233,25 @@ static const struct drm_display_mode edid_4k_modes[] = { 3840, 4016, 4104, 4400, 0, 2160, 2168, 2178, 2250, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 30, }, + .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, /* 2 - 3840x2160@25Hz */ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4896, 4984, 5280, 0, 2160, 2168, 2178, 2250, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 25, }, + .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, /* 3 - 3840x2160@24Hz */ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 5116, 5204, 5500, 0, 2160, 2168, 2178, 2250, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 24, }, + .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, /* 4 - 4096x2160@24Hz (SMPTE) */ { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 5116, 5204, 5500, 0, 2160, 2168, 2178, 2250, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 24, }, + .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, }, }; /*** DDC fetch and block validation ***/ @@ -2740,7 +2740,7 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid, #define TRADITIONAL_GAMMA_SDR (0x1 << 0) #define TRADITIONAL_GAMMA_HDR (0x1 << 1) #define SMPTE_ST2084 (0x1 << 2) -#define FUTURE_EOTF (0x1 << 3) +#define HYBRID_LOG_GAMMA (0x1 << 3) #define RESERVED_EOTF (0x3 << 4) #define STATIC_METADATA_TYPE1 (0x1 << 0) @@ -3710,6 +3710,8 @@ static uint16_t eotf_supported(const u8 *edid_ext) val |= TRADITIONAL_GAMMA_HDR; if (edid_ext[2] & SMPTE_ST2084) val |= SMPTE_ST2084; + if (edid_ext[2] & HYBRID_LOG_GAMMA) + val |= HYBRID_LOG_GAMMA; return val; } @@ -4735,10 +4737,10 @@ drm_hdmi_infoframe_set_hdr_metadata(struct hdmi_drm_infoframe *frame, hdr_source_metadata = (struct hdr_static_metadata *)hdr_metadata; - frame->length = sizeof(struct hdr_static_metadata); + frame->length = 26; frame->eotf = hdr_source_metadata->eotf; - frame->type = hdr_source_metadata->type; + frame->metadata_type = hdr_source_metadata->type; for (i = 0; i < 3; i++) { frame->display_primaries_x[i] = diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index d7f39a03..966ea635 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -255,13 +255,13 @@ drm_gem_object_release_handle(int id, void *ptr, void *data) struct drm_gem_object *obj = ptr; struct drm_device *dev = obj->dev; + if (dev->driver->gem_close_object) + dev->driver->gem_close_object(obj, file_priv); + if (drm_core_check_feature(dev, DRIVER_PRIME)) drm_gem_remove_prime_handles(obj, file_priv); drm_vma_node_revoke(&obj->vma_node, file_priv->filp); - if (dev->driver->gem_close_object) - dev->driver->gem_close_object(obj, file_priv); - drm_gem_object_handle_unreference_unlocked(obj); return 0; diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 34757168..0914c886 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -57,6 +57,9 @@ static int drm_getunique(struct drm_device *dev, void *data, struct drm_unique *u = data; struct drm_master *master = file_priv->master; + if (!master) + return -EINVAL; + if (u->unique_len >= master->unique_len) { if (copy_to_user(u->unique, master->unique, master->unique_len)) return -EFAULT; diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 6f207d59..7ea65c41 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -61,9 +62,11 @@ */ struct drm_prime_member { - struct list_head entry; struct dma_buf *dma_buf; uint32_t handle; + + struct rb_node dmabuf_rb; + struct rb_node handle_rb; }; struct drm_prime_attachment { @@ -71,15 +74,11 @@ struct drm_prime_attachment { enum dma_data_direction dir; }; -struct drm_prime_callback_data { - struct drm_gem_object *obj; - struct sg_table *sgt; -}; - static int drm_prime_add_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t handle) { struct drm_prime_member *member; + struct rb_node **p, *rb; member = kmalloc(sizeof(*member), GFP_KERNEL); if (!member) @@ -88,18 +87,56 @@ static int drm_prime_add_buf_handle(struct drm_prime_file_private *prime_fpriv, get_dma_buf(dma_buf); member->dma_buf = dma_buf; member->handle = handle; - list_add(&member->entry, &prime_fpriv->head); + + rb = NULL; + p = &prime_fpriv->dmabufs.rb_node; + while (*p) { + struct drm_prime_member *pos; + + rb = *p; + pos = rb_entry(rb, struct drm_prime_member, dmabuf_rb); + if (dma_buf > pos->dma_buf) + p = &rb->rb_right; + else + p = &rb->rb_left; + } + rb_link_node(&member->dmabuf_rb, rb, p); + rb_insert_color(&member->dmabuf_rb, &prime_fpriv->dmabufs); + + rb = NULL; + p = &prime_fpriv->handles.rb_node; + while (*p) { + struct drm_prime_member *pos; + + rb = *p; + pos = rb_entry(rb, struct drm_prime_member, handle_rb); + if (handle > pos->handle) + p = &rb->rb_right; + else + p = &rb->rb_left; + } + rb_link_node(&member->handle_rb, rb, p); + rb_insert_color(&member->handle_rb, &prime_fpriv->handles); + return 0; } static struct dma_buf *drm_prime_lookup_buf_by_handle(struct drm_prime_file_private *prime_fpriv, uint32_t handle) { - struct drm_prime_member *member; + struct rb_node *rb; - list_for_each_entry(member, &prime_fpriv->head, entry) { + rb = prime_fpriv->handles.rb_node; + while (rb) { + struct drm_prime_member *member; + + member = rb_entry(rb, struct drm_prime_member, handle_rb); if (member->handle == handle) return member->dma_buf; + else if (member->handle < handle) + rb = rb->rb_right; + else + rb = rb->rb_left; } return NULL; @@ -109,14 +146,23 @@ static int drm_prime_lookup_buf_handle(struct drm_prime_file_private *prime_fpri struct dma_buf *dma_buf, uint32_t *handle) { - struct drm_prime_member *member; + struct rb_node *rb; - list_for_each_entry(member, &prime_fpriv->head, entry) { + rb = prime_fpriv->dmabufs.rb_node; + while (rb) { + struct drm_prime_member *member; + + member = rb_entry(rb, struct drm_prime_member, dmabuf_rb); if (member->dma_buf == dma_buf) { *handle = member->handle; return 0; + } else if (member->dma_buf < dma_buf) { + rb = rb->rb_right; + } else { + rb = rb->rb_left; } } + return -ENOENT; } @@ -147,37 +193,51 @@ static void drm_gem_map_detach(struct dma_buf *dma_buf, struct drm_prime_attachment *prime_attach = attach->priv; struct drm_gem_object *obj = dma_buf->priv; struct drm_device *dev = obj->dev; - struct sg_table *sgt; - if (dev->driver->gem_prime_unpin) - dev->driver->gem_prime_unpin(obj); - - if (!prime_attach) - return; + if (prime_attach) { + struct sg_table *sgt = prime_attach->sgt; + + if (sgt) { + DEFINE_DMA_ATTRS(attrs); + dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs); + if (prime_attach->dir != DMA_NONE) + dma_unmap_sg_attrs(attach->dev, sgt->sgl, + sgt->nents, + prime_attach->dir, + &attrs); + sg_free_table(sgt); + } - sgt = prime_attach->sgt; - if (sgt) { - if (prime_attach->dir != DMA_NONE) - dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, - prime_attach->dir); - sg_free_table(sgt); + kfree(sgt); + kfree(prime_attach); + attach->priv = NULL; } - kfree(sgt); - kfree(prime_attach); - attach->priv = NULL; + if (dev->driver->gem_prime_unpin) + dev->driver->gem_prime_unpin(obj); } void drm_prime_remove_buf_handle_locked(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf) { - struct drm_prime_member *member, *safe; + struct rb_node *rb; + + rb = prime_fpriv->dmabufs.rb_node; + while (rb) { + struct drm_prime_member *member; - list_for_each_entry_safe(member, safe, &prime_fpriv->head, entry) { + member = rb_entry(rb, struct drm_prime_member, dmabuf_rb); if (member->dma_buf == dma_buf) { + rb_erase(&member->handle_rb, &prime_fpriv->handles); + rb_erase(&member->dmabuf_rb, &prime_fpriv->dmabufs); + dma_buf_put(dma_buf); - list_del(&member->entry); kfree(member); + return; + } else if (member->dma_buf < dma_buf) { + rb = rb->rb_right; + } else { + rb = rb->rb_left; } } } @@ -206,7 +266,9 @@ static struct sg_table *drm_gem_map_dma_buf(struct dma_buf_attachment *attach, sgt = obj->dev->driver->gem_prime_get_sg_table(obj); if (!IS_ERR(sgt)) { - if (!dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir)) { + DEFINE_DMA_ATTRS(attrs); + dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs); + if (!dma_map_sg_attrs(attach->dev, sgt->sgl, sgt->nents, dir, &attrs)) { sg_free_table(sgt); kfree(sgt); sgt = ERR_PTR(-ENOMEM); @@ -226,19 +288,51 @@ static void drm_gem_unmap_dma_buf(struct dma_buf_attachment *attach, /* nothing to be done here */ } +/** + * drm_gem_dmabuf_export - dma_buf export implementation for GEM + * @dma_buf: buffer to be exported + * + * This wraps dma_buf_export() for use by generic GEM drivers that are using + * drm_gem_dmabuf_release(). In addition to calling dma_buf_export(), we take + * a reference to the &drm_device and the exported &drm_gem_object (stored in + * exp_info->priv) which is released by drm_gem_dmabuf_release(). + * + * Returns the new dmabuf. + */ +struct dma_buf *drm_gem_dmabuf_export(struct drm_device *dev, + struct dma_buf_export_info *exp_info) +{ + struct dma_buf *dma_buf; + + dma_buf = dma_buf_export(exp_info); + if (IS_ERR(dma_buf)) + return dma_buf; + + drm_dev_ref(dev); + drm_gem_object_reference(exp_info->priv); + + return dma_buf; +} +EXPORT_SYMBOL(drm_gem_dmabuf_export); + /** * drm_gem_dmabuf_release - dma_buf release implementation for GEM * @dma_buf: buffer to be released * * Generic release function for dma_bufs exported as PRIME buffers. GEM drivers * must use this in their dma_buf ops structure as the release callback. + * drm_gem_dmabuf_release() should be used in conjunction with + * drm_gem_dmabuf_export(). */ void drm_gem_dmabuf_release(struct dma_buf *dma_buf) { struct drm_gem_object *obj = dma_buf->priv; + struct drm_device *dev = obj->dev; /* drop the reference on the export fd holds */ drm_gem_object_unreference_unlocked(obj); + + drm_dev_unref(dev); } EXPORT_SYMBOL(drm_gem_dmabuf_release); @@ -387,7 +481,7 @@ struct dma_buf *drm_gem_prime_export(struct drm_device *dev, if (dev->driver->gem_prime_res_obj) exp_info.resv = dev->driver->gem_prime_res_obj(obj); - return dma_buf_export(&exp_info); + return drm_gem_dmabuf_export(dev, &exp_info); } EXPORT_SYMBOL(drm_gem_prime_export); @@ -418,8 +512,6 @@ static struct dma_buf *export_and_register_object(struct drm_device *dev, */ obj->dma_buf = dmabuf; get_dma_buf(obj->dma_buf); - /* Grab a new ref since the callers is now used by the dma-buf */ - drm_gem_object_reference(obj); return dmabuf; } @@ -524,23 +616,6 @@ int drm_gem_prime_handle_to_fd(struct drm_device *dev, } EXPORT_SYMBOL(drm_gem_prime_handle_to_fd); -static void drm_gem_prime_dmabuf_release_callback(void *data) -{ - struct drm_prime_callback_data *cb_data = data; - - if (cb_data && cb_data->obj && cb_data->obj->import_attach) { - struct dma_buf_attachment *attach = cb_data->obj->import_attach; - struct sg_table *sgt = cb_data->sgt; - - if (sgt) - dma_buf_unmap_attachment(attach, sgt, - DMA_BIDIRECTIONAL); - dma_buf_detach(attach->dmabuf, attach); - drm_gem_object_unreference_unlocked(cb_data->obj); - kfree(cb_data); - } -} - /** * drm_gem_prime_import - helper library implementation of the import callback * @dev: drm_device to import into @@ -555,7 +630,6 @@ struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev, struct dma_buf_attachment *attach; struct sg_table *sgt; struct drm_gem_object *obj; - struct drm_prime_callback_data *cb_data; int ret; if (dma_buf->ops == &drm_gem_prime_dmabuf_ops) { @@ -570,13 +644,6 @@ struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev, } } - cb_data = dma_buf_get_release_callback_data(dma_buf, - drm_gem_prime_dmabuf_release_callback); - if (cb_data && cb_data->obj && cb_data->obj->dev == dev) { - drm_gem_object_reference(cb_data->obj); - return cb_data->obj; - } - if (!dev->driver->gem_prime_import_sg_table) return ERR_PTR(-EINVAL); @@ -585,16 +652,11 @@ struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev, return ERR_CAST(attach); get_dma_buf(dma_buf); - cb_data = kmalloc(sizeof(*cb_data), GFP_KERNEL); - if (!cb_data) { - ret = -ENOMEM; - goto fail_detach; - } sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); if (IS_ERR(sgt)) { ret = PTR_ERR(sgt); - goto fail_free; + goto fail_detach; } obj = dev->driver->gem_prime_import_sg_table(dev, attach, sgt); @@ -602,20 +664,13 @@ struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev, ret = PTR_ERR(obj); goto fail_unmap; } + obj->import_attach = attach; - cb_data->obj = obj; - cb_data->sgt = sgt; - dma_buf_set_release_callback(dma_buf, - drm_gem_prime_dmabuf_release_callback, cb_data); - dma_buf_put(dma_buf); - drm_gem_object_reference(obj); return obj; fail_unmap: dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL); -fail_free: - kfree(cb_data); fail_detach: dma_buf_detach(dma_buf, attach); dma_buf_put(dma_buf); @@ -670,7 +725,7 @@ int drm_gem_prime_fd_to_handle(struct drm_device *dev, get_dma_buf(dma_buf); } - /* drm_gem_handle_create_tail unlocks dev->object_name_lock. */ + /* _handle_create_tail unconditionally unlocks dev->object_name_lock. */ ret = drm_gem_handle_create_tail(file_priv, obj, handle); drm_gem_object_unreference_unlocked(obj); if (ret) @@ -678,11 +733,10 @@ int drm_gem_prime_fd_to_handle(struct drm_device *dev, ret = drm_prime_add_buf_handle(&file_priv->prime, dma_buf, *handle); + mutex_unlock(&file_priv->prime.lock); if (ret) goto fail; - mutex_unlock(&file_priv->prime.lock); - dma_buf_put(dma_buf); return 0; @@ -692,11 +746,14 @@ int drm_gem_prime_fd_to_handle(struct drm_device *dev, * to detach.. which seems ok.. */ drm_gem_handle_delete(file_priv, *handle); + dma_buf_put(dma_buf); + return ret; + out_unlock: mutex_unlock(&dev->object_name_lock); out_put: - dma_buf_put(dma_buf); mutex_unlock(&file_priv->prime.lock); + dma_buf_put(dma_buf); return ret; } EXPORT_SYMBOL(drm_gem_prime_fd_to_handle); @@ -770,40 +827,40 @@ EXPORT_SYMBOL(drm_prime_pages_to_sg); /** * drm_prime_sg_to_page_addr_arrays - convert an sg table into a page array * @sgt: scatter-gather table to convert - * @pages: array of page pointers to store the page array in + * @pages: optional array of page pointers to store the page array in * @addrs: optional array to store the dma bus address of each page - * @max_pages: size of both the passed-in arrays + * @max_entries: size of both the passed-in arrays * * Exports an sg table into an array of pages and addresses. This is currently * required by the TTM driver in order to do correct fault handling. */ int drm_prime_sg_to_page_addr_arrays(struct sg_table *sgt, struct page **pages, - dma_addr_t *addrs, int max_pages) + dma_addr_t *addrs, int max_entries) { unsigned count; struct scatterlist *sg; struct page *page; - u32 len; - int pg_index; + u32 len, index; dma_addr_t addr; - pg_index = 0; + index = 0; for_each_sg(sgt->sgl, sg, sgt->nents, count) { len = sg->length; page = sg_page(sg); addr = sg_dma_address(sg); while (len > 0) { - if (WARN_ON(pg_index >= max_pages)) + if (WARN_ON(index >= max_entries)) return -1; - pages[pg_index] = page; + if (pages) + pages[index] = page; if (addrs) - addrs[pg_index] = addr; + addrs[index] = addr; page++; addr += PAGE_SIZE; len -= PAGE_SIZE; - pg_index++; + index++; } } return 0; @@ -834,12 +891,13 @@ EXPORT_SYMBOL(drm_prime_gem_destroy); void drm_prime_init_file_private(struct drm_prime_file_private *prime_fpriv) { - INIT_LIST_HEAD(&prime_fpriv->head); mutex_init(&prime_fpriv->lock); + prime_fpriv->dmabufs = RB_ROOT; + prime_fpriv->handles = RB_ROOT; } void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv) { /* by now drm_gem_release should've made sure the list is empty */ - WARN_ON(!list_empty(&prime_fpriv->head)); + WARN_ON(!RB_EMPTY_ROOT(&prime_fpriv->dmabufs)); } diff --git a/drivers/gpu/drm/i915/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/i915_gem_dmabuf.c index e9c2bfd8..d4a02162 100644 --- a/drivers/gpu/drm/i915/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/i915_gem_dmabuf.c @@ -244,7 +244,7 @@ struct dma_buf *i915_gem_prime_export(struct drm_device *dev, return ERR_PTR(ret); } - return dma_buf_export(&exp_info); + return drm_gem_dmabuf_export(dev, &exp_info); } static int i915_gem_object_get_pages_dmabuf(struct drm_i915_gem_object *obj) diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index a58edabe..e2aad6e2 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -510,9 +510,15 @@ dw_hdmi_rockchip_mode_valid(struct drm_connector *connector, return MODE_BAD; hdmi = to_rockchip_hdmi(encoder); - if (hdmi->dev_type == RK3368_HDMI && mode->clock > 340000 && + if ((hdmi->dev_type == RK3368_HDMI || hdmi->dev_type == RK3328_HDMI) && + mode->clock > 340000 && !drm_mode_is_420(&connector->display_info, mode)) return MODE_BAD; + + /* Skip bad clocks for RK3288 */ + if (hdmi->dev_type == RK3288_HDMI && (mode->clock < 27500 || mode->clock > 340000)) + return MODE_CLOCK_RANGE; + /* * ensure all drm display mode can work, if someone want support more * resolutions, please limit the possible_crtc, only connect to @@ -722,7 +728,9 @@ dw_hdmi_rockchip_select_output(struct drm_connector_state *conn_state, /* BT2020 require color depth at lest 10bit */ *color_depth = 10; /* We prefer use YCbCr422 to send 10bit */ - if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422) + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422 && + info->max_tmds_clock <= 340000 && + hdmi->dev_type != RK3288_HDMI) *color_format = DRM_HDMI_OUTPUT_YCBCR422; } diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c index 273a52b5..85bbd19c 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c @@ -649,6 +649,7 @@ void rockchip_gem_free_object(struct drm_gem_object *obj) dma_unmap_sg(drm->dev, rk_obj->sgt->sgl, rk_obj->sgt->nents, DMA_BIDIRECTIONAL); } + drm_prime_gem_destroy(obj, rk_obj->sgt); } else { rockchip_gem_free_buf(rk_obj); } diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 76610608..b3f7a8eb 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -1429,7 +1429,7 @@ static void vop_crtc_disable(struct drm_crtc *crtc) vop->is_iommu_enabled = false; } - pm_runtime_put(vop->dev); + pm_runtime_put_sync(vop->dev); clk_disable_unprepare(vop->dclk); clk_disable_unprepare(vop->aclk); clk_disable_unprepare(vop->hclk); @@ -1653,6 +1653,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane, int ymirror, xmirror; uint32_t val; bool rb_swap, global_alpha_en; + int skip_lines = 0; #if defined(CONFIG_ROCKCHIP_DRM_DEBUG) bool AFBC_flag = false; @@ -1689,8 +1690,14 @@ static void vop_plane_atomic_update(struct drm_plane *plane, } mode = &crtc->state->adjusted_mode; + + /* + * force skip lines if image too big. + */ actual_w = drm_rect_width(src) >> 16; - actual_h = drm_rect_height(src) >> 16; + if (actual_w == 3840 && is_yuv_support(fb->pixel_format)) + skip_lines = 1; + actual_h = drm_rect_height(src) >> (16 + skip_lines); act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff); dsp_info = (drm_rect_height(dest) - 1) << 16; @@ -1724,15 +1731,16 @@ static void vop_plane_atomic_update(struct drm_plane *plane, s = to_rockchip_crtc_state(crtc->state); spin_lock(&vop->reg_lock); + VOP_WIN_SET(vop, win, yuv_clip, 0); VOP_WIN_SET(vop, win, xmirror, xmirror); VOP_WIN_SET(vop, win, ymirror, ymirror); VOP_WIN_SET(vop, win, format, vop_plane_state->format); - VOP_WIN_SET(vop, win, yrgb_vir, fb->pitches[0] >> 2); + VOP_WIN_SET(vop, win, yrgb_vir, fb->pitches[0] >> (2 - skip_lines)); VOP_WIN_SET(vop, win, yrgb_mst, vop_plane_state->yrgb_mst); VOP_WIN_SET(vop, win, yrgb_mst1, vop_plane_state->yrgb_mst); if (is_yuv_support(fb->pixel_format)) { - VOP_WIN_SET(vop, win, uv_vir, fb->pitches[1] >> 2); + VOP_WIN_SET(vop, win, uv_vir, fb->pitches[1] >> (2 - skip_lines)); VOP_WIN_SET(vop, win, uv_mst, vop_plane_state->uv_mst); } VOP_WIN_SET(vop, win, fmt_10, is_yuv_10bit(fb->pixel_format)); @@ -2537,6 +2545,7 @@ static void vop_update_csc(struct drm_crtc *crtc) VOP_CTRL_SET(vop, dsp_data_swap, 0); VOP_CTRL_SET(vop, out_mode, s->output_mode); + VOP_CTRL_SET(vop, yuv_clip, 0); switch (s->bus_format) { case MEDIA_BUS_FMT_RGB565_1X16: diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h index 618de17e..391998c7 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h @@ -178,6 +178,7 @@ struct vop_ctrl { struct vop_reg dsp_lut_en; struct vop_reg out_mode; + struct vop_reg yuv_clip; struct vop_reg xmirror; struct vop_reg ymirror; @@ -409,6 +410,7 @@ struct vop_win_phy { struct vop_reg format; struct vop_reg fmt_10; struct vop_reg csc_mode; + struct vop_reg yuv_clip; struct vop_reg xmirror; struct vop_reg ymirror; struct vop_reg rb_swap; diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c index 9c96d561..aeb1c764 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c @@ -119,6 +119,7 @@ static const struct vop_win_phy rk3288_win01_data = { .fmt_10 = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 4), .csc_mode = VOP_REG_VER(RK3288_WIN0_CTRL0, 0x3, 10, 3, 2, -1), .rb_swap = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 12), + .yuv_clip = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 20), .xmirror = VOP_REG_VER(RK3368_WIN0_CTRL0, 0x1, 21, 3, 2, -1), .ymirror = VOP_REG_VER(RK3368_WIN0_CTRL0, 0x1, 22, 3, 2, -1), .act_info = VOP_REG(RK3288_WIN0_ACT_INFO, 0x1fff1fff, 0), @@ -286,6 +287,7 @@ static const struct vop_ctrl rk3288_ctrl_data = { .bcsh_color_bar = VOP_REG(RK3288_BCSH_COLOR_BAR, 0xffffff, 8), .bcsh_en = VOP_REG(RK3288_BCSH_COLOR_BAR, 0x1, 0), + .yuv_clip = VOP_REG(RK3288_DSP_CTRL0, 0x1, 21), .xmirror = VOP_REG(RK3288_DSP_CTRL0, 0x1, 22), .ymirror = VOP_REG(RK3288_DSP_CTRL0, 0x1, 23), @@ -964,6 +966,7 @@ static const struct vop_ctrl rk3328_ctrl_data = { .dsp_lut_en = VOP_REG(RK3328_DSP_CTRL1, 0x1, 0), .out_mode = VOP_REG(RK3328_DSP_CTRL0, 0xf, 0), + .yuv_clip = VOP_REG(RK3328_DSP_CTRL0, 0x1, 21), .xmirror = VOP_REG(RK3328_DSP_CTRL0, 0x1, 22), .ymirror = VOP_REG(RK3328_DSP_CTRL0, 0x1, 23), diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index 01e16e14..da06f1c1 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -625,7 +625,7 @@ struct dma_buf *tegra_gem_prime_export(struct drm_device *drm, exp_info.flags = flags; exp_info.priv = gem; - return dma_buf_export(&exp_info); + return drm_gem_dmabuf_export(drm, &exp_info); } struct drm_gem_object *tegra_gem_prime_import(struct drm_device *drm, diff --git a/drivers/gpu/drm/udl/udl_dmabuf.c b/drivers/gpu/drm/udl/udl_dmabuf.c index e2243edd..ac90ffdb 100644 --- a/drivers/gpu/drm/udl/udl_dmabuf.c +++ b/drivers/gpu/drm/udl/udl_dmabuf.c @@ -209,7 +209,7 @@ struct dma_buf *udl_gem_prime_export(struct drm_device *dev, exp_info.flags = flags; exp_info.priv = obj; - return dma_buf_export(&exp_info); + return drm_gem_dmabuf_export(dev, &exp_info); } static int udl_prime_create(struct drm_device *dev, diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c index 9c927d35..d189843f 100644 --- a/drivers/input/serio/serport.c +++ b/drivers/input/serio/serport.c @@ -71,10 +71,7 @@ static void serport_serio_close(struct serio *serio) spin_lock_irqsave(&serport->lock, flags); clear_bit(SERPORT_ACTIVE, &serport->flags); - set_bit(SERPORT_DEAD, &serport->flags); spin_unlock_irqrestore(&serport->lock, flags); - - wake_up_interruptible(&serport->wait); } /* @@ -248,6 +245,19 @@ static long serport_ldisc_compat_ioctl(struct tty_struct *tty, } #endif +static int serport_ldisc_hangup(struct tty_struct *tty) +{ + struct serport *serport = (struct serport *) tty->disc_data; + unsigned long flags; + + spin_lock_irqsave(&serport->lock, flags); + set_bit(SERPORT_DEAD, &serport->flags); + spin_unlock_irqrestore(&serport->lock, flags); + + wake_up_interruptible(&serport->wait); + return 0; +} + static void serport_ldisc_write_wakeup(struct tty_struct * tty) { struct serport *serport = (struct serport *) tty->disc_data; @@ -274,6 +284,7 @@ static struct tty_ldisc_ops serport_ldisc = { .compat_ioctl = serport_ldisc_compat_ioctl, #endif .receive_buf = serport_ldisc_receive, + .hangup = serport_ldisc_hangup, .write_wakeup = serport_ldisc_write_wakeup }; diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c index 8c75a513..4f5d3822 100644 --- a/drivers/media/cec/cec-adap.c +++ b/drivers/media/cec/cec-adap.c @@ -202,7 +202,10 @@ static void cec_queue_msg_fh(struct cec_fh *fh, const struct cec_msg *msg) { static const struct cec_event ev_lost_msgs = { .event = CEC_EVENT_LOST_MSGS, - .lost_msgs.lost_msgs = 1, + .flags = 0, + { + .lost_msgs = { 1 }, + }, }; struct cec_msg_entry *entry; @@ -1793,6 +1796,9 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg, int la_idx = cec_log_addr2idx(adap, dest_laddr); bool from_unregistered = init_laddr == 0xf; struct cec_msg tx_cec_msg = { }; +#ifdef CONFIG_MEDIA_CEC_RC + int scancode; +#endif dprintk(2, "%s: %*ph\n", __func__, msg->len, msg->msg); @@ -1888,11 +1894,9 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg, */ case 0x60: if (msg->len == 2) - rc_keydown(adap->rc, RC_TYPE_CEC, - msg->msg[2], 0); + scancode = msg->msg[2]; else - rc_keydown(adap->rc, RC_TYPE_CEC, - msg->msg[2] << 8 | msg->msg[3], 0); + scancode = msg->msg[2] << 8 | msg->msg[3]; break; /* * Other function messages that are not handled. @@ -1905,11 +1909,54 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg, */ case 0x56: case 0x57: case 0x67: case 0x68: case 0x69: case 0x6a: + scancode = -1; break; default: - rc_keydown(adap->rc, RC_TYPE_CEC, msg->msg[2], 0); + scancode = msg->msg[2]; + break; + } + + /* Was repeating, but keypress timed out */ + if (adap->rc_repeating && !adap->rc->keypressed) { + adap->rc_repeating = false; + adap->rc_last_scancode = -1; + } + /* Different keypress from last time, ends repeat mode */ + if (adap->rc_last_scancode != scancode) { + rc_keyup(adap->rc); + adap->rc_repeating = false; + } + /* We can't handle this scancode */ + if (scancode < 0) { + adap->rc_last_scancode = scancode; + break; + } + + /* Send key press */ + rc_keydown(adap->rc, RC_TYPE_CEC, scancode, 0); + + /* When in repeating mode, we're done */ + if (adap->rc_repeating) + break; + + /* + * We are not repeating, but the new scancode is + * the same as the last one, and this second key press is + * within 550 ms (the 'Follower Safety Timeout') from the + * previous key press, so we now enable the repeating mode. + */ + if (adap->rc_last_scancode == scancode && + msg->rx_ts - adap->rc_last_keypress < 550 * NSEC_PER_MSEC) { + adap->rc_repeating = true; break; } + /* + * Not in repeating mode, so avoid triggering repeat mode + * by calling keyup. + */ + rc_keyup(adap->rc); + adap->rc_last_scancode = scancode; + adap->rc_last_keypress = msg->rx_ts; #endif break; @@ -1919,6 +1966,8 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg, break; #ifdef CONFIG_MEDIA_CEC_RC rc_keyup(adap->rc); + adap->rc_repeating = false; + adap->rc_last_scancode = -1; #endif break; diff --git a/drivers/media/cec/cec-core.c b/drivers/media/cec/cec-core.c index 969f770a..65d763be 100644 --- a/drivers/media/cec/cec-core.c +++ b/drivers/media/cec/cec-core.c @@ -263,7 +263,7 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, return adap; /* Prepare the RC input device */ - adap->rc = rc_allocate_device(RC_DRIVER_SCANCODE); + adap->rc = rc_allocate_device(); if (!adap->rc) { pr_err("cec-%s: failed to allocate memory for rc_dev\n", name); @@ -283,11 +283,13 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, adap->rc->input_id.vendor = 0; adap->rc->input_id.product = 0; adap->rc->input_id.version = 1; + adap->rc->driver_type = RC_DRIVER_SCANCODE; adap->rc->driver_name = CEC_NAME; adap->rc->allowed_protocols = RC_BIT_CEC; adap->rc->priv = adap; adap->rc->map_name = RC_MAP_CEC; adap->rc->timeout = MS_TO_NS(100); + adap->rc_last_scancode = -1; #endif return adap; } @@ -319,6 +321,17 @@ int cec_register_adapter(struct cec_adapter *adap, adap->rc = NULL; return res; } + /* + * The REP_DELAY for CEC is really the time between the initial + * 'User Control Pressed' message and the second. The first + * keypress is always seen as non-repeating, the second + * (provided it has the same UI Command) will start the 'Press + * and Hold' (aka repeat) behavior. By setting REP_DELAY to the + * same value as REP_PERIOD the expected CEC behavior is + * reproduced. + */ + adap->rc->input_dev->rep[REP_DELAY] = + adap->rc->input_dev->rep[REP_PERIOD]; } #endif diff --git a/drivers/media/common/siano/smsir.h b/drivers/media/common/siano/smsir.h index fc8b7925..d9abd96e 100644 --- a/drivers/media/common/siano/smsir.h +++ b/drivers/media/common/siano/smsir.h @@ -30,8 +30,6 @@ along with this program. If not, see . #include #include -#define IR_DEFAULT_TIMEOUT 100 - struct smscore_device_t; struct ir_t { diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index fbbd3bbc..9939aed6 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-behold.o \ rc-behold-columbus.o \ rc-budget-ci-old.o \ + rc-cec.o \ rc-cinergy-1400.o \ rc-cinergy.o \ rc-delock-61959.o \ @@ -47,6 +48,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-it913x-v1.o \ rc-it913x-v2.o \ rc-kaiomy.o \ + rc-khadas.o \ rc-kworld-315u.o \ rc-kworld-pc150u.o \ rc-kworld-plus-tv-analog.o \ @@ -65,7 +67,9 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-nec-terratec-cinergy-xs.o \ rc-norwood.o \ rc-npgtech.o \ + rc-odroid.o \ rc-pctv-sedna.o \ + rc-pine64.o \ rc-pinnacle-color.o \ rc-pinnacle-grey.o \ rc-pinnacle-pctv-hd.o \ @@ -81,6 +85,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-rc6-mce.o \ rc-real-audio-220-32-keys.o \ rc-reddo.o \ + rc-roc-cc.o \ rc-snapstream-firefly.o \ rc-streamzap.o \ rc-tbs-nec.o \ @@ -99,6 +104,9 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-tt-1500.o \ rc-twinhan-dtv-cab-ci.o \ rc-twinhan1027.o \ + rc-trn9.o \ + rc-wetek-hub.o \ + rc-wetek-play-2.o \ rc-videomate-m1f.o \ rc-videomate-s350.o \ rc-videomate-tv-pvr.o \ diff --git a/drivers/media/rc/keymaps/rc-cec.c b/drivers/media/rc/keymaps/rc-cec.c new file mode 100644 index 00000000..fb0c2b1f --- /dev/null +++ b/drivers/media/rc/keymaps/rc-cec.c @@ -0,0 +1,182 @@ +/* Keytable for the CEC remote control + * + * Copyright (c) 2015 by Kamil Debski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +/* + * CEC Spec "High-Definition Multimedia Interface Specification" can be obtained + * here: http://xtreamerdev.googlecode.com/files/CEC_Specs.pdf + * The list of control codes is listed in Table 27: User Control Codes p. 95 + */ + +static struct rc_map_table cec[] = { + { 0x00, KEY_OK }, + { 0x01, KEY_UP }, + { 0x02, KEY_DOWN }, + { 0x03, KEY_LEFT }, + { 0x04, KEY_RIGHT }, + { 0x05, KEY_RIGHT_UP }, + { 0x06, KEY_RIGHT_DOWN }, + { 0x07, KEY_LEFT_UP }, + { 0x08, KEY_LEFT_DOWN }, + { 0x09, KEY_ROOT_MENU }, /* CEC Spec: Device Root Menu - see Note 2 */ + /* + * Note 2: This is the initial display that a device shows. It is + * device-dependent and can be, for example, a contents menu, setup + * menu, favorite menu or other menu. The actual menu displayed + * may also depend on the device's current state. + */ + { 0x0a, KEY_SETUP }, + { 0x0b, KEY_MENU }, /* CEC Spec: Contents Menu */ + { 0x0c, KEY_FAVORITES }, /* CEC Spec: Favorite Menu */ + { 0x0d, KEY_EXIT }, + /* 0x0e-0x0f: Reserved */ + { 0x10, KEY_MEDIA_TOP_MENU }, + { 0x11, KEY_CONTEXT_MENU }, + /* 0x12-0x1c: Reserved */ + { 0x1d, KEY_DIGITS }, /* CEC Spec: select/toggle a Number Entry Mode */ + { 0x1e, KEY_NUMERIC_11 }, + { 0x1f, KEY_NUMERIC_12 }, + /* 0x20-0x29: Keys 0 to 9 */ + { 0x20, KEY_NUMERIC_0 }, + { 0x21, KEY_NUMERIC_1 }, + { 0x22, KEY_NUMERIC_2 }, + { 0x23, KEY_NUMERIC_3 }, + { 0x24, KEY_NUMERIC_4 }, + { 0x25, KEY_NUMERIC_5 }, + { 0x26, KEY_NUMERIC_6 }, + { 0x27, KEY_NUMERIC_7 }, + { 0x28, KEY_NUMERIC_8 }, + { 0x29, KEY_NUMERIC_9 }, + { 0x2a, KEY_DOT }, + { 0x2b, KEY_ENTER }, + { 0x2c, KEY_CLEAR }, + /* 0x2d-0x2e: Reserved */ + { 0x2f, KEY_NEXT_FAVORITE }, /* CEC Spec: Next Favorite */ + { 0x30, KEY_CHANNELUP }, + { 0x31, KEY_CHANNELDOWN }, + { 0x32, KEY_PREVIOUS }, /* CEC Spec: Previous Channel */ + { 0x33, KEY_SOUND }, /* CEC Spec: Sound Select */ + { 0x34, KEY_VIDEO }, /* 0x34: CEC Spec: Input Select */ + { 0x35, KEY_INFO }, /* CEC Spec: Display Information */ + { 0x36, KEY_HELP }, + { 0x37, KEY_PAGEUP }, + { 0x38, KEY_PAGEDOWN }, + /* 0x39-0x3f: Reserved */ + { 0x40, KEY_POWER }, + { 0x41, KEY_VOLUMEUP }, + { 0x42, KEY_VOLUMEDOWN }, + { 0x43, KEY_MUTE }, + { 0x44, KEY_PLAYCD }, + { 0x45, KEY_STOPCD }, + { 0x46, KEY_PAUSECD }, + { 0x47, KEY_RECORD }, + { 0x48, KEY_REWIND }, + { 0x49, KEY_FASTFORWARD }, + { 0x4a, KEY_EJECTCD }, /* CEC Spec: Eject */ + { 0x4b, KEY_FORWARD }, + { 0x4c, KEY_BACK }, + { 0x4d, KEY_STOP_RECORD }, /* CEC Spec: Stop-Record */ + { 0x4e, KEY_PAUSE_RECORD }, /* CEC Spec: Pause-Record */ + /* 0x4f: Reserved */ + { 0x50, KEY_ANGLE }, + { 0x51, KEY_TV2 }, + { 0x52, KEY_VOD }, /* CEC Spec: Video on Demand */ + { 0x53, KEY_EPG }, + { 0x54, KEY_TIME }, /* CEC Spec: Timer */ + { 0x55, KEY_CONFIG }, + /* + * The following codes are hard to implement at this moment, as they + * carry an additional additional argument. Most likely changes to RC + * framework are necessary. + * For now they are interpreted by the CEC framework as non keycodes + * and are passed as messages enabling user application to parse them. + */ + /* 0x56: CEC Spec: Select Broadcast Type */ + /* 0x57: CEC Spec: Select Sound presentation */ + { 0x58, KEY_AUDIO_DESC }, /* CEC 2.0 and up */ + { 0x59, KEY_WWW }, /* CEC 2.0 and up */ + { 0x5a, KEY_3D_MODE }, /* CEC 2.0 and up */ + /* 0x5b-0x5f: Reserved */ + { 0x60, KEY_PLAYCD }, /* CEC Spec: Play Function */ + { 0x6005, KEY_FASTFORWARD }, + { 0x6006, KEY_FASTFORWARD }, + { 0x6007, KEY_FASTFORWARD }, + { 0x6015, KEY_SLOW }, + { 0x6016, KEY_SLOW }, + { 0x6017, KEY_SLOW }, + { 0x6009, KEY_FASTREVERSE }, + { 0x600a, KEY_FASTREVERSE }, + { 0x600b, KEY_FASTREVERSE }, + { 0x6019, KEY_SLOWREVERSE }, + { 0x601a, KEY_SLOWREVERSE }, + { 0x601b, KEY_SLOWREVERSE }, + { 0x6020, KEY_REWIND }, + { 0x6024, KEY_PLAYCD }, + { 0x6025, KEY_PAUSECD }, + { 0x61, KEY_PLAYPAUSE }, /* CEC Spec: Pause-Play Function */ + { 0x62, KEY_RECORD }, /* Spec: Record Function */ + { 0x63, KEY_PAUSE_RECORD }, /* CEC Spec: Pause-Record Function */ + { 0x64, KEY_STOPCD }, /* CEC Spec: Stop Function */ + { 0x65, KEY_MUTE }, /* CEC Spec: Mute Function */ + { 0x66, KEY_UNMUTE }, /* CEC Spec: Restore the volume */ + /* + * The following codes are hard to implement at this moment, as they + * carry an additional additional argument. Most likely changes to RC + * framework are necessary. + * For now they are interpreted by the CEC framework as non keycodes + * and are passed as messages enabling user application to parse them. + */ + /* 0x67: CEC Spec: Tune Function */ + /* 0x68: CEC Spec: Seleect Media Function */ + /* 0x69: CEC Spec: Select A/V Input Function */ + /* 0x6a: CEC Spec: Select Audio Input Function */ + { 0x6b, KEY_POWER }, /* CEC Spec: Power Toggle Function */ + { 0x6c, KEY_SLEEP }, /* CEC Spec: Power Off Function */ + { 0x6d, KEY_WAKEUP }, /* CEC Spec: Power On Function */ + /* 0x6e-0x70: Reserved */ + { 0x71, KEY_BLUE }, /* CEC Spec: F1 (Blue) */ + { 0x72, KEY_RED }, /* CEC Spec: F2 (Red) */ + { 0x73, KEY_GREEN }, /* CEC Spec: F3 (Green) */ + { 0x74, KEY_YELLOW }, /* CEC Spec: F4 (Yellow) */ + { 0x75, KEY_F5 }, + { 0x76, KEY_DATA }, /* CEC Spec: Data - see Note 3 */ + /* + * Note 3: This is used, for example, to enter or leave a digital TV + * data broadcast application. + */ + /* 0x77-0xff: Reserved */ +}; + +static struct rc_map_list cec_map = { + .map = { + .scan = cec, + .size = ARRAY_SIZE(cec), + .rc_type = RC_TYPE_CEC, + .name = RC_MAP_CEC, + } +}; + +static int __init init_rc_map_cec(void) +{ + return rc_map_register(&cec_map); +} + +static void __exit exit_rc_map_cec(void) +{ + rc_map_unregister(&cec_map); +} + +subsys_initcall(init_rc_map_cec); +module_exit(exit_rc_map_cec); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Kamil Debski"); diff --git a/drivers/media/rc/keymaps/rc-khadas.c b/drivers/media/rc/keymaps/rc-khadas.c new file mode 100644 index 00000000..492368db --- /dev/null +++ b/drivers/media/rc/keymaps/rc-khadas.c @@ -0,0 +1,52 @@ +/* Keytable for Khadas IR Remote Controller + * + * Copyright (c) 2018 Shenzhen Wesion Technology Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +static struct rc_map_table khadas[] = { + { 0x14, KEY_POWER }, + { 0x07, KEY_OK }, + { 0x03, KEY_UP }, + { 0x02, KEY_DOWN }, + { 0x0e, KEY_LEFT }, + { 0x1a, KEY_RIGHT }, + { 0x13, KEY_MENU }, + { 0x01, KEY_BACK }, + { 0x0b, KEY_VOLUMEUP }, + { 0x58, KEY_VOLUMEDOWN }, + { 0x48, KEY_HOME }, + { 0x5b, KEY_CONTEXT_MENU }, +}; + +static struct rc_map_list khadas_map = { + .map = { + .scan = khadas, + .size = ARRAY_SIZE(khadas), + .rc_type = RC_TYPE_NEC, + .name = RC_MAP_KHADAS, + } +}; + +static int __init init_rc_map_khadas(void) +{ + return rc_map_register(&khadas_map); +} + +static void __exit exit_rc_map_khadas(void) +{ + rc_map_unregister(&khadas_map); +} + +module_init(init_rc_map_khadas) +module_exit(exit_rc_map_khadas) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Khadas"); diff --git a/drivers/media/rc/keymaps/rc-odroid.c b/drivers/media/rc/keymaps/rc-odroid.c new file mode 100644 index 00000000..52089f0b --- /dev/null +++ b/drivers/media/rc/keymaps/rc-odroid.c @@ -0,0 +1,52 @@ +/* Keytable for ODROID IR Remote Controller + * + * Copyright (c) 2017 Hardkernel co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +static struct rc_map_table odroid[] = { + { 0xb2dc, KEY_POWER }, + { 0xb288, KEY_MUTE }, + { 0xb282, KEY_HOME }, + { 0xb2ce, KEY_OK }, + { 0xb2ca, KEY_UP }, + { 0xb299, KEY_LEFT }, + { 0xb2c1, KEY_RIGHT }, + { 0xb2d2, KEY_DOWN }, + { 0xb2c5, KEY_MENU }, + { 0xb29a, KEY_BACK }, + { 0xb281, KEY_VOLUMEDOWN }, + { 0xb280, KEY_VOLUMEUP }, +}; + +static struct rc_map_list odroid_map = { + .map = { + .scan = odroid, + .size = ARRAY_SIZE(odroid), + .rc_type = RC_TYPE_NEC, + .name = RC_MAP_ODROID, + } +}; + +static int __init init_rc_map_odroid(void) +{ + return rc_map_register(&odroid_map); +} + +static void __exit exit_rc_map_odroid(void) +{ + rc_map_unregister(&odroid_map); +} + +module_init(init_rc_map_odroid) +module_exit(exit_rc_map_odroid) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Hardkernel co., Ltd."); diff --git a/drivers/media/rc/keymaps/rc-pine64.c b/drivers/media/rc/keymaps/rc-pine64.c new file mode 100644 index 00000000..bdf3975e --- /dev/null +++ b/drivers/media/rc/keymaps/rc-pine64.c @@ -0,0 +1,65 @@ +/* Keytable for Pine64 IR Remote Controller + * + * Copyright (c) 2017 PINE64 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +static struct rc_map_table pine64[] = { + { 0x404000, KEY_NUMERIC_0 }, + { 0x404001, KEY_NUMERIC_1 }, + { 0x404002, KEY_NUMERIC_2 }, + { 0x404003, KEY_NUMERIC_3 }, + { 0x404004, KEY_NUMERIC_4 }, + { 0x404005, KEY_NUMERIC_5 }, + { 0x404006, KEY_NUMERIC_6 }, + { 0x404007, KEY_NUMERIC_7 }, + { 0x404008, KEY_NUMERIC_8 }, + { 0x404009, KEY_NUMERIC_9 }, + { 0x40400a, KEY_MUTE }, + { 0x40400b, KEY_UP }, + { 0x40400c, KEY_BACKSPACE }, + { 0x40400d, KEY_OK }, + { 0x40400e, KEY_DOWN }, + { 0x404010, KEY_LEFT }, + { 0x404011, KEY_RIGHT }, + { 0x404017, KEY_VOLUMEDOWN }, + { 0x404018, KEY_VOLUMEUP }, + { 0x40401a, KEY_HOME }, + { 0x40401d, KEY_MENU }, + { 0x40401f, KEY_WWW }, + { 0x404045, KEY_BACK }, + { 0x404047, KEY_INFO }, + { 0x40404d, KEY_POWER }, +}; + +static struct rc_map_list pine64_map = { + .map = { + .scan = pine64, + .size = ARRAY_SIZE(pine64), + .rc_type = RC_TYPE_NEC, + .name = RC_MAP_PINE64, + } +}; + +static int __init init_rc_map_pine64(void) +{ + return rc_map_register(&pine64_map); +} + +static void __exit exit_rc_map_pine64(void) +{ + rc_map_unregister(&pine64_map); +} + +module_init(init_rc_map_pine64) +module_exit(exit_rc_map_pine64) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("PINE64"); diff --git a/drivers/media/rc/keymaps/rc-roc-cc.c b/drivers/media/rc/keymaps/rc-roc-cc.c new file mode 100644 index 00000000..3a2a255d --- /dev/null +++ b/drivers/media/rc/keymaps/rc-roc-cc.c @@ -0,0 +1,52 @@ +/* Keytable for ROC-RK3328-CC IR Remote Controller + * + * Copyright (c) 2017 ROC-RK3328-CC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +static struct rc_map_table roc_cc[] = { + { 0x28d7, KEY_POWER }, + { 0xc837, KEY_MUTE }, + { 0xe01f, KEY_ENTER}, + { 0xc03f, KEY_UP }, + { 0x40bf, KEY_DOWN }, + { 0x708f, KEY_LEFT }, + { 0x58a7, KEY_RIGHT }, + { 0x1ae5, KEY_VOLUMEDOWN }, + { 0xd02f, KEY_VOLUMEUP }, + { 0x3ac5, KEY_WWW }, + { 0x807f, KEY_BACK }, + { 0x12ed, KEY_HOME }, +}; + +static struct rc_map_list roc_cc_map = { + .map = { + .scan = roc_cc, + .size = ARRAY_SIZE(roc_cc), + .rc_type = RC_TYPE_NEC, + .name = RC_MAP_ROC_CC, + } +}; + +static int __init init_rc_map_roc_cc(void) +{ + return rc_map_register(&roc_cc_map); +} + +static void __exit exit_rc_map_roc_cc(void) +{ + rc_map_unregister(&roc_cc_map); +} + +module_init(init_rc_map_roc_cc) +module_exit(exit_rc_map_roc_cc) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("ROC-RK3328-CC"); diff --git a/drivers/media/rc/keymaps/rc-trn9.c b/drivers/media/rc/keymaps/rc-trn9.c new file mode 100644 index 00000000..f81bc3a4 --- /dev/null +++ b/drivers/media/rc/keymaps/rc-trn9.c @@ -0,0 +1,52 @@ +/* Keytable for T-Chip TRN9 IR Remote Controller + * + * Copyright (c) 2018 Omegamoon + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +static struct rc_map_table trn9[] = { + { 0x0014, KEY_POWER }, + { 0x0013, KEY_MENU }, + { 0x0003, KEY_UP }, + { 0x0002, KEY_DOWN }, + { 0x000e, KEY_LEFT }, + { 0x001a, KEY_RIGHT }, + { 0x0007, KEY_OK }, + { 0x0058, KEY_VOLUMEDOWN }, + { 0x005c, KEY_MUTE }, + { 0x000b, KEY_VOLUMEUP }, + { 0x0001, KEY_BACK }, + { 0x0048, KEY_HOME }, +}; + +static struct rc_map_list trn9_map = { + .map = { + .scan = trn9, + .size = ARRAY_SIZE(trn9), + .rc_type = RC_TYPE_NEC, + .name = RC_MAP_TRN9, + } +}; + +static int __init init_rc_map_trn9(void) +{ + return rc_map_register(&trn9_map); +} + +static void __exit exit_rc_map_trn9(void) +{ + rc_map_unregister(&trn9_map); +} + +module_init(init_rc_map_trn9) +module_exit(exit_rc_map_trn9) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Omegamoon"); diff --git a/drivers/media/rc/keymaps/rc-wetek-hub.c b/drivers/media/rc/keymaps/rc-wetek-hub.c new file mode 100644 index 00000000..0955ecfc --- /dev/null +++ b/drivers/media/rc/keymaps/rc-wetek-hub.c @@ -0,0 +1,52 @@ +/* Keytable for WeTek Hub Remote Controller + * + * Copyright (c) 2017 WeTek + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +static struct rc_map_table wetek_hub[] = { + { 0x77f1, KEY_POWER }, + { 0x77f2, KEY_HOME }, + { 0x77f3, KEY_MUTE }, + { 0x77f4, KEY_UP }, + { 0x77f5, KEY_DOWN }, + { 0x77f6, KEY_LEFT }, + { 0x77f7, KEY_RIGHT }, + { 0x77f8, KEY_OK }, + { 0x77f9, KEY_BACK }, + { 0x77fa, KEY_MENU }, + { 0x77fb, KEY_VOLUMEUP }, + { 0x77fc, KEY_VOLUMEDOWN }, +}; + +static struct rc_map_list wetek_hub_map = { + .map = { + .scan = wetek_hub, + .size = ARRAY_SIZE(wetek_hub), + .rc_type = RC_TYPE_NEC, + .name = RC_MAP_WETEK_HUB, + } +}; + +static int __init init_rc_map_wetek_hub(void) +{ + return rc_map_register(&wetek_hub_map); +} + +static void __exit exit_rc_map_wetek_hub(void) +{ + rc_map_unregister(&wetek_hub_map); +} + +module_init(init_rc_map_wetek_hub) +module_exit(exit_rc_map_wetek_hub) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("WeTek"); diff --git a/drivers/media/rc/keymaps/rc-wetek-play-2.c b/drivers/media/rc/keymaps/rc-wetek-play-2.c new file mode 100644 index 00000000..37586ced --- /dev/null +++ b/drivers/media/rc/keymaps/rc-wetek-play-2.c @@ -0,0 +1,83 @@ +/* Keytable for WeTek Play 2 Remote Controller + * + * Copyright (c) 2017 WeTek + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +static struct rc_map_table wetek_play_2[] = { + { 0x5e5f02, KEY_POWER }, + { 0x5e5f46, KEY_POWER2 }, + { 0x5e5f10, KEY_MUTE }, + { 0x5e5f22, KEY_NUMERIC_1 }, + { 0x5e5f23, KEY_NUMERIC_2 }, + { 0x5e5f24, KEY_NUMERIC_3 }, + { 0x5e5f25, KEY_NUMERIC_4 }, + { 0x5e5f26, KEY_NUMERIC_5 }, + { 0x5e5f27, KEY_NUMERIC_6 }, + { 0x5e5f28, KEY_NUMERIC_7 }, + { 0x5e5f29, KEY_NUMERIC_8 }, + { 0x5e5f30, KEY_NUMERIC_9 }, + { 0x5e5f71, KEY_BACKSPACE }, + { 0x5e5f21, KEY_NUMERIC_0 }, + { 0x5e5f72, KEY_CAPSLOCK }, + { 0x5e5f03, KEY_HOME }, + { 0x5e5f48, KEY_MENU }, + { 0x5e5f61, KEY_BACK }, + { 0x5e5f83, KEY_INFO }, + { 0x5e5f84, KEY_COMPOSE }, + { 0x5e5f77, KEY_HELP }, + { 0x5e5f50, KEY_UP }, + { 0x5e5f4b, KEY_DOWN }, + { 0x5e5f4c, KEY_LEFT }, + { 0x5e5f4d, KEY_RIGHT }, + { 0x5e5f47, KEY_OK }, + { 0x5e5f44, KEY_VOLUMEUP }, + { 0x5e5f43, KEY_VOLUMEDOWN }, + { 0x5e5f41, KEY_CHANNELUP }, + { 0x5e5f42, KEY_CHANNELDOWN }, + { 0x5e5f4f, KEY_ZENKAKUHANKAKU }, + { 0x5e5f82, KEY_TEXT }, + { 0x5e5f73, KEY_RED }, + { 0x5e5f74, KEY_GREEN }, + { 0x5e5f75, KEY_YELLOW }, + { 0x5e5f76, KEY_BLUE }, + { 0x5e5f67, KEY_PREVIOUS }, + { 0x5e5f79, KEY_REWIND }, + { 0x5e5f80, KEY_FASTFORWARD }, + { 0x5e5f81, KEY_NEXT }, + { 0x5e5f04, KEY_RECORD }, + { 0x5e5f2c, KEY_PLAYPAUSE }, + { 0x5e5f2b, KEY_STOP }, +}; + +static struct rc_map_list wetek_play_2_map = { + .map = { + .scan = wetek_play_2, + .size = ARRAY_SIZE(wetek_play_2), + .rc_type = RC_TYPE_NEC, + .name = RC_MAP_WETEK_PLAY_2, + } +}; + +static int __init init_rc_map_wetek_play_2(void) +{ + return rc_map_register(&wetek_play_2_map); +} + +static void __exit exit_rc_map_wetek_play_2(void) +{ + rc_map_unregister(&wetek_play_2_map); +} + +module_init(init_rc_map_wetek_play_2) +module_exit(exit_rc_map_wetek_play_2) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("WeTek"); diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 3f0f71ad..a639ea65 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -801,6 +801,7 @@ static struct { { RC_BIT_SHARP, "sharp" }, { RC_BIT_MCE_KBD, "mce_kbd" }, { RC_BIT_XMP, "xmp" }, + { RC_BIT_CEC, "cec" }, }; /** diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig index 7496f332..b24e753c 100644 --- a/drivers/media/usb/Kconfig +++ b/drivers/media/usb/Kconfig @@ -60,5 +60,11 @@ source "drivers/media/usb/hackrf/Kconfig" source "drivers/media/usb/msi2500/Kconfig" endif +if MEDIA_CEC_SUPPORT + comment "USB HDMI CEC adapters" +source "drivers/media/usb/pulse8-cec/Kconfig" +source "drivers/media/usb/rainshadow-cec/Kconfig" +endif + endif #MEDIA_USB_SUPPORT endif #USB diff --git a/drivers/media/usb/Makefile b/drivers/media/usb/Makefile index 8874ba77..738b993e 100644 --- a/drivers/media/usb/Makefile +++ b/drivers/media/usb/Makefile @@ -24,3 +24,5 @@ obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ obj-$(CONFIG_VIDEO_USBTV) += usbtv/ obj-$(CONFIG_VIDEO_GO7007) += go7007/ obj-$(CONFIG_DVB_AS102) += as102/ +obj-$(CONFIG_USB_PULSE8_CEC) += pulse8-cec/ +obj-$(CONFIG_USB_RAINSHADOW_CEC) += rainshadow-cec/ diff --git a/drivers/media/usb/pulse8-cec/Kconfig b/drivers/media/usb/pulse8-cec/Kconfig new file mode 100644 index 00000000..18ead448 --- /dev/null +++ b/drivers/media/usb/pulse8-cec/Kconfig @@ -0,0 +1,11 @@ +config USB_PULSE8_CEC + tristate "Pulse Eight HDMI CEC" + depends on USB_ACM + select CEC_CORE + select SERIO + select SERIO_SERPORT + ---help--- + This is a cec driver for the Pulse Eight HDMI CEC device. + + To compile this driver as a module, choose M here: the + module will be called pulse8-cec. diff --git a/drivers/media/usb/pulse8-cec/Makefile b/drivers/media/usb/pulse8-cec/Makefile new file mode 100644 index 00000000..9800690b --- /dev/null +++ b/drivers/media/usb/pulse8-cec/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_USB_PULSE8_CEC) += pulse8-cec.o diff --git a/drivers/media/usb/pulse8-cec/pulse8-cec.c b/drivers/media/usb/pulse8-cec/pulse8-cec.c new file mode 100644 index 00000000..50146f26 --- /dev/null +++ b/drivers/media/usb/pulse8-cec/pulse8-cec.c @@ -0,0 +1,757 @@ +/* + * Pulse Eight HDMI CEC driver + * + * Copyright 2016 Hans Verkuil ["Power On"], ["Power] or ["Power Toggle"], or if it + * receives with its own physical address. It also does this + * if it receives [0x03 0x00] from an LG TV. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +MODULE_AUTHOR("Hans Verkuil "); +MODULE_DESCRIPTION("Pulse Eight HDMI CEC driver"); +MODULE_LICENSE("GPL"); + +static int debug; +static int persistent_config; +module_param(debug, int, 0644); +module_param(persistent_config, int, 0644); +MODULE_PARM_DESC(debug, "debug level (0-1)"); +MODULE_PARM_DESC(persistent_config, "read config from persistent memory (0-1)"); + +enum pulse8_msgcodes { + MSGCODE_NOTHING = 0, + MSGCODE_PING, + MSGCODE_TIMEOUT_ERROR, + MSGCODE_HIGH_ERROR, + MSGCODE_LOW_ERROR, + MSGCODE_FRAME_START, + MSGCODE_FRAME_DATA, + MSGCODE_RECEIVE_FAILED, + MSGCODE_COMMAND_ACCEPTED, /* 0x08 */ + MSGCODE_COMMAND_REJECTED, + MSGCODE_SET_ACK_MASK, + MSGCODE_TRANSMIT, + MSGCODE_TRANSMIT_EOM, + MSGCODE_TRANSMIT_IDLETIME, + MSGCODE_TRANSMIT_ACK_POLARITY, + MSGCODE_TRANSMIT_LINE_TIMEOUT, + MSGCODE_TRANSMIT_SUCCEEDED, /* 0x10 */ + MSGCODE_TRANSMIT_FAILED_LINE, + MSGCODE_TRANSMIT_FAILED_ACK, + MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA, + MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE, + MSGCODE_FIRMWARE_VERSION, + MSGCODE_START_BOOTLOADER, + MSGCODE_GET_BUILDDATE, + MSGCODE_SET_CONTROLLED, /* 0x18 */ + MSGCODE_GET_AUTO_ENABLED, + MSGCODE_SET_AUTO_ENABLED, + MSGCODE_GET_DEFAULT_LOGICAL_ADDRESS, + MSGCODE_SET_DEFAULT_LOGICAL_ADDRESS, + MSGCODE_GET_LOGICAL_ADDRESS_MASK, + MSGCODE_SET_LOGICAL_ADDRESS_MASK, + MSGCODE_GET_PHYSICAL_ADDRESS, + MSGCODE_SET_PHYSICAL_ADDRESS, /* 0x20 */ + MSGCODE_GET_DEVICE_TYPE, + MSGCODE_SET_DEVICE_TYPE, + MSGCODE_GET_HDMI_VERSION, + MSGCODE_SET_HDMI_VERSION, + MSGCODE_GET_OSD_NAME, + MSGCODE_SET_OSD_NAME, + MSGCODE_WRITE_EEPROM, + MSGCODE_GET_ADAPTER_TYPE, /* 0x28 */ + MSGCODE_SET_ACTIVE_SOURCE, + + MSGCODE_FRAME_EOM = 0x80, + MSGCODE_FRAME_ACK = 0x40, +}; + +#define MSGSTART 0xff +#define MSGEND 0xfe +#define MSGESC 0xfd +#define MSGOFFSET 3 + +#define DATA_SIZE 256 + +#define PING_PERIOD (15 * HZ) + +struct pulse8 { + struct device *dev; + struct serio *serio; + struct cec_adapter *adap; + unsigned int vers; + struct completion cmd_done; + struct work_struct work; + struct delayed_work ping_eeprom_work; + struct cec_msg rx_msg; + u8 data[DATA_SIZE]; + unsigned int len; + u8 buf[DATA_SIZE]; + unsigned int idx; + bool escape; + bool started; + struct mutex config_lock; + struct mutex write_lock; + bool config_pending; + bool restoring_config; + bool autonomous; +}; + +static void pulse8_ping_eeprom_work_handler(struct work_struct *work); + +static void pulse8_irq_work_handler(struct work_struct *work) +{ + struct pulse8 *pulse8 = + container_of(work, struct pulse8, work); + + switch (pulse8->data[0] & 0x3f) { + case MSGCODE_FRAME_DATA: + cec_received_msg(pulse8->adap, &pulse8->rx_msg); + break; + case MSGCODE_TRANSMIT_SUCCEEDED: + cec_transmit_attempt_done(pulse8->adap, CEC_TX_STATUS_OK); + break; + case MSGCODE_TRANSMIT_FAILED_ACK: + cec_transmit_attempt_done(pulse8->adap, CEC_TX_STATUS_NACK); + break; + case MSGCODE_TRANSMIT_FAILED_LINE: + case MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA: + case MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE: + cec_transmit_attempt_done(pulse8->adap, CEC_TX_STATUS_ERROR); + break; + } +} + +static irqreturn_t pulse8_interrupt(struct serio *serio, unsigned char data, + unsigned int flags) +{ + struct pulse8 *pulse8 = serio_get_drvdata(serio); + + if (!pulse8->started && data != MSGSTART) + return IRQ_HANDLED; + if (data == MSGESC) { + pulse8->escape = true; + return IRQ_HANDLED; + } + if (pulse8->escape) { + data += MSGOFFSET; + pulse8->escape = false; + } else if (data == MSGEND) { + struct cec_msg *msg = &pulse8->rx_msg; + + if (debug) + dev_info(pulse8->dev, "received: %*ph\n", + pulse8->idx, pulse8->buf); + pulse8->data[0] = pulse8->buf[0]; + switch (pulse8->buf[0] & 0x3f) { + case MSGCODE_FRAME_START: + msg->len = 1; + msg->msg[0] = pulse8->buf[1]; + break; + case MSGCODE_FRAME_DATA: + if (msg->len == CEC_MAX_MSG_SIZE) + break; + msg->msg[msg->len++] = pulse8->buf[1]; + if (pulse8->buf[0] & MSGCODE_FRAME_EOM) + schedule_work(&pulse8->work); + break; + case MSGCODE_TRANSMIT_SUCCEEDED: + case MSGCODE_TRANSMIT_FAILED_LINE: + case MSGCODE_TRANSMIT_FAILED_ACK: + case MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA: + case MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE: + schedule_work(&pulse8->work); + break; + case MSGCODE_HIGH_ERROR: + case MSGCODE_LOW_ERROR: + case MSGCODE_RECEIVE_FAILED: + case MSGCODE_TIMEOUT_ERROR: + break; + case MSGCODE_COMMAND_ACCEPTED: + case MSGCODE_COMMAND_REJECTED: + default: + if (pulse8->idx == 0) + break; + memcpy(pulse8->data, pulse8->buf, pulse8->idx); + pulse8->len = pulse8->idx; + complete(&pulse8->cmd_done); + break; + } + pulse8->idx = 0; + pulse8->started = false; + return IRQ_HANDLED; + } else if (data == MSGSTART) { + pulse8->idx = 0; + pulse8->started = true; + return IRQ_HANDLED; + } + + if (pulse8->idx >= DATA_SIZE) { + dev_dbg(pulse8->dev, + "throwing away %d bytes of garbage\n", pulse8->idx); + pulse8->idx = 0; + } + pulse8->buf[pulse8->idx++] = data; + return IRQ_HANDLED; +} + +static void pulse8_disconnect(struct serio *serio) +{ + struct pulse8 *pulse8 = serio_get_drvdata(serio); + + cec_unregister_adapter(pulse8->adap); + cancel_delayed_work_sync(&pulse8->ping_eeprom_work); + dev_info(&serio->dev, "disconnected\n"); + serio_close(serio); + serio_set_drvdata(serio, NULL); + kfree(pulse8); +} + +static int pulse8_send(struct serio *serio, const u8 *command, u8 cmd_len) +{ + int err = 0; + + err = serio_write(serio, MSGSTART); + if (err) + return err; + for (; !err && cmd_len; command++, cmd_len--) { + if (*command >= MSGESC) { + err = serio_write(serio, MSGESC); + if (!err) + err = serio_write(serio, *command - MSGOFFSET); + } else { + err = serio_write(serio, *command); + } + } + if (!err) + err = serio_write(serio, MSGEND); + + return err; +} + +static int pulse8_send_and_wait_once(struct pulse8 *pulse8, + const u8 *cmd, u8 cmd_len, + u8 response, u8 size) +{ + int err; + + /*dev_info(pulse8->dev, "transmit: %*ph\n", cmd_len, cmd);*/ + init_completion(&pulse8->cmd_done); + + err = pulse8_send(pulse8->serio, cmd, cmd_len); + if (err) + return err; + + if (!wait_for_completion_timeout(&pulse8->cmd_done, HZ)) + return -ETIMEDOUT; + if ((pulse8->data[0] & 0x3f) == MSGCODE_COMMAND_REJECTED && + cmd[0] != MSGCODE_SET_CONTROLLED && + cmd[0] != MSGCODE_SET_AUTO_ENABLED && + cmd[0] != MSGCODE_GET_BUILDDATE) + return -ENOTTY; + if (response && + ((pulse8->data[0] & 0x3f) != response || pulse8->len < size + 1)) { + dev_info(pulse8->dev, "transmit: failed %02x\n", + pulse8->data[0] & 0x3f); + return -EIO; + } + return 0; +} + +static int pulse8_send_and_wait(struct pulse8 *pulse8, + const u8 *cmd, u8 cmd_len, u8 response, u8 size) +{ + u8 cmd_sc[2]; + int err; + + mutex_lock(&pulse8->write_lock); + err = pulse8_send_and_wait_once(pulse8, cmd, cmd_len, response, size); + + if (err == -ENOTTY) { + cmd_sc[0] = MSGCODE_SET_CONTROLLED; + cmd_sc[1] = 1; + err = pulse8_send_and_wait_once(pulse8, cmd_sc, 2, + MSGCODE_COMMAND_ACCEPTED, 1); + if (err) + goto unlock; + err = pulse8_send_and_wait_once(pulse8, cmd, cmd_len, + response, size); + } + +unlock: + mutex_unlock(&pulse8->write_lock); + return err == -ENOTTY ? -EIO : err; +} + +static int pulse8_setup(struct pulse8 *pulse8, struct serio *serio, + struct cec_log_addrs *log_addrs, u16 *pa) +{ + u8 *data = pulse8->data + 1; + u8 cmd[2]; + int err; + struct tm tm; + time_t date; + + pulse8->vers = 0; + + cmd[0] = MSGCODE_FIRMWARE_VERSION; + err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 2); + if (err) + return err; + pulse8->vers = (data[0] << 8) | data[1]; + dev_info(pulse8->dev, "Firmware version %04x\n", pulse8->vers); + if (pulse8->vers < 2) { + *pa = CEC_PHYS_ADDR_INVALID; + return 0; + } + + cmd[0] = MSGCODE_GET_BUILDDATE; + err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 4); + if (err) + return err; + date = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; + time_to_tm(date, 0, &tm); + dev_info(pulse8->dev, "Firmware build date %04ld.%02d.%02d %02d:%02d:%02d\n", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + + dev_dbg(pulse8->dev, "Persistent config:\n"); + cmd[0] = MSGCODE_GET_AUTO_ENABLED; + err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1); + if (err) + return err; + pulse8->autonomous = data[0]; + dev_dbg(pulse8->dev, "Autonomous mode: %s", + data[0] ? "on" : "off"); + + cmd[0] = MSGCODE_GET_DEVICE_TYPE; + err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1); + if (err) + return err; + log_addrs->primary_device_type[0] = data[0]; + dev_dbg(pulse8->dev, "Primary device type: %d\n", data[0]); + switch (log_addrs->primary_device_type[0]) { + case CEC_OP_PRIM_DEVTYPE_TV: + log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_TV; + log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_TV; + break; + case CEC_OP_PRIM_DEVTYPE_RECORD: + log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_RECORD; + log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_RECORD; + break; + case CEC_OP_PRIM_DEVTYPE_TUNER: + log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_TUNER; + log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_TUNER; + break; + case CEC_OP_PRIM_DEVTYPE_PLAYBACK: + log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_PLAYBACK; + log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_PLAYBACK; + break; + case CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM: + log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_PLAYBACK; + log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM; + break; + case CEC_OP_PRIM_DEVTYPE_SWITCH: + log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_UNREGISTERED; + log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_SWITCH; + break; + case CEC_OP_PRIM_DEVTYPE_PROCESSOR: + log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_SPECIFIC; + log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_SWITCH; + break; + default: + log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_UNREGISTERED; + log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_SWITCH; + dev_info(pulse8->dev, "Unknown Primary Device Type: %d\n", + log_addrs->primary_device_type[0]); + break; + } + + cmd[0] = MSGCODE_GET_LOGICAL_ADDRESS_MASK; + err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 2); + if (err) + return err; + log_addrs->log_addr_mask = (data[0] << 8) | data[1]; + dev_dbg(pulse8->dev, "Logical address ACK mask: %x\n", + log_addrs->log_addr_mask); + if (log_addrs->log_addr_mask) + log_addrs->num_log_addrs = 1; + + cmd[0] = MSGCODE_GET_PHYSICAL_ADDRESS; + err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1); + if (err) + return err; + *pa = (data[0] << 8) | data[1]; + dev_dbg(pulse8->dev, "Physical address: %x.%x.%x.%x\n", + cec_phys_addr_exp(*pa)); + + cmd[0] = MSGCODE_GET_HDMI_VERSION; + err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1); + if (err) + return err; + log_addrs->cec_version = data[0]; + dev_dbg(pulse8->dev, "CEC version: %d\n", log_addrs->cec_version); + + cmd[0] = MSGCODE_GET_OSD_NAME; + err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 0); + if (err) + return err; + strncpy(log_addrs->osd_name, data, 13); + dev_dbg(pulse8->dev, "OSD name: %s\n", log_addrs->osd_name); + + return 0; +} + +static int pulse8_apply_persistent_config(struct pulse8 *pulse8, + struct cec_log_addrs *log_addrs, + u16 pa) +{ + int err; + + err = cec_s_log_addrs(pulse8->adap, log_addrs, false); + if (err) + return err; + + cec_s_phys_addr(pulse8->adap, pa, false); + + return 0; +} + +static int pulse8_cec_adap_enable(struct cec_adapter *adap, bool enable) +{ + struct pulse8 *pulse8 = cec_get_drvdata(adap); + u8 cmd[16]; + int err; + + cmd[0] = MSGCODE_SET_CONTROLLED; + cmd[1] = enable; + err = pulse8_send_and_wait(pulse8, cmd, 2, + MSGCODE_COMMAND_ACCEPTED, 1); + return enable ? err : 0; +} + +static int pulse8_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) +{ + struct pulse8 *pulse8 = cec_get_drvdata(adap); + u16 mask = 0; + u16 pa = adap->phys_addr; + u8 cmd[16]; + int err = 0; + + mutex_lock(&pulse8->config_lock); + if (log_addr != CEC_LOG_ADDR_INVALID) + mask = 1 << log_addr; + cmd[0] = MSGCODE_SET_ACK_MASK; + cmd[1] = mask >> 8; + cmd[2] = mask & 0xff; + err = pulse8_send_and_wait(pulse8, cmd, 3, + MSGCODE_COMMAND_ACCEPTED, 0); + if ((err && mask != 0) || pulse8->restoring_config) + goto unlock; + + cmd[0] = MSGCODE_SET_AUTO_ENABLED; + cmd[1] = log_addr == CEC_LOG_ADDR_INVALID ? 0 : 1; + err = pulse8_send_and_wait(pulse8, cmd, 2, + MSGCODE_COMMAND_ACCEPTED, 0); + if (err) + goto unlock; + pulse8->autonomous = cmd[1]; + if (log_addr == CEC_LOG_ADDR_INVALID) + goto unlock; + + cmd[0] = MSGCODE_SET_DEVICE_TYPE; + cmd[1] = adap->log_addrs.primary_device_type[0]; + err = pulse8_send_and_wait(pulse8, cmd, 2, + MSGCODE_COMMAND_ACCEPTED, 0); + if (err) + goto unlock; + + switch (adap->log_addrs.primary_device_type[0]) { + case CEC_OP_PRIM_DEVTYPE_TV: + mask = CEC_LOG_ADDR_MASK_TV; + break; + case CEC_OP_PRIM_DEVTYPE_RECORD: + mask = CEC_LOG_ADDR_MASK_RECORD; + break; + case CEC_OP_PRIM_DEVTYPE_TUNER: + mask = CEC_LOG_ADDR_MASK_TUNER; + break; + case CEC_OP_PRIM_DEVTYPE_PLAYBACK: + mask = CEC_LOG_ADDR_MASK_PLAYBACK; + break; + case CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM: + mask = CEC_LOG_ADDR_MASK_AUDIOSYSTEM; + break; + case CEC_OP_PRIM_DEVTYPE_SWITCH: + mask = CEC_LOG_ADDR_MASK_UNREGISTERED; + break; + case CEC_OP_PRIM_DEVTYPE_PROCESSOR: + mask = CEC_LOG_ADDR_MASK_SPECIFIC; + break; + default: + mask = 0; + break; + } + cmd[0] = MSGCODE_SET_LOGICAL_ADDRESS_MASK; + cmd[1] = mask >> 8; + cmd[2] = mask & 0xff; + err = pulse8_send_and_wait(pulse8, cmd, 3, + MSGCODE_COMMAND_ACCEPTED, 0); + if (err) + goto unlock; + + cmd[0] = MSGCODE_SET_DEFAULT_LOGICAL_ADDRESS; + cmd[1] = log_addr; + err = pulse8_send_and_wait(pulse8, cmd, 2, + MSGCODE_COMMAND_ACCEPTED, 0); + if (err) + goto unlock; + + cmd[0] = MSGCODE_SET_PHYSICAL_ADDRESS; + cmd[1] = pa >> 8; + cmd[2] = pa & 0xff; + err = pulse8_send_and_wait(pulse8, cmd, 3, + MSGCODE_COMMAND_ACCEPTED, 0); + if (err) + goto unlock; + + cmd[0] = MSGCODE_SET_HDMI_VERSION; + cmd[1] = adap->log_addrs.cec_version; + err = pulse8_send_and_wait(pulse8, cmd, 2, + MSGCODE_COMMAND_ACCEPTED, 0); + if (err) + goto unlock; + + if (adap->log_addrs.osd_name[0]) { + size_t osd_len = strlen(adap->log_addrs.osd_name); + char *osd_str = cmd + 1; + + cmd[0] = MSGCODE_SET_OSD_NAME; + strncpy(cmd + 1, adap->log_addrs.osd_name, 13); + if (osd_len < 4) { + memset(osd_str + osd_len, ' ', 4 - osd_len); + osd_len = 4; + osd_str[osd_len] = '\0'; + strcpy(adap->log_addrs.osd_name, osd_str); + } + err = pulse8_send_and_wait(pulse8, cmd, 1 + osd_len, + MSGCODE_COMMAND_ACCEPTED, 0); + if (err) + goto unlock; + } + +unlock: + if (pulse8->restoring_config) + pulse8->restoring_config = false; + else + pulse8->config_pending = true; + mutex_unlock(&pulse8->config_lock); + return err; +} + +static int pulse8_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, + u32 signal_free_time, struct cec_msg *msg) +{ + struct pulse8 *pulse8 = cec_get_drvdata(adap); + u8 cmd[2]; + unsigned int i; + int err; + + cmd[0] = MSGCODE_TRANSMIT_IDLETIME; + cmd[1] = signal_free_time; + err = pulse8_send_and_wait(pulse8, cmd, 2, + MSGCODE_COMMAND_ACCEPTED, 1); + cmd[0] = MSGCODE_TRANSMIT_ACK_POLARITY; + cmd[1] = cec_msg_is_broadcast(msg); + if (!err) + err = pulse8_send_and_wait(pulse8, cmd, 2, + MSGCODE_COMMAND_ACCEPTED, 1); + cmd[0] = msg->len == 1 ? MSGCODE_TRANSMIT_EOM : MSGCODE_TRANSMIT; + cmd[1] = msg->msg[0]; + if (!err) + err = pulse8_send_and_wait(pulse8, cmd, 2, + MSGCODE_COMMAND_ACCEPTED, 1); + if (!err && msg->len > 1) { + cmd[0] = msg->len == 2 ? MSGCODE_TRANSMIT_EOM : + MSGCODE_TRANSMIT; + cmd[1] = msg->msg[1]; + err = pulse8_send_and_wait(pulse8, cmd, 2, + MSGCODE_COMMAND_ACCEPTED, 1); + for (i = 0; !err && i + 2 < msg->len; i++) { + cmd[0] = (i + 2 == msg->len - 1) ? + MSGCODE_TRANSMIT_EOM : MSGCODE_TRANSMIT; + cmd[1] = msg->msg[i + 2]; + err = pulse8_send_and_wait(pulse8, cmd, 2, + MSGCODE_COMMAND_ACCEPTED, 1); + } + } + + return err; +} + +static int pulse8_received(struct cec_adapter *adap, struct cec_msg *msg) +{ + return -ENOMSG; +} + +static const struct cec_adap_ops pulse8_cec_adap_ops = { + .adap_enable = pulse8_cec_adap_enable, + .adap_log_addr = pulse8_cec_adap_log_addr, + .adap_transmit = pulse8_cec_adap_transmit, + .received = pulse8_received, +}; + +static int pulse8_connect(struct serio *serio, struct serio_driver *drv) +{ + u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_PHYS_ADDR | CEC_CAP_MONITOR_ALL; + struct pulse8 *pulse8; + int err = -ENOMEM; + struct cec_log_addrs log_addrs = {}; + u16 pa = CEC_PHYS_ADDR_INVALID; + + pulse8 = kzalloc(sizeof(*pulse8), GFP_KERNEL); + + if (!pulse8) + return -ENOMEM; + + pulse8->serio = serio; + pulse8->adap = cec_allocate_adapter(&pulse8_cec_adap_ops, pulse8, + dev_name(&serio->dev), caps, 1); + err = PTR_ERR_OR_ZERO(pulse8->adap); + if (err < 0) + goto free_device; + + pulse8->dev = &serio->dev; + serio_set_drvdata(serio, pulse8); + INIT_WORK(&pulse8->work, pulse8_irq_work_handler); + mutex_init(&pulse8->write_lock); + mutex_init(&pulse8->config_lock); + pulse8->config_pending = false; + + err = serio_open(serio, drv); + if (err) + goto delete_adap; + + err = pulse8_setup(pulse8, serio, &log_addrs, &pa); + if (err) + goto close_serio; + + err = cec_register_adapter(pulse8->adap, &serio->dev); + if (err < 0) + goto close_serio; + + pulse8->dev = &pulse8->adap->devnode.dev; + + if (persistent_config && pulse8->autonomous) { + err = pulse8_apply_persistent_config(pulse8, &log_addrs, pa); + if (err) + goto close_serio; + pulse8->restoring_config = true; + } + + INIT_DELAYED_WORK(&pulse8->ping_eeprom_work, + pulse8_ping_eeprom_work_handler); + schedule_delayed_work(&pulse8->ping_eeprom_work, PING_PERIOD); + + return 0; + +close_serio: + serio_close(serio); +delete_adap: + cec_delete_adapter(pulse8->adap); + serio_set_drvdata(serio, NULL); +free_device: + kfree(pulse8); + return err; +} + +static void pulse8_ping_eeprom_work_handler(struct work_struct *work) +{ + struct pulse8 *pulse8 = + container_of(work, struct pulse8, ping_eeprom_work.work); + u8 cmd; + + schedule_delayed_work(&pulse8->ping_eeprom_work, PING_PERIOD); + cmd = MSGCODE_PING; + pulse8_send_and_wait(pulse8, &cmd, 1, + MSGCODE_COMMAND_ACCEPTED, 0); + + if (pulse8->vers < 2) + return; + + mutex_lock(&pulse8->config_lock); + if (pulse8->config_pending && persistent_config) { + dev_dbg(pulse8->dev, "writing pending config to EEPROM\n"); + cmd = MSGCODE_WRITE_EEPROM; + if (pulse8_send_and_wait(pulse8, &cmd, 1, + MSGCODE_COMMAND_ACCEPTED, 0)) + dev_info(pulse8->dev, "failed to write pending config to EEPROM\n"); + else + pulse8->config_pending = false; + } + mutex_unlock(&pulse8->config_lock); +} + +static const struct serio_device_id pulse8_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_PULSE8_CEC, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, pulse8_serio_ids); + +static struct serio_driver pulse8_drv = { + .driver = { + .name = "pulse8-cec", + }, + .description = "Pulse Eight HDMI CEC driver", + .id_table = pulse8_serio_ids, + .interrupt = pulse8_interrupt, + .connect = pulse8_connect, + .disconnect = pulse8_disconnect, +}; + +module_serio_driver(pulse8_drv); diff --git a/drivers/media/usb/rainshadow-cec/Kconfig b/drivers/media/usb/rainshadow-cec/Kconfig new file mode 100644 index 00000000..030ef01b --- /dev/null +++ b/drivers/media/usb/rainshadow-cec/Kconfig @@ -0,0 +1,11 @@ +config USB_RAINSHADOW_CEC + tristate "RainShadow Tech HDMI CEC" + depends on USB_ACM + select CEC_CORE + select SERIO + select SERIO_SERPORT + ---help--- + This is a cec driver for the RainShadow Tech HDMI CEC device. + + To compile this driver as a module, choose M here: the + module will be called rainshadow-cec. diff --git a/drivers/media/usb/rainshadow-cec/Makefile b/drivers/media/usb/rainshadow-cec/Makefile new file mode 100644 index 00000000..a79fbc77 --- /dev/null +++ b/drivers/media/usb/rainshadow-cec/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_USB_RAINSHADOW_CEC) += rainshadow-cec.o diff --git a/drivers/media/usb/rainshadow-cec/rainshadow-cec.c b/drivers/media/usb/rainshadow-cec/rainshadow-cec.c new file mode 100644 index 00000000..cecdcbcd --- /dev/null +++ b/drivers/media/usb/rainshadow-cec/rainshadow-cec.c @@ -0,0 +1,384 @@ +/* + * RainShadow Tech HDMI CEC driver + * + * Copyright 2016 Hans Verkuil +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +MODULE_AUTHOR("Hans Verkuil "); +MODULE_DESCRIPTION("RainShadow Tech HDMI CEC driver"); +MODULE_LICENSE("GPL"); + +#define DATA_SIZE 256 + +struct rain { + struct device *dev; + struct serio *serio; + struct cec_adapter *adap; + struct completion cmd_done; + struct work_struct work; + + /* Low-level ringbuffer, collecting incoming characters */ + char buf[DATA_SIZE]; + unsigned int buf_rd_idx; + unsigned int buf_wr_idx; + unsigned int buf_len; + spinlock_t buf_lock; + + /* command buffer */ + char cmd[DATA_SIZE]; + unsigned int cmd_idx; + bool cmd_started; + + /* reply to a command, only used to store the firmware version */ + char cmd_reply[DATA_SIZE]; + + struct mutex write_lock; +}; + +static void rain_process_msg(struct rain *rain) +{ + struct cec_msg msg = {}; + const char *cmd = rain->cmd + 3; + int stat = -1; + + for (; *cmd; cmd++) { + if (!isxdigit(*cmd)) + continue; + if (isxdigit(cmd[0]) && isxdigit(cmd[1])) { + if (msg.len == CEC_MAX_MSG_SIZE) + break; + if (hex2bin(msg.msg + msg.len, cmd, 1)) + continue; + msg.len++; + cmd++; + continue; + } + if (!cmd[1]) + stat = hex_to_bin(cmd[0]); + break; + } + + if (rain->cmd[0] == 'R') { + if (stat == 1 || stat == 2) + cec_received_msg(rain->adap, &msg); + return; + } + + switch (stat) { + case 1: + cec_transmit_attempt_done(rain->adap, CEC_TX_STATUS_OK); + break; + case 2: + cec_transmit_attempt_done(rain->adap, CEC_TX_STATUS_NACK); + break; + default: + cec_transmit_attempt_done(rain->adap, CEC_TX_STATUS_LOW_DRIVE); + break; + } +} + +static void rain_irq_work_handler(struct work_struct *work) +{ + struct rain *rain = + container_of(work, struct rain, work); + + while (true) { + unsigned long flags; + char data; + + spin_lock_irqsave(&rain->buf_lock, flags); + if (!rain->buf_len) { + spin_unlock_irqrestore(&rain->buf_lock, flags); + break; + } + + data = rain->buf[rain->buf_rd_idx]; + rain->buf_len--; + rain->buf_rd_idx = (rain->buf_rd_idx + 1) & 0xff; + + spin_unlock_irqrestore(&rain->buf_lock, flags); + + if (!rain->cmd_started && data != '?') + continue; + + switch (data) { + case '\r': + rain->cmd[rain->cmd_idx] = '\0'; + dev_dbg(rain->dev, "received: %s\n", rain->cmd); + if (!memcmp(rain->cmd, "REC", 3) || + !memcmp(rain->cmd, "STA", 3)) { + rain_process_msg(rain); + } else { + strcpy(rain->cmd_reply, rain->cmd); + complete(&rain->cmd_done); + } + rain->cmd_idx = 0; + rain->cmd_started = false; + break; + + case '\n': + rain->cmd_idx = 0; + rain->cmd_started = false; + break; + + case '?': + rain->cmd_idx = 0; + rain->cmd_started = true; + break; + + default: + if (rain->cmd_idx >= DATA_SIZE - 1) { + dev_dbg(rain->dev, + "throwing away %d bytes of garbage\n", rain->cmd_idx); + rain->cmd_idx = 0; + } + rain->cmd[rain->cmd_idx++] = data; + break; + } + } +} + +static irqreturn_t rain_interrupt(struct serio *serio, unsigned char data, + unsigned int flags) +{ + struct rain *rain = serio_get_drvdata(serio); + + if (rain->buf_len == DATA_SIZE) { + dev_warn_once(rain->dev, "buffer overflow\n"); + return IRQ_HANDLED; + } + spin_lock(&rain->buf_lock); + rain->buf_len++; + rain->buf[rain->buf_wr_idx] = data; + rain->buf_wr_idx = (rain->buf_wr_idx + 1) & 0xff; + spin_unlock(&rain->buf_lock); + schedule_work(&rain->work); + return IRQ_HANDLED; +} + +static void rain_disconnect(struct serio *serio) +{ + struct rain *rain = serio_get_drvdata(serio); + + cancel_work_sync(&rain->work); + cec_unregister_adapter(rain->adap); + dev_info(&serio->dev, "disconnected\n"); + serio_close(serio); + serio_set_drvdata(serio, NULL); + kfree(rain); +} + +static int rain_send(struct rain *rain, const char *command) +{ + int err = serio_write(rain->serio, '!'); + + dev_dbg(rain->dev, "send: %s\n", command); + while (!err && *command) + err = serio_write(rain->serio, *command++); + if (!err) + err = serio_write(rain->serio, '~'); + + return err; +} + +static int rain_send_and_wait(struct rain *rain, + const char *cmd, const char *reply) +{ + int err; + + init_completion(&rain->cmd_done); + + mutex_lock(&rain->write_lock); + err = rain_send(rain, cmd); + if (err) + goto err; + + if (!wait_for_completion_timeout(&rain->cmd_done, HZ)) { + err = -ETIMEDOUT; + goto err; + } + if (reply && strncmp(rain->cmd_reply, reply, strlen(reply))) { + dev_dbg(rain->dev, + "transmit of '%s': received '%s' instead of '%s'\n", + cmd, rain->cmd_reply, reply); + err = -EIO; + } +err: + mutex_unlock(&rain->write_lock); + return err; +} + +static int rain_setup(struct rain *rain, struct serio *serio, + struct cec_log_addrs *log_addrs, u16 *pa) +{ + int err; + + err = rain_send_and_wait(rain, "R", "REV"); + if (err) + return err; + dev_info(rain->dev, "Firmware version %s\n", rain->cmd_reply + 4); + + err = rain_send_and_wait(rain, "Q 1", "QTY"); + if (err) + return err; + err = rain_send_and_wait(rain, "c0000", "CFG"); + if (err) + return err; + return rain_send_and_wait(rain, "A F 0000", "ADR"); +} + +static int rain_cec_adap_enable(struct cec_adapter *adap, bool enable) +{ + return 0; +} + +static int rain_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) +{ + struct rain *rain = cec_get_drvdata(adap); + u8 cmd[16]; + + if (log_addr == CEC_LOG_ADDR_INVALID) + log_addr = CEC_LOG_ADDR_UNREGISTERED; + snprintf(cmd, sizeof(cmd), "A %x", log_addr); + return rain_send_and_wait(rain, cmd, "ADR"); +} + +static int rain_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, + u32 signal_free_time, struct cec_msg *msg) +{ + struct rain *rain = cec_get_drvdata(adap); + char cmd[2 * CEC_MAX_MSG_SIZE + 16]; + unsigned int i; + int err; + + if (msg->len == 1) { + snprintf(cmd, sizeof(cmd), "x%x", cec_msg_destination(msg)); + } else { + char hex[3]; + + snprintf(cmd, sizeof(cmd), "x%x %02x ", + cec_msg_destination(msg), msg->msg[1]); + for (i = 2; i < msg->len; i++) { + snprintf(hex, sizeof(hex), "%02x", msg->msg[i]); + strlcat(cmd, hex, sizeof(cmd)); + } + } + mutex_lock(&rain->write_lock); + err = rain_send(rain, cmd); + mutex_unlock(&rain->write_lock); + return err; +} + +static const struct cec_adap_ops rain_cec_adap_ops = { + .adap_enable = rain_cec_adap_enable, + .adap_log_addr = rain_cec_adap_log_addr, + .adap_transmit = rain_cec_adap_transmit, +}; + +static int rain_connect(struct serio *serio, struct serio_driver *drv) +{ + u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_PHYS_ADDR | CEC_CAP_MONITOR_ALL; + struct rain *rain; + int err = -ENOMEM; + struct cec_log_addrs log_addrs = {}; + u16 pa = CEC_PHYS_ADDR_INVALID; + + rain = kzalloc(sizeof(*rain), GFP_KERNEL); + + if (!rain) + return -ENOMEM; + + rain->serio = serio; + rain->adap = cec_allocate_adapter(&rain_cec_adap_ops, rain, + dev_name(&serio->dev), caps, 1); + err = PTR_ERR_OR_ZERO(rain->adap); + if (err < 0) + goto free_device; + + rain->dev = &serio->dev; + serio_set_drvdata(serio, rain); + INIT_WORK(&rain->work, rain_irq_work_handler); + mutex_init(&rain->write_lock); + spin_lock_init(&rain->buf_lock); + + err = serio_open(serio, drv); + if (err) + goto delete_adap; + + err = rain_setup(rain, serio, &log_addrs, &pa); + if (err) + goto close_serio; + + err = cec_register_adapter(rain->adap, &serio->dev); + if (err < 0) + goto close_serio; + + rain->dev = &rain->adap->devnode.dev; + return 0; + +close_serio: + serio_close(serio); +delete_adap: + cec_delete_adapter(rain->adap); + serio_set_drvdata(serio, NULL); +free_device: + kfree(rain); + return err; +} + +static const struct serio_device_id rain_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_RAINSHADOW_CEC, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, rain_serio_ids); + +static struct serio_driver rain_drv = { + .driver = { + .name = "rainshadow-cec", + }, + .description = "RainShadow Tech HDMI CEC driver", + .id_table = rain_serio_ids, + .interrupt = rain_interrupt, + .connect = rain_connect, + .disconnect = rain_disconnect, +}; + +module_serio_driver(rain_drv); diff --git a/drivers/mfd/fusb302.c b/drivers/mfd/fusb302.c index 240cecac..8fe41632 100644 --- a/drivers/mfd/fusb302.c +++ b/drivers/mfd/fusb302.c @@ -217,8 +217,32 @@ static int fusb302_set_pos_power_by_charge_ic(struct fusb30x_chip *chip) max_vol = 0; max_cur = 0; psy = power_supply_get_by_phandle(chip->dev->of_node, "charge-dev"); - if (!psy || IS_ERR(psy)) - return -1; + if (!psy || IS_ERR(psy)) { + int ret; + u32 value; + + ret = of_property_read_u32(chip->dev->of_node, "max-input-voltage", &value); + if (ret) { + dev_err(chip->dev, "'max-input-voltage' not found!\n"); + return -1; + } + + max_vol = value / 1000; + + ret = of_property_read_u32(chip->dev->of_node, "max-input-current", &value); + + if (ret) { + dev_err(chip->dev, "'max-input-current' not found!\n"); + return -1; + } + + max_cur = value / 1000; + + if (max_vol > 0 && max_cur > 0) + fusb_set_pos_power(chip, max_vol, max_cur); + + return 0; + } psp = POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX; if (power_supply_get_property(psy, psp, &val) == 0) diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig index 87cc07de..00dfaea0 100644 --- a/drivers/mmc/core/Kconfig +++ b/drivers/mmc/core/Kconfig @@ -16,3 +16,25 @@ config MMC_PARANOID_SD_INIT about re-trying SD init requests. This can be a useful work-around for buggy controllers and hardware. Enable if you are experiencing issues with SD detection. + +config PWRSEQ_EMMC + tristate "HW reset support for eMMC" + default y + depends on OF + help + This selects Hardware reset support aka pwrseq-emmc for eMMC + devices. By default this option is set to y. + + This driver can also be built as a module. If so, the module + will be called pwrseq_emmc. + +config PWRSEQ_SIMPLE + tristate "Simple HW reset support for MMC" + default y + depends on OF + help + This selects simple hardware reset support aka pwrseq-simple for MMC + devices. By default this option is set to y. + + This driver can also be built as a module. If so, the module + will be called pwrseq_simple. diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index 2c25138f..f007151d 100644 --- a/drivers/mmc/core/Makefile +++ b/drivers/mmc/core/Makefile @@ -8,5 +8,7 @@ mmc_core-y := core.o bus.o host.o \ sdio.o sdio_ops.o sdio_bus.o \ sdio_cis.o sdio_io.o sdio_irq.o \ quirks.o slot-gpio.o -mmc_core-$(CONFIG_OF) += pwrseq.o pwrseq_simple.o pwrseq_emmc.o +mmc_core-$(CONFIG_OF) += pwrseq.o +obj-$(CONFIG_PWRSEQ_SIMPLE) += pwrseq_simple.o +obj-$(CONFIG_PWRSEQ_EMMC) += pwrseq_emmc.o mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 3e3c79fe..d2c59b5e 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1574,7 +1574,7 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr) return ocr; } -int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage) +int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage) { int err = 0; int old_signal_voltage = host->ios.signal_voltage; @@ -1590,20 +1590,46 @@ int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage) } -int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr) +void mmc_set_initial_signal_voltage(struct mmc_host *host) { - struct mmc_command cmd = {0}; - int err = 0; - u32 clock; + /* Try to set signal voltage to 3.3V but fall back to 1.8v or 1.2v */ + if (!mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330)) + dev_dbg(mmc_dev(host), "Initial signal voltage of 3.3v\n"); + else if (!mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180)) + dev_dbg(mmc_dev(host), "Initial signal voltage of 1.8v\n"); + else if (!mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120)) + dev_dbg(mmc_dev(host), "Initial signal voltage of 1.2v\n"); +} - BUG_ON(!host); +int mmc_host_set_uhs_voltage(struct mmc_host *host) +{ + u32 clock; /* - * Send CMD11 only if the request is to switch the card to - * 1.8V signalling. + * During a signal voltage level switch, the clock must be gated + * for 5 ms according to the SD spec */ - if (signal_voltage == MMC_SIGNAL_VOLTAGE_330) - return __mmc_set_signal_voltage(host, signal_voltage); + clock = host->ios.clock; + host->ios.clock = 0; + mmc_set_ios(host); + + if (mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180)) + return -EAGAIN; + + /* Keep clock gated for at least 10 ms, though spec only says 5 ms */ + mmc_delay(10); + host->ios.clock = clock; + mmc_set_ios(host); + + return 0; +} + +int mmc_set_uhs_voltage(struct mmc_host *host, u32 ocr) +{ + struct mmc_command cmd = {0}; + int err = 0; + + BUG_ON(!host); /* * If we cannot switch voltages, return failure so the caller @@ -1635,15 +1661,8 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr) err = -EAGAIN; goto power_cycle; } - /* - * During a signal voltage level switch, the clock must be gated - * for 5 ms according to the SD spec - */ - clock = host->ios.clock; - host->ios.clock = 0; - mmc_set_ios(host); - if (__mmc_set_signal_voltage(host, signal_voltage)) { + if (mmc_host_set_uhs_voltage(host)) { /* * Voltages may not have been switched, but we've already * sent CMD11, so a power cycle is required anyway @@ -1652,11 +1671,6 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr) goto power_cycle; } - /* Keep clock gated for at least 10 ms, though spec only says 5 ms */ - mmc_delay(10); - host->ios.clock = clock; - mmc_set_ios(host); - /* Wait for at least 1 ms according to spec */ mmc_delay(1); @@ -1751,13 +1765,7 @@ void mmc_power_up(struct mmc_host *host, u32 ocr) /* Set initial state and call mmc_set_ios */ mmc_set_initial_state(host); - /* Try to set signal voltage to 3.3V but fall back to 1.8v or 1.2v */ - if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330) == 0) - dev_dbg(mmc_dev(host), "Initial signal voltage of 3.3v\n"); - else if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180) == 0) - dev_dbg(mmc_dev(host), "Initial signal voltage of 1.8v\n"); - else if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120) == 0) - dev_dbg(mmc_dev(host), "Initial signal voltage of 1.2v\n"); + mmc_set_initial_signal_voltage(host); /* * This delay should be sufficient to allow the power supply @@ -1784,6 +1792,14 @@ void mmc_power_off(struct mmc_host *host) if (host->ios.power_mode == MMC_POWER_OFF) return; + mmc_set_initial_signal_voltage(host); + + /* + * This delay should be sufficient to allow the power supply + * to reach the minimum voltage. + */ + mmc_delay(10); + mmc_pwrseq_power_off(host); host->ios.clock = 0; diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index ed7c3167..26347222 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -43,8 +43,10 @@ void mmc_set_clock(struct mmc_host *host, unsigned int hz); void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode); void mmc_set_bus_width(struct mmc_host *host, unsigned int width); u32 mmc_select_voltage(struct mmc_host *host, u32 ocr); -int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr); -int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage); +int mmc_set_uhs_voltage(struct mmc_host *host, u32 ocr); +int mmc_host_set_uhs_voltage(struct mmc_host *host); +int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage); +void mmc_set_initial_signal_voltage(struct mmc_host *host); void mmc_set_timing(struct mmc_host *host, unsigned int timing); void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type); int mmc_select_drive_strength(struct mmc_card *card, unsigned int max_dtr, diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index a814eb68..dd0040a1 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1088,14 +1088,14 @@ static int mmc_select_hs_ddr(struct mmc_card *card) */ err = -EINVAL; if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_1_2V) - err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); if (err && (card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_1_8V)) - err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); /* make sure vccq is 3.3v after switching disaster */ if (err) - err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330); + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330); if (!err) mmc_set_timing(host, MMC_TIMING_MMC_DDR52); @@ -1251,7 +1251,7 @@ int mmc_hs400_to_hs200(struct mmc_card *card) static int mmc_select_hs400es(struct mmc_card *card) { struct mmc_host *host = card->host; - int err = 0; + int err = -EINVAL; u8 val; if (!(host->caps & MMC_CAP_8_BIT_DATA)) { @@ -1259,11 +1259,11 @@ static int mmc_select_hs400es(struct mmc_card *card) goto out_err; } - if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_2V) - err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); + if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400_1_2V) + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); - if (err && card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_8V) - err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); + if (err && card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400_1_8V) + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); /* If fails try again during next card power cycle */ if (err) @@ -1362,10 +1362,10 @@ static int mmc_select_hs200(struct mmc_card *card) old_signal_voltage = host->ios.signal_voltage; if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_2V) - err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); if (err && card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_8V) - err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); /* If fails try again during next card power cycle */ if (err) @@ -1393,7 +1393,7 @@ static int mmc_select_hs200(struct mmc_card *card) err: if (err) { /* fall back to the old signal voltage, if fails report error */ - if (__mmc_set_signal_voltage(host, old_signal_voltage)) + if (mmc_set_signal_voltage(host, old_signal_voltage)) err = -EIO; pr_err("%s: %s failed, error %d\n", mmc_hostname(card->host), diff --git a/drivers/mmc/core/pwrseq.c b/drivers/mmc/core/pwrseq.c index 4c1d1757..9386c477 100644 --- a/drivers/mmc/core/pwrseq.c +++ b/drivers/mmc/core/pwrseq.c @@ -8,88 +8,55 @@ * MMC power sequence management */ #include -#include #include +#include #include -#include #include #include "pwrseq.h" -struct mmc_pwrseq_match { - const char *compatible; - struct mmc_pwrseq *(*alloc)(struct mmc_host *host, struct device *dev); -}; - -static struct mmc_pwrseq_match pwrseq_match[] = { - { - .compatible = "mmc-pwrseq-simple", - .alloc = mmc_pwrseq_simple_alloc, - }, { - .compatible = "mmc-pwrseq-emmc", - .alloc = mmc_pwrseq_emmc_alloc, - }, -}; - -static struct mmc_pwrseq_match *mmc_pwrseq_find(struct device_node *np) -{ - struct mmc_pwrseq_match *match = ERR_PTR(-ENODEV); - int i; - - for (i = 0; i < ARRAY_SIZE(pwrseq_match); i++) { - if (of_device_is_compatible(np, pwrseq_match[i].compatible)) { - match = &pwrseq_match[i]; - break; - } - } - - return match; -} +static DEFINE_MUTEX(pwrseq_list_mutex); +static LIST_HEAD(pwrseq_list); int mmc_pwrseq_alloc(struct mmc_host *host) { - struct platform_device *pdev; struct device_node *np; - struct mmc_pwrseq_match *match; - struct mmc_pwrseq *pwrseq; - int ret = 0; + struct mmc_pwrseq *p; np = of_parse_phandle(host->parent->of_node, "mmc-pwrseq", 0); if (!np) return 0; - pdev = of_find_device_by_node(np); - if (!pdev) { - ret = -ENODEV; - goto err; - } + mutex_lock(&pwrseq_list_mutex); + list_for_each_entry(p, &pwrseq_list, pwrseq_node) { + if (p->dev->of_node == np) { + if (!try_module_get(p->owner)) + dev_err(host->parent, + "increasing module refcount failed\n"); + else + host->pwrseq = p; - match = mmc_pwrseq_find(np); - if (IS_ERR(match)) { - ret = PTR_ERR(match); - goto err; + break; + } } - pwrseq = match->alloc(host, &pdev->dev); - if (IS_ERR(pwrseq)) { - ret = PTR_ERR(pwrseq); - goto err; - } + of_node_put(np); + mutex_unlock(&pwrseq_list_mutex); + + if (!host->pwrseq) + return -EPROBE_DEFER; - host->pwrseq = pwrseq; dev_info(host->parent, "allocated mmc-pwrseq\n"); -err: - of_node_put(np); - return ret; + return 0; } void mmc_pwrseq_pre_power_on(struct mmc_host *host) { struct mmc_pwrseq *pwrseq = host->pwrseq; - if (pwrseq && pwrseq->ops && pwrseq->ops->pre_power_on) + if (pwrseq && pwrseq->ops->pre_power_on) pwrseq->ops->pre_power_on(host); } @@ -97,7 +64,7 @@ void mmc_pwrseq_post_power_on(struct mmc_host *host) { struct mmc_pwrseq *pwrseq = host->pwrseq; - if (pwrseq && pwrseq->ops && pwrseq->ops->post_power_on) + if (pwrseq && pwrseq->ops->post_power_on) pwrseq->ops->post_power_on(host); } @@ -105,7 +72,7 @@ void mmc_pwrseq_power_off(struct mmc_host *host) { struct mmc_pwrseq *pwrseq = host->pwrseq; - if (pwrseq && pwrseq->ops && pwrseq->ops->power_off) + if (pwrseq && pwrseq->ops->power_off) pwrseq->ops->power_off(host); } @@ -113,8 +80,31 @@ void mmc_pwrseq_free(struct mmc_host *host) { struct mmc_pwrseq *pwrseq = host->pwrseq; - if (pwrseq && pwrseq->ops && pwrseq->ops->free) - pwrseq->ops->free(host); + if (pwrseq) { + module_put(pwrseq->owner); + host->pwrseq = NULL; + } +} + +int mmc_pwrseq_register(struct mmc_pwrseq *pwrseq) +{ + if (!pwrseq || !pwrseq->ops || !pwrseq->dev) + return -EINVAL; - host->pwrseq = NULL; + mutex_lock(&pwrseq_list_mutex); + list_add(&pwrseq->pwrseq_node, &pwrseq_list); + mutex_unlock(&pwrseq_list_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(mmc_pwrseq_register); + +void mmc_pwrseq_unregister(struct mmc_pwrseq *pwrseq) +{ + if (pwrseq) { + mutex_lock(&pwrseq_list_mutex); + list_del(&pwrseq->pwrseq_node); + mutex_unlock(&pwrseq_list_mutex); + } } +EXPORT_SYMBOL_GPL(mmc_pwrseq_unregister); diff --git a/drivers/mmc/core/pwrseq.h b/drivers/mmc/core/pwrseq.h index 096da48c..d69e751f 100644 --- a/drivers/mmc/core/pwrseq.h +++ b/drivers/mmc/core/pwrseq.h @@ -8,32 +8,39 @@ #ifndef _MMC_CORE_PWRSEQ_H #define _MMC_CORE_PWRSEQ_H +#include + struct mmc_pwrseq_ops { void (*pre_power_on)(struct mmc_host *host); void (*post_power_on)(struct mmc_host *host); void (*power_off)(struct mmc_host *host); - void (*free)(struct mmc_host *host); }; struct mmc_pwrseq { - struct mmc_pwrseq_ops *ops; + const struct mmc_pwrseq_ops *ops; + struct device *dev; + struct list_head pwrseq_node; + struct module *owner; }; #ifdef CONFIG_OF +int mmc_pwrseq_register(struct mmc_pwrseq *pwrseq); +void mmc_pwrseq_unregister(struct mmc_pwrseq *pwrseq); + int mmc_pwrseq_alloc(struct mmc_host *host); void mmc_pwrseq_pre_power_on(struct mmc_host *host); void mmc_pwrseq_post_power_on(struct mmc_host *host); void mmc_pwrseq_power_off(struct mmc_host *host); void mmc_pwrseq_free(struct mmc_host *host); -struct mmc_pwrseq *mmc_pwrseq_simple_alloc(struct mmc_host *host, - struct device *dev); -struct mmc_pwrseq *mmc_pwrseq_emmc_alloc(struct mmc_host *host, - struct device *dev); - #else +static inline int mmc_pwrseq_register(struct mmc_pwrseq *pwrseq) +{ + return -ENOSYS; +} +static inline void mmc_pwrseq_unregister(struct mmc_pwrseq *pwrseq) {} static inline int mmc_pwrseq_alloc(struct mmc_host *host) { return 0; } static inline void mmc_pwrseq_pre_power_on(struct mmc_host *host) {} static inline void mmc_pwrseq_post_power_on(struct mmc_host *host) {} diff --git a/drivers/mmc/core/pwrseq_emmc.c b/drivers/mmc/core/pwrseq_emmc.c index ad4f94ec..adc9c0c6 100644 --- a/drivers/mmc/core/pwrseq_emmc.c +++ b/drivers/mmc/core/pwrseq_emmc.c @@ -9,6 +9,9 @@ */ #include #include +#include +#include +#include #include #include #include @@ -25,6 +28,8 @@ struct mmc_pwrseq_emmc { struct gpio_desc *reset_gpio; }; +#define to_pwrseq_emmc(p) container_of(p, struct mmc_pwrseq_emmc, pwrseq) + static void __mmc_pwrseq_emmc_reset(struct mmc_pwrseq_emmc *pwrseq) { gpiod_set_value(pwrseq->reset_gpio, 1); @@ -35,27 +40,11 @@ static void __mmc_pwrseq_emmc_reset(struct mmc_pwrseq_emmc *pwrseq) static void mmc_pwrseq_emmc_reset(struct mmc_host *host) { - struct mmc_pwrseq_emmc *pwrseq = container_of(host->pwrseq, - struct mmc_pwrseq_emmc, pwrseq); + struct mmc_pwrseq_emmc *pwrseq = to_pwrseq_emmc(host->pwrseq); __mmc_pwrseq_emmc_reset(pwrseq); } -static void mmc_pwrseq_emmc_free(struct mmc_host *host) -{ - struct mmc_pwrseq_emmc *pwrseq = container_of(host->pwrseq, - struct mmc_pwrseq_emmc, pwrseq); - - unregister_restart_handler(&pwrseq->reset_nb); - gpiod_put(pwrseq->reset_gpio); - kfree(pwrseq); -} - -static struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = { - .post_power_on = mmc_pwrseq_emmc_reset, - .free = mmc_pwrseq_emmc_free, -}; - static int mmc_pwrseq_emmc_reset_nb(struct notifier_block *this, unsigned long mode, void *cmd) { @@ -66,21 +55,22 @@ static int mmc_pwrseq_emmc_reset_nb(struct notifier_block *this, return NOTIFY_DONE; } -struct mmc_pwrseq *mmc_pwrseq_emmc_alloc(struct mmc_host *host, - struct device *dev) +static const struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = { + .post_power_on = mmc_pwrseq_emmc_reset, +}; + +static int mmc_pwrseq_emmc_probe(struct platform_device *pdev) { struct mmc_pwrseq_emmc *pwrseq; - int ret = 0; + struct device *dev = &pdev->dev; - pwrseq = kzalloc(sizeof(struct mmc_pwrseq_emmc), GFP_KERNEL); + pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL); if (!pwrseq) - return ERR_PTR(-ENOMEM); + return -ENOMEM; - pwrseq->reset_gpio = gpiod_get(dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(pwrseq->reset_gpio)) { - ret = PTR_ERR(pwrseq->reset_gpio); - goto free; - } + pwrseq->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(pwrseq->reset_gpio)) + return PTR_ERR(pwrseq->reset_gpio); /* * register reset handler to ensure emmc reset also from @@ -92,9 +82,38 @@ struct mmc_pwrseq *mmc_pwrseq_emmc_alloc(struct mmc_host *host, register_restart_handler(&pwrseq->reset_nb); pwrseq->pwrseq.ops = &mmc_pwrseq_emmc_ops; + pwrseq->pwrseq.dev = dev; + pwrseq->pwrseq.owner = THIS_MODULE; + platform_set_drvdata(pdev, pwrseq); - return &pwrseq->pwrseq; -free: - kfree(pwrseq); - return ERR_PTR(ret); + return mmc_pwrseq_register(&pwrseq->pwrseq); } + +static int mmc_pwrseq_emmc_remove(struct platform_device *pdev) +{ + struct mmc_pwrseq_emmc *pwrseq = platform_get_drvdata(pdev); + + unregister_restart_handler(&pwrseq->reset_nb); + mmc_pwrseq_unregister(&pwrseq->pwrseq); + + return 0; +} + +static const struct of_device_id mmc_pwrseq_emmc_of_match[] = { + { .compatible = "mmc-pwrseq-emmc",}, + {/* sentinel */}, +}; + +MODULE_DEVICE_TABLE(of, mmc_pwrseq_emmc_of_match); + +static struct platform_driver mmc_pwrseq_emmc_driver = { + .probe = mmc_pwrseq_emmc_probe, + .remove = mmc_pwrseq_emmc_remove, + .driver = { + .name = "pwrseq_emmc", + .of_match_table = mmc_pwrseq_emmc_of_match, + }, +}; + +module_platform_driver(mmc_pwrseq_emmc_driver); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c index d10538bb..13ef162c 100644 --- a/drivers/mmc/core/pwrseq_simple.c +++ b/drivers/mmc/core/pwrseq_simple.c @@ -8,12 +8,16 @@ * Simple MMC power sequence management */ #include +#include #include +#include +#include #include #include #include -#include #include +#include +#include #include @@ -22,28 +26,34 @@ struct mmc_pwrseq_simple { struct mmc_pwrseq pwrseq; bool clk_enabled; + u32 post_power_on_delay_ms; + u32 power_off_delay_us; struct clk *ext_clk; struct gpio_descs *reset_gpios; }; +#define to_pwrseq_simple(p) container_of(p, struct mmc_pwrseq_simple, pwrseq) + static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq, int value) { - int i; struct gpio_descs *reset_gpios = pwrseq->reset_gpios; - int values[reset_gpios->ndescs]; - for (i = 0; i < reset_gpios->ndescs; i++) - values[i] = value; + if (!IS_ERR(reset_gpios)) { + int i; + int values[reset_gpios->ndescs]; + + for (i = 0; i < reset_gpios->ndescs; i++) + values[i] = value; - gpiod_set_array_value_cansleep(reset_gpios->ndescs, reset_gpios->desc, - values); + gpiod_set_array_value_cansleep( + reset_gpios->ndescs, reset_gpios->desc, values); + } } static void mmc_pwrseq_simple_pre_power_on(struct mmc_host *host) { - struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq, - struct mmc_pwrseq_simple, pwrseq); + struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq); if (!IS_ERR(pwrseq->ext_clk) && !pwrseq->clk_enabled) { clk_prepare_enable(pwrseq->ext_clk); @@ -55,75 +65,93 @@ static void mmc_pwrseq_simple_pre_power_on(struct mmc_host *host) static void mmc_pwrseq_simple_post_power_on(struct mmc_host *host) { - struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq, - struct mmc_pwrseq_simple, pwrseq); + struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq); mmc_pwrseq_simple_set_gpios_value(pwrseq, 0); + + if (pwrseq->post_power_on_delay_ms) + msleep(pwrseq->post_power_on_delay_ms); } static void mmc_pwrseq_simple_power_off(struct mmc_host *host) { - struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq, - struct mmc_pwrseq_simple, pwrseq); + struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq); mmc_pwrseq_simple_set_gpios_value(pwrseq, 1); + if (pwrseq->power_off_delay_us) + usleep_range(pwrseq->power_off_delay_us, + 2 * pwrseq->power_off_delay_us); + if (!IS_ERR(pwrseq->ext_clk) && pwrseq->clk_enabled) { clk_disable_unprepare(pwrseq->ext_clk); pwrseq->clk_enabled = false; } } -static void mmc_pwrseq_simple_free(struct mmc_host *host) -{ - struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq, - struct mmc_pwrseq_simple, pwrseq); - - gpiod_put_array(pwrseq->reset_gpios); - - if (!IS_ERR(pwrseq->ext_clk)) - clk_put(pwrseq->ext_clk); - - kfree(pwrseq); -} - -static struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = { +static const struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = { .pre_power_on = mmc_pwrseq_simple_pre_power_on, .post_power_on = mmc_pwrseq_simple_post_power_on, .power_off = mmc_pwrseq_simple_power_off, - .free = mmc_pwrseq_simple_free, }; -struct mmc_pwrseq *mmc_pwrseq_simple_alloc(struct mmc_host *host, - struct device *dev) +static const struct of_device_id mmc_pwrseq_simple_of_match[] = { + { .compatible = "mmc-pwrseq-simple",}, + {/* sentinel */}, +}; +MODULE_DEVICE_TABLE(of, mmc_pwrseq_simple_of_match); + +static int mmc_pwrseq_simple_probe(struct platform_device *pdev) { struct mmc_pwrseq_simple *pwrseq; - int ret = 0; + struct device *dev = &pdev->dev; - pwrseq = kzalloc(sizeof(*pwrseq), GFP_KERNEL); + pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL); if (!pwrseq) - return ERR_PTR(-ENOMEM); - - pwrseq->ext_clk = clk_get(dev, "ext_clock"); - if (IS_ERR(pwrseq->ext_clk) && - PTR_ERR(pwrseq->ext_clk) != -ENOENT) { - ret = PTR_ERR(pwrseq->ext_clk); - goto free; + return -ENOMEM; + + pwrseq->ext_clk = devm_clk_get(dev, "ext_clock"); + if (IS_ERR(pwrseq->ext_clk) && PTR_ERR(pwrseq->ext_clk) != -ENOENT) + return PTR_ERR(pwrseq->ext_clk); + + pwrseq->reset_gpios = devm_gpiod_get_array(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(pwrseq->reset_gpios) && + PTR_ERR(pwrseq->reset_gpios) != -ENOENT && + PTR_ERR(pwrseq->reset_gpios) != -ENOSYS) { + return PTR_ERR(pwrseq->reset_gpios); } - pwrseq->reset_gpios = gpiod_get_array(dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(pwrseq->reset_gpios)) { - ret = PTR_ERR(pwrseq->reset_gpios); - goto clk_put; - } + device_property_read_u32(dev, "post-power-on-delay-ms", + &pwrseq->post_power_on_delay_ms); + device_property_read_u32(dev, "power-off-delay-us", + &pwrseq->power_off_delay_us); + pwrseq->pwrseq.dev = dev; pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops; + pwrseq->pwrseq.owner = THIS_MODULE; + platform_set_drvdata(pdev, pwrseq); - return &pwrseq->pwrseq; -clk_put: - if (!IS_ERR(pwrseq->ext_clk)) - clk_put(pwrseq->ext_clk); -free: - kfree(pwrseq); - return ERR_PTR(ret); + return mmc_pwrseq_register(&pwrseq->pwrseq); } + +static int mmc_pwrseq_simple_remove(struct platform_device *pdev) +{ + struct mmc_pwrseq_simple *pwrseq = platform_get_drvdata(pdev); + + mmc_pwrseq_unregister(&pwrseq->pwrseq); + + return 0; +} + +static struct platform_driver mmc_pwrseq_simple_driver = { + .probe = mmc_pwrseq_simple_probe, + .remove = mmc_pwrseq_simple_remove, + .driver = { + .name = "pwrseq_simple", + .of_match_table = mmc_pwrseq_simple_of_match, + }, +}; + +module_platform_driver(mmc_pwrseq_simple_driver); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index cd437d6b..2808a281 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -742,8 +742,7 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr) */ if (!mmc_host_is_spi(host) && rocr && ((*rocr & 0x41000000) == 0x41000000)) { - err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, - pocr); + err = mmc_set_uhs_voltage(host, pocr); if (err == -EAGAIN) { retries--; goto try_again; @@ -899,6 +898,18 @@ unsigned mmc_sd_get_max_clock(struct mmc_card *card) return max_dtr; } +static bool mmc_sd_card_using_v18(struct mmc_card *card) +{ + /* + * According to the SD spec., the Bus Speed Mode (function group 1) bits + * 2 to 4 are zero if the card is initialized at 3.3V signal level. Thus + * they can be used to determine if the card has already switched to + * 1.8V signaling. + */ + return card->sw_caps.sd3_bus_mode & + (SD_MODE_UHS_SDR50 | SD_MODE_UHS_SDR104 | SD_MODE_UHS_DDR50); +} + /* * Handle the detection and initialisation of a card. * @@ -912,10 +923,11 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, int err; u32 cid[4]; u32 rocr = 0; + bool v18_fixup_failed = false; BUG_ON(!host); WARN_ON(!host->claimed); - +retry: err = mmc_sd_get_cid(host, ocr, cid, &rocr); if (err) return err; @@ -981,6 +993,36 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, if (err) goto free_card; + /* + * If the card has not been power cycled, it may still be using 1.8V + * signaling. Detect that situation and try to initialize a UHS-I (1.8V) + * transfer mode. + */ + if (!v18_fixup_failed && !mmc_host_is_spi(host) && mmc_host_uhs(host) && + mmc_sd_card_using_v18(card) && + host->ios.signal_voltage != MMC_SIGNAL_VOLTAGE_180) { + /* + * Re-read switch information in case it has changed since + * oldcard was initialized. + */ + if (oldcard) { + err = mmc_read_switch(card); + if (err) + goto free_card; + } + if (mmc_sd_card_using_v18(card)) { + if (mmc_host_set_uhs_voltage(host) || + mmc_sd_init_uhs_card(card)) { + v18_fixup_failed = true; + mmc_power_cycle(host, ocr); + if (!oldcard) + mmc_remove_card(card); + goto retry; + } + goto done; + } + } + /* Initialization sequence for UHS-I cards */ if (rocr & SD_ROCR_S18A && mmc_host_uhs(host)) { err = mmc_sd_init_uhs_card(card); @@ -1013,7 +1055,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, mmc_set_bus_width(host, MMC_BUS_WIDTH_4); } } - +done: host->card = card; return 0; diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index c586b11a..f2214185 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -648,8 +648,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, * to make sure which speed mode should work. */ if (!powered_resume && (rocr & ocr & R4_18V_PRESENT)) { - err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, - ocr_card); + err = mmc_set_uhs_voltage(host, ocr_card); if (err == -EAGAIN) { mmc_sdio_resend_if_cond(host, card); retries--; diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c index 43ab6913..15d342ee 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -13,26 +13,106 @@ * option) any later version. * */ +#include #include #include -#define RTL821x_PHYSR 0x11 -#define RTL821x_PHYSR_DUPLEX 0x2000 -#define RTL821x_PHYSR_SPEED 0xc000 -#define RTL821x_INER 0x12 -#define RTL821x_INER_INIT 0x6400 -#define RTL821x_INSR 0x13 -#define RTL8211E_INER_LINK_STATUS 0x400 +#define RTL821x_PHYSR 0x11 +#define RTL821x_PHYSR_DUPLEX BIT(13) +#define RTL821x_PHYSR_SPEED GENMASK(15, 14) -#define RTL8211F_INER_LINK_STATUS 0x0010 -#define RTL8211F_INSR 0x1d -#define RTL8211F_PAGE_SELECT 0x1f -#define RTL8211F_TX_DELAY 0x100 +#define RTL821x_INER 0x12 +#define RTL8211B_INER_INIT 0x6400 +#define RTL8211E_INER_LINK_STATUS BIT(10) +#define RTL8211E_INER_ANEG_COMPLETED BIT(11) +#define RTL8211E_INER_PAGE_RECEIVED BIT(12) +#define RTL8211E_INER_ANEG_ERROR BIT(15) +#define RTL8211F_INER_LINK_STATUS BIT(4) +#define RTL8211F_INER_PHY_REGISTER_ACCESSIBLE BIT(5) +#define RTL8211F_INER_WOL_PME BIT(7) +#define RTL8211F_INER_ALDPS_STATE_CHANGE BIT(9) +#define RTL8211F_INER_JABBER BIT(10) + +#define RTL821x_INSR 0x13 + +#define RTL821x_PAGE_SELECT 0x1f + +#define RTL8211F_INSR 0x1d + +#define RTL8211F_RX_DELAY_REG 0x15 +#define RTL8211F_RX_DELAY_EN BIT(3) +#define RTL8211F_TX_DELAY_REG 0x11 +#define RTL8211F_TX_DELAY_EN BIT(8) + +#define RTL8201F_ISR 0x1e +#define RTL8201F_IER 0x13 + +#define RTL8211F_INTBCR 0x16 +#define RTL8211F_INTBCR_INTB_PMEB BIT(5) MODULE_DESCRIPTION("Realtek PHY driver"); MODULE_AUTHOR("Johnson Leung"); MODULE_LICENSE("GPL"); +static int rtl8211x_page_read(struct phy_device *phydev, u16 page, u16 address) +{ + int ret; + + ret = phy_write(phydev, RTL821x_PAGE_SELECT, page); + if (ret) + return ret; + + ret = phy_read(phydev, address); + + /* restore to default page 0 */ + phy_write(phydev, RTL821x_PAGE_SELECT, 0x0); + + return ret; +} + +static int rtl8211x_page_write(struct phy_device *phydev, u16 page, + u16 address, u16 val) +{ + int ret; + + ret = phy_write(phydev, RTL821x_PAGE_SELECT, page); + if (ret) + return ret; + + ret = phy_write(phydev, address, val); + + /* restore to default page 0 */ + phy_write(phydev, RTL821x_PAGE_SELECT, 0x0); + + return ret; +} + +static int rtl8211x_page_mask_bits(struct phy_device *phydev, u16 page, + u16 address, u16 mask, u16 set) +{ + int ret; + u16 val; + + ret = rtl8211x_page_read(phydev, page, address); + if (ret < 0) + return ret; + + val = ret & 0xffff; + val &= ~mask; + val |= (set & mask); + + return rtl8211x_page_write(phydev, page, address, val); +} + +static int rtl8201_ack_interrupt(struct phy_device *phydev) +{ + int err; + + err = phy_read(phydev, RTL8201F_ISR); + + return (err < 0) ? err : 0; +} + static int rtl821x_ack_interrupt(struct phy_device *phydev) { int err; @@ -46,21 +126,30 @@ static int rtl8211f_ack_interrupt(struct phy_device *phydev) { int err; - phy_write(phydev, RTL8211F_PAGE_SELECT, 0xa43); - err = phy_read(phydev, RTL8211F_INSR); - /* restore to default page 0 */ - phy_write(phydev, RTL8211F_PAGE_SELECT, 0x0); + err = rtl8211x_page_read(phydev, 0xa43, RTL8211F_INSR); return (err < 0) ? err : 0; } +static int rtl8201_config_intr(struct phy_device *phydev) +{ + u16 val; + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) + val = BIT(13) | BIT(12) | BIT(11); + else + val = 0; + + return rtl8211x_page_write(phydev, 0x7, RTL8201F_IER, val); +} + static int rtl8211b_config_intr(struct phy_device *phydev) { int err; if (phydev->interrupts == PHY_INTERRUPT_ENABLED) err = phy_write(phydev, RTL821x_INER, - RTL821x_INER_INIT); + RTL8211B_INER_INIT); else err = phy_write(phydev, RTL821x_INER, 0); @@ -83,34 +172,75 @@ static int rtl8211e_config_intr(struct phy_device *phydev) static int rtl8211f_config_intr(struct phy_device *phydev) { int err; + u16 val; - if (phydev->interrupts == PHY_INTERRUPT_ENABLED) - err = phy_write(phydev, RTL821x_INER, - RTL8211F_INER_LINK_STATUS); - else - err = phy_write(phydev, RTL821x_INER, 0); + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { + /* + * The interrupt pin has two functions: + * 0: INTB: it acts as interrupt pin which can be configured + * through RTL821x_INER and the status can be read through + * RTL8211F_INSR + * 1: PMEB: a special "Power Management Event" mode for + * Wake-on-LAN operation (with support for a "pulse low" + * wave format). Interrupts configured through RTL821x_INER + * will not work in this mode + * + * select INTB mode in the "INTB pin control" register to + * ensure that the interrupt pin is in the correct mode. + */ + err = rtl8211x_page_mask_bits(phydev, 0xd40, RTL8211F_INTBCR, + RTL8211F_INTBCR_INTB_PMEB, 0); + if (err) + return err; - return err; + val = RTL8211F_INER_LINK_STATUS; + } else { + val = 0; + } + + return rtl8211x_page_write(phydev, 0xa42, RTL821x_INER, val); } static int rtl8211f_config_init(struct phy_device *phydev) { int ret; - u16 reg; + u16 val; ret = genphy_config_init(phydev); if (ret < 0) return ret; - if (phydev->interface == PHY_INTERFACE_MODE_RGMII) { - /* enable TXDLY */ - phy_write(phydev, RTL8211F_PAGE_SELECT, 0xd08); - reg = phy_read(phydev, 0x11); - reg |= RTL8211F_TX_DELAY; - phy_write(phydev, 0x11, reg); - /* restore to default page 0 */ - phy_write(phydev, RTL8211F_PAGE_SELECT, 0x0); - } + /* + * enable TX-delay for rgmii-id and rgmii-txid, otherwise disable it. + * this is needed because it can be enabled by pin strapping and + * conflict with the TX-delay configured by the MAC. + */ + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) + val = RTL8211F_TX_DELAY_EN; + else + val = 0; + + ret = rtl8211x_page_mask_bits(phydev, 0xd08, RTL8211F_TX_DELAY_REG, + RTL8211F_TX_DELAY_EN, val); + if (ret) + return ret; + + /* + * enable RX-delay for rgmii-id and rgmii-rxid, otherwise disable it. + * this is needed because it can be enabled by pin strapping and + * conflict with the RX-delay configured by the MAC. + */ + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) + val = RTL8211F_RX_DELAY_EN; + else + val = 0; + + ret = rtl8211x_page_mask_bits(phydev, 0xd08, RTL8211F_RX_DELAY_REG, + RTL8211F_RX_DELAY_EN, val); + if (ret) + return ret; return 0; } @@ -125,6 +255,18 @@ static struct phy_driver realtek_drvs[] = { .config_aneg = &genphy_config_aneg, .read_status = &genphy_read_status, .driver = { .owner = THIS_MODULE,}, + }, { + .phy_id = 0x001cc816, + .name = "RTL8201F 10/100Mbps Ethernet", + .phy_id_mask = 0x001fffff, + .features = PHY_BASIC_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .config_aneg = &genphy_config_aneg, + .read_status = &genphy_read_status, + .ack_interrupt = &rtl8201_ack_interrupt, + .config_intr = &rtl8201_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, }, { .phy_id = 0x001cc912, .name = "RTL8211B Gigabit Ethernet", @@ -182,6 +324,7 @@ static struct phy_driver realtek_drvs[] = { module_phy_driver(realtek_drvs); static struct mdio_device_id __maybe_unused realtek_tbl[] = { + { 0x001cc816, 0x001fffff }, { 0x001cc912, 0x001fffff }, { 0x001cc914, 0x001fffff }, { 0x001cc915, 0x001fffff }, diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/Kconfig b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/Kconfig index 303e009b..10c06951 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/Kconfig +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/Kconfig @@ -38,6 +38,9 @@ config BCMDHD_SDIO config BCMDHD_PCIE bool "PCIe bus interface support" depends on BCMDHD && PCI +config BCMDHD_USB + bool "USB bus interface support" + depends on BCMDHD && USB endchoice choice diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/Makefile b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/Makefile index a24a0826..5588178b 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/Makefile +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/Makefile @@ -6,122 +6,165 @@ MODULE_NAME = bcmdhd CONFIG_BCMDHD_SDIO := y #CONFIG_BCMDHD_PCIE := y +#CONFIG_BCMDHD_USB := y CONFIG_BCMDHD_OOB := y CONFIG_BCMDHD_PROPTXSTATUS := y CONFIG_BCMDHD_AG := y #CONFIG_DHD_USE_STATIC_BUF := y CONFIG_VTS_SUPPORT := y +#CONFIG_LOGTRACE := y -DHDCFLAGS = -Wall -Wstrict-prototypes -Dlinux -DBCMDRIVER -DSDTEST \ +CONFIG_MACH_PLATFORM := y +#CONFIG_BCMDHD_DTS := y + +DHDCFLAGS = -Wall -Wstrict-prototypes -Dlinux -DBCMDRIVER \ -DBCMDONGLEHOST -DUNRELEASEDCHIP -DBCMDMA32 -DBCMFILEIMAGE \ -DDHDTHREAD -DDHD_DEBUG -DSHOW_EVENTS -DBCMDBG -DGET_OTP_MAC_ENABLE \ -DWIFI_ACT_FRAME -DARP_OFFLOAD_SUPPORT -DSUPPORT_PM2_ONLY \ -DKEEP_ALIVE -DPKT_FILTER_SUPPORT -DPNO_SUPPORT -DDHDTCPACK_SUPPRESS \ - -DDHD_DONOT_FORWARD_BCMEVENT_AS_NETWORK_PKT -DRXFRAME_THREAD \ - -DTSQ_MULTIPLIER -DMFP -DWL_EXT_IAPSTA \ + -DDHD_DONOT_FORWARD_BCMEVENT_AS_NETWORK_PKT \ + -DMULTIPLE_SUPPLICANT -DTSQ_MULTIPLIER -DMFP \ + -DWL_EXT_IAPSTA -DSUPPORT_P2P_GO_PS \ -DENABLE_INSMOD_NO_FW_LOAD -DDHD_UNSUPPORT_IF_CNTS \ -Idrivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd \ -Idrivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include -DHDOFILES = aiutils.o siutils.o sbutils.o bcmutils.o bcmwifi_channels.o \ - dhd_linux.o dhd_linux_platdev.o dhd_linux_sched.o dhd_pno.o \ - dhd_common.o dhd_ip.o dhd_linux_wq.o dhd_custom_gpio.o \ - bcmevent.o hndpmu.o linux_osl.o wldev_common.o wl_android.o \ +DHDOFILES = aiutils.o siutils.o sbutils.o bcmutils.o bcmwifi_channels.o \ + dhd_linux.o dhd_linux_platdev.o dhd_linux_sched.o dhd_pno.o \ + dhd_common.o dhd_ip.o dhd_linux_wq.o dhd_custom_gpio.o \ + bcmevent.o hndpmu.o linux_osl.o wldev_common.o wl_android.o \ hnd_pktq.o hnd_pktpool.o dhd_config.o wl_android_ext.o +#BCMDHD_SDIO ifneq ($(CONFIG_BCMDHD_SDIO),) -DHDCFLAGS += \ - -DBCMSDIO -DMMC_SDIO_ABORT -DBCMLXSDMMC -DUSE_SDIOFIFO_IOVAR \ - -DBDC -DDHD_USE_IDLECOUNT -DBCMSDIOH_TXGLOM -DBCMSDIOH_TXGLOM_EXT \ - -DCUSTOM_SDIO_F2_BLKSIZE=256 - -DHDOFILES += bcmsdh.o bcmsdh_linux.o bcmsdh_sdmmc.o bcmsdh_sdmmc_linux.o \ - dhd_sdio.o dhd_cdc.o dhd_wlfc.o - +DHDCFLAGS += -DBCMSDIO -DMMC_SDIO_ABORT -DBCMLXSDMMC -DUSE_SDIOFIFO_IOVAR \ + -DSDTEST -DBDC -DDHD_USE_IDLECOUNT -DCUSTOM_SDIO_F2_BLKSIZE=256 \ + -DBCMSDIOH_TXGLOM -DBCMSDIOH_TXGLOM_EXT -DRXFRAME_THREAD ifeq ($(CONFIG_BCMDHD_OOB),y) -DHDCFLAGS += -DOOB_INTR_ONLY -DCUSTOMER_OOB -DHW_OOB + DHDCFLAGS += -DOOB_INTR_ONLY -DCUSTOMER_OOB -DHW_OOB ifeq ($(CONFIG_BCMDHD_DISABLE_WOWLAN),y) -DHDCFLAGS += -DDISABLE_WOWLAN + DHDCFLAGS += -DDISABLE_WOWLAN endif else -DHDCFLAGS += -DSDIO_ISR_THREAD -endif + DHDCFLAGS += -DSDIO_ISR_THREAD endif -ifeq ($(CONFIG_BCMDHD_PROPTXSTATUS),y) -ifneq ($(CONFIG_BCMDHD_SDIO),) -DHDCFLAGS += -DPROP_TXSTATUS -endif -ifneq ($(CONFIG_CFG80211),) -DHDCFLAGS += -DPROP_TXSTATUS_VSDB -endif +DHDOFILES += bcmsdh.o bcmsdh_linux.o bcmsdh_sdmmc.o bcmsdh_sdmmc_linux.o \ + dhd_sdio.o dhd_cdc.o dhd_wlfc.o endif +#BCMDHD_PCIE ifneq ($(CONFIG_BCMDHD_PCIE),) -DHDCFLAGS += \ - -DPCIE_FULL_DONGLE -DBCMPCIE -DCUSTOM_DPC_PRIO_SETTING=-1 +DHDCFLAGS += -DPCIE_FULL_DONGLE -DBCMPCIE -DCUSTOM_DPC_PRIO_SETTING=-1 \ + -DDONGLE_ENABLE_ISOLATION +ifneq ($(CONFIG_PCI_MSI),) + DHDCFLAGS += -DDHD_USE_MSI +endif ifeq ($(CONFIG_DHD_USE_STATIC_BUF),y) -DHDCFLAGS += -DDHD_USE_STATIC_CTRLBUF + DHDCFLAGS += -DDHD_USE_STATIC_CTRLBUF endif -DHDOFILES += dhd_pcie.o dhd_pcie_linux.o pcie_core.o dhd_flowring.o \ +DHDOFILES += dhd_pcie.o dhd_pcie_linux.o pcie_core.o dhd_flowring.o \ dhd_msgbuf.o endif +#BCMDHD_USB +ifneq ($(CONFIG_BCMDHD_USB),) +DHDCFLAGS += -DUSBOS_TX_THREAD -DBCMDBUS -DBCMTRXV2 -DDBUS_USB_LOOPBACK \ + -DBDC +DHDCFLAGS += -DBCM_REQUEST_FW -DEXTERNAL_FW_PATH +#DHDCFLAGS :=$(filter-out -DENABLE_INSMOD_NO_FW_LOAD,$(DHDCFLAGS)) + +DHDOFILES += dbus.o dbus_usb.o dbus_usb_linux.o dhd_cdc.o dhd_wlfc.o +endif + +ifeq ($(CONFIG_BCMDHD_PROPTXSTATUS),y) +ifneq ($(CONFIG_BCMDHD_USB),) + DHDCFLAGS += -DPROP_TXSTATUS +endif +ifneq ($(CONFIG_BCMDHD_SDIO),) + DHDCFLAGS += -DPROP_TXSTATUS +endif +ifneq ($(CONFIG_CFG80211),) + DHDCFLAGS += -DPROP_TXSTATUS_VSDB +endif +endif + +#VTS_SUPPORT ifeq ($(CONFIG_VTS_SUPPORT),y) -DHDCFLAGS += \ - -DGSCAN_SUPPORT -DRTT_SUPPORT -DCUSTOM_FORCE_NODFS_FLAG \ - -DLINKSTAT_SUPPORT -DDEBUGABILITY -DDBG_PKT_MON -DKEEP_ALIVE -DPKT_FILTER_SUPPORT \ - -DAPF -DNDO_CONFIG_SUPPORT -DRSSI_MONITOR_SUPPORT -DDHDTCPACK_SUPPRESS -DDHD_WAKE_STATUS \ +ifneq ($(CONFIG_CFG80211),) +DHDCFLAGS += -DGSCAN_SUPPORT -DRTT_SUPPORT -DCUSTOM_FORCE_NODFS_FLAG \ + -DLINKSTAT_SUPPORT -DDEBUGABILITY -DDBG_PKT_MON -DPKT_FILTER_SUPPORT \ + -DAPF -DNDO_CONFIG_SUPPORT -DRSSI_MONITOR_SUPPORT -DDHD_WAKE_STATUS \ -DCUSTOM_COUNTRY_CODE -DDHD_FW_COREDUMP -DEXPLICIT_DISCIF_CLEANUP -DHDOFILES += dhd_debug_linux.o dhd_debug.o bcmxtlv.o \ - dhd_rtt.o bcm_app_utils.o +DHDOFILES += bcmxtlv.o dhd_rtt.o bcm_app_utils.o +CONFIG_LOGTRACE := y +endif +endif + +#LOGTRACE +ifeq ($(CONFIG_LOGTRACE),y) + DHDCFLAGS += -DSHOW_LOGTRACE + DHDOFILES += dhd_debug_linux.o dhd_debug.o dhd_mschdbg.o endif +# MESH support for kernel 3.10 later +ifeq ($(CONFIG_WL_MESH),y) + DHDCFLAGS += -DWLMESH +ifneq ($(CONFIG_BCMDHD_PCIE),) + DHDCFLAGS += -DBCM_HOST_BUF -DDMA_HOST_BUFFER_LEN=0x80000 +endif + DHDCFLAGS += -DDHD_UPDATE_INTF_MAC + DHDCFLAGS :=$(filter-out -DDHD_FW_COREDUMP,$(DHDCFLAGS)) + DHDCFLAGS :=$(filter-out -DSET_RANDOM_MAC_SOFTAP,$(DHDCFLAGS)) +endif + +#obj-$(CONFIG_RKWIFI) += bcmdhd.o obj-$(CONFIG_AP6XXX) += bcmdhd.o bcmdhd-objs += $(DHDOFILES) -#ifeq ($(CONFIG_MACH_PLATFORM),y) -DHDOFILES += dhd_gpio.o -DHDCFLAGS += -DCUSTOMER_HW -DDHD_OF_SUPPORT -#DHDCFLAGS += -DBCMWAPI_WPI -DBCMWAPI_WAI -#endif +ifeq ($(CONFIG_MACH_PLATFORM),y) + DHDOFILES += dhd_gpio.o +ifeq ($(CONFIG_BCMDHD_DTS),y) + DHDCFLAGS += -DCONFIG_DTS +else + DHDCFLAGS += -DCUSTOMER_HW -DDHD_OF_SUPPORT +endif +# DHDCFLAGS += -DBCMWAPI_WPI -DBCMWAPI_WAI +endif ifeq ($(CONFIG_BCMDHD_AG),y) -DHDCFLAGS += -DBAND_AG + DHDCFLAGS += -DBAND_AG endif ifeq ($(CONFIG_DHD_USE_STATIC_BUF),y) -obj-m += dhd_static_buf.o -DHDCFLAGS += -DSTATIC_WL_PRIV_STRUCT -DENHANCED_STATIC_BUF -DHDCFLAGS += -DDHD_USE_STATIC_MEMDUMP -DCONFIG_DHD_USE_STATIC_BUF + obj-m += dhd_static_buf.o + DHDCFLAGS += -DSTATIC_WL_PRIV_STRUCT -DENHANCED_STATIC_BUF + DHDCFLAGS += -DDHD_USE_STATIC_MEMDUMP -DCONFIG_DHD_USE_STATIC_BUF endif ifneq ($(CONFIG_WIRELESS_EXT),) -DHDOFILES += wl_iw.o wl_escan.o -DHDCFLAGS += -DSOFTAP -DWL_WIRELESS_EXT -DUSE_IW -DWL_ESCAN + DHDOFILES += wl_iw.o wl_escan.o + DHDCFLAGS += -DSOFTAP -DWL_WIRELESS_EXT -DUSE_IW -DWL_ESCAN endif ifneq ($(CONFIG_CFG80211),) -DHDOFILES += wl_cfg80211.o wl_cfgp2p.o wl_linux_mon.o wl_cfg_btcoex.o wl_cfgvendor.o -DHDOFILES += dhd_cfg80211.o -DHDCFLAGS += -DWL_CFG80211 -DWLP2P -DWL_CFG80211_STA_EVENT -DWL_ENABLE_P2P_IF -#DHDCFLAGS += -DWL_IFACE_COMB_NUM_CHANNELS -DHDCFLAGS += -DCUSTOM_ROAM_TRIGGER_SETTING=-65 -DHDCFLAGS += -DCUSTOM_ROAM_DELTA_SETTING=15 -DHDCFLAGS += -DCUSTOM_KEEP_ALIVE_SETTING=28000 -DHDCFLAGS += -DCUSTOM_PNO_EVENT_LOCK_xTIME=7 -DHDCFLAGS += -DWL_SUPPORT_AUTO_CHANNEL -DHDCFLAGS += -DWL_SUPPORT_BACKPORTED_KPATCHES -DHDCFLAGS += -DESCAN_RESULT_PATCH -DHDCFLAGS += -DVSDB -DWL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST -DHDCFLAGS += -DWLTDLS -DMIRACAST_AMPDU_SIZE=8 -DHDCFLAGS += -DWL_VIRTUAL_APSTA + DHDOFILES += wl_cfg80211.o wl_cfgp2p.o wl_linux_mon.o wl_cfg_btcoex.o wl_cfgvendor.o + DHDOFILES += dhd_cfg80211.o + DHDCFLAGS += -DWL_CFG80211 -DWLP2P -DWL_CFG80211_STA_EVENT -DWL_ENABLE_P2P_IF +# DHDCFLAGS += -DWL_IFACE_COMB_NUM_CHANNELS + DHDCFLAGS += -DCUSTOM_ROAM_TRIGGER_SETTING=-65 + DHDCFLAGS += -DCUSTOM_ROAM_DELTA_SETTING=15 + DHDCFLAGS += -DCUSTOM_KEEP_ALIVE_SETTING=28000 + DHDCFLAGS += -DCUSTOM_PNO_EVENT_LOCK_xTIME=7 + DHDCFLAGS += -DWL_SUPPORT_AUTO_CHANNEL + DHDCFLAGS += -DWL_SUPPORT_BACKPORTED_KPATCHES + DHDCFLAGS += -DESCAN_RESULT_PATCH -DESCAN_BUF_OVERFLOW_MGMT + DHDCFLAGS += -DVSDB -DWL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST + DHDCFLAGS += -DWLTDLS -DMIRACAST_AMPDU_SIZE=8 + DHDCFLAGS += -DWL_VIRTUAL_APSTA endif EXTRA_CFLAGS = $(DHDCFLAGS) ifeq ($(CONFIG_BCMDHD),m) -DHDCFLAGS += -DMULTIPLE_SUPPLICANT EXTRA_LDFLAGS += --strip-debug -else -DHDCFLAGS += -DBUILD_IN_KERNEL endif diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/bcmsdh_linux.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/bcmsdh_linux.c index 9e3f641b..41462a37 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/bcmsdh_linux.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/bcmsdh_linux.c @@ -52,7 +52,6 @@ extern void dhdsdio_isr(void * args); #endif /* defined(CONFIG_ARCH_ODIN) */ #include - /* driver info, initialized when bcmsdh_register is called */ static bcmsdh_driver_t drvinfo = {NULL, NULL, NULL, NULL}; @@ -363,13 +362,13 @@ int bcmsdh_oob_intr_register(bcmsdh_info_t *bcmsdh, bcmsdh_cb_fn_t oob_irq_handl SDLX_MSG(("%s: irq is already registered\n", __FUNCTION__)); return -EBUSY; } - SDLX_MSG(("%s %s irq=%d flags=0x%X\n", __FUNCTION__, #ifdef HW_OOB - "HW_OOB", + printf("%s: HW_OOB irq=%d flags=0x%X\n", __FUNCTION__, + (int)bcmsdh_osinfo->oob_irq_num, (int)bcmsdh_osinfo->oob_irq_flags); #else - "SW_OOB", + printf("%s: SW_OOB irq=%d flags=0x%X\n", __FUNCTION__, + (int)bcmsdh_osinfo->oob_irq_num, (int)bcmsdh_osinfo->oob_irq_flags); #endif - (int)bcmsdh_osinfo->oob_irq_num, (int)bcmsdh_osinfo->oob_irq_flags)); bcmsdh_osinfo->oob_irq_handler = oob_irq_handler; bcmsdh_osinfo->oob_irq_handler_context = oob_irq_handler_context; bcmsdh_osinfo->oob_irq_enabled = TRUE; @@ -398,6 +397,7 @@ int bcmsdh_oob_intr_register(bcmsdh_info_t *bcmsdh, bcmsdh_cb_fn_t oob_irq_handl else bcmsdh_osinfo->oob_irq_wake_enabled = TRUE; #endif + return 0; } @@ -423,7 +423,7 @@ void bcmsdh_oob_intr_unregister(bcmsdh_info_t *bcmsdh) free_irq(bcmsdh_osinfo->oob_irq_num, bcmsdh); bcmsdh_osinfo->oob_irq_registered = FALSE; } -#endif +#endif /* Module parameters specific to each host-controller driver */ diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/bcmsdh_sdmmc.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/bcmsdh_sdmmc.c index ccfcce2c..31b05ce7 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/bcmsdh_sdmmc.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/bcmsdh_sdmmc.c @@ -995,7 +995,6 @@ sdioh_set_mode(sdioh_info_t *sd, uint mode) sd->txglom_mode = mode; else if (mode == SDPCM_TXGLOM_MDESC) sd->txglom_mode = mode; - printf("%s: set txglom_mode to %s\n", __FUNCTION__, mode==SDPCM_TXGLOM_MDESC?"multi-desc":"copy"); return (sd->txglom_mode); } @@ -1288,8 +1287,8 @@ sdioh_request_packet_chain(sdioh_info_t *sd, uint fix_inc, uint write, uint func if (sd_msglevel & SDH_COST_VAL) { getnstimeofday(&now); - sd_cost(("%s: rw=%d, cost=%lds %luus\n", __FUNCTION__, - write, now.tv_sec-before.tv_sec, now.tv_nsec/1000-before.tv_nsec/1000)); + sd_cost(("%s: rw=%d, ttl_len=%d, cost=%lds %luus\n", __FUNCTION__, + write, ttl_len, now.tv_sec-before.tv_sec, now.tv_nsec/1000-before.tv_nsec/1000)); } sd_trace(("%s: Exit\n", __FUNCTION__)); diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/bcmsdh_sdmmc_linux.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/bcmsdh_sdmmc_linux.c old mode 100755 new mode 100644 index 8864582b..35b91ff7 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/bcmsdh_sdmmc_linux.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/bcmsdh_sdmmc_linux.c @@ -225,14 +225,14 @@ static const struct sdio_device_id bcmsdh_sdmmc_ids[] = { { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334) }, { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4324) }, { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43239) }, - { SDIO_DEVICE_CLASS(SDIO_CLASS_NONE) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_ANY_ID) }, { 0, 0, 0, 0 /* end: all zeroes */ }, }; MODULE_DEVICE_TABLE(sdio, bcmsdh_sdmmc_ids); -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM_SLEEP) +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) static int bcmsdh_sdmmc_suspend(struct device *pdev) { int err; @@ -275,9 +275,7 @@ static int bcmsdh_sdmmc_suspend(struct device *pdev) static int bcmsdh_sdmmc_resume(struct device *pdev) { -#if defined(OOB_INTR_ONLY) sdioh_info_t *sdioh; -#endif struct sdio_func *func = dev_to_sdio_func(pdev); printf("%s Enter func->num=%d\n", __FUNCTION__, func->num); @@ -285,10 +283,8 @@ static int bcmsdh_sdmmc_resume(struct device *pdev) return 0; dhd_mmc_suspend = FALSE; -#if defined(OOB_INTR_ONLY) sdioh = sdio_get_drvdata(func); bcmsdh_resume(sdioh->bcmsdh); -#endif smp_mb(); printf("%s Exit\n", __FUNCTION__); @@ -346,7 +342,7 @@ static struct sdio_driver bcmsdh_sdmmc_driver = { .remove = bcmsdh_sdmmc_remove, .name = "bcmsdh_sdmmc", .id_table = bcmsdh_sdmmc_ids, -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM_SLEEP) +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) .drv = { .pm = &bcmsdh_sdmmc_pm_ops, }, diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dbus.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dbus.c new file mode 100644 index 00000000..aeec7761 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dbus.c @@ -0,0 +1,2929 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/** @file dbus.c + * + * Hides details of USB / SDIO / SPI interfaces and OS details. It is intended to shield details and + * provide the caller with one common bus interface for all dongle devices. In practice, it is only + * used for USB interfaces. DBUS is not a protocol, but an abstraction layer. + * + * Copyright (C) 1999-2016, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * + * <> + * + * $Id: dbus.c 553311 2015-04-29 10:23:08Z $ + */ + + +#include "osl.h" +#include "dbus.h" +#include +#include +#include +#include +#ifdef PROP_TXSTATUS /* a form of flow control between host and dongle */ +#include +#endif +#include + +#if defined(BCM_REQUEST_FW) +#include +#include +#include +#include +#include +#include +#include +#endif + + + +#if defined(BCM_REQUEST_FW) +#ifndef VARS_MAX +#define VARS_MAX 8192 +#endif +#endif + +#ifdef DBUS_USB_LOOPBACK +extern bool is_loopback_pkt(void *buf); +extern int matches_loopback_pkt(void *buf); +#endif + +/** General info for all BUS types */ +typedef struct dbus_irbq { + dbus_irb_t *head; + dbus_irb_t *tail; + int cnt; +} dbus_irbq_t; + +/** + * This private structure dhd_bus_t is also declared in dbus_usb_linux.c. + * All the fields must be consistent in both declarations. + */ +typedef struct dhd_bus { + dbus_pub_t pub; /* MUST BE FIRST */ + dhd_pub_t *dhd; + + void *cbarg; + dbus_callbacks_t *cbs; /* callbacks to higher level, e.g. dhd_linux.c */ + void *bus_info; + dbus_intf_t *drvintf; /* callbacks to lower level, e.g. dbus_usb.c or dbus_usb_linux.c */ + uint8 *fw; + int fwlen; + uint32 errmask; + int rx_low_watermark; /* avoid rx overflow by filling rx with free IRBs */ + int tx_low_watermark; + bool txoff; + bool txoverride; /* flow control related */ + bool rxoff; + bool tx_timer_ticking; + + + dbus_irbq_t *rx_q; + dbus_irbq_t *tx_q; + + uint8 *nvram; + int nvram_len; + uint8 *image; /* buffer for combine fw and nvram */ + int image_len; + uint8 *orig_fw; + int origfw_len; + int decomp_memsize; + dbus_extdl_t extdl; + int nvram_nontxt; +#if defined(BCM_REQUEST_FW) + void *firmware; + void *nvfile; +#endif + char *fw_path; /* module_param: path to firmware image */ + char *nv_path; /* module_param: path to nvram vars file */ +} dhd_bus_t; + +struct exec_parms { + union { + /* Can consolidate same params, if need be, but this shows + * group of parameters per function + */ + struct { + dbus_irbq_t *q; + dbus_irb_t *b; + } qenq; + + struct { + dbus_irbq_t *q; + } qdeq; + }; +}; + +#define EXEC_RXLOCK(info, fn, a) \ + info->drvintf->exec_rxlock(dhd_bus->bus_info, ((exec_cb_t)fn), ((struct exec_parms *) a)) + +#define EXEC_TXLOCK(info, fn, a) \ + info->drvintf->exec_txlock(dhd_bus->bus_info, ((exec_cb_t)fn), ((struct exec_parms *) a)) + +/* + * Callbacks common for all BUS + */ +static void dbus_if_send_irb_timeout(void *handle, dbus_irb_tx_t *txirb); +static void dbus_if_send_irb_complete(void *handle, dbus_irb_tx_t *txirb, int status); +static void dbus_if_recv_irb_complete(void *handle, dbus_irb_rx_t *rxirb, int status); +static void dbus_if_errhandler(void *handle, int err); +static void dbus_if_ctl_complete(void *handle, int type, int status); +static void dbus_if_state_change(void *handle, int state); +static void *dbus_if_pktget(void *handle, uint len, bool send); +static void dbus_if_pktfree(void *handle, void *p, bool send); +static struct dbus_irb *dbus_if_getirb(void *cbarg, bool send); +static void dbus_if_rxerr_indicate(void *handle, bool on); + +void * dhd_dbus_probe_cb(void *arg, const char *desc, uint32 bustype, + uint16 bus_no, uint16 slot, uint32 hdrlen); +void dhd_dbus_disconnect_cb(void *arg); +void dbus_detach(dhd_bus_t *pub); + +/** functions in this file that are called by lower DBUS levels, e.g. dbus_usb.c */ +static dbus_intf_callbacks_t dbus_intf_cbs = { + dbus_if_send_irb_timeout, + dbus_if_send_irb_complete, + dbus_if_recv_irb_complete, + dbus_if_errhandler, + dbus_if_ctl_complete, + dbus_if_state_change, + NULL, /* isr */ + NULL, /* dpc */ + NULL, /* watchdog */ + dbus_if_pktget, + dbus_if_pktfree, + dbus_if_getirb, + dbus_if_rxerr_indicate +}; + +/* + * Need global for probe() and disconnect() since + * attach() is not called at probe and detach() + * can be called inside disconnect() + */ +static dbus_intf_t *g_busintf = NULL; +static probe_cb_t probe_cb = NULL; +static disconnect_cb_t disconnect_cb = NULL; +static void *probe_arg = NULL; +static void *disc_arg = NULL; + +#if defined(BCM_REQUEST_FW) +int8 *nonfwnvram = NULL; /* stand-alone multi-nvram given with driver load */ +int nonfwnvramlen = 0; +#endif /* #if defined(BCM_REQUEST_FW) */ + +static void* q_enq(dbus_irbq_t *q, dbus_irb_t *b); +static void* q_enq_exec(struct exec_parms *args); +static dbus_irb_t*q_deq(dbus_irbq_t *q); +static void* q_deq_exec(struct exec_parms *args); +static int dbus_tx_timer_init(dhd_bus_t *dhd_bus); +static int dbus_tx_timer_start(dhd_bus_t *dhd_bus, uint timeout); +static int dbus_tx_timer_stop(dhd_bus_t *dhd_bus); +static int dbus_irbq_init(dhd_bus_t *dhd_bus, dbus_irbq_t *q, int nq, int size_irb); +static int dbus_irbq_deinit(dhd_bus_t *dhd_bus, dbus_irbq_t *q, int size_irb); +static int dbus_rxirbs_fill(dhd_bus_t *dhd_bus); +static int dbus_send_irb(dbus_pub_t *pub, uint8 *buf, int len, void *pkt, void *info); +static void dbus_disconnect(void *handle); +static void *dbus_probe(void *arg, const char *desc, uint32 bustype, + uint16 bus_no, uint16 slot, uint32 hdrlen); + +#if defined(BCM_REQUEST_FW) +extern char * dngl_firmware; +extern unsigned int dngl_fwlen; +#ifndef EXTERNAL_FW_PATH +static int dbus_get_nvram(dhd_bus_t *dhd_bus); +static int dbus_jumbo_nvram(dhd_bus_t *dhd_bus); +static int dbus_otp(dhd_bus_t *dhd_bus, uint16 *boardtype, uint16 *boardrev); +static int dbus_select_nvram(dhd_bus_t *dhd_bus, int8 *jumbonvram, int jumbolen, +uint16 boardtype, uint16 boardrev, int8 **nvram, int *nvram_len); +#endif /* !EXTERNAL_FW_PATH */ +extern int dbus_zlib_decomp(dhd_bus_t *dhd_bus); +extern void *dbus_zlib_calloc(int num, int size); +extern void dbus_zlib_free(void *ptr); +#endif + +/* function */ +void +dbus_flowctrl_tx(void *dbi, bool on) +{ + dhd_bus_t *dhd_bus = dbi; + + if (dhd_bus == NULL) + return; + + DBUSTRACE(("%s on %d\n", __FUNCTION__, on)); + + if (dhd_bus->txoff == on) + return; + + dhd_bus->txoff = on; + + if (dhd_bus->cbs && dhd_bus->cbs->txflowcontrol) + dhd_bus->cbs->txflowcontrol(dhd_bus->cbarg, on); +} + +/** + * if lower level DBUS signaled a rx error, more free rx IRBs should be allocated or flow control + * should kick in to make more free rx IRBs available. + */ +static void +dbus_if_rxerr_indicate(void *handle, bool on) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) handle; + + DBUSTRACE(("%s, on %d\n", __FUNCTION__, on)); + + if (dhd_bus == NULL) + return; + + if (dhd_bus->txoverride == on) + return; + + dhd_bus->txoverride = on; /* flow control */ + + if (!on) + dbus_rxirbs_fill(dhd_bus); + +} + +/** q_enq()/q_deq() are executed with protection via exec_rxlock()/exec_txlock() */ +static void* +q_enq(dbus_irbq_t *q, dbus_irb_t *b) +{ + ASSERT(q->tail != b); + ASSERT(b->next == NULL); + b->next = NULL; + if (q->tail) { + q->tail->next = b; + q->tail = b; + } else + q->head = q->tail = b; + + q->cnt++; + + return b; +} + +static void* +q_enq_exec(struct exec_parms *args) +{ + return q_enq(args->qenq.q, args->qenq.b); +} + +static dbus_irb_t* +q_deq(dbus_irbq_t *q) +{ + dbus_irb_t *b; + + b = q->head; + if (b) { + q->head = q->head->next; + b->next = NULL; + + if (q->head == NULL) + q->tail = q->head; + + q->cnt--; + } + return b; +} + +static void* +q_deq_exec(struct exec_parms *args) +{ + return q_deq(args->qdeq.q); +} + +/** + * called during attach phase. Status @ Dec 2012: this function does nothing since for all of the + * lower DBUS levels dhd_bus->drvintf->tx_timer_init is NULL. + */ +static int +dbus_tx_timer_init(dhd_bus_t *dhd_bus) +{ + if (dhd_bus && dhd_bus->drvintf && dhd_bus->drvintf->tx_timer_init) + return dhd_bus->drvintf->tx_timer_init(dhd_bus->bus_info); + else + return DBUS_ERR; +} + +static int +dbus_tx_timer_start(dhd_bus_t *dhd_bus, uint timeout) +{ + if (dhd_bus == NULL) + return DBUS_ERR; + + if (dhd_bus->tx_timer_ticking) + return DBUS_OK; + + if (dhd_bus->drvintf && dhd_bus->drvintf->tx_timer_start) { + if (dhd_bus->drvintf->tx_timer_start(dhd_bus->bus_info, timeout) == DBUS_OK) { + dhd_bus->tx_timer_ticking = TRUE; + return DBUS_OK; + } + } + + return DBUS_ERR; +} + +static int +dbus_tx_timer_stop(dhd_bus_t *dhd_bus) +{ + if (dhd_bus == NULL) + return DBUS_ERR; + + if (!dhd_bus->tx_timer_ticking) + return DBUS_OK; + + if (dhd_bus->drvintf && dhd_bus->drvintf->tx_timer_stop) { + if (dhd_bus->drvintf->tx_timer_stop(dhd_bus->bus_info) == DBUS_OK) { + dhd_bus->tx_timer_ticking = FALSE; + return DBUS_OK; + } + } + + return DBUS_ERR; +} + +/** called during attach phase. */ +static int +dbus_irbq_init(dhd_bus_t *dhd_bus, dbus_irbq_t *q, int nq, int size_irb) +{ + int i; + dbus_irb_t *irb; + + ASSERT(q); + ASSERT(dhd_bus); + + for (i = 0; i < nq; i++) { + /* MALLOC dbus_irb_tx or dbus_irb_rx, but cast to simple dbus_irb_t linkedlist */ + irb = (dbus_irb_t *) MALLOC(dhd_bus->pub.osh, size_irb); + if (irb == NULL) { + ASSERT(irb); + return DBUS_ERR; + } + bzero(irb, size_irb); + + /* q_enq() does not need to go through EXEC_xxLOCK() during init() */ + q_enq(q, irb); + } + + return DBUS_OK; +} + +/** called during detach phase or when attach failed */ +static int +dbus_irbq_deinit(dhd_bus_t *dhd_bus, dbus_irbq_t *q, int size_irb) +{ + dbus_irb_t *irb; + + ASSERT(q); + ASSERT(dhd_bus); + + /* q_deq() does not need to go through EXEC_xxLOCK() + * during deinit(); all callbacks are stopped by this time + */ + while ((irb = q_deq(q)) != NULL) { + MFREE(dhd_bus->pub.osh, irb, size_irb); + } + + if (q->cnt) + DBUSERR(("deinit: q->cnt=%d > 0\n", q->cnt)); + return DBUS_OK; +} + +/** multiple code paths require the rx queue to be filled with more free IRBs */ +static int +dbus_rxirbs_fill(dhd_bus_t *dhd_bus) +{ + int err = DBUS_OK; + + + dbus_irb_rx_t *rxirb; + struct exec_parms args; + + ASSERT(dhd_bus); + if (dhd_bus->pub.busstate != DBUS_STATE_UP) { + DBUSERR(("dbus_rxirbs_fill: DBUS not up \n")); + return DBUS_ERR; + } else if (!dhd_bus->drvintf || (dhd_bus->drvintf->recv_irb == NULL)) { + /* Lower edge bus interface does not support recv_irb(). + * No need to pre-submit IRBs in this case. + */ + return DBUS_ERR; + } + + /* The dongle recv callback is freerunning without lock. So multiple callbacks(and this + * refill) can run in parallel. While the rxoff condition is triggered outside, + * below while loop has to check and abort posting more to avoid RPC rxq overflow. + */ + args.qdeq.q = dhd_bus->rx_q; + while ((!dhd_bus->rxoff) && + (rxirb = (EXEC_RXLOCK(dhd_bus, q_deq_exec, &args))) != NULL) { + err = dhd_bus->drvintf->recv_irb(dhd_bus->bus_info, rxirb); + if (err == DBUS_ERR_RXDROP || err == DBUS_ERR_RXFAIL) { + /* Add the the free rxirb back to the queue + * and wait till later + */ + bzero(rxirb, sizeof(dbus_irb_rx_t)); + args.qenq.q = dhd_bus->rx_q; + args.qenq.b = (dbus_irb_t *) rxirb; + EXEC_RXLOCK(dhd_bus, q_enq_exec, &args); + break; + } else if (err != DBUS_OK) { + int i = 0; + while (i++ < 100) { + DBUSERR(("%s :: memory leak for rxirb note?\n", __FUNCTION__)); + } + } + } + return err; +} /* dbus_rxirbs_fill */ + +/** called when the DBUS interface state changed. */ +void +dbus_flowctrl_rx(dbus_pub_t *pub, bool on) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) pub; + + if (dhd_bus == NULL) + return; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (dhd_bus->rxoff == on) + return; + + dhd_bus->rxoff = on; + + if (dhd_bus->pub.busstate == DBUS_STATE_UP) { + if (!on) { + /* post more irbs, resume rx if necessary */ + dbus_rxirbs_fill(dhd_bus); + if (dhd_bus && dhd_bus->drvintf->recv_resume) { + dhd_bus->drvintf->recv_resume(dhd_bus->bus_info); + } + } else { + /* ??? cancell posted irbs first */ + + if (dhd_bus && dhd_bus->drvintf->recv_stop) { + dhd_bus->drvintf->recv_stop(dhd_bus->bus_info); + } + } + } +} + +/** + * Several code paths in this file want to send a buffer to the dongle. This function handles both + * sending of a buffer or a pkt. + */ +static int +dbus_send_irb(dbus_pub_t *pub, uint8 *buf, int len, void *pkt, void *info) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) pub; + int err = DBUS_OK; + dbus_irb_tx_t *txirb = NULL; + int txirb_pending; + struct exec_parms args; + + if (dhd_bus == NULL) + return DBUS_ERR; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (dhd_bus->pub.busstate == DBUS_STATE_UP || + dhd_bus->pub.busstate == DBUS_STATE_SLEEP) { + args.qdeq.q = dhd_bus->tx_q; + if (dhd_bus->drvintf) + txirb = EXEC_TXLOCK(dhd_bus, q_deq_exec, &args); + + if (txirb == NULL) { + DBUSERR(("Out of tx dbus_bufs\n")); + return DBUS_ERR; + } + + if (pkt != NULL) { + txirb->pkt = pkt; + txirb->buf = NULL; + txirb->len = 0; + } else if (buf != NULL) { + txirb->pkt = NULL; + txirb->buf = buf; + txirb->len = len; + } else { + ASSERT(0); /* Should not happen */ + } + txirb->info = info; + txirb->arg = NULL; + txirb->retry_count = 0; + + if (dhd_bus->drvintf && dhd_bus->drvintf->send_irb) { + /* call lower DBUS level send_irb function */ + err = dhd_bus->drvintf->send_irb(dhd_bus->bus_info, txirb); + if (err == DBUS_ERR_TXDROP) { + /* tx fail and no completion routine to clean up, reclaim irb NOW */ + DBUSERR(("%s: send_irb failed, status = %d\n", __FUNCTION__, err)); + bzero(txirb, sizeof(dbus_irb_tx_t)); + args.qenq.q = dhd_bus->tx_q; + args.qenq.b = (dbus_irb_t *) txirb; + EXEC_TXLOCK(dhd_bus, q_enq_exec, &args); + } else { + dbus_tx_timer_start(dhd_bus, DBUS_TX_TIMEOUT_INTERVAL); + txirb_pending = dhd_bus->pub.ntxq - dhd_bus->tx_q->cnt; + if (txirb_pending > (dhd_bus->tx_low_watermark * 3)) { + dbus_flowctrl_tx(dhd_bus, TRUE); + } + } + } + } else { + err = DBUS_ERR_TXFAIL; + DBUSTRACE(("%s: bus down, send_irb failed\n", __FUNCTION__)); + } + + return err; +} /* dbus_send_irb */ + +#if defined(BCM_REQUEST_FW) + +/** + * Before downloading a firmware image into the dongle, the validity of the image must be checked. + */ +static int +check_file(osl_t *osh, unsigned char *headers) +{ + struct trx_header *trx; + int actual_len = -1; + + /* Extract trx header */ + trx = (struct trx_header *)headers; + if (ltoh32(trx->magic) != TRX_MAGIC) { + printf("Error: trx bad hdr %x\n", ltoh32(trx->magic)); + return -1; + } + + headers += SIZEOF_TRX(trx); + + /* TRX V1: get firmware len */ + /* TRX V2: get firmware len and DSG/CFG lengths */ + if (ltoh32(trx->flag_version) & TRX_UNCOMP_IMAGE) { + actual_len = ltoh32(trx->offsets[TRX_OFFSETS_DLFWLEN_IDX]) + + SIZEOF_TRX(trx); +#ifdef BCMTRXV2 + if (ISTRX_V2(trx)) { + actual_len += ltoh32(trx->offsets[TRX_OFFSETS_DSG_LEN_IDX]) + + ltoh32(trx->offsets[TRX_OFFSETS_CFG_LEN_IDX]); + } +#endif + return actual_len; + } else { + printf("compressed image\n"); + } + + return -1; +} + +#ifdef EXTERNAL_FW_PATH +static int +dbus_get_fw_nvram(dhd_bus_t *dhd_bus, char *pfw_path, char *pnv_path) +{ + int bcmerror = -1, i; + uint len, total_len; + void *nv_image = NULL, *fw_image = NULL; + char *nv_memblock = NULL, *fw_memblock = NULL; + char *bufp; + bool file_exists; + uint8 nvram_words_pad = 0; + uint memblock_size = 2048; + uint8 *memptr; + int actual_fwlen; + struct trx_header *hdr; + uint32 img_offset = 0; + int offset = 0; + + /* For Get nvram */ + file_exists = ((pnv_path != NULL) && (pnv_path[0] != '\0')); + if (file_exists) { + nv_image = dhd_os_open_image(pnv_path); + if (nv_image == NULL) { + printf("%s: Open nvram file failed %s\n", __FUNCTION__, pnv_path); + goto err; + } + } + nv_memblock = MALLOC(dhd_bus->pub.osh, MAX_NVRAMBUF_SIZE); + if (nv_memblock == NULL) { + DBUSERR(("%s: Failed to allocate memory %d bytes\n", + __FUNCTION__, MAX_NVRAMBUF_SIZE)); + goto err; + } + len = dhd_os_get_image_block(nv_memblock, MAX_NVRAMBUF_SIZE, nv_image); + if (len > 0 && len < MAX_NVRAMBUF_SIZE) { + bufp = (char *)nv_memblock; + bufp[len] = 0; + dhd_bus->nvram_len = process_nvram_vars(bufp, len); + if (dhd_bus->nvram_len % 4) + nvram_words_pad = 4 - dhd_bus->nvram_len % 4; + } else { + DBUSERR(("%s: error reading nvram file: %d\n", __FUNCTION__, len)); + bcmerror = DBUS_ERR_NVRAM; + goto err; + } + if (nv_image) + dhd_os_close_image(nv_image); + + /* For Get first block of fw to calculate total_len */ + file_exists = ((pfw_path != NULL) && (pfw_path[0] != '\0')); + if (file_exists) { + fw_image = dhd_os_open_image(pfw_path); + if (fw_image == NULL) { + printf("%s: Open fw file failed %s\n", __FUNCTION__, pfw_path); + goto err; + } + } + memptr = fw_memblock = MALLOC(dhd_bus->pub.osh, memblock_size); + if (fw_memblock == NULL) { + DBUSERR(("%s: Failed to allocate memory %d bytes\n", __FUNCTION__, + memblock_size)); + goto err; + } + len = dhd_os_get_image_block((char*)memptr, memblock_size, fw_image); + if ((actual_fwlen = check_file(dhd_bus->pub.osh, memptr)) <= 0) { + DBUSERR(("%s: bad firmware format!\n", __FUNCTION__)); + goto err; + } + + total_len = actual_fwlen + dhd_bus->nvram_len + nvram_words_pad; + dhd_bus->image = MALLOC(dhd_bus->pub.osh, total_len); + dhd_bus->image_len = total_len; + if (dhd_bus->image == NULL) { + DBUSERR(("%s: malloc failed!\n", __FUNCTION__)); + goto err; + } + + /* Step1: Copy trx header + firmwre */ + memptr = fw_memblock; + do { + if (len < 0) { + DBUSERR(("%s: dhd_os_get_image_block failed (%d)\n", __FUNCTION__, len)); + bcmerror = BCME_ERROR; + goto err; + } + bcopy(memptr, dhd_bus->image+offset, len); + offset += len; + } while ((len = dhd_os_get_image_block((char*)memptr, memblock_size, fw_image))); + /* Step2: Copy NVRAM + pad */ + hdr = (struct trx_header *)dhd_bus->image; + img_offset = SIZEOF_TRX(hdr) + hdr->offsets[TRX_OFFSETS_DLFWLEN_IDX]; + bcopy(nv_memblock, (uint8 *)(dhd_bus->image + img_offset), + dhd_bus->nvram_len); + img_offset += dhd_bus->nvram_len; + if (nvram_words_pad) { + bzero(&dhd_bus->image[img_offset], nvram_words_pad); + img_offset += nvram_words_pad; + } +#ifdef BCMTRXV2 + /* Step3: Copy DSG/CFG for V2 */ + if (ISTRX_V2(hdr) && + (hdr->offsets[TRX_OFFSETS_DSG_LEN_IDX] || + hdr->offsets[TRX_OFFSETS_CFG_LEN_IDX])) { + DBUSERR(("%s: fix me\n", __FUNCTION__)); + } +#endif /* BCMTRXV2 */ + /* Step4: update TRX header for nvram size */ + hdr = (struct trx_header *)dhd_bus->image; + hdr->len = htol32(total_len); + /* Pass the actual fw len */ + hdr->offsets[TRX_OFFSETS_NVM_LEN_IDX] = + htol32(dhd_bus->nvram_len + nvram_words_pad); + /* Calculate CRC over header */ + hdr->crc32 = hndcrc32((uint8 *)&hdr->flag_version, + SIZEOF_TRX(hdr) - OFFSETOF(struct trx_header, flag_version), + CRC32_INIT_VALUE); + + /* Calculate CRC over data */ + for (i = SIZEOF_TRX(hdr); i < total_len; ++i) + hdr->crc32 = hndcrc32((uint8 *)&dhd_bus->image[i], 1, hdr->crc32); + hdr->crc32 = htol32(hdr->crc32); + + bcmerror = DBUS_OK; + +err: + if (fw_memblock) + MFREE(dhd_bus->pub.osh, fw_memblock, MAX_NVRAMBUF_SIZE); + if (fw_image) + dhd_os_close_image(fw_image); + if (nv_memblock) + MFREE(dhd_bus->pub.osh, nv_memblock, MAX_NVRAMBUF_SIZE); + if (nv_image) + dhd_os_close_image(nv_image); + + return bcmerror; +} + +/** + * during driver initialization ('attach') or after PnP 'resume', firmware needs to be loaded into + * the dongle + */ +static int +dbus_do_download(dhd_bus_t *dhd_bus, char *pfw_path, char *pnv_path) +{ + int err = DBUS_OK; + + err = dbus_get_fw_nvram(dhd_bus, pfw_path, pnv_path); + if (err) { + DBUSERR(("dbus_do_download: fail to get nvram %d\n", err)); + return err; + } + + if (dhd_bus->drvintf->dlstart && dhd_bus->drvintf->dlrun) { + err = dhd_bus->drvintf->dlstart(dhd_bus->bus_info, + dhd_bus->image, dhd_bus->image_len); + if (err == DBUS_OK) { + err = dhd_bus->drvintf->dlrun(dhd_bus->bus_info); + } + } else + err = DBUS_ERR; + + if (dhd_bus->image) { + MFREE(dhd_bus->pub.osh, dhd_bus->image, dhd_bus->image_len); + dhd_bus->image = NULL; + dhd_bus->image_len = 0; + } + + return err; +} /* dbus_do_download */ +#else + +/** + * It is easy for the user to pass one jumbo nvram file to the driver than a set of smaller files. + * The 'jumbo nvram' file format is essentially a set of nvram files. Before commencing firmware + * download, the dongle needs to be probed so that the correct nvram contents within the jumbo nvram + * file is selected. + */ +static int +dbus_jumbo_nvram(dhd_bus_t *dhd_bus) +{ + int8 *nvram = NULL; + int nvram_len = 0; + int ret = DBUS_OK; + uint16 boardrev = 0xFFFF; + uint16 boardtype = 0xFFFF; + + /* read the otp for boardrev & boardtype + * if boardtype/rev are present in otp + * select nvram data for that boardtype/rev + */ + dbus_otp(dhd_bus, &boardtype, &boardrev); + + ret = dbus_select_nvram(dhd_bus, dhd_bus->extdl.vars, dhd_bus->extdl.varslen, + boardtype, boardrev, &nvram, &nvram_len); + + if (ret == DBUS_JUMBO_BAD_FORMAT) + return DBUS_ERR_NVRAM; + else if (ret == DBUS_JUMBO_NOMATCH && + (boardtype != 0xFFFF || boardrev != 0xFFFF)) { + DBUSERR(("No matching NVRAM for boardtype 0x%02x boardrev 0x%02x\n", + boardtype, boardrev)); + return DBUS_ERR_NVRAM; + } + dhd_bus->nvram = nvram; + dhd_bus->nvram_len = nvram_len; + + return DBUS_OK; +} + +/** before commencing fw download, the correct NVRAM image to download has to be picked */ +static int +dbus_get_nvram(dhd_bus_t *dhd_bus) +{ + int len, i; + struct trx_header *hdr; + int actual_fwlen; + uint32 img_offset = 0; + + dhd_bus->nvram_len = 0; + if (dhd_bus->extdl.varslen) { + if (DBUS_OK != dbus_jumbo_nvram(dhd_bus)) + return DBUS_ERR_NVRAM; + DBUSERR(("NVRAM %d bytes downloaded\n", dhd_bus->nvram_len)); + } +#if defined(BCM_REQUEST_FW) + else if (nonfwnvram) { + dhd_bus->nvram = nonfwnvram; + dhd_bus->nvram_len = nonfwnvramlen; + DBUSERR(("NVRAM %d bytes downloaded\n", dhd_bus->nvram_len)); + } +#endif + if (dhd_bus->nvram) { + uint8 nvram_words_pad = 0; + /* Validate the format/length etc of the file */ + if ((actual_fwlen = check_file(dhd_bus->pub.osh, dhd_bus->fw)) <= 0) { + DBUSERR(("%s: bad firmware format!\n", __FUNCTION__)); + return DBUS_ERR_NVRAM; + } + + if (!dhd_bus->nvram_nontxt) { + /* host supplied nvram could be in .txt format + * with all the comments etc... + */ + dhd_bus->nvram_len = process_nvram_vars(dhd_bus->nvram, + dhd_bus->nvram_len); + } + if (dhd_bus->nvram_len % 4) + nvram_words_pad = 4 - dhd_bus->nvram_len % 4; + + len = actual_fwlen + dhd_bus->nvram_len + nvram_words_pad; + dhd_bus->image = MALLOC(dhd_bus->pub.osh, len); + dhd_bus->image_len = len; + if (dhd_bus->image == NULL) { + DBUSERR(("%s: malloc failed!\n", __FUNCTION__)); + return DBUS_ERR_NVRAM; + } + hdr = (struct trx_header *)dhd_bus->fw; + /* Step1: Copy trx header + firmwre */ + img_offset = SIZEOF_TRX(hdr) + hdr->offsets[TRX_OFFSETS_DLFWLEN_IDX]; + bcopy(dhd_bus->fw, dhd_bus->image, img_offset); + /* Step2: Copy NVRAM + pad */ + bcopy(dhd_bus->nvram, (uint8 *)(dhd_bus->image + img_offset), + dhd_bus->nvram_len); + img_offset += dhd_bus->nvram_len; + if (nvram_words_pad) { + bzero(&dhd_bus->image[img_offset], + nvram_words_pad); + img_offset += nvram_words_pad; + } +#ifdef BCMTRXV2 + /* Step3: Copy DSG/CFG for V2 */ + if (ISTRX_V2(hdr) && + (hdr->offsets[TRX_OFFSETS_DSG_LEN_IDX] || + hdr->offsets[TRX_OFFSETS_CFG_LEN_IDX])) { + + bcopy(dhd_bus->fw + SIZEOF_TRX(hdr) + + hdr->offsets[TRX_OFFSETS_DLFWLEN_IDX] + + hdr->offsets[TRX_OFFSETS_NVM_LEN_IDX], + dhd_bus->image + img_offset, + hdr->offsets[TRX_OFFSETS_DSG_LEN_IDX] + + hdr->offsets[TRX_OFFSETS_CFG_LEN_IDX]); + + img_offset += hdr->offsets[TRX_OFFSETS_DSG_LEN_IDX] + + hdr->offsets[TRX_OFFSETS_CFG_LEN_IDX]; + } +#endif /* BCMTRXV2 */ + /* Step4: update TRX header for nvram size */ + hdr = (struct trx_header *)dhd_bus->image; + hdr->len = htol32(len); + /* Pass the actual fw len */ + hdr->offsets[TRX_OFFSETS_NVM_LEN_IDX] = + htol32(dhd_bus->nvram_len + nvram_words_pad); + /* Calculate CRC over header */ + hdr->crc32 = hndcrc32((uint8 *)&hdr->flag_version, + SIZEOF_TRX(hdr) - OFFSETOF(struct trx_header, flag_version), + CRC32_INIT_VALUE); + + /* Calculate CRC over data */ + for (i = SIZEOF_TRX(hdr); i < len; ++i) + hdr->crc32 = hndcrc32((uint8 *)&dhd_bus->image[i], 1, hdr->crc32); + hdr->crc32 = htol32(hdr->crc32); + } else { + dhd_bus->image = dhd_bus->fw; + dhd_bus->image_len = (uint32)dhd_bus->fwlen; + } + + return DBUS_OK; +} /* dbus_get_nvram */ + +/** + * during driver initialization ('attach') or after PnP 'resume', firmware needs to be loaded into + * the dongle + */ +static int +dbus_do_download(dhd_bus_t *dhd_bus) +{ + int err = DBUS_OK; +#ifndef BCM_REQUEST_FW + int decomp_override = 0; +#endif +#ifdef BCM_REQUEST_FW + uint16 boardrev = 0xFFFF, boardtype = 0xFFFF; + int8 *temp_nvram; + int temp_len; +#endif + +#if defined(BCM_REQUEST_FW) + dhd_bus->firmware = dbus_get_fw_nvfile(dhd_bus->pub.attrib.devid, + dhd_bus->pub.attrib.chiprev, &dhd_bus->fw, &dhd_bus->fwlen, + DBUS_FIRMWARE, 0, 0); + if (!dhd_bus->firmware) + return DBUS_ERR; +#endif + + dhd_bus->image = dhd_bus->fw; + dhd_bus->image_len = (uint32)dhd_bus->fwlen; + +#ifndef BCM_REQUEST_FW + if (UNZIP_ENAB(dhd_bus) && !decomp_override) { + err = dbus_zlib_decomp(dhd_bus); + if (err) { + DBUSERR(("dbus_attach: fw decompress fail %d\n", err)); + return err; + } + } +#endif + +#if defined(BCM_REQUEST_FW) + /* check if firmware is appended with nvram file */ + err = dbus_otp(dhd_bus, &boardtype, &boardrev); + /* check if nvram is provided as separte file */ + nonfwnvram = NULL; + nonfwnvramlen = 0; + dhd_bus->nvfile = dbus_get_fw_nvfile(dhd_bus->pub.attrib.devid, + dhd_bus->pub.attrib.chiprev, (void *)&temp_nvram, &temp_len, + DBUS_NVFILE, boardtype, boardrev); + if (dhd_bus->nvfile) { + int8 *tmp = MALLOC(dhd_bus->pub.osh, temp_len); + if (tmp) { + bcopy(temp_nvram, tmp, temp_len); + nonfwnvram = tmp; + nonfwnvramlen = temp_len; + } else { + err = DBUS_ERR; + goto fail; + } + } +#endif /* defined(BCM_REQUEST_FW) */ + + err = dbus_get_nvram(dhd_bus); + if (err) { + DBUSERR(("dbus_do_download: fail to get nvram %d\n", err)); + return err; + } + + + if (dhd_bus->drvintf->dlstart && dhd_bus->drvintf->dlrun) { + err = dhd_bus->drvintf->dlstart(dhd_bus->bus_info, + dhd_bus->image, dhd_bus->image_len); + + if (err == DBUS_OK) + err = dhd_bus->drvintf->dlrun(dhd_bus->bus_info); + } else + err = DBUS_ERR; + + if (dhd_bus->nvram) { + MFREE(dhd_bus->pub.osh, dhd_bus->image, dhd_bus->image_len); + dhd_bus->image = dhd_bus->fw; + dhd_bus->image_len = (uint32)dhd_bus->fwlen; + } + +#ifndef BCM_REQUEST_FW + if (UNZIP_ENAB(dhd_bus) && (!decomp_override) && dhd_bus->orig_fw) { + MFREE(dhd_bus->pub.osh, dhd_bus->fw, dhd_bus->decomp_memsize); + dhd_bus->image = dhd_bus->fw = dhd_bus->orig_fw; + dhd_bus->image_len = dhd_bus->fwlen = dhd_bus->origfw_len; + } +#endif + +#if defined(BCM_REQUEST_FW) +fail: + if (dhd_bus->firmware) { + dbus_release_fw_nvfile(dhd_bus->firmware); + dhd_bus->firmware = NULL; + } + if (dhd_bus->nvfile) { + dbus_release_fw_nvfile(dhd_bus->nvfile); + dhd_bus->nvfile = NULL; + } + if (nonfwnvram) { + MFREE(dhd_bus->pub.osh, nonfwnvram, nonfwnvramlen); + nonfwnvram = NULL; + nonfwnvramlen = 0; + } +#endif + return err; +} /* dbus_do_download */ +#endif /* EXTERNAL_FW_PATH */ +#endif + +/** required for DBUS deregistration */ +static void +dbus_disconnect(void *handle) +{ + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (disconnect_cb) + disconnect_cb(disc_arg); +} + +/** + * This function is called when the sent irb times out without a tx response status. + * DBUS adds reliability by resending timed out IRBs DBUS_TX_RETRY_LIMIT times. + */ +static void +dbus_if_send_irb_timeout(void *handle, dbus_irb_tx_t *txirb) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) handle; + + if ((dhd_bus == NULL) || (dhd_bus->drvintf == NULL) || (txirb == NULL)) { + return; + } + + DBUSTRACE(("%s\n", __FUNCTION__)); + + return; + +} /* dbus_if_send_irb_timeout */ + +/** + * When lower DBUS level signals that a send IRB completed, either successful or not, the higher + * level (e.g. dhd_linux.c) has to be notified, and transmit flow control has to be evaluated. + */ +static void BCMFASTPATH +dbus_if_send_irb_complete(void *handle, dbus_irb_tx_t *txirb, int status) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) handle; + int txirb_pending; + struct exec_parms args; + void *pktinfo; + + if ((dhd_bus == NULL) || (txirb == NULL)) { + return; + } + + DBUSTRACE(("%s: status = %d\n", __FUNCTION__, status)); + + dbus_tx_timer_stop(dhd_bus); + + /* re-queue BEFORE calling send_complete which will assume that this irb + is now available. + */ + pktinfo = txirb->info; + bzero(txirb, sizeof(dbus_irb_tx_t)); + args.qenq.q = dhd_bus->tx_q; + args.qenq.b = (dbus_irb_t *) txirb; + EXEC_TXLOCK(dhd_bus, q_enq_exec, &args); + + if (dhd_bus->pub.busstate != DBUS_STATE_DOWN) { + if ((status == DBUS_OK) || (status == DBUS_ERR_NODEVICE)) { + if (dhd_bus->cbs && dhd_bus->cbs->send_complete) + dhd_bus->cbs->send_complete(dhd_bus->cbarg, pktinfo, + status); + + if (status == DBUS_OK) { + txirb_pending = dhd_bus->pub.ntxq - dhd_bus->tx_q->cnt; + if (txirb_pending) + dbus_tx_timer_start(dhd_bus, DBUS_TX_TIMEOUT_INTERVAL); + if ((txirb_pending < dhd_bus->tx_low_watermark) && + dhd_bus->txoff && !dhd_bus->txoverride) { + dbus_flowctrl_tx(dhd_bus, OFF); + } + } + } else { + DBUSERR(("%s: %d WARNING freeing orphan pkt %p\n", __FUNCTION__, __LINE__, + pktinfo)); +#if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_TXNOCOPY) || defined(BCM_RPC_TOC) + if (pktinfo) + if (dhd_bus->cbs && dhd_bus->cbs->send_complete) + dhd_bus->cbs->send_complete(dhd_bus->cbarg, pktinfo, + status); +#else + dbus_if_pktfree(dhd_bus, (void*)pktinfo, TRUE); +#endif /* defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_TXNOCOPY) || defined(BCM_RPC_TOC) */ + } + } else { + DBUSERR(("%s: %d WARNING freeing orphan pkt %p\n", __FUNCTION__, __LINE__, + pktinfo)); +#if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_TXNOCOPY) || defined(BCM_RPC_TOC) + if (pktinfo) + if (dhd_bus->cbs && dhd_bus->cbs->send_complete) + dhd_bus->cbs->send_complete(dhd_bus->cbarg, pktinfo, + status); +#else + dbus_if_pktfree(dhd_bus, (void*)pktinfo, TRUE); +#endif /* defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_TXNOCOPY) defined(BCM_RPC_TOC) */ + } +} /* dbus_if_send_irb_complete */ + +/** + * When lower DBUS level signals that a receive IRB completed, either successful or not, the higher + * level (e.g. dhd_linux.c) has to be notified, and fresh free receive IRBs may have to be given + * to lower levels. + */ +static void BCMFASTPATH +dbus_if_recv_irb_complete(void *handle, dbus_irb_rx_t *rxirb, int status) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) handle; + int rxirb_pending; + struct exec_parms args; + + if ((dhd_bus == NULL) || (rxirb == NULL)) { + return; + } + DBUSTRACE(("%s\n", __FUNCTION__)); + if (dhd_bus->pub.busstate != DBUS_STATE_DOWN && + dhd_bus->pub.busstate != DBUS_STATE_SLEEP) { + if (status == DBUS_OK) { + if ((rxirb->buf != NULL) && (rxirb->actual_len > 0)) { +#ifdef DBUS_USB_LOOPBACK + if (is_loopback_pkt(rxirb->buf)) { + matches_loopback_pkt(rxirb->buf); + } else +#endif + if (dhd_bus->cbs && dhd_bus->cbs->recv_buf) { + dhd_bus->cbs->recv_buf(dhd_bus->cbarg, rxirb->buf, + rxirb->actual_len); + } + } else if (rxirb->pkt != NULL) { + if (dhd_bus->cbs && dhd_bus->cbs->recv_pkt) + dhd_bus->cbs->recv_pkt(dhd_bus->cbarg, rxirb->pkt); + } else { + ASSERT(0); /* Should not happen */ + } + + rxirb_pending = dhd_bus->pub.nrxq - dhd_bus->rx_q->cnt - 1; + if ((rxirb_pending <= dhd_bus->rx_low_watermark) && + !dhd_bus->rxoff) { + DBUSTRACE(("Low watermark so submit more %d <= %d \n", + dhd_bus->rx_low_watermark, rxirb_pending)); + dbus_rxirbs_fill(dhd_bus); + } else if (dhd_bus->rxoff) + DBUSTRACE(("rx flow controlled. not filling more. cut_rxq=%d\n", + dhd_bus->rx_q->cnt)); + } else if (status == DBUS_ERR_NODEVICE) { + DBUSERR(("%s: %d status = %d, buf %p\n", __FUNCTION__, __LINE__, status, + rxirb->buf)); +#if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_RXNOCOPY) + if (rxirb->buf) { + PKTFRMNATIVE(dhd_bus->pub.osh, rxirb->buf); + PKTFREE(dhd_bus->pub.osh, rxirb->buf, FALSE); + } +#endif /* BCM_RPC_NOCOPY || BCM_RPC_TXNOCOPY || BCM_RPC_TOC */ + } else { + if (status != DBUS_ERR_RXZLP) + DBUSERR(("%s: %d status = %d, buf %p\n", __FUNCTION__, __LINE__, + status, rxirb->buf)); +#if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_RXNOCOPY) + if (rxirb->buf) { + PKTFRMNATIVE(dhd_bus->pub.osh, rxirb->buf); + PKTFREE(dhd_bus->pub.osh, rxirb->buf, FALSE); + } +#endif /* BCM_RPC_NOCOPY || BCM_RPC_TXNOCOPY || BCM_RPC_TOC */ + } + } else { + DBUSTRACE(("%s: DBUS down, ignoring recv callback. buf %p\n", __FUNCTION__, + rxirb->buf)); +#if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_RXNOCOPY) + if (rxirb->buf) { + PKTFRMNATIVE(dhd_bus->pub.osh, rxirb->buf); + PKTFREE(dhd_bus->pub.osh, rxirb->buf, FALSE); + } +#endif /* BCM_RPC_NOCOPY || BCM_RPC_TXNOCOPY || BCM_RPC_TOC */ + } + if (dhd_bus->rx_q != NULL) { + bzero(rxirb, sizeof(dbus_irb_rx_t)); + args.qenq.q = dhd_bus->rx_q; + args.qenq.b = (dbus_irb_t *) rxirb; + EXEC_RXLOCK(dhd_bus, q_enq_exec, &args); + } else + MFREE(dhd_bus->pub.osh, rxirb, sizeof(dbus_irb_tx_t)); +} /* dbus_if_recv_irb_complete */ + +/** + * Accumulate errors signaled by lower DBUS levels and signal them to higher (e.g. dhd_linux.c) + * level. + */ +static void +dbus_if_errhandler(void *handle, int err) +{ + dhd_bus_t *dhd_bus = handle; + uint32 mask = 0; + + if (dhd_bus == NULL) + return; + + switch (err) { + case DBUS_ERR_TXFAIL: + dhd_bus->pub.stats.tx_errors++; + mask |= ERR_CBMASK_TXFAIL; + break; + case DBUS_ERR_TXDROP: + dhd_bus->pub.stats.tx_dropped++; + mask |= ERR_CBMASK_TXFAIL; + break; + case DBUS_ERR_RXFAIL: + dhd_bus->pub.stats.rx_errors++; + mask |= ERR_CBMASK_RXFAIL; + break; + case DBUS_ERR_RXDROP: + dhd_bus->pub.stats.rx_dropped++; + mask |= ERR_CBMASK_RXFAIL; + break; + default: + break; + } + + if (dhd_bus->cbs && dhd_bus->cbs->errhandler && (dhd_bus->errmask & mask)) + dhd_bus->cbs->errhandler(dhd_bus->cbarg, err); +} + +/** + * When lower DBUS level signals control IRB completed, higher level (e.g. dhd_linux.c) has to be + * notified. + */ +static void +dbus_if_ctl_complete(void *handle, int type, int status) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) handle; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (dhd_bus == NULL) { + DBUSERR(("%s: dhd_bus is NULL\n", __FUNCTION__)); + return; + } + + if (dhd_bus->pub.busstate != DBUS_STATE_DOWN) { + if (dhd_bus->cbs && dhd_bus->cbs->ctl_complete) + dhd_bus->cbs->ctl_complete(dhd_bus->cbarg, type, status); + } +} + +/** + * Rx related functionality (flow control, posting of free IRBs to rx queue) is dependent upon the + * bus state. When lower DBUS level signals a change in the interface state, take appropriate action + * and forward the signaling to the higher (e.g. dhd_linux.c) level. + */ +static void +dbus_if_state_change(void *handle, int state) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) handle; + int old_state; + + if (dhd_bus == NULL) + return; + + if (dhd_bus->pub.busstate == state) + return; + old_state = dhd_bus->pub.busstate; + if (state == DBUS_STATE_DISCONNECT) { + DBUSERR(("DBUS disconnected\n")); + } + + /* Ignore USB SUSPEND while not up yet */ + if (state == DBUS_STATE_SLEEP && old_state != DBUS_STATE_UP) + return; + + DBUSTRACE(("dbus state change from %d to to %d\n", old_state, state)); + + /* Don't update state if it's PnP firmware re-download */ + if (state != DBUS_STATE_PNP_FWDL) + dhd_bus->pub.busstate = state; + else + dbus_flowctrl_rx(handle, FALSE); + if (state == DBUS_STATE_SLEEP) + dbus_flowctrl_rx(handle, TRUE); + if (state == DBUS_STATE_UP) { + dbus_rxirbs_fill(dhd_bus); + dbus_flowctrl_rx(handle, FALSE); + } + + if (dhd_bus->cbs && dhd_bus->cbs->state_change) + dhd_bus->cbs->state_change(dhd_bus->cbarg, state); +} + +/** Forward request for packet from lower DBUS layer to higher layer (e.g. dhd_linux.c) */ +static void * +dbus_if_pktget(void *handle, uint len, bool send) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) handle; + void *p = NULL; + + if (dhd_bus == NULL) + return NULL; + + if (dhd_bus->cbs && dhd_bus->cbs->pktget) + p = dhd_bus->cbs->pktget(dhd_bus->cbarg, len, send); + else + ASSERT(0); + + return p; +} + +/** Forward request to free packet from lower DBUS layer to higher layer (e.g. dhd_linux.c) */ +static void +dbus_if_pktfree(void *handle, void *p, bool send) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) handle; + + if (dhd_bus == NULL) + return; + + if (dhd_bus->cbs && dhd_bus->cbs->pktfree) + dhd_bus->cbs->pktfree(dhd_bus->cbarg, p, send); + else + ASSERT(0); +} + +/** Lower DBUS level requests either a send or receive IRB */ +static struct dbus_irb* +dbus_if_getirb(void *cbarg, bool send) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) cbarg; + struct exec_parms args; + struct dbus_irb *irb; + + if ((dhd_bus == NULL) || (dhd_bus->pub.busstate != DBUS_STATE_UP)) + return NULL; + + if (send == TRUE) { + args.qdeq.q = dhd_bus->tx_q; + irb = EXEC_TXLOCK(dhd_bus, q_deq_exec, &args); + } else { + args.qdeq.q = dhd_bus->rx_q; + irb = EXEC_RXLOCK(dhd_bus, q_deq_exec, &args); + } + + return irb; +} + +/** + * Called as part of DBUS bus registration. Calls back into higher level (e.g. dhd_linux.c) probe + * function. + */ +static void * +dbus_probe(void *arg, const char *desc, uint32 bustype, uint16 bus_no, + uint16 slot, uint32 hdrlen) +{ + DBUSTRACE(("%s\n", __FUNCTION__)); + if (probe_cb) { + disc_arg = probe_cb(probe_arg, desc, bustype, bus_no, slot, hdrlen); + return disc_arg; + } + + return (void *)DBUS_ERR; +} + +/** + * As part of initialization, higher level (e.g. dhd_linux.c) requests DBUS to prepare for + * action. + */ +int +dhd_bus_register(void) +{ + int err; + + DBUSTRACE(("%s: Enter\n", __FUNCTION__)); + + probe_cb = dhd_dbus_probe_cb; + disconnect_cb = dhd_dbus_disconnect_cb; + probe_arg = NULL; + + err = dbus_bus_register(0xa5c, 0x48f, dbus_probe, /* call lower DBUS level register function */ + dbus_disconnect, NULL, &g_busintf, NULL, NULL); + + /* Device not detected */ + if (err == DBUS_ERR_NODEVICE) + err = DBUS_OK; + + return err; +} + +dhd_pub_t *g_pub = NULL; +void +dhd_bus_unregister(void) +{ + int ret; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + DHD_MUTEX_LOCK(); + if (g_pub) { + g_pub->dhd_remove = TRUE; + if (!g_pub->bus) { + dhd_dbus_disconnect_cb(g_pub->bus); + } + } + probe_cb = NULL; + DHD_MUTEX_UNLOCK(); + ret = dbus_bus_deregister(); + disconnect_cb = NULL; + probe_arg = NULL; +} + +/** As part of initialization, data structures have to be allocated and initialized */ +dhd_bus_t * +dbus_attach(osl_t *osh, int rxsize, int nrxq, int ntxq, dhd_pub_t *pub, + dbus_callbacks_t *cbs, dbus_extdl_t *extdl, struct shared_info *sh) +{ + dhd_bus_t *dhd_bus; + int err; + + if ((g_busintf == NULL) || (g_busintf->attach == NULL) || (cbs == NULL)) + return NULL; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if ((nrxq <= 0) || (ntxq <= 0)) + return NULL; + + dhd_bus = MALLOC(osh, sizeof(dhd_bus_t)); + if (dhd_bus == NULL) { + DBUSERR(("%s: malloc failed %d\n", __FUNCTION__, sizeof(dhd_bus_t))); + return NULL; + } + + bzero(dhd_bus, sizeof(dhd_bus_t)); + + /* BUS-specific driver interface (at a lower DBUS level) */ + dhd_bus->drvintf = g_busintf; + dhd_bus->cbarg = pub; + dhd_bus->cbs = cbs; + + dhd_bus->pub.sh = sh; + dhd_bus->pub.osh = osh; + dhd_bus->pub.rxsize = rxsize; + + dhd_bus->pub.nrxq = nrxq; + dhd_bus->rx_low_watermark = nrxq / 2; /* keep enough posted rx urbs */ + dhd_bus->pub.ntxq = ntxq; + dhd_bus->tx_low_watermark = ntxq / 4; /* flow control when too many tx urbs posted */ + + dhd_bus->tx_q = MALLOC(osh, sizeof(dbus_irbq_t)); + if (dhd_bus->tx_q == NULL) + goto error; + else { + bzero(dhd_bus->tx_q, sizeof(dbus_irbq_t)); + err = dbus_irbq_init(dhd_bus, dhd_bus->tx_q, ntxq, sizeof(dbus_irb_tx_t)); + if (err != DBUS_OK) + goto error; + } + + dhd_bus->rx_q = MALLOC(osh, sizeof(dbus_irbq_t)); + if (dhd_bus->rx_q == NULL) + goto error; + else { + bzero(dhd_bus->rx_q, sizeof(dbus_irbq_t)); + err = dbus_irbq_init(dhd_bus, dhd_bus->rx_q, nrxq, sizeof(dbus_irb_rx_t)); + if (err != DBUS_OK) + goto error; + } + + + dhd_bus->bus_info = (void *)g_busintf->attach(&dhd_bus->pub, + dhd_bus, &dbus_intf_cbs); + if (dhd_bus->bus_info == NULL) + goto error; + + dbus_tx_timer_init(dhd_bus); + +#if defined(BCM_REQUEST_FW) + /* Need to copy external image for re-download */ + if (extdl && extdl->fw && (extdl->fwlen > 0)) { + dhd_bus->extdl.fw = MALLOC(osh, extdl->fwlen); + if (dhd_bus->extdl.fw) { + bcopy(extdl->fw, dhd_bus->extdl.fw, extdl->fwlen); + dhd_bus->extdl.fwlen = extdl->fwlen; + } + } + + if (extdl && extdl->vars && (extdl->varslen > 0)) { + dhd_bus->extdl.vars = MALLOC(osh, extdl->varslen); + if (dhd_bus->extdl.vars) { + bcopy(extdl->vars, dhd_bus->extdl.vars, extdl->varslen); + dhd_bus->extdl.varslen = extdl->varslen; + } + } +#endif + + return (dhd_bus_t *)dhd_bus; + +error: + DBUSERR(("%s: Failed\n", __FUNCTION__)); + dbus_detach(dhd_bus); + return NULL; +} /* dbus_attach */ + +void +dbus_detach(dhd_bus_t *pub) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) pub; + osl_t *osh; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (dhd_bus == NULL) + return; + + dbus_tx_timer_stop(dhd_bus); + + osh = pub->pub.osh; + + if (dhd_bus->drvintf && dhd_bus->drvintf->detach) + dhd_bus->drvintf->detach((dbus_pub_t *)dhd_bus, dhd_bus->bus_info); + + if (dhd_bus->tx_q) { + dbus_irbq_deinit(dhd_bus, dhd_bus->tx_q, sizeof(dbus_irb_tx_t)); + MFREE(osh, dhd_bus->tx_q, sizeof(dbus_irbq_t)); + dhd_bus->tx_q = NULL; + } + + if (dhd_bus->rx_q) { + dbus_irbq_deinit(dhd_bus, dhd_bus->rx_q, sizeof(dbus_irb_rx_t)); + MFREE(osh, dhd_bus->rx_q, sizeof(dbus_irbq_t)); + dhd_bus->rx_q = NULL; + } + + + if (dhd_bus->extdl.fw && (dhd_bus->extdl.fwlen > 0)) { + MFREE(osh, dhd_bus->extdl.fw, dhd_bus->extdl.fwlen); + dhd_bus->extdl.fw = NULL; + dhd_bus->extdl.fwlen = 0; + } + + if (dhd_bus->extdl.vars && (dhd_bus->extdl.varslen > 0)) { + MFREE(osh, dhd_bus->extdl.vars, dhd_bus->extdl.varslen); + dhd_bus->extdl.vars = NULL; + dhd_bus->extdl.varslen = 0; + } + + MFREE(osh, dhd_bus, sizeof(dhd_bus_t)); +} /* dbus_detach */ + +int dbus_dlneeded(dhd_bus_t *pub) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) pub; + int dlneeded = DBUS_ERR; + + if (!dhd_bus) { + DBUSERR(("%s: dhd_bus is NULL\n", __FUNCTION__)); + return DBUS_ERR; + } + + DBUSTRACE(("%s: state %d\n", __FUNCTION__, dhd_bus->pub.busstate)); + + if (dhd_bus->drvintf->dlneeded) { + dlneeded = dhd_bus->drvintf->dlneeded(dhd_bus->bus_info); + } + printf("%s: dlneeded=%d\n", __FUNCTION__, dlneeded); + + /* dlneeded > 0: need to download + * dlneeded = 0: downloaded + * dlneeded < 0: bus error*/ + return dlneeded; +} + +#if defined(BCM_REQUEST_FW) +int dbus_download_firmware(dhd_bus_t *pub, char *pfw_path, char *pnv_path) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) pub; + int err = DBUS_OK; + + if (!dhd_bus) { + DBUSERR(("%s: dhd_bus is NULL\n", __FUNCTION__)); + return DBUS_ERR; + } + + DBUSTRACE(("%s: state %d\n", __FUNCTION__, dhd_bus->pub.busstate)); + + dhd_bus->pub.busstate = DBUS_STATE_DL_PENDING; +#ifdef EXTERNAL_FW_PATH + err = dbus_do_download(dhd_bus, pfw_path, pnv_path); +#else + err = dbus_do_download(dhd_bus); +#endif /* EXTERNAL_FW_PATH */ + if (err == DBUS_OK) { + dhd_bus->pub.busstate = DBUS_STATE_DL_DONE; + } else { + DBUSERR(("%s: download failed (%d)\n", __FUNCTION__, err)); + } + + return err; +} +#endif + +/** + * higher layer requests us to 'up' the interface to the dongle. Prerequisite is that firmware (not + * bootloader) must be active in the dongle. + */ +int +dbus_up(struct dhd_bus *pub) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) pub; + int err = DBUS_OK; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (dhd_bus == NULL) { + DBUSERR(("%s: dhd_bus is NULL\n", __FUNCTION__)); + return DBUS_ERR; + } + + if ((dhd_bus->pub.busstate == DBUS_STATE_DL_DONE) || + (dhd_bus->pub.busstate == DBUS_STATE_DOWN) || + (dhd_bus->pub.busstate == DBUS_STATE_SLEEP)) { + if (dhd_bus->drvintf && dhd_bus->drvintf->up) { + err = dhd_bus->drvintf->up(dhd_bus->bus_info); + + if (err == DBUS_OK) { + dbus_rxirbs_fill(dhd_bus); + } + } + } else + err = DBUS_ERR; + + return err; +} + +/** higher layer requests us to 'down' the interface to the dongle. */ +int +dbus_down(dbus_pub_t *pub) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) pub; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (dhd_bus == NULL) + return DBUS_ERR; + + dbus_tx_timer_stop(dhd_bus); + + if (dhd_bus->pub.busstate == DBUS_STATE_UP || + dhd_bus->pub.busstate == DBUS_STATE_SLEEP) { + if (dhd_bus->drvintf && dhd_bus->drvintf->down) + return dhd_bus->drvintf->down(dhd_bus->bus_info); + } + + return DBUS_ERR; +} + +int +dbus_shutdown(dbus_pub_t *pub) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) pub; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (dhd_bus == NULL) + return DBUS_ERR; + + if (dhd_bus->drvintf && dhd_bus->drvintf->shutdown) + return dhd_bus->drvintf->shutdown(dhd_bus->bus_info); + + return DBUS_OK; +} + +int +dbus_stop(struct dhd_bus *pub) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) pub; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (dhd_bus == NULL) + return DBUS_ERR; + + if (dhd_bus->pub.busstate == DBUS_STATE_UP || + dhd_bus->pub.busstate == DBUS_STATE_SLEEP) { + if (dhd_bus->drvintf && dhd_bus->drvintf->stop) + return dhd_bus->drvintf->stop(dhd_bus->bus_info); + } + + return DBUS_ERR; +} + +int dbus_send_txdata(dbus_pub_t *dbus, void *pktbuf) +{ + return dbus_send_pkt(dbus, pktbuf, pktbuf /* pktinfo */); +} + +int +dbus_send_buf(dbus_pub_t *pub, uint8 *buf, int len, void *info) +{ + return dbus_send_irb(pub, buf, len, NULL, info); +} + +int +dbus_send_pkt(dbus_pub_t *pub, void *pkt, void *info) +{ + return dbus_send_irb(pub, NULL, 0, pkt, info); +} + +int +dbus_send_ctl(struct dhd_bus *pub, uint8 *buf, int len) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) pub; + + if (dhd_bus == NULL) { + DBUSERR(("%s: dhd_bus is NULL\n", __FUNCTION__)); + return DBUS_ERR; + } + + if (dhd_bus->pub.busstate == DBUS_STATE_UP || + dhd_bus->pub.busstate == DBUS_STATE_SLEEP) { + if (dhd_bus->drvintf && dhd_bus->drvintf->send_ctl) + return dhd_bus->drvintf->send_ctl(dhd_bus->bus_info, buf, len); + } else { + DBUSERR(("%s: bustate=%d\n", __FUNCTION__, dhd_bus->pub.busstate)); + } + + return DBUS_ERR; +} + +int +dbus_recv_ctl(struct dhd_bus *pub, uint8 *buf, int len) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) pub; + + if ((dhd_bus == NULL) || (buf == NULL)) + return DBUS_ERR; + + if (dhd_bus->pub.busstate == DBUS_STATE_UP || + dhd_bus->pub.busstate == DBUS_STATE_SLEEP) { + if (dhd_bus->drvintf && dhd_bus->drvintf->recv_ctl) + return dhd_bus->drvintf->recv_ctl(dhd_bus->bus_info, buf, len); + } + + return DBUS_ERR; +} + +/** Only called via RPC (Dec 2012) */ +int +dbus_recv_bulk(dbus_pub_t *pub, uint32 ep_idx) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) pub; + + dbus_irb_rx_t *rxirb; + struct exec_parms args; + int status; + + + if (dhd_bus == NULL) + return DBUS_ERR; + + args.qdeq.q = dhd_bus->rx_q; + if (dhd_bus->pub.busstate == DBUS_STATE_UP) { + if (dhd_bus->drvintf && dhd_bus->drvintf->recv_irb_from_ep) { + if ((rxirb = (EXEC_RXLOCK(dhd_bus, q_deq_exec, &args))) != NULL) { + status = dhd_bus->drvintf->recv_irb_from_ep(dhd_bus->bus_info, + rxirb, ep_idx); + if (status == DBUS_ERR_RXDROP) { + bzero(rxirb, sizeof(dbus_irb_rx_t)); + args.qenq.q = dhd_bus->rx_q; + args.qenq.b = (dbus_irb_t *) rxirb; + EXEC_RXLOCK(dhd_bus, q_enq_exec, &args); + } + } + } + } + + return DBUS_ERR; +} + +/** only called by dhd_cdc.c (Dec 2012) */ +int +dbus_poll_intr(dbus_pub_t *pub) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) pub; + + int status = DBUS_ERR; + + if (dhd_bus == NULL) + return DBUS_ERR; + + if (dhd_bus->pub.busstate == DBUS_STATE_UP) { + if (dhd_bus->drvintf && dhd_bus->drvintf->recv_irb_from_ep) { + status = dhd_bus->drvintf->recv_irb_from_ep(dhd_bus->bus_info, + NULL, 0xff); + } + } + return status; +} + +/** called by nobody (Dec 2012) */ +void * +dbus_pktget(dbus_pub_t *pub, int len) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) pub; + + if ((dhd_bus == NULL) || (len < 0)) + return NULL; + + return PKTGET(dhd_bus->pub.osh, len, TRUE); +} + +/** called by nobody (Dec 2012) */ +void +dbus_pktfree(dbus_pub_t *pub, void* pkt) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) pub; + + if ((dhd_bus == NULL) || (pkt == NULL)) + return; + + PKTFREE(dhd_bus->pub.osh, pkt, TRUE); +} + +/** called by nobody (Dec 2012) */ +int +dbus_get_stats(dbus_pub_t *pub, dbus_stats_t *stats) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) pub; + + if ((dhd_bus == NULL) || (stats == NULL)) + return DBUS_ERR; + + bcopy(&dhd_bus->pub.stats, stats, sizeof(dbus_stats_t)); + + return DBUS_OK; +} + +int +dbus_get_attrib(dhd_bus_t *pub, dbus_attrib_t *attrib) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) pub; + int err = DBUS_ERR; + + if ((dhd_bus == NULL) || (attrib == NULL)) + return DBUS_ERR; + + if (dhd_bus->drvintf && dhd_bus->drvintf->get_attrib) { + err = dhd_bus->drvintf->get_attrib(dhd_bus->bus_info, + &dhd_bus->pub.attrib); + } + + bcopy(&dhd_bus->pub.attrib, attrib, sizeof(dbus_attrib_t)); + return err; +} + +int +dbus_get_device_speed(dbus_pub_t *pub) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) pub; + + if (dhd_bus == NULL) + return INVALID_SPEED; + + return (dhd_bus->pub.device_speed); +} + +int +dbus_set_config(dbus_pub_t *pub, dbus_config_t *config) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) pub; + int err = DBUS_ERR; + + if ((dhd_bus == NULL) || (config == NULL)) + return DBUS_ERR; + + if (dhd_bus->drvintf && dhd_bus->drvintf->set_config) { + err = dhd_bus->drvintf->set_config(dhd_bus->bus_info, + config); + + if ((config->config_id == DBUS_CONFIG_ID_AGGR_LIMIT) && + (!err) && + (dhd_bus->pub.busstate == DBUS_STATE_UP)) { + dbus_rxirbs_fill(dhd_bus); + } + } + + return err; +} + +int +dbus_get_config(dbus_pub_t *pub, dbus_config_t *config) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) pub; + int err = DBUS_ERR; + + if ((dhd_bus == NULL) || (config == NULL)) + return DBUS_ERR; + + if (dhd_bus->drvintf && dhd_bus->drvintf->get_config) { + err = dhd_bus->drvintf->get_config(dhd_bus->bus_info, + config); + } + + return err; +} + +int +dbus_set_errmask(dbus_pub_t *pub, uint32 mask) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) pub; + int err = DBUS_OK; + + if (dhd_bus == NULL) + return DBUS_ERR; + + dhd_bus->errmask = mask; + return err; +} + +int +dbus_pnp_resume(dbus_pub_t *pub, int *fw_reload) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) pub; + int err = DBUS_ERR; + bool fwdl = FALSE; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (dhd_bus == NULL) + return DBUS_ERR; + + if (dhd_bus->pub.busstate == DBUS_STATE_UP) { + return DBUS_OK; + } + + + + if (dhd_bus->drvintf->pnp) { + err = dhd_bus->drvintf->pnp(dhd_bus->bus_info, + DBUS_PNP_RESUME); + } + + if (dhd_bus->drvintf->recv_needed) { + if (dhd_bus->drvintf->recv_needed(dhd_bus->bus_info)) { + /* Refill after sleep/hibernate */ + dbus_rxirbs_fill(dhd_bus); + } + } + + + if (fw_reload) + *fw_reload = fwdl; + + return err; +} /* dbus_pnp_resume */ + +int +dbus_pnp_sleep(dbus_pub_t *pub) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) pub; + int err = DBUS_ERR; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (dhd_bus == NULL) + return DBUS_ERR; + + dbus_tx_timer_stop(dhd_bus); + + if (dhd_bus->drvintf && dhd_bus->drvintf->pnp) { + err = dhd_bus->drvintf->pnp(dhd_bus->bus_info, + DBUS_PNP_SLEEP); + } + + return err; +} + +int +dbus_pnp_disconnect(dbus_pub_t *pub) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) pub; + int err = DBUS_ERR; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (dhd_bus == NULL) + return DBUS_ERR; + + dbus_tx_timer_stop(dhd_bus); + + if (dhd_bus->drvintf && dhd_bus->drvintf->pnp) { + err = dhd_bus->drvintf->pnp(dhd_bus->bus_info, + DBUS_PNP_DISCONNECT); + } + + return err; +} + +int +dhd_bus_iovar_op(dhd_pub_t *dhdp, const char *name, + void *params, int plen, void *arg, int len, bool set) +{ + dhd_bus_t *dhd_bus = (dhd_bus_t *) dhdp->bus; + int err = DBUS_ERR; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (dhd_bus == NULL) + return DBUS_ERR; + + if (dhd_bus->drvintf && dhd_bus->drvintf->iovar_op) { + err = dhd_bus->drvintf->iovar_op(dhd_bus->bus_info, + name, params, plen, arg, len, set); + } + + return err; +} + + +void * +dhd_dbus_txq(const dbus_pub_t *pub) +{ + return NULL; +} + +uint +dhd_dbus_hdrlen(const dbus_pub_t *pub) +{ + return 0; +} + +void * +dbus_get_devinfo(dbus_pub_t *pub) +{ + return pub->dev_info; +} + +#if defined(BCM_REQUEST_FW) && !defined(EXTERNAL_FW_PATH) +static int +dbus_otp(dhd_bus_t *dhd_bus, uint16 *boardtype, uint16 *boardrev) +{ + uint32 value = 0; + uint8 *cis; + uint16 *otpinfo; + uint32 i; + bool standard_cis = TRUE; + uint8 tup, tlen; + bool btype_present = FALSE; + bool brev_present = FALSE; + int ret; + int devid; + uint16 btype = 0; + uint16 brev = 0; + uint32 otp_size = 0, otp_addr = 0, otp_sw_rgn = 0; + + if (dhd_bus == NULL || dhd_bus->drvintf == NULL || + dhd_bus->drvintf->readreg == NULL) + return DBUS_ERR; + + devid = dhd_bus->pub.attrib.devid; + + if ((devid == BCM43234_CHIP_ID) || (devid == BCM43235_CHIP_ID) || + (devid == BCM43236_CHIP_ID)) { + + otp_size = BCM_OTP_SIZE_43236; + otp_sw_rgn = BCM_OTP_SW_RGN_43236; + otp_addr = BCM_OTP_ADDR_43236; + + } else { + return DBUS_ERR_NVRAM; + } + + cis = MALLOC(dhd_bus->pub.osh, otp_size * 2); + if (cis == NULL) + return DBUS_ERR; + + otpinfo = (uint16 *) cis; + + for (i = 0; i < otp_size; i++) { + + ret = dhd_bus->drvintf->readreg(dhd_bus->bus_info, + otp_addr + ((otp_sw_rgn + i) << 1), 2, &value); + + if (ret != DBUS_OK) { + MFREE(dhd_bus->pub.osh, cis, otp_size * 2); + return ret; + } + otpinfo[i] = (uint16) value; + } + + for (i = 0; i < (otp_size << 1); ) { + + if (standard_cis) { + tup = cis[i++]; + if (tup == CISTPL_NULL || tup == CISTPL_END) + tlen = 0; + else + tlen = cis[i++]; + } else { + if (cis[i] == CISTPL_NULL || cis[i] == CISTPL_END) { + tlen = 0; + tup = cis[i]; + } else { + tlen = cis[i]; + tup = CISTPL_BRCM_HNBU; + } + ++i; + } + + if (tup == CISTPL_END || (i + tlen) >= (otp_size << 1)) { + break; + } + + switch (tup) { + + case CISTPL_BRCM_HNBU: + + switch (cis[i]) { + + case HNBU_BOARDTYPE: + + btype = (uint16) ((cis[i + 2] << 8) + cis[i + 1]); + btype_present = TRUE; + DBUSTRACE(("%s: HNBU_BOARDTYPE = 0x%2x\n", __FUNCTION__, + (uint32)btype)); + break; + + case HNBU_BOARDREV: + + if (tlen == 2) + brev = (uint16) cis[i + 1]; + else + brev = (uint16) ((cis[i + 2] << 8) + cis[i + 1]); + brev_present = TRUE; + DBUSTRACE(("%s: HNBU_BOARDREV = 0x%2x\n", __FUNCTION__, + (uint32)*boardrev)); + break; + + case HNBU_HNBUCIS: + DBUSTRACE(("%s: HNBU_HNBUCIS\n", __FUNCTION__)); + tlen++; + standard_cis = FALSE; + break; + } + break; + } + + i += tlen; + } + + MFREE(dhd_bus->pub.osh, cis, otp_size * 2); + + if (btype_present == TRUE && brev_present == TRUE) { + *boardtype = btype; + *boardrev = brev; + DBUSERR(("otp boardtype = 0x%2x boardrev = 0x%2x\n", + *boardtype, *boardrev)); + + return DBUS_OK; + } + else + return DBUS_ERR; +} /* dbus_otp */ + +static int +dbus_select_nvram(dhd_bus_t *dhd_bus, int8 *jumbonvram, int jumbolen, +uint16 boardtype, uint16 boardrev, int8 **nvram, int *nvram_len) +{ + /* Multi board nvram file format is contenation of nvram info with \r + * The file format for two contatenated set is + * \nBroadcom Jumbo Nvram file\nfirst_set\nsecond_set\nthird_set\n + */ + uint8 *nvram_start = NULL, *nvram_end = NULL; + uint8 *nvram_start_prev = NULL, *nvram_end_prev = NULL; + uint16 btype = 0, brev = 0; + int len = 0; + char *field; + + *nvram = NULL; + *nvram_len = 0; + + if (strncmp(BCM_JUMBO_START, jumbonvram, strlen(BCM_JUMBO_START))) { + /* single nvram file in the native format */ + DBUSTRACE(("%s: Non-Jumbo NVRAM File \n", __FUNCTION__)); + *nvram = jumbonvram; + *nvram_len = jumbolen; + return DBUS_OK; + } else { + DBUSTRACE(("%s: Jumbo NVRAM File \n", __FUNCTION__)); + } + + /* sanity test the end of the config sets for proper ending */ + if (jumbonvram[jumbolen - 1] != BCM_JUMBO_NVRAM_DELIMIT || + jumbonvram[jumbolen - 2] != '\0') { + DBUSERR(("%s: Bad Jumbo NVRAM file format\n", __FUNCTION__)); + return DBUS_JUMBO_BAD_FORMAT; + } + + dhd_bus->nvram_nontxt = DBUS_NVRAM_NONTXT; + + nvram_start = jumbonvram; + + while (*nvram_start != BCM_JUMBO_NVRAM_DELIMIT && len < jumbolen) { + + /* consume the first file info line + * \nBroadcom Jumbo Nvram file\nfile1\n ... + */ + len ++; + nvram_start ++; + } + + nvram_end = nvram_start; + + /* search for "boardrev=0xabcd" and "boardtype=0x1234" information in + * the concatenated nvram config files /sets + */ + + while (len < jumbolen) { + + if (*nvram_end == '\0') { + /* end of a config set is marked by multiple null characters */ + len ++; + nvram_end ++; + DBUSTRACE(("%s: NULL chr len = %d char = 0x%x\n", __FUNCTION__, + len, *nvram_end)); + continue; + + } else if (*nvram_end == BCM_JUMBO_NVRAM_DELIMIT) { + + /* config set delimiter is reached */ + /* check if next config set is present or not + * return if next config is not present + */ + + /* start search the next config set */ + nvram_start_prev = nvram_start; + nvram_end_prev = nvram_end; + + nvram_end ++; + nvram_start = nvram_end; + btype = brev = 0; + DBUSTRACE(("%s: going to next record len = %d " + "char = 0x%x \n", __FUNCTION__, len, *nvram_end)); + len ++; + if (len >= jumbolen) { + + *nvram = nvram_start_prev; + *nvram_len = (int)(nvram_end_prev - nvram_start_prev); + + DBUSTRACE(("%s: no more len = %d nvram_end = 0x%p", + __FUNCTION__, len, nvram_end)); + + return DBUS_JUMBO_NOMATCH; + + } else { + continue; + } + + } else { + + DBUSTRACE(("%s: config str = %s\n", __FUNCTION__, nvram_end)); + + if (bcmp(nvram_end, "boardtype", strlen("boardtype")) == 0) { + + field = strchr(nvram_end, '='); + field++; + btype = (uint16)bcm_strtoul(field, NULL, 0); + + DBUSTRACE(("%s: btype = 0x%x boardtype = 0x%x \n", __FUNCTION__, + btype, boardtype)); + } + + if (bcmp(nvram_end, "boardrev", strlen("boardrev")) == 0) { + + field = strchr(nvram_end, '='); + field++; + brev = (uint16)bcm_strtoul(field, NULL, 0); + + DBUSTRACE(("%s: brev = 0x%x boardrev = 0x%x \n", __FUNCTION__, + brev, boardrev)); + } + if (btype == boardtype && brev == boardrev) { + /* locate nvram config set end - ie.find '\r' char */ + while (*nvram_end != BCM_JUMBO_NVRAM_DELIMIT) + nvram_end ++; + *nvram = nvram_start; + *nvram_len = (int) (nvram_end - nvram_start); + DBUSTRACE(("found len = %d nvram_start = 0x%p " + "nvram_end = 0x%p\n", *nvram_len, nvram_start, nvram_end)); + return DBUS_OK; + } + + len += (strlen(nvram_end) + 1); + nvram_end += (strlen(nvram_end) + 1); + } + } + return DBUS_JUMBO_NOMATCH; +} /* dbus_select_nvram */ + +#endif + +#define DBUS_NRXQ 50 +#define DBUS_NTXQ 100 + +static void +dhd_dbus_send_complete(void *handle, void *info, int status) +{ + dhd_pub_t *dhd = (dhd_pub_t *)handle; + void *pkt = info; + + if ((dhd == NULL) || (pkt == NULL)) { + DBUSERR(("dhd or pkt is NULL\n")); + return; + } + + if (status == DBUS_OK) { + dhd->dstats.tx_packets++; + } else { + DBUSERR(("TX error=%d\n", status)); + dhd->dstats.tx_errors++; + } +#ifdef PROP_TXSTATUS + if (DHD_PKTTAG_WLFCPKT(PKTTAG(pkt)) && + (dhd_wlfc_txcomplete(dhd, pkt, status == 0) != WLFC_UNSUPPORTED)) { + return; + } +#endif /* PROP_TXSTATUS */ + PKTFREE(dhd->osh, pkt, TRUE); +} + +static void +dhd_dbus_recv_pkt(void *handle, void *pkt) +{ + uchar reorder_info_buf[WLHOST_REORDERDATA_TOTLEN]; + uint reorder_info_len; + uint pkt_count; + dhd_pub_t *dhd = (dhd_pub_t *)handle; + int ifidx = 0; + + if (dhd == NULL) { + DBUSERR(("%s: dhd is NULL\n", __FUNCTION__)); + return; + } + + /* If the protocol uses a data header, check and remove it */ + if (dhd_prot_hdrpull(dhd, &ifidx, pkt, reorder_info_buf, + &reorder_info_len) != 0) { + DBUSERR(("rx protocol error\n")); + PKTFREE(dhd->osh, pkt, FALSE); + dhd->rx_errors++; + return; + } + + if (reorder_info_len) { + /* Reordering info from the firmware */ + dhd_process_pkt_reorder_info(dhd, reorder_info_buf, reorder_info_len, + &pkt, &pkt_count); + if (pkt_count == 0) + return; + } + else { + pkt_count = 1; + } + dhd_rx_frame(dhd, ifidx, pkt, pkt_count, 0); +} + +static void +dhd_dbus_recv_buf(void *handle, uint8 *buf, int len) +{ + dhd_pub_t *dhd = (dhd_pub_t *)handle; + void *pkt; + + if (dhd == NULL) { + DBUSERR(("%s: dhd is NULL\n", __FUNCTION__)); + return; + } + + if ((pkt = PKTGET(dhd->osh, len, FALSE)) == NULL) { + DBUSERR(("PKTGET (rx) failed=%d\n", len)); + return; + } + + bcopy(buf, PKTDATA(dhd->osh, pkt), len); + dhd_dbus_recv_pkt(dhd, pkt); +} + +static void +dhd_dbus_txflowcontrol(void *handle, bool onoff) +{ + dhd_pub_t *dhd = (dhd_pub_t *)handle; + bool wlfc_enabled = FALSE; + + if (dhd == NULL) { + DBUSERR(("%s: dhd is NULL\n", __FUNCTION__)); + return; + } + +#ifdef PROP_TXSTATUS + wlfc_enabled = (dhd_wlfc_flowcontrol(dhd, onoff, !onoff) != WLFC_UNSUPPORTED); +#endif + + if (!wlfc_enabled) { + dhd_txflowcontrol(dhd, ALL_INTERFACES, onoff); + } +} + +static void +dhd_dbus_errhandler(void *handle, int err) +{ +} + +static void +dhd_dbus_ctl_complete(void *handle, int type, int status) +{ + dhd_pub_t *dhd = (dhd_pub_t *)handle; + + if (dhd == NULL) { + DBUSERR(("%s: dhd is NULL\n", __FUNCTION__)); + return; + } + + if (type == DBUS_CBCTL_READ) { + if (status == DBUS_OK) + dhd->rx_ctlpkts++; + else + dhd->rx_ctlerrs++; + } else if (type == DBUS_CBCTL_WRITE) { + if (status == DBUS_OK) + dhd->tx_ctlpkts++; + else + dhd->tx_ctlerrs++; + } + + dhd_prot_ctl_complete(dhd); +} + +static void +dhd_dbus_state_change(void *handle, int state) +{ + dhd_pub_t *dhd = (dhd_pub_t *)handle; + + if (dhd == NULL) { + DBUSERR(("%s: dhd is NULL\n", __FUNCTION__)); + return; + } + + switch (state) { + + case DBUS_STATE_DL_NEEDED: + DBUSERR(("%s: firmware request cannot be handled\n", __FUNCTION__)); + break; + case DBUS_STATE_DOWN: + DBUSTRACE(("%s: DBUS is down\n", __FUNCTION__)); + dhd->busstate = DHD_BUS_DOWN; + break; + case DBUS_STATE_UP: + DBUSTRACE(("%s: DBUS is up\n", __FUNCTION__)); + dhd->busstate = DHD_BUS_DATA; + break; + default: + break; + } + + DBUSERR(("%s: DBUS current state=%d\n", __FUNCTION__, state)); +} + +static void * +dhd_dbus_pktget(void *handle, uint len, bool send) +{ + dhd_pub_t *dhd = (dhd_pub_t *)handle; + void *p = NULL; + + if (dhd == NULL) { + DBUSERR(("%s: dhd is NULL\n", __FUNCTION__)); + return NULL; + } + + if (send == TRUE) { + dhd_os_sdlock_txq(dhd); + p = PKTGET(dhd->osh, len, TRUE); + dhd_os_sdunlock_txq(dhd); + } else { + dhd_os_sdlock_rxq(dhd); + p = PKTGET(dhd->osh, len, FALSE); + dhd_os_sdunlock_rxq(dhd); + } + + return p; +} + +static void +dhd_dbus_pktfree(void *handle, void *p, bool send) +{ + dhd_pub_t *dhd = (dhd_pub_t *)handle; + + if (dhd == NULL) { + DBUSERR(("%s: dhd is NULL\n", __FUNCTION__)); + return; + } + + if (send == TRUE) { +#ifdef PROP_TXSTATUS + if (DHD_PKTTAG_WLFCPKT(PKTTAG(p)) && + (dhd_wlfc_txcomplete(dhd, p, FALSE) != WLFC_UNSUPPORTED)) { + return; + } +#endif /* PROP_TXSTATUS */ + + dhd_os_sdlock_txq(dhd); + PKTFREE(dhd->osh, p, TRUE); + dhd_os_sdunlock_txq(dhd); + } else { + dhd_os_sdlock_rxq(dhd); + PKTFREE(dhd->osh, p, FALSE); + dhd_os_sdunlock_rxq(dhd); + } +} + + +static dbus_callbacks_t dhd_dbus_cbs = { + dhd_dbus_send_complete, + dhd_dbus_recv_buf, + dhd_dbus_recv_pkt, + dhd_dbus_txflowcontrol, + dhd_dbus_errhandler, + dhd_dbus_ctl_complete, + dhd_dbus_state_change, + dhd_dbus_pktget, + dhd_dbus_pktfree +}; + +uint +dhd_bus_chip(struct dhd_bus *bus) +{ + ASSERT(bus != NULL); + return bus->pub.attrib.devid; +} + +uint +dhd_bus_chiprev(struct dhd_bus *bus) +{ + ASSERT(bus); + ASSERT(bus != NULL); + return bus->pub.attrib.chiprev; +} + +void +dhd_bus_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf) +{ + bcm_bprintf(strbuf, "Bus USB\n"); +} + +void +dhd_bus_clearcounts(dhd_pub_t *dhdp) +{ +} + +int +dhd_bus_txdata(struct dhd_bus *bus, void *pktbuf) +{ + DBUSTRACE(("%s\n", __FUNCTION__)); + if (bus->txoff) { + DBUSTRACE(("txoff\n")); + return BCME_EPERM; + } + return dbus_send_txdata(&bus->pub, pktbuf); +} + +static void +dhd_dbus_advertise_bus_cleanup(dhd_pub_t *dhdp) +{ + unsigned long flags; + int timeleft; + + DHD_LINUX_GENERAL_LOCK(dhdp, flags); + dhdp->busstate = DHD_BUS_DOWN_IN_PROGRESS; + DHD_LINUX_GENERAL_UNLOCK(dhdp, flags); + + timeleft = dhd_os_busbusy_wait_negation(dhdp, &dhdp->dhd_bus_busy_state); + if ((timeleft == 0) || (timeleft == 1)) { + DBUSERR(("%s : Timeout due to dhd_bus_busy_state=0x%x\n", + __FUNCTION__, dhdp->dhd_bus_busy_state)); + ASSERT(0); + } + + return; +} + +static void +dhd_dbus_advertise_bus_remove(dhd_pub_t *dhdp) +{ + unsigned long flags; + int timeleft; + + DHD_LINUX_GENERAL_LOCK(dhdp, flags); + dhdp->busstate = DHD_BUS_REMOVE; + DHD_LINUX_GENERAL_UNLOCK(dhdp, flags); + + timeleft = dhd_os_busbusy_wait_negation(dhdp, &dhdp->dhd_bus_busy_state); + if ((timeleft == 0) || (timeleft == 1)) { + DBUSERR(("%s : Timeout due to dhd_bus_busy_state=0x%x\n", + __FUNCTION__, dhdp->dhd_bus_busy_state)); + ASSERT(0); + } + + return; +} + +int +dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag) +{ + int bcmerror = 0; + unsigned long flags; + wifi_adapter_info_t *adapter = (wifi_adapter_info_t *)dhdp->adapter; + + if (flag == TRUE) { + if (!dhdp->dongle_reset) { + DBUSERR(("%s: == Power OFF ==\n", __FUNCTION__)); + dhd_dbus_advertise_bus_cleanup(dhdp); + dhd_os_wd_timer(dhdp, 0); +#if !defined(IGNORE_ETH0_DOWN) + /* Force flow control as protection when stop come before ifconfig_down */ + dhd_txflowcontrol(dhdp, ALL_INTERFACES, ON); +#endif /* !defined(IGNORE_ETH0_DOWN) */ + dbus_stop(dhdp->bus); + + dhdp->dongle_reset = TRUE; + dhdp->up = FALSE; + + DHD_LINUX_GENERAL_LOCK(dhdp, flags); + dhdp->busstate = DHD_BUS_DOWN; + DHD_LINUX_GENERAL_UNLOCK(dhdp, flags); + wifi_clr_adapter_status(adapter, WIFI_STATUS_FW_READY); + + printf("%s: WLAN OFF DONE\n", __FUNCTION__); + /* App can now remove power from device */ + } else + bcmerror = BCME_ERROR; + } else { + /* App must have restored power to device before calling */ + printf("\n\n%s: == WLAN ON ==\n", __FUNCTION__); + if (dhdp->dongle_reset) { + /* Turn on WLAN */ + DHD_MUTEX_UNLOCK(); + wait_event_interruptible_timeout(adapter->status_event, + wifi_get_adapter_status(adapter, WIFI_STATUS_FW_READY), + msecs_to_jiffies(DHD_FW_READY_TIMEOUT)); + DHD_MUTEX_LOCK(); + bcmerror = dbus_up(dhdp->bus); + if (bcmerror == BCME_OK) { + dhdp->dongle_reset = FALSE; + dhdp->up = TRUE; +#if !defined(IGNORE_ETH0_DOWN) + /* Restore flow control */ + dhd_txflowcontrol(dhdp, ALL_INTERFACES, OFF); +#endif + dhd_os_wd_timer(dhdp, dhd_watchdog_ms); + + DBUSTRACE(("%s: WLAN ON DONE\n", __FUNCTION__)); + } else { + DBUSERR(("%s: failed to dbus_up with code %d\n", __FUNCTION__, bcmerror)); + } + } + } + +#ifdef PKT_STATICS + memset((uint8*) &tx_statics, 0, sizeof(pkt_statics_t)); +#endif + return bcmerror; +} + +void +dhd_set_path_params(struct dhd_bus *bus) +{ + /* External conf takes precedence if specified */ + dhd_conf_preinit(bus->dhd); + + if (bus->dhd->conf_path[0] == '\0') { + dhd_conf_set_path(bus->dhd, "config.txt", bus->dhd->conf_path, bus->nv_path); + } + if (bus->dhd->clm_path[0] == '\0') { + dhd_conf_set_path(bus->dhd, "clm.blob", bus->dhd->clm_path, bus->fw_path); + } +#ifdef CONFIG_PATH_AUTO_SELECT + dhd_conf_set_conf_name_by_chip(bus->dhd, bus->dhd->conf_path); +#endif + + dhd_conf_read_config(bus->dhd, bus->dhd->conf_path); + + dhd_conf_set_fw_name_by_chip(bus->dhd, bus->fw_path); + dhd_conf_set_nv_name_by_chip(bus->dhd, bus->nv_path); + dhd_conf_set_clm_name_by_chip(bus->dhd, bus->dhd->clm_path); + + printf("Final fw_path=%s\n", bus->fw_path); + printf("Final nv_path=%s\n", bus->nv_path); + printf("Final clm_path=%s\n", bus->dhd->clm_path); + printf("Final conf_path=%s\n", bus->dhd->conf_path); + +} + +void +dhd_bus_update_fw_nv_path(struct dhd_bus *bus, char *pfw_path, + char *pnv_path, char *pclm_path, char *pconf_path) +{ + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (bus == NULL) { + DBUSERR(("%s: bus is NULL\n", __FUNCTION__)); + return; + } + + bus->fw_path = pfw_path; + bus->nv_path = pnv_path; + bus->dhd->clm_path = pclm_path; + bus->dhd->conf_path = pconf_path; + + dhd_set_path_params(bus); + +} + +/* + * hdrlen is space to reserve in pkt headroom for DBUS + */ +void * +dhd_dbus_probe_cb(void *arg, const char *desc, uint32 bustype, + uint16 bus_no, uint16 slot, uint32 hdrlen) +{ + osl_t *osh = NULL; + dhd_bus_t *bus = NULL; + dhd_pub_t *pub = NULL; + uint rxsz; + int dlneeded = 0; + wifi_adapter_info_t *adapter = NULL; + + DBUSTRACE(("%s: Enter\n", __FUNCTION__)); + + adapter = dhd_wifi_platform_get_adapter(bustype, bus_no, slot); + + if (!g_pub) { + /* Ask the OS interface part for an OSL handle */ + if (!(osh = osl_attach(NULL, bustype, TRUE))) { + DBUSERR(("%s: OSL attach failed\n", __FUNCTION__)); + goto fail; + } + + /* Attach to the dhd/OS interface */ + if (!(pub = dhd_attach(osh, bus, hdrlen, adapter))) { + DBUSERR(("%s: dhd_attach failed\n", __FUNCTION__)); + goto fail; + } + } else { + pub = g_pub; + } + + if (pub->bus) { + DBUSERR(("%s: wrong probe\n", __FUNCTION__)); + goto fail; + } + + rxsz = dhd_get_rxsz(pub); + bus = dbus_attach(osh, rxsz, DBUS_NRXQ, DBUS_NTXQ, pub, &dhd_dbus_cbs, NULL, NULL); + if (bus) { + pub->bus = bus; + bus->dhd = pub; + + dlneeded = dbus_dlneeded(bus); + if (dlneeded >= 0) { + if (!g_pub) { + dhd_conf_reset(pub); + dhd_conf_set_chiprev(pub, bus->pub.attrib.devid, bus->pub.attrib.chiprev); + dhd_conf_preinit(pub); + } + } + + if (g_pub || dhd_download_fw_on_driverload) { + if (dlneeded == 0) { + wifi_set_adapter_status(adapter, WIFI_STATUS_FW_READY); +#ifdef BCM_REQUEST_FW + } else if (dlneeded > 0) { + dhd_set_path(bus->dhd); + if (dbus_download_firmware(bus, bus->fw_path, bus->nv_path) != DBUS_OK) + goto fail; +#endif + } + } + } else { + DBUSERR(("%s: dbus_attach failed\n", __FUNCTION__)); + } + + if (!g_pub) { + /* Ok, finish the attach to the OS network interface */ + if (dhd_register_if(pub, 0, TRUE) != 0) { + DBUSERR(("%s: dhd_register_if failed\n", __FUNCTION__)); + goto fail; + } + pub->hang_report = TRUE; +#if defined(MULTIPLE_SUPPLICANT) + wl_android_post_init(); // terence 20120530: fix critical section in dhd_open and dhdsdio_probe +#endif + g_pub = pub; + } + + DBUSTRACE(("%s: Exit\n", __FUNCTION__)); + wifi_clr_adapter_status(adapter, WIFI_STATUS_DETTACH); + wifi_set_adapter_status(adapter, WIFI_STATUS_ATTACH); + wake_up_interruptible(&adapter->status_event); + /* This is passed to dhd_dbus_disconnect_cb */ + return bus; + +fail: + if (pub && pub->bus) { + dbus_detach(pub->bus); + pub->bus = NULL; + } + /* Release resources in reverse order */ + if (!g_pub) { + if (pub) { + dhd_detach(pub); + dhd_free(pub); + } + if (osh) { + osl_detach(osh); + } + } + + printf("%s: Failed\n", __FUNCTION__); + return NULL; +} + +void +dhd_dbus_disconnect_cb(void *arg) +{ + dhd_bus_t *bus = (dhd_bus_t *)arg; + dhd_pub_t *pub = g_pub; + osl_t *osh; + wifi_adapter_info_t *adapter = NULL; + + adapter = (wifi_adapter_info_t *)pub->adapter; + + if (pub && !pub->dhd_remove && bus == NULL) { + DBUSERR(("%s: bus is NULL\n", __FUNCTION__)); + return; + } + if (!adapter) { + DBUSERR(("%s: adapter is NULL\n", __FUNCTION__)); + return; + } + + printf("%s: Enter dhd_remove=%d on %s\n", __FUNCTION__, + pub->dhd_remove, adapter->name); + if (!pub->dhd_remove) { + /* Advertise bus remove during rmmod */ + dhd_dbus_advertise_bus_remove(bus->dhd); + dbus_detach(pub->bus); + pub->bus = NULL; + wifi_clr_adapter_status(adapter, WIFI_STATUS_ATTACH); + wifi_set_adapter_status(adapter, WIFI_STATUS_DETTACH); + wake_up_interruptible(&adapter->status_event); + } else { + osh = pub->osh; + dhd_detach(pub); + if (pub->bus) { + dbus_detach(pub->bus); + pub->bus = NULL; + } + dhd_free(pub); + g_pub = NULL; + if (MALLOCED(osh)) { + DBUSERR(("%s: MEMORY LEAK %d bytes\n", __FUNCTION__, MALLOCED(osh))); + } + osl_detach(osh); + } + + DBUSTRACE(("%s: Exit\n", __FUNCTION__)); +} + +#ifdef LINUX_EXTERNAL_MODULE_DBUS + +static int __init +bcm_dbus_module_init(void) +{ + printf("Inserting bcm_dbus module \n"); + return 0; +} + +static void __exit +bcm_dbus_module_exit(void) +{ + printf("Removing bcm_dbus module \n"); + return; +} + +EXPORT_SYMBOL(dbus_pnp_sleep); +EXPORT_SYMBOL(dbus_get_devinfo); +EXPORT_SYMBOL(dbus_detach); +EXPORT_SYMBOL(dbus_get_attrib); +EXPORT_SYMBOL(dbus_down); +EXPORT_SYMBOL(dbus_pnp_resume); +EXPORT_SYMBOL(dbus_set_config); +EXPORT_SYMBOL(dbus_flowctrl_rx); +EXPORT_SYMBOL(dbus_up); +EXPORT_SYMBOL(dbus_get_device_speed); +EXPORT_SYMBOL(dbus_send_pkt); +EXPORT_SYMBOL(dbus_recv_ctl); +EXPORT_SYMBOL(dbus_attach); + +MODULE_LICENSE("GPL"); + +module_init(bcm_dbus_module_init); +module_exit(bcm_dbus_module_exit); + +#endif /* #ifdef LINUX_EXTERNAL_MODULE_DBUS */ diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dbus_usb.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dbus_usb.c new file mode 100644 index 00000000..3be28b2d --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dbus_usb.c @@ -0,0 +1,1173 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Dongle BUS interface for USB, OS independent + * + * Copyright (C) 1999-2016, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * + * <> + * + * $Id: dbus_usb.c 565557 2015-06-22 19:29:44Z $ + */ + +/** + * @file @brief + * This file contains DBUS code that is USB, but not OS specific. DBUS is a Broadcom proprietary + * host specific abstraction layer. + */ + +#include +#include +#include +#include +#include +#include +#include + +uint dbus_msglevel = DBUS_ERROR_VAL; +module_param(dbus_msglevel, int, 0); + + +#define USB_DLIMAGE_RETRY_TIMEOUT 3000 /* retry Timeout */ +#define USB_SFLASH_DLIMAGE_SPINWAIT 150 /* in unit of ms */ +#define USB_SFLASH_DLIMAGE_LIMIT 2000 /* spinwait limit (ms) */ +#define POSTBOOT_ID 0xA123 /* ID to detect if dongle has boot up */ +#define USB_RESETCFG_SPINWAIT 1 /* wait after resetcfg (ms) */ +#define USB_DEV_ISBAD(u) (u->pub->attrib.devid == 0xDEAD) +#define USB_DLGO_SPINWAIT 100 /* wait after DL_GO (ms) */ +#define TEST_CHIP 0x4328 + +typedef struct { + dbus_pub_t *pub; + + void *cbarg; + dbus_intf_callbacks_t *cbs; /** callbacks into higher DBUS level (dbus.c) */ + dbus_intf_t *drvintf; + void *usbosl_info; + uint32 rdlram_base_addr; + uint32 rdlram_size; +} usb_info_t; + +/* + * Callbacks common to all USB + */ +static void dbus_usb_disconnect(void *handle); +static void dbus_usb_send_irb_timeout(void *handle, dbus_irb_tx_t *txirb); +static void dbus_usb_send_irb_complete(void *handle, dbus_irb_tx_t *txirb, int status); +static void dbus_usb_recv_irb_complete(void *handle, dbus_irb_rx_t *rxirb, int status); +static void dbus_usb_errhandler(void *handle, int err); +static void dbus_usb_ctl_complete(void *handle, int type, int status); +static void dbus_usb_state_change(void *handle, int state); +static struct dbus_irb* dbus_usb_getirb(void *handle, bool send); +static void dbus_usb_rxerr_indicate(void *handle, bool on); +#if !defined(BCM_REQUEST_FW) +static int dbus_usb_resetcfg(usb_info_t *usbinfo); +#endif +static int dbus_usb_iovar_op(void *bus, const char *name, + void *params, int plen, void *arg, int len, bool set); +static int dbus_iovar_process(usb_info_t* usbinfo, const char *name, + void *params, int plen, void *arg, int len, bool set); +static int dbus_usb_doiovar(usb_info_t *bus, const bcm_iovar_t *vi, uint32 actionid, + const char *name, void *params, int plen, void *arg, int len, int val_size); +static int dhdusb_downloadvars(usb_info_t *bus, void *arg, int len); + +static int dbus_usb_dl_writeimage(usb_info_t *usbinfo, uint8 *fw, int fwlen); +static int dbus_usb_dlstart(void *bus, uint8 *fw, int len); +static int dbus_usb_dlneeded(void *bus); +static int dbus_usb_dlrun(void *bus); +static int dbus_usb_rdl_dwnld_state(usb_info_t *usbinfo); + + +/* OS specific */ +extern bool dbus_usbos_dl_cmd(void *info, uint8 cmd, void *buffer, int buflen); +extern int dbus_usbos_wait(void *info, uint16 ms); +extern int dbus_write_membytes(usb_info_t *usbinfo, bool set, uint32 address, + uint8 *data, uint size); +extern bool dbus_usbos_dl_send_bulk(void *info, void *buffer, int len); +extern int dbus_usbos_loopback_tx(void *usbos_info_ptr, int cnt, int size); + +/** + * These functions are called by the lower DBUS level (dbus_usb_os.c) to notify this DBUS level + * (dbus_usb.c) of an event. + */ +static dbus_intf_callbacks_t dbus_usb_intf_cbs = { + dbus_usb_send_irb_timeout, + dbus_usb_send_irb_complete, + dbus_usb_recv_irb_complete, + dbus_usb_errhandler, + dbus_usb_ctl_complete, + dbus_usb_state_change, + NULL, /* isr */ + NULL, /* dpc */ + NULL, /* watchdog */ + NULL, /* dbus_if_pktget */ + NULL, /* dbus_if_pktfree */ + dbus_usb_getirb, + dbus_usb_rxerr_indicate +}; + +/* IOVar table */ +enum { + IOV_SET_DOWNLOAD_STATE = 1, + IOV_DBUS_MSGLEVEL, + IOV_MEMBYTES, + IOV_VARS, + IOV_LOOPBACK_TX +}; + +const bcm_iovar_t dhdusb_iovars[] = { + {"vars", IOV_VARS, 0, IOVT_BUFFER, 0 }, + {"dbus_msglevel", IOV_DBUS_MSGLEVEL, 0, IOVT_UINT32, 0 }, + {"dwnldstate", IOV_SET_DOWNLOAD_STATE, 0, IOVT_BOOL, 0 }, + {"membytes", IOV_MEMBYTES, 0, IOVT_BUFFER, 2 * sizeof(int) }, + {"usb_lb_txfer", IOV_LOOPBACK_TX, 0, IOVT_BUFFER, 2 * sizeof(int) }, + {NULL, 0, 0, 0, 0 } +}; + +/* + * Need global for probe() and disconnect() since + * attach() is not called at probe and detach() + * can be called inside disconnect() + */ +static probe_cb_t probe_cb = NULL; +static disconnect_cb_t disconnect_cb = NULL; +static void *probe_arg = NULL; +static void *disc_arg = NULL; +static dbus_intf_t *g_dbusintf = NULL; +static dbus_intf_t dbus_usb_intf; /** functions called by higher layer DBUS into lower layer */ + +/* + * dbus_intf_t common to all USB + * These functions override dbus_usb_.c. + */ +static void *dbus_usb_attach(dbus_pub_t *pub, void *cbarg, dbus_intf_callbacks_t *cbs); +static void dbus_usb_detach(dbus_pub_t *pub, void *info); +static void * dbus_usb_probe(void *arg, const char *desc, uint32 bustype, + uint16 bus_no, uint16 slot, uint32 hdrlen); + +/* functions */ + +/** + * As part of DBUS initialization/registration, the higher level DBUS (dbus.c) needs to know what + * lower level DBUS functions to call (in both dbus_usb.c and dbus_usb_os.c). + */ +static void * +dbus_usb_probe(void *arg, const char *desc, uint32 bustype, uint16 bus_no, + uint16 slot, uint32 hdrlen) +{ + DBUSTRACE(("%s(): \n", __FUNCTION__)); + if (probe_cb) { + + if (g_dbusintf != NULL) { + /* First, initialize all lower-level functions as default + * so that dbus.c simply calls directly to dbus_usb_os.c. + */ + bcopy(g_dbusintf, &dbus_usb_intf, sizeof(dbus_intf_t)); + + /* Second, selectively override functions we need, if any. */ + dbus_usb_intf.attach = dbus_usb_attach; + dbus_usb_intf.detach = dbus_usb_detach; + dbus_usb_intf.iovar_op = dbus_usb_iovar_op; + dbus_usb_intf.dlstart = dbus_usb_dlstart; + dbus_usb_intf.dlneeded = dbus_usb_dlneeded; + dbus_usb_intf.dlrun = dbus_usb_dlrun; + } + + disc_arg = probe_cb(probe_arg, "DBUS USB", USB_BUS, bus_no, slot, hdrlen); + return disc_arg; + } + + return NULL; +} + +/** + * On return, *intf contains this or lower-level DBUS functions to be called by higher + * level (dbus.c) + */ +int +dbus_bus_register(int vid, int pid, probe_cb_t prcb, + disconnect_cb_t discb, void *prarg, dbus_intf_t **intf, void *param1, void *param2) +{ + int err; + + DBUSTRACE(("%s(): \n", __FUNCTION__)); + probe_cb = prcb; + disconnect_cb = discb; + probe_arg = prarg; + + *intf = &dbus_usb_intf; + + err = dbus_bus_osl_register(vid, pid, dbus_usb_probe, + dbus_usb_disconnect, NULL, &g_dbusintf, param1, param2); + + ASSERT(g_dbusintf); + return err; +} + +int +dbus_bus_deregister() +{ + DBUSTRACE(("%s(): \n", __FUNCTION__)); + return dbus_bus_osl_deregister(); +} + +/** initialization consists of registration followed by 'attach'. */ +void * +dbus_usb_attach(dbus_pub_t *pub, void *cbarg, dbus_intf_callbacks_t *cbs) +{ + usb_info_t *usb_info; + + DBUSTRACE(("%s(): \n", __FUNCTION__)); + + if ((g_dbusintf == NULL) || (g_dbusintf->attach == NULL)) + return NULL; + + /* Sanity check for BUS_INFO() */ + ASSERT(OFFSETOF(usb_info_t, pub) == 0); + + usb_info = MALLOC(pub->osh, sizeof(usb_info_t)); + if (usb_info == NULL) + return NULL; + + bzero(usb_info, sizeof(usb_info_t)); + + usb_info->pub = pub; + usb_info->cbarg = cbarg; + usb_info->cbs = cbs; + + usb_info->usbosl_info = (dbus_pub_t *)g_dbusintf->attach(pub, + usb_info, &dbus_usb_intf_cbs); + if (usb_info->usbosl_info == NULL) { + MFREE(pub->osh, usb_info, sizeof(usb_info_t)); + return NULL; + } + + /* Save USB OS-specific driver entry points */ + usb_info->drvintf = g_dbusintf; + + pub->bus = usb_info; +#if !defined(BCM_REQUEST_FW) + if (!dbus_usb_resetcfg(usb_info)) { + usb_info->pub->busstate = DBUS_STATE_DL_DONE; + } +#endif + /* Return Lower layer info */ + return (void *) usb_info->usbosl_info; +} + +void +dbus_usb_detach(dbus_pub_t *pub, void *info) +{ + usb_info_t *usb_info = (usb_info_t *) pub->bus; + osl_t *osh = pub->osh; + + if (usb_info == NULL) + return; + + if (usb_info->drvintf && usb_info->drvintf->detach) + usb_info->drvintf->detach(pub, usb_info->usbosl_info); + + MFREE(osh, usb_info, sizeof(usb_info_t)); +} + +void +dbus_usb_disconnect(void *handle) +{ + DBUSTRACE(("%s(): \n", __FUNCTION__)); + if (disconnect_cb) + disconnect_cb(disc_arg); +} + +/** + * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be + * notified. + */ +static void +dbus_usb_send_irb_timeout(void *handle, dbus_irb_tx_t *txirb) +{ + usb_info_t *usb_info = (usb_info_t *) handle; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (usb_info == NULL) + return; + + if (usb_info->cbs && usb_info->cbs->send_irb_timeout) + usb_info->cbs->send_irb_timeout(usb_info->cbarg, txirb); +} + +/** + * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be + * notified. + */ +static void +dbus_usb_send_irb_complete(void *handle, dbus_irb_tx_t *txirb, int status) +{ + usb_info_t *usb_info = (usb_info_t *) handle; + + if (usb_info == NULL) + return; + + if (usb_info->cbs && usb_info->cbs->send_irb_complete) + usb_info->cbs->send_irb_complete(usb_info->cbarg, txirb, status); +} + +/** + * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be + * notified. + */ +static void +dbus_usb_recv_irb_complete(void *handle, dbus_irb_rx_t *rxirb, int status) +{ + usb_info_t *usb_info = (usb_info_t *) handle; + + if (usb_info == NULL) + return; + + if (usb_info->cbs && usb_info->cbs->recv_irb_complete) + usb_info->cbs->recv_irb_complete(usb_info->cbarg, rxirb, status); +} + +/** Lower DBUS level (dbus_usb_os.c) requests a free IRB. Pass this on to the higher DBUS level. */ +static struct dbus_irb* +dbus_usb_getirb(void *handle, bool send) +{ + usb_info_t *usb_info = (usb_info_t *) handle; + + if (usb_info == NULL) + return NULL; + + if (usb_info->cbs && usb_info->cbs->getirb) + return usb_info->cbs->getirb(usb_info->cbarg, send); + + return NULL; +} + +/** + * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be + * notified. + */ +static void +dbus_usb_rxerr_indicate(void *handle, bool on) +{ + usb_info_t *usb_info = (usb_info_t *) handle; + + if (usb_info == NULL) + return; + + if (usb_info->cbs && usb_info->cbs->rxerr_indicate) + usb_info->cbs->rxerr_indicate(usb_info->cbarg, on); +} + +/** + * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be + * notified. + */ +static void +dbus_usb_errhandler(void *handle, int err) +{ + usb_info_t *usb_info = (usb_info_t *) handle; + + if (usb_info == NULL) + return; + + if (usb_info->cbs && usb_info->cbs->errhandler) + usb_info->cbs->errhandler(usb_info->cbarg, err); +} + +/** + * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be + * notified. + */ +static void +dbus_usb_ctl_complete(void *handle, int type, int status) +{ + usb_info_t *usb_info = (usb_info_t *) handle; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (usb_info == NULL) { + DBUSERR(("%s: usb_info is NULL\n", __FUNCTION__)); + return; + } + + if (usb_info->cbs && usb_info->cbs->ctl_complete) + usb_info->cbs->ctl_complete(usb_info->cbarg, type, status); +} + +/** + * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be + * notified. + */ +static void +dbus_usb_state_change(void *handle, int state) +{ + usb_info_t *usb_info = (usb_info_t *) handle; + + if (usb_info == NULL) + return; + + if (usb_info->cbs && usb_info->cbs->state_change) + usb_info->cbs->state_change(usb_info->cbarg, state); +} + +/** called by higher DBUS level (dbus.c) */ +static int +dbus_usb_iovar_op(void *bus, const char *name, + void *params, int plen, void *arg, int len, bool set) +{ + int err = DBUS_OK; + + err = dbus_iovar_process((usb_info_t*)bus, name, params, plen, arg, len, set); + return err; +} + +/** process iovar request from higher DBUS level */ +static int +dbus_iovar_process(usb_info_t* usbinfo, const char *name, + void *params, int plen, void *arg, int len, bool set) +{ + const bcm_iovar_t *vi = NULL; + int bcmerror = 0; + int val_size; + uint32 actionid; + + DBUSTRACE(("%s: Enter\n", __FUNCTION__)); + + ASSERT(name); + ASSERT(len >= 0); + + /* Get MUST have return space */ + ASSERT(set || (arg && len)); + + /* Set does NOT take qualifiers */ + ASSERT(!set || (!params && !plen)); + + /* Look up var locally; if not found pass to host driver */ + if ((vi = bcm_iovar_lookup(dhdusb_iovars, name)) == NULL) { + /* Not Supported */ + bcmerror = BCME_UNSUPPORTED; + DBUSTRACE(("%s: IOVAR %s is not supported\n", name, __FUNCTION__)); + goto exit; + + } + + DBUSTRACE(("%s: %s %s, len %d plen %d\n", __FUNCTION__, + name, (set ? "set" : "get"), len, plen)); + + /* set up 'params' pointer in case this is a set command so that + * the convenience int and bool code can be common to set and get + */ + if (params == NULL) { + params = arg; + plen = len; + } + + if (vi->type == IOVT_VOID) + val_size = 0; + else if (vi->type == IOVT_BUFFER) + val_size = len; + else + /* all other types are integer sized */ + val_size = sizeof(int); + + actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid); + bcmerror = dbus_usb_doiovar(usbinfo, vi, actionid, + name, params, plen, arg, len, val_size); + +exit: + return bcmerror; +} /* dbus_iovar_process */ + +static int +dbus_usb_doiovar(usb_info_t *bus, const bcm_iovar_t *vi, uint32 actionid, const char *name, + void *params, int plen, void *arg, int len, int val_size) +{ + int bcmerror = 0; + int32 int_val = 0; + int32 int_val2 = 0; + bool bool_val = 0; + + DBUSTRACE(("%s: Enter, action %d name %s params %p plen %d arg %p len %d val_size %d\n", + __FUNCTION__, actionid, name, params, plen, arg, len, val_size)); + + if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, IOV_ISSET(actionid))) != 0) + goto exit; + + if (plen >= (int)sizeof(int_val)) + bcopy(params, &int_val, sizeof(int_val)); + + if (plen >= (int)sizeof(int_val) * 2) + bcopy((void*)((uintptr)params + sizeof(int_val)), &int_val2, sizeof(int_val2)); + + bool_val = (int_val != 0) ? TRUE : FALSE; + + switch (actionid) { + + case IOV_SVAL(IOV_MEMBYTES): + case IOV_GVAL(IOV_MEMBYTES): + { + uint32 address; + uint size, dsize; + uint8 *data; + + bool set = (actionid == IOV_SVAL(IOV_MEMBYTES)); + + ASSERT(plen >= 2*sizeof(int)); + + address = (uint32)int_val; + BCM_REFERENCE(address); + bcopy((char *)params + sizeof(int_val), &int_val, sizeof(int_val)); + size = (uint)int_val; + + /* Do some validation */ + dsize = set ? plen - (2 * sizeof(int)) : len; + if (dsize < size) { + DBUSTRACE(("%s: error on %s membytes, addr 0x%08x size %d dsize %d\n", + __FUNCTION__, (set ? "set" : "get"), address, size, dsize)); + bcmerror = BCME_BADARG; + break; + } + DBUSTRACE(("%s: Request to %s %d bytes at address 0x%08x\n", __FUNCTION__, + (set ? "write" : "read"), size, address)); + + /* Generate the actual data pointer */ + data = set ? (uint8*)params + 2 * sizeof(int): (uint8*)arg; + + /* Call to do the transfer */ + bcmerror = dbus_usb_dl_writeimage(BUS_INFO(bus, usb_info_t), data, size); + } + break; + + + case IOV_SVAL(IOV_SET_DOWNLOAD_STATE): + + if (bool_val == TRUE) { + bcmerror = dbus_usb_dlneeded(bus); + dbus_usb_rdl_dwnld_state(BUS_INFO(bus, usb_info_t)); + } else { + usb_info_t *usbinfo = BUS_INFO(bus, usb_info_t); + bcmerror = dbus_usb_dlrun(bus); + usbinfo->pub->busstate = DBUS_STATE_DL_DONE; + } + break; + + case IOV_SVAL(IOV_VARS): + bcmerror = dhdusb_downloadvars(BUS_INFO(bus, usb_info_t), arg, len); + break; + + case IOV_GVAL(IOV_DBUS_MSGLEVEL): + int_val = (int32)dbus_msglevel; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_DBUS_MSGLEVEL): + dbus_msglevel = int_val; + break; + +#ifdef DBUS_USB_LOOPBACK + case IOV_SVAL(IOV_LOOPBACK_TX): + bcmerror = dbus_usbos_loopback_tx(BUS_INFO(bus, usb_info_t), int_val, + int_val2); + break; +#endif + default: + bcmerror = BCME_UNSUPPORTED; + break; + } + +exit: + return bcmerror; +} /* dbus_usb_doiovar */ + +/** higher DBUS level (dbus.c) wants to set NVRAM variables in dongle */ +static int +dhdusb_downloadvars(usb_info_t *bus, void *arg, int len) +{ + int bcmerror = 0; + uint32 varsize; + uint32 varaddr; + uint32 varsizew; + + if (!len) { + bcmerror = BCME_BUFTOOSHORT; + goto err; + } + + /* RAM size is not set. Set it at dbus_usb_dlneeded */ + if (!bus->rdlram_size) + bcmerror = BCME_ERROR; + + /* Even if there are no vars are to be written, we still need to set the ramsize. */ + varsize = len ? ROUNDUP(len, 4) : 0; + varaddr = (bus->rdlram_size - 4) - varsize; + + /* Write the vars list */ + DBUSTRACE(("WriteVars: @%x varsize=%d\n", varaddr, varsize)); + bcmerror = dbus_write_membytes(bus->usbosl_info, TRUE, (varaddr + bus->rdlram_base_addr), + arg, varsize); + + /* adjust to the user specified RAM */ + DBUSTRACE(("Usable memory size: %d\n", bus->rdlram_size)); + DBUSTRACE(("Vars are at %d, orig varsize is %d\n", varaddr, varsize)); + + varsize = ((bus->rdlram_size - 4) - varaddr); + + /* + * Determine the length token: + * Varsize, converted to words, in lower 16-bits, checksum in upper 16-bits. + */ + if (bcmerror) { + varsizew = 0; + } else { + varsizew = varsize / 4; + varsizew = (~varsizew << 16) | (varsizew & 0x0000FFFF); + varsizew = htol32(varsizew); + } + + DBUSTRACE(("New varsize is %d, length token=0x%08x\n", varsize, varsizew)); + + /* Write the length token to the last word */ + bcmerror = dbus_write_membytes(bus->usbosl_info, TRUE, ((bus->rdlram_size - 4) + + bus->rdlram_base_addr), (uint8*)&varsizew, 4); +err: + return bcmerror; +} /* dbus_usb_doiovar */ + +#if !defined(BCM_REQUEST_FW) +/** + * After downloading firmware into dongle and starting it, we need to know if the firmware is + * indeed up and running. + */ +static int +dbus_usb_resetcfg(usb_info_t *usbinfo) +{ + void *osinfo; + bootrom_id_t id; + uint16 waittime = 0; + + uint32 starttime = 0; + uint32 endtime = 0; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (usbinfo == NULL) + return DBUS_ERR; + + osinfo = usbinfo->usbosl_info; + ASSERT(osinfo); + + /* Give dongle chance to boot */ + dbus_usbos_wait(osinfo, USB_SFLASH_DLIMAGE_SPINWAIT); + waittime = USB_SFLASH_DLIMAGE_SPINWAIT; + while (waittime < USB_DLIMAGE_RETRY_TIMEOUT) { + + starttime = OSL_SYSUPTIME(); + + id.chip = 0xDEAD; /* Get the ID */ + dbus_usbos_dl_cmd(osinfo, DL_GETVER, &id, sizeof(bootrom_id_t)); + id.chip = ltoh32(id.chip); + + endtime = OSL_SYSUPTIME(); + waittime += (endtime - starttime); + + if (id.chip == POSTBOOT_ID) + break; + } + + if (id.chip == POSTBOOT_ID) { + DBUSERR(("%s: download done. Bootup time = %d ms postboot chip 0x%x/rev 0x%x\n", + __FUNCTION__, waittime, id.chip, id.chiprev)); + + dbus_usbos_dl_cmd(osinfo, DL_RESETCFG, &id, sizeof(bootrom_id_t)); + + dbus_usbos_wait(osinfo, USB_RESETCFG_SPINWAIT); + return DBUS_OK; + } else { + DBUSERR(("%s: Cannot talk to Dongle. Wait time = %d ms. Firmware is not UP \n", + __FUNCTION__, waittime)); + return DBUS_ERR; + } + + return DBUS_OK; +} +#endif + +/** before firmware download, the dongle has to be prepared to receive the fw image */ +static int +dbus_usb_rdl_dwnld_state(usb_info_t *usbinfo) +{ + void *osinfo = usbinfo->usbosl_info; + rdl_state_t state; + int err = DBUS_OK; + + /* 1) Prepare USB boot loader for runtime image */ + dbus_usbos_dl_cmd(osinfo, DL_START, &state, sizeof(rdl_state_t)); + + state.state = ltoh32(state.state); + state.bytes = ltoh32(state.bytes); + + /* 2) Check we are in the Waiting state */ + if (state.state != DL_WAITING) { + DBUSERR(("%s: Failed to DL_START\n", __FUNCTION__)); + err = DBUS_ERR; + goto fail; + } + +fail: + return err; +} + +/** + * Dongle contains bootcode in ROM but firmware is (partially) contained in dongle RAM. Therefore, + * firmware has to be downloaded into dongle RAM. + */ +static int +dbus_usb_dl_writeimage(usb_info_t *usbinfo, uint8 *fw, int fwlen) +{ + osl_t *osh = usbinfo->pub->osh; + void *osinfo = usbinfo->usbosl_info; + unsigned int sendlen, sent, dllen; + char *bulkchunk = NULL, *dlpos; + rdl_state_t state; + int err = DBUS_OK; + bootrom_id_t id; + uint16 wait, wait_time; + uint32 dl_trunk_size = RDL_CHUNK; + + if (BCM4350_CHIP(usbinfo->pub->attrib.devid)) + dl_trunk_size = RDL_CHUNK_MAX; + + while (!bulkchunk) { + bulkchunk = MALLOC(osh, dl_trunk_size); + if (dl_trunk_size == RDL_CHUNK) + break; + if (!bulkchunk) { + dl_trunk_size /= 2; + if (dl_trunk_size < RDL_CHUNK) + dl_trunk_size = RDL_CHUNK; + } + } + + if (bulkchunk == NULL) { + err = DBUS_ERR; + goto fail; + } + + sent = 0; + dlpos = fw; + dllen = fwlen; + + /* Get chip id and rev */ + id.chip = usbinfo->pub->attrib.devid; + id.chiprev = usbinfo->pub->attrib.chiprev; + + DBUSTRACE(("enter %s: fwlen=%d\n", __FUNCTION__, fwlen)); + + dbus_usbos_dl_cmd(osinfo, DL_GETSTATE, &state, sizeof(rdl_state_t)); + + /* 3) Load the image */ + while ((sent < dllen)) { + /* Wait until the usb device reports it received all the bytes we sent */ + + if (sent < dllen) { + if ((dllen-sent) < dl_trunk_size) + sendlen = dllen-sent; + else + sendlen = dl_trunk_size; + + /* simply avoid having to send a ZLP by ensuring we never have an even + * multiple of 64 + */ + if (!(sendlen % 64)) + sendlen -= 4; + + /* send data */ + memcpy(bulkchunk, dlpos, sendlen); + if (!dbus_usbos_dl_send_bulk(osinfo, bulkchunk, sendlen)) { + err = DBUS_ERR; + goto fail; + } + + dlpos += sendlen; + sent += sendlen; + DBUSTRACE(("%s: sendlen %d\n", __FUNCTION__, sendlen)); + } + + wait = 0; + wait_time = USB_SFLASH_DLIMAGE_SPINWAIT; + while (!dbus_usbos_dl_cmd(osinfo, DL_GETSTATE, &state, + sizeof(rdl_state_t))) { + if ((id.chip == 43236) && (id.chiprev == 0)) { + DBUSERR(("%s: 43236a0 SFlash delay, waiting for dongle crc check " + "completion!!!\n", __FUNCTION__)); + dbus_usbos_wait(osinfo, wait_time); + wait += wait_time; + if (wait >= USB_SFLASH_DLIMAGE_LIMIT) { + DBUSERR(("%s: DL_GETSTATE Failed xxxx\n", __FUNCTION__)); + err = DBUS_ERR; + goto fail; + break; + } + } else { + DBUSERR(("%s: DL_GETSTATE Failed xxxx\n", __FUNCTION__)); + err = DBUS_ERR; + goto fail; + } + } + + state.state = ltoh32(state.state); + state.bytes = ltoh32(state.bytes); + + /* restart if an error is reported */ + if ((state.state == DL_BAD_HDR) || (state.state == DL_BAD_CRC)) { + DBUSERR(("%s: Bad Hdr or Bad CRC\n", __FUNCTION__)); + err = DBUS_ERR; + goto fail; + } + + } +fail: + if (bulkchunk) + MFREE(osh, bulkchunk, dl_trunk_size); + + return err; +} /* dbus_usb_dl_writeimage */ + +/** Higher level DBUS layer (dbus.c) requests this layer to download image into dongle */ +static int +dbus_usb_dlstart(void *bus, uint8 *fw, int len) +{ + usb_info_t *usbinfo = BUS_INFO(bus, usb_info_t); + int err; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (usbinfo == NULL) + return DBUS_ERR; + + if (USB_DEV_ISBAD(usbinfo)) + return DBUS_ERR; + + err = dbus_usb_rdl_dwnld_state(usbinfo); + + if (DBUS_OK == err) { + err = dbus_usb_dl_writeimage(usbinfo, fw, len); + if (err == DBUS_OK) + usbinfo->pub->busstate = DBUS_STATE_DL_DONE; + else + usbinfo->pub->busstate = DBUS_STATE_DL_PENDING; + } else + usbinfo->pub->busstate = DBUS_STATE_DL_PENDING; + + return err; +} + +static bool +dbus_usb_update_chipinfo(usb_info_t *usbinfo, uint32 chip) +{ + bool retval = TRUE; + /* based on the CHIP Id, store the ram size which is needed for NVRAM download. */ + switch (chip) { + + case 0x4319: + usbinfo->rdlram_size = RDL_RAM_SIZE_4319; + usbinfo->rdlram_base_addr = RDL_RAM_BASE_4319; + break; + + case 0x4329: + usbinfo->rdlram_size = RDL_RAM_SIZE_4329; + usbinfo->rdlram_base_addr = RDL_RAM_BASE_4329; + break; + + case 43234: + case 43235: + case 43236: + usbinfo->rdlram_size = RDL_RAM_SIZE_43236; + usbinfo->rdlram_base_addr = RDL_RAM_BASE_43236; + break; + + case 0x4328: + usbinfo->rdlram_size = RDL_RAM_SIZE_4328; + usbinfo->rdlram_base_addr = RDL_RAM_BASE_4328; + break; + + case 0x4322: + usbinfo->rdlram_size = RDL_RAM_SIZE_4322; + usbinfo->rdlram_base_addr = RDL_RAM_BASE_4322; + break; + + case 0x4360: + case 0xAA06: + usbinfo->rdlram_size = RDL_RAM_SIZE_4360; + usbinfo->rdlram_base_addr = RDL_RAM_BASE_4360; + break; + + case 43242: + case 43243: + usbinfo->rdlram_size = RDL_RAM_SIZE_43242; + usbinfo->rdlram_base_addr = RDL_RAM_BASE_43242; + break; + + case 43143: + usbinfo->rdlram_size = RDL_RAM_SIZE_43143; + usbinfo->rdlram_base_addr = RDL_RAM_BASE_43143; + break; + + case 0x4350: + case 43556: + case 43558: + case 43569: + usbinfo->rdlram_size = RDL_RAM_SIZE_4350; + usbinfo->rdlram_base_addr = RDL_RAM_BASE_4350; + break; + + case POSTBOOT_ID: + break; + + default: + DBUSERR(("%s: Chip 0x%x Ram size is not known\n", __FUNCTION__, chip)); + retval = FALSE; + break; + + } + + return retval; +} /* dbus_usb_update_chipinfo */ + +/** higher DBUS level (dbus.c) wants to know if firmware download is required. */ +static int +dbus_usb_dlneeded(void *bus) +{ + usb_info_t *usbinfo = BUS_INFO(bus, usb_info_t); + void *osinfo; + bootrom_id_t id; + int dl_needed = 1; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (usbinfo == NULL) + return DBUS_ERR; + + osinfo = usbinfo->usbosl_info; + ASSERT(osinfo); + + /* Check if firmware downloaded already by querying runtime ID */ + id.chip = 0xDEAD; + dbus_usbos_dl_cmd(osinfo, DL_GETVER, &id, sizeof(bootrom_id_t)); + + id.chip = ltoh32(id.chip); + id.chiprev = ltoh32(id.chiprev); + + if (FALSE == dbus_usb_update_chipinfo(usbinfo, id.chip)) { + dl_needed = DBUS_ERR; + goto exit; + } + + DBUSERR(("%s: chip 0x%x rev 0x%x\n", __FUNCTION__, id.chip, id.chiprev)); + if (id.chip == POSTBOOT_ID) { + /* This code is needed to support two enumerations on USB1.1 scenario */ + DBUSERR(("%s: Firmware already downloaded\n", __FUNCTION__)); + + dbus_usbos_dl_cmd(osinfo, DL_RESETCFG, &id, sizeof(bootrom_id_t)); + dl_needed = DBUS_OK; + if (usbinfo->pub->busstate == DBUS_STATE_DL_PENDING) + usbinfo->pub->busstate = DBUS_STATE_DL_DONE; + } else { + usbinfo->pub->attrib.devid = id.chip; + usbinfo->pub->attrib.chiprev = id.chiprev; + } + +exit: + return dl_needed; +} + +/** After issuing firmware download, higher DBUS level (dbus.c) wants to start the firmware. */ +static int +dbus_usb_dlrun(void *bus) +{ + usb_info_t *usbinfo = BUS_INFO(bus, usb_info_t); + void *osinfo; + rdl_state_t state; + int err = DBUS_OK; + + DBUSTRACE(("%s\n", __FUNCTION__)); + + if (usbinfo == NULL) + return DBUS_ERR; + + if (USB_DEV_ISBAD(usbinfo)) + return DBUS_ERR; + + osinfo = usbinfo->usbosl_info; + ASSERT(osinfo); + + /* Check we are runnable */ + dbus_usbos_dl_cmd(osinfo, DL_GETSTATE, &state, sizeof(rdl_state_t)); + + state.state = ltoh32(state.state); + state.bytes = ltoh32(state.bytes); + + /* Start the image */ + if (state.state == DL_RUNNABLE) { + DBUSTRACE(("%s: Issue DL_GO\n", __FUNCTION__)); + dbus_usbos_dl_cmd(osinfo, DL_GO, &state, sizeof(rdl_state_t)); + + if (usbinfo->pub->attrib.devid == TEST_CHIP) + dbus_usbos_wait(osinfo, USB_DLGO_SPINWAIT); + +// dbus_usb_resetcfg(usbinfo); + /* The Donlge may go for re-enumeration. */ + } else { + DBUSERR(("%s: Dongle not runnable\n", __FUNCTION__)); + err = DBUS_ERR; + } + + return err; +} + +/** + * As preparation for firmware download, higher DBUS level (dbus.c) requests the firmware image + * to be used for the type of dongle detected. Directly called by dbus.c (so not via a callback + * construction) + */ +void +dbus_bus_fw_get(void *bus, uint8 **fw, int *fwlen, int *decomp) +{ + usb_info_t *usbinfo = BUS_INFO(bus, usb_info_t); + unsigned int devid; + unsigned int crev; + + devid = usbinfo->pub->attrib.devid; + crev = usbinfo->pub->attrib.chiprev; + + *fw = NULL; + *fwlen = 0; + + switch (devid) { + case BCM43236_CHIP_ID: + case BCM43235_CHIP_ID: + case BCM43234_CHIP_ID: + case BCM43238_CHIP_ID: { + if (crev == 3 || crev == 2 || crev == 1) { +#ifdef EMBED_IMAGE_43236b + *fw = (uint8 *)dlarray_43236b; + *fwlen = sizeof(dlarray_43236b); + +#endif + } + } break; + case BCM4360_CHIP_ID: + case BCM4352_CHIP_ID: + case BCM43526_CHIP_ID: +#ifdef EMBED_IMAGE_43526a + if (crev <= 2) { + *fw = (uint8 *)dlarray_43526a; + *fwlen = sizeof(dlarray_43526a); + } +#endif +#ifdef EMBED_IMAGE_43526b + if (crev > 2) { + *fw = (uint8 *)dlarray_43526b; + *fwlen = sizeof(dlarray_43526b); + } +#endif + break; + + case BCM43242_CHIP_ID: +#ifdef EMBED_IMAGE_43242a0 + *fw = (uint8 *)dlarray_43242a0; + *fwlen = sizeof(dlarray_43242a0); +#endif + break; + + case BCM43143_CHIP_ID: +#ifdef EMBED_IMAGE_43143a0 + *fw = (uint8 *)dlarray_43143a0; + *fwlen = sizeof(dlarray_43143a0); +#endif +#ifdef EMBED_IMAGE_43143b0 + *fw = (uint8 *)dlarray_43143b0; + *fwlen = sizeof(dlarray_43143b0); +#endif + break; + + case BCM4350_CHIP_ID: + case BCM4354_CHIP_ID: + case BCM43556_CHIP_ID: + case BCM43558_CHIP_ID: + case BCM43566_CHIP_ID: + case BCM43568_CHIP_ID: + case BCM43570_CHIP_ID: + case BCM4358_CHIP_ID: +#ifdef EMBED_IMAGE_4350a0 + if (crev == 0) { + *fw = (uint8 *)dlarray_4350a0; + *fwlen = sizeof(dlarray_4350a0); + } +#endif +#ifdef EMBED_IMAGE_4350b0 + if (crev == 1) { + *fw = (uint8 *)dlarray_4350b0; + *fwlen = sizeof(dlarray_4350b0); + } +#endif +#ifdef EMBED_IMAGE_4350b1 + if (crev == 2) { + *fw = (uint8 *)dlarray_4350b1; + *fwlen = sizeof(dlarray_4350b1); + } +#endif +#ifdef EMBED_IMAGE_43556b1 + if (crev == 2) { + *fw = (uint8 *)dlarray_43556b1; + *fwlen = sizeof(dlarray_43556b1); + } +#endif +#ifdef EMBED_IMAGE_4350c0 + if (crev == 3) { + *fw = (uint8 *)dlarray_4350c0; + *fwlen = sizeof(dlarray_4350c0); + } +#endif /* EMBED_IMAGE_4350c0 */ +#ifdef EMBED_IMAGE_4350c1 + if (crev == 4) { + *fw = (uint8 *)dlarray_4350c1; + *fwlen = sizeof(dlarray_4350c1); + } +#endif /* EMBED_IMAGE_4350c1 */ + break; + case BCM43569_CHIP_ID: +#ifdef EMBED_IMAGE_43569a0 + if (crev == 0) { + *fw = (uint8 *)dlarray_43569a0; + *fwlen = sizeof(dlarray_43569a0); + } +#endif /* EMBED_IMAGE_43569a0 */ + break; + default: +#ifdef EMBED_IMAGE_GENERIC + *fw = (uint8 *)dlarray; + *fwlen = sizeof(dlarray); +#endif + break; + } +} /* dbus_bus_fw_get */ diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dbus_usb_linux.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dbus_usb_linux.c new file mode 100644 index 00000000..8aa9646c --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dbus_usb_linux.c @@ -0,0 +1,3404 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Dongle BUS interface + * USB Linux Implementation + * + * Copyright (C) 1999-2016, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * + * <> + * + * $Id: dbus_usb_linux.c 564663 2015-06-18 02:34:42Z $ + */ + +/** + * @file @brief + * This file contains DBUS code that is USB *and* OS (Linux) specific. DBUS is a Broadcom + * proprietary host specific abstraction layer. + */ + +#include +#include + +/** + * DBUS_LINUX_RXDPC is created for router platform performance tuning. A separate thread is created + * to handle USB RX and avoid the call chain getting too long and enhance cache hit rate. + * + * DBUS_LINUX_RXDPC setting is in wlconfig file. + */ + +/* + * If DBUS_LINUX_RXDPC is off, spin_lock_bh() for CTFPOOL in + * linux_osl.c has to be changed to spin_lock_irqsave() because + * PKTGET/PKTFREE are no longer in bottom half. + * + * Right now we have another queue rpcq in wl_linux.c. Maybe we + * can eliminate that one to reduce the overhead. + * + * Enabling 2nd EP and DBUS_LINUX_RXDPC causing traffic from + * both EP's to be queued in the same rx queue. If we want + * RXDPC to work with 2nd EP. The EP for RPC call return + * should bypass the dpc and go directly up. + */ + +/* #define DBUS_LINUX_RXDPC */ + +/* Dbus histogram for ntxq, nrxq, dpc parameter tuning */ +/* #define DBUS_LINUX_HIST */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(USBOS_THREAD) || defined(USBOS_TX_THREAD) + +/** + * The usb-thread is designed to provide currency on multiprocessors and SMP linux kernels. On the + * dual cores platform, the WLAN driver, without threads, executed only on CPU0. The driver consumed + * almost of 100% on CPU0, while CPU1 remained idle. The behavior was observed on Broadcom's STB. + * + * The WLAN driver consumed most of CPU0 and not CPU1 because tasklets/queues, software irq, and + * hardware irq are executing from CPU0, only. CPU0 became the system's bottle-neck. TPUT is lower + * and system's responsiveness is slower. + * + * To improve system responsiveness and TPUT usb-thread was implemented. The system's threads could + * be scheduled to run on any core. One core could be processing data in the usb-layer and the other + * core could be processing data in the wl-layer. + * + * For further info see [WlThreadAndUsbThread] Twiki. + */ + +#include +#include +#include +#include +#include +#include +#endif /* USBOS_THREAD || USBOS_TX_THREAD */ + + + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) +#define KERNEL26 +#endif + +/** + * Starting with the 3.10 kernel release, dynamic PM support for USB is present whenever + * the kernel was built with CONFIG_PM_RUNTIME enabled. The CONFIG_USB_SUSPEND option has + * been eliminated. + */ +#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 21)) && defined(CONFIG_USB_SUSPEND)) \ + || ((LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) && defined(CONFIG_PM_RUNTIME)) +/* For USB power management support, see Linux kernel: Documentation/usb/power-management.txt */ +#define USB_SUSPEND_AVAILABLE +#endif + +/* Define alternate fw/nvram paths used in Android */ +#ifdef OEM_ANDROID +#define CONFIG_ANDROID_BCMDHD_FW_PATH "broadcom/dhd/firmware/fw.bin.trx" +#define CONFIG_ANDROID_BCMDHD_NVRAM_PATH "broadcom/dhd/nvrams/nvm.txt" +#endif /* OEM_ANDROID */ + +static inline int usb_submit_urb_linux(struct urb *urb) +{ + +#ifdef BCM_MAX_URB_LEN + if (urb && (urb->transfer_buffer_length > BCM_MAX_URB_LEN)) { + DBUSERR(("URB transfer length=%d exceeded %d ra=%p\n", urb->transfer_buffer_length, + BCM_MAX_URB_LEN, __builtin_return_address(0))); + return DBUS_ERR; + } +#endif + +#ifdef KERNEL26 + return usb_submit_urb(urb, GFP_ATOMIC); +#else + return usb_submit_urb(urb); +#endif + +} + +#define USB_SUBMIT_URB(urb) usb_submit_urb_linux(urb) + +#ifdef KERNEL26 + +#define USB_ALLOC_URB() usb_alloc_urb(0, GFP_ATOMIC) +#define USB_UNLINK_URB(urb) (usb_kill_urb(urb)) +#define USB_FREE_URB(urb) (usb_free_urb(urb)) +#define USB_REGISTER() usb_register(&dbus_usbdev) +#define USB_DEREGISTER() usb_deregister(&dbus_usbdev) + +#ifdef USB_SUSPEND_AVAILABLE + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)) +#define USB_AUTOPM_SET_INTERFACE(intf) usb_autopm_set_interface(intf) +#else +#define USB_ENABLE_AUTOSUSPEND(udev) usb_enable_autosuspend(udev) +#define USB_DISABLE_AUTOSUSPEND(udev) usb_disable_autosuspend(udev) +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)) */ + +#define USB_AUTOPM_GET_INTERFACE(intf) usb_autopm_get_interface(intf) +#define USB_AUTOPM_PUT_INTERFACE(intf) usb_autopm_put_interface(intf) +#define USB_AUTOPM_GET_INTERFACE_ASYNC(intf) usb_autopm_get_interface_async(intf) +#define USB_AUTOPM_PUT_INTERFACE_ASYNC(intf) usb_autopm_put_interface_async(intf) +#define USB_MARK_LAST_BUSY(dev) usb_mark_last_busy(dev) + +#else /* USB_SUSPEND_AVAILABLE */ + +#define USB_AUTOPM_GET_INTERFACE(intf) do {} while (0) +#define USB_AUTOPM_PUT_INTERFACE(intf) do {} while (0) +#define USB_AUTOPM_GET_INTERFACE_ASYNC(intf) do {} while (0) +#define USB_AUTOPM_PUT_INTERFACE_ASYNC(intf) do {} while (0) +#define USB_MARK_LAST_BUSY(dev) do {} while (0) +#endif /* USB_SUSPEND_AVAILABLE */ + +#define USB_CONTROL_MSG(dev, pipe, request, requesttype, value, index, data, size, timeout) \ + usb_control_msg((dev), (pipe), (request), (requesttype), (value), (index), \ + (data), (size), (timeout)) +#define USB_BULK_MSG(dev, pipe, data, len, actual_length, timeout) \ + usb_bulk_msg((dev), (pipe), (data), (len), (actual_length), (timeout)) +#define USB_BUFFER_ALLOC(dev, size, mem, dma) usb_buffer_alloc(dev, size, mem, dma) +#define USB_BUFFER_FREE(dev, size, data, dma) usb_buffer_free(dev, size, data, dma) + +#ifdef WL_URB_ZPKT +#define URB_QUEUE_BULK URB_ZERO_PACKET +#else +#define URB_QUEUE_BULK 0 +#endif /* WL_URB_ZPKT */ + +#define CALLBACK_ARGS struct urb *urb, struct pt_regs *regs +#define CALLBACK_ARGS_DATA urb, regs +#define CONFIGDESC(usb) (&((usb)->actconfig)->desc) +#define IFPTR(usb, idx) ((usb)->actconfig->interface[idx]) +#define IFALTS(usb, idx) (IFPTR((usb), (idx))->altsetting[0]) +#define IFDESC(usb, idx) IFALTS((usb), (idx)).desc +#define IFEPDESC(usb, idx, ep) (IFALTS((usb), (idx)).endpoint[ep]).desc + +#else /* KERNEL26 */ + +#define USB_ALLOC_URB() usb_alloc_urb(0) +#define USB_UNLINK_URB(urb) usb_unlink_urb(urb) +#define USB_FREE_URB(urb) (usb_free_urb(urb)) +#define USB_REGISTER() usb_register(&dbus_usbdev) +#define USB_DEREGISTER() usb_deregister(&dbus_usbdev) +#define USB_AUTOPM_GET_INTERFACE(intf) do {} while (0) +#define USB_AUTOPM_GET_INTERFACE_ASYNC(intf) do {} while (0) +#define USB_AUTOPM_PUT_INTERFACE_ASYNC(intf) do {} while (0) +#define USB_MARK_LAST_BUSY(dev) do {} while (0) + +#define USB_CONTROL_MSG(dev, pipe, request, requesttype, value, index, data, size, timeout) \ + usb_control_msg((dev), (pipe), (request), (requesttype), (value), (index), \ + (data), (size), (timeout)) +#define USB_BUFFER_ALLOC(dev, size, mem, dma) kmalloc(size, mem) +#define USB_BUFFER_FREE(dev, size, data, dma) kfree(data) + +#ifdef WL_URB_ZPKT +#define URB_QUEUE_BULK USB_QUEUE_BULK|URB_ZERO_PACKET +#else +#define URB_QUEUE_BULK 0 +#endif /* WL_URB_ZPKT */ + +#define CALLBACK_ARGS struct urb *urb +#define CALLBACK_ARGS_DATA urb +#define CONFIGDESC(usb) ((usb)->actconfig) +#define IFPTR(usb, idx) (&(usb)->actconfig->interface[idx]) +#define IFALTS(usb, idx) ((usb)->actconfig->interface[idx].altsetting[0]) +#define IFDESC(usb, idx) IFALTS((usb), (idx)) +#define IFEPDESC(usb, idx, ep) (IFALTS((usb), (idx)).endpoint[ep]) + + +#endif /* KERNEL26 */ + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) +#define USB_SPEED_SUPER 5 +#endif /* #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) */ + +#define CONTROL_IF 0 +#define BULK_IF 0 + +#ifdef BCMUSBDEV_COMPOSITE +#define USB_COMPIF_MAX 4 + +#define USB_CLASS_WIRELESS 0xe0 +#define USB_CLASS_MISC 0xef +#define USB_SUBCLASS_COMMON 0x02 +#define USB_PROTO_IAD 0x01 +#define USB_PROTO_VENDOR 0xff + +#define USB_QUIRK_NO_SET_INTF 0x04 /* device does not support set_interface */ +#endif /* BCMUSBDEV_COMPOSITE */ + +#define USB_SYNC_WAIT_TIMEOUT 300 /* ms */ + +/* Private data kept in skb */ +#define SKB_PRIV(skb, idx) (&((void **)skb->cb)[idx]) +#define SKB_PRIV_URB(skb) (*(struct urb **)SKB_PRIV(skb, 0)) + +#ifndef DBUS_USB_RXQUEUE_BATCH_ADD +/* items to add each time within limit */ +#define DBUS_USB_RXQUEUE_BATCH_ADD 8 +#endif + +#ifndef DBUS_USB_RXQUEUE_LOWER_WATERMARK +/* add a new batch req to rx queue when waiting item count reduce to this number */ +#define DBUS_USB_RXQUEUE_LOWER_WATERMARK 4 +#endif + +enum usbos_suspend_state { + USBOS_SUSPEND_STATE_DEVICE_ACTIVE = 0, /* Device is busy, won't allow suspend */ + USBOS_SUSPEND_STATE_SUSPEND_PENDING, /* Device is idle, can be suspended */ + /* Wating PM to suspend */ + USBOS_SUSPEND_STATE_SUSPENDED /* Device suspended */ +}; + +enum usbos_request_state { + USBOS_REQUEST_STATE_UNSCHEDULED = 0, /* USB TX request not scheduled */ + USBOS_REQUEST_STATE_SCHEDULED, /* USB TX request given to TX thread */ + USBOS_REQUEST_STATE_SUBMITTED /* USB TX request submitted */ +}; + +typedef struct { + uint32 notification; + uint32 reserved; +} intr_t; + +typedef struct { + dbus_pub_t *pub; + + void *cbarg; + dbus_intf_callbacks_t *cbs; + + /* Imported */ + struct usb_device *usb; /* USB device pointer from OS */ + struct urb *intr_urb; /* URB for interrupt endpoint */ + struct list_head req_rxfreeq; + struct list_head req_txfreeq; + struct list_head req_rxpostedq; /* Posted down to USB driver for RX */ + struct list_head req_txpostedq; /* Posted down to USB driver for TX */ + spinlock_t rxfree_lock; /* Lock for rx free list */ + spinlock_t txfree_lock; /* Lock for tx free list */ + spinlock_t rxposted_lock; /* Lock for rx posted list */ + spinlock_t txposted_lock; /* Lock for tx posted list */ + uint rx_pipe, tx_pipe, intr_pipe, rx_pipe2; /* Pipe numbers for USB I/O */ + uint rxbuf_len; + + struct list_head req_rxpendingq; /* RXDPC: Pending for dpc to send up */ + spinlock_t rxpending_lock; /* RXDPC: Lock for rx pending list */ + long dpc_pid; + struct semaphore dpc_sem; + struct completion dpc_exited; + int rxpending; + + struct urb *ctl_urb; + int ctl_in_pipe, ctl_out_pipe; + struct usb_ctrlrequest ctl_write; + struct usb_ctrlrequest ctl_read; + struct semaphore ctl_lock; /* Lock for CTRL transfers via tx_thread */ +#ifdef USBOS_TX_THREAD + enum usbos_request_state ctl_state; +#endif /* USBOS_TX_THREAD */ + + spinlock_t rxlock; /* Lock for rxq management */ + spinlock_t txlock; /* Lock for txq management */ + + int intr_size; /* Size of interrupt message */ + int interval; /* Interrupt polling interval */ + intr_t intr; /* Data buffer for interrupt endpoint */ + + int maxps; + atomic_t txposted; + atomic_t rxposted; + atomic_t txallocated; + atomic_t rxallocated; + bool rxctl_deferrespok; /* Get a response for setup from dongle */ + + wait_queue_head_t wait; + bool waitdone; + int sync_urb_status; + + struct urb *blk_urb; /* Used for downloading embedded image */ + +#ifdef USBOS_THREAD + spinlock_t ctrl_lock; + spinlock_t usbos_list_lock; + struct list_head usbos_list; + struct list_head usbos_free_list; + atomic_t usbos_list_cnt; + wait_queue_head_t usbos_queue_head; + struct task_struct *usbos_kt; +#endif /* USBOS_THREAD */ + +#ifdef USBOS_TX_THREAD + spinlock_t usbos_tx_list_lock; + struct list_head usbos_tx_list; + wait_queue_head_t usbos_tx_queue_head; + struct task_struct *usbos_tx_kt; +#endif /* USBOS_TX_THREAD */ + + struct dma_pool *qtd_pool; /* QTD pool for USB optimization only */ + int tx_ep, rx_ep, rx2_ep; /* EPs for USB optimization */ + struct usb_device *usb_device; /* USB device for optimization */ +} usbos_info_t; + +typedef struct urb_req { + void *pkt; + int buf_len; + struct urb *urb; + void *arg; + usbos_info_t *usbinfo; + struct list_head urb_list; +} urb_req_t; + +#ifdef USBOS_THREAD +typedef struct usbos_list_entry { + struct list_head list; /* must be first */ + void *urb_context; + int urb_length; + int urb_status; +} usbos_list_entry_t; + +static void* dbus_usbos_thread_init(usbos_info_t *usbos_info); +static void dbus_usbos_thread_deinit(usbos_info_t *usbos_info); +static void dbus_usbos_dispatch_schedule(CALLBACK_ARGS); +static int dbus_usbos_thread_func(void *data); +#endif /* USBOS_THREAD */ + +#ifdef USBOS_TX_THREAD +void* dbus_usbos_tx_thread_init(usbos_info_t *usbos_info); +void dbus_usbos_tx_thread_deinit(usbos_info_t *usbos_info); +int dbus_usbos_tx_thread_func(void *data); +#endif /* USBOS_TX_THREAD */ + +/* Shared Function prototypes */ +bool dbus_usbos_dl_cmd(usbos_info_t *usbinfo, uint8 cmd, void *buffer, int buflen); +int dbus_usbos_wait(usbos_info_t *usbinfo, uint16 ms); +bool dbus_usbos_dl_send_bulk(usbos_info_t *usbinfo, void *buffer, int len); +int dbus_write_membytes(usbos_info_t *usbinfo, bool set, uint32 address, uint8 *data, uint size); + +/* Local function prototypes */ +static void dbus_usbos_send_complete(CALLBACK_ARGS); +static void dbus_usbos_recv_complete(CALLBACK_ARGS); +static int dbus_usbos_errhandler(void *bus, int err); +static int dbus_usbos_state_change(void *bus, int state); +static void dbusos_stop(usbos_info_t *usbos_info); + +#ifdef KERNEL26 +static int dbus_usbos_probe(struct usb_interface *intf, const struct usb_device_id *id); +static void dbus_usbos_disconnect(struct usb_interface *intf); +#if defined(USB_SUSPEND_AVAILABLE) +static int dbus_usbos_resume(struct usb_interface *intf); +static int dbus_usbos_suspend(struct usb_interface *intf, pm_message_t message); +/* at the moment, used for full dongle host driver only */ +static int dbus_usbos_reset_resume(struct usb_interface *intf); +#endif /* USB_SUSPEND_AVAILABLE */ +#else /* KERNEL26 */ +static void *dbus_usbos_probe(struct usb_device *usb, unsigned int ifnum, + const struct usb_device_id *id); +static void dbus_usbos_disconnect(struct usb_device *usb, void *ptr); +#endif /* KERNEL26 */ + + +/** + * have to disable missing-field-initializers warning as last element {} triggers it + * and different versions of kernel have different number of members so it is impossible + * to specify the initializer. BTW issuing the warning here is bug og GCC as universal + * zero {0} specified in C99 standard as correct way of initialization of struct to all zeros + */ +#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == \ + 4 && __GNUC_MINOR__ >= 6)) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + +static struct usb_device_id devid_table[] = { + { USB_DEVICE(BCM_DNGL_VID, 0x0000) }, /* Configurable via register() */ +#if defined(BCM_REQUEST_FW) + { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BL_PID_4328) }, + { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BL_PID_4322) }, + { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BL_PID_4319) }, + { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BL_PID_43236) }, + { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BL_PID_43143) }, + { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BL_PID_43242) }, + { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BL_PID_4360) }, + { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BL_PID_4350) }, + { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BL_PID_43569) }, +#endif +#ifdef EXTENDED_VID_PID + EXTENDED_VID_PID, +#endif /* EXTENDED_VID_PID */ + { USB_DEVICE(BCM_DNGL_VID, BCM_DNGL_BDC_PID) }, /* Default BDC */ + { } +}; + +#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == \ + 4 && __GNUC_MINOR__ >= 6)) +#pragma GCC diagnostic pop +#endif + +MODULE_DEVICE_TABLE(usb, devid_table); + +/** functions called by the Linux kernel USB subsystem */ +static struct usb_driver dbus_usbdev = { + name: "dbus_usbdev", + probe: dbus_usbos_probe, + disconnect: dbus_usbos_disconnect, + id_table: devid_table, +#if defined(USB_SUSPEND_AVAILABLE) + suspend: dbus_usbos_suspend, + resume: dbus_usbos_resume, + reset_resume: dbus_usbos_reset_resume, + /* Linux USB core will allow autosuspend for devices bound to this driver */ + supports_autosuspend: 1 +#endif /* USB_SUSPEND_AVAILABLE */ +}; + +/** + * This stores USB info during Linux probe callback since attach() is not called yet at this point + */ +typedef struct { + void *usbos_info; + struct usb_device *usb; /* USB device pointer from OS */ + uint rx_pipe; /* Pipe numbers for USB I/O */ + uint tx_pipe; /* Pipe numbers for USB I/O */ + uint intr_pipe; /* Pipe numbers for USB I/O */ + uint rx_pipe2; /* Pipe numbers for USB I/O */ + int intr_size; /* Size of interrupt message */ + int interval; /* Interrupt polling interval */ + bool dldone; + int vid; + int pid; + bool dereged; + bool disc_cb_done; + DEVICE_SPEED device_speed; + enum usbos_suspend_state suspend_state; + struct usb_interface *intf; +} probe_info_t; + +/* + * USB Linux dbus_intf_t + */ +static void *dbus_usbos_intf_attach(dbus_pub_t *pub, void *cbarg, dbus_intf_callbacks_t *cbs); +static void dbus_usbos_intf_detach(dbus_pub_t *pub, void *info); +static int dbus_usbos_intf_send_irb(void *bus, dbus_irb_tx_t *txirb); +static int dbus_usbos_intf_recv_irb(void *bus, dbus_irb_rx_t *rxirb); +static int dbus_usbos_intf_recv_irb_from_ep(void *bus, dbus_irb_rx_t *rxirb, uint32 ep_idx); +static int dbus_usbos_intf_cancel_irb(void *bus, dbus_irb_tx_t *txirb); +static int dbus_usbos_intf_send_ctl(void *bus, uint8 *buf, int len); +static int dbus_usbos_intf_recv_ctl(void *bus, uint8 *buf, int len); +static int dbus_usbos_intf_get_attrib(void *bus, dbus_attrib_t *attrib); +static int dbus_usbos_intf_up(void *bus); +static int dbus_usbos_intf_down(void *bus); +static int dbus_usbos_intf_stop(void *bus); +static int dbus_usbos_readreg(void *bus, uint32 regaddr, int datalen, uint32 *value); +extern int dbus_usbos_loopback_tx(void *usbos_info_ptr, int cnt, int size); +int dbus_usbos_writereg(void *bus, uint32 regaddr, int datalen, uint32 data); +static int dbus_usbos_intf_set_config(void *bus, dbus_config_t *config); +static bool dbus_usbos_intf_recv_needed(void *bus); +static void *dbus_usbos_intf_exec_rxlock(void *bus, exec_cb_t cb, struct exec_parms *args); +static void *dbus_usbos_intf_exec_txlock(void *bus, exec_cb_t cb, struct exec_parms *args); +#ifdef BCMUSBDEV_COMPOSITE +static int dbus_usbos_intf_wlan(struct usb_device *usb); +#endif /* BCMUSBDEV_COMPOSITE */ + +/** functions called by dbus_usb.c */ +static dbus_intf_t dbus_usbos_intf = { + .attach = dbus_usbos_intf_attach, + .detach = dbus_usbos_intf_detach, + .up = dbus_usbos_intf_up, + .down = dbus_usbos_intf_down, + .send_irb = dbus_usbos_intf_send_irb, + .recv_irb = dbus_usbos_intf_recv_irb, + .cancel_irb = dbus_usbos_intf_cancel_irb, + .send_ctl = dbus_usbos_intf_send_ctl, + .recv_ctl = dbus_usbos_intf_recv_ctl, + .get_stats = NULL, + .get_attrib = dbus_usbos_intf_get_attrib, + .remove = NULL, + .resume = NULL, + .suspend = NULL, + .stop = dbus_usbos_intf_stop, + .reset = NULL, + .pktget = NULL, + .pktfree = NULL, + .iovar_op = NULL, + .dump = NULL, + .set_config = dbus_usbos_intf_set_config, + .get_config = NULL, + .device_exists = NULL, + .dlneeded = NULL, + .dlstart = NULL, + .dlrun = NULL, + .recv_needed = dbus_usbos_intf_recv_needed, + .exec_rxlock = dbus_usbos_intf_exec_rxlock, + .exec_txlock = dbus_usbos_intf_exec_txlock, + + .tx_timer_init = NULL, + .tx_timer_start = NULL, + .tx_timer_stop = NULL, + + .sched_dpc = NULL, + .lock = NULL, + .unlock = NULL, + .sched_probe_cb = NULL, + + .shutdown = NULL, + + .recv_stop = NULL, + .recv_resume = NULL, + + .recv_irb_from_ep = dbus_usbos_intf_recv_irb_from_ep, + .readreg = dbus_usbos_readreg +}; + +static probe_info_t g_probe_info; +static probe_cb_t probe_cb = NULL; +static disconnect_cb_t disconnect_cb = NULL; +static void *probe_arg = NULL; +static void *disc_arg = NULL; + + + +static volatile int loopback_rx_cnt, loopback_tx_cnt; +int loopback_size; +bool is_loopback_pkt(void *buf); +int matches_loopback_pkt(void *buf); + +/** + * multiple code paths in this file dequeue a URB request, this function makes sure that it happens + * in a concurrency save manner. Don't call this from a sleepable process context. + */ +static urb_req_t * BCMFASTPATH +dbus_usbos_qdeq(struct list_head *urbreq_q, spinlock_t *lock) +{ + unsigned long flags; + urb_req_t *req; + + ASSERT(urbreq_q != NULL); + + spin_lock_irqsave(lock, flags); + + if (list_empty(urbreq_q)) { + req = NULL; + } else { + ASSERT(urbreq_q->next != NULL); + ASSERT(urbreq_q->next != urbreq_q); +#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + req = list_entry(urbreq_q->next, urb_req_t, urb_list); +#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + list_del_init(&req->urb_list); + } + + spin_unlock_irqrestore(lock, flags); + + return req; +} + +static void BCMFASTPATH +dbus_usbos_qenq(struct list_head *urbreq_q, urb_req_t *req, spinlock_t *lock) +{ + unsigned long flags; + + spin_lock_irqsave(lock, flags); + + list_add_tail(&req->urb_list, urbreq_q); + + spin_unlock_irqrestore(lock, flags); +} + +/** + * multiple code paths in this file remove a URB request from a list, this function makes sure that + * it happens in a concurrency save manner. Don't call this from a sleepable process context. + * Is quite similar to dbus_usbos_qdeq(), I wonder why this function is needed. + */ +static void +dbus_usbos_req_del(urb_req_t *req, spinlock_t *lock) +{ + unsigned long flags; + + spin_lock_irqsave(lock, flags); + + list_del_init(&req->urb_list); + + spin_unlock_irqrestore(lock, flags); +} + + +/** + * Driver requires a pool of URBs to operate. This function is called during + * initialization (attach phase), allocates a number of URBs, and puts them + * on the free (req_rxfreeq and req_txfreeq) queue + */ +static int +dbus_usbos_urbreqs_alloc(usbos_info_t *usbos_info, uint32 count, bool is_rx) +{ + int i; + int allocated = 0; + int err = DBUS_OK; + + for (i = 0; i < count; i++) { + urb_req_t *req; + + req = MALLOC(usbos_info->pub->osh, sizeof(urb_req_t)); + if (req == NULL) { + DBUSERR(("%s: MALLOC req failed\n", __FUNCTION__)); + err = DBUS_ERR_NOMEM; + goto fail; + } + bzero(req, sizeof(urb_req_t)); + + req->urb = USB_ALLOC_URB(); + if (req->urb == NULL) { + DBUSERR(("%s: USB_ALLOC_URB req->urb failed\n", __FUNCTION__)); + err = DBUS_ERR_NOMEM; + goto fail; + } + + INIT_LIST_HEAD(&req->urb_list); + + if (is_rx) { +#if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_RXNOCOPY) + /* don't allocate now. Do it on demand */ + req->pkt = NULL; +#else + /* pre-allocate buffers never to be released */ + req->pkt = MALLOC(usbos_info->pub->osh, usbos_info->rxbuf_len); + if (req->pkt == NULL) { + DBUSERR(("%s: MALLOC req->pkt failed\n", __FUNCTION__)); + err = DBUS_ERR_NOMEM; + goto fail; + } +#endif + req->buf_len = usbos_info->rxbuf_len; + dbus_usbos_qenq(&usbos_info->req_rxfreeq, req, &usbos_info->rxfree_lock); + } else { + req->buf_len = 0; + dbus_usbos_qenq(&usbos_info->req_txfreeq, req, &usbos_info->txfree_lock); + } + allocated++; + continue; + +fail: + if (req) { + if (is_rx && req->pkt) { +#if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_RXNOCOPY) + /* req->pkt is NULL in "NOCOPY" mode */ +#else + MFREE(usbos_info->pub->osh, req->pkt, req->buf_len); +#endif + } + if (req->urb) { + USB_FREE_URB(req->urb); + } + MFREE(usbos_info->pub->osh, req, sizeof(urb_req_t)); + } + break; + } + + atomic_add(allocated, is_rx ? &usbos_info->rxallocated : &usbos_info->txallocated); + + if (is_rx) { + DBUSTRACE(("%s: add %d (total %d) rx buf, each has %d bytes\n", __FUNCTION__, + allocated, atomic_read(&usbos_info->rxallocated), usbos_info->rxbuf_len)); + } else { + DBUSTRACE(("%s: add %d (total %d) tx req\n", __FUNCTION__, + allocated, atomic_read(&usbos_info->txallocated))); + } + + return err; +} /* dbus_usbos_urbreqs_alloc */ + +/** Typically called during detach or when attach failed. Don't call until all URBs unlinked */ +static int +dbus_usbos_urbreqs_free(usbos_info_t *usbos_info, bool is_rx) +{ + int rtn = 0; + urb_req_t *req; + struct list_head *req_q; + spinlock_t *lock; + + if (is_rx) { + req_q = &usbos_info->req_rxfreeq; + lock = &usbos_info->rxfree_lock; + } else { + req_q = &usbos_info->req_txfreeq; + lock = &usbos_info->txfree_lock; + } + while ((req = dbus_usbos_qdeq(req_q, lock)) != NULL) { + + if (is_rx) { + if (req->pkt) { + /* We do MFREE instead of PKTFREE because the pkt has been + * converted to native already + */ + MFREE(usbos_info->pub->osh, req->pkt, req->buf_len); + req->pkt = NULL; + req->buf_len = 0; + } + } else { + /* sending req should not be assigned pkt buffer */ + ASSERT(req->pkt == NULL); + } + + if (req->urb) { + USB_FREE_URB(req->urb); + req->urb = NULL; + } + MFREE(usbos_info->pub->osh, req, sizeof(urb_req_t)); + + rtn++; + } + return rtn; +} /* dbus_usbos_urbreqs_free */ + +/** + * called by Linux kernel on URB completion. Upper DBUS layer (dbus_usb.c) has to be notified of + * send completion. + */ +void +dbus_usbos_send_complete(CALLBACK_ARGS) +{ + urb_req_t *req = urb->context; + dbus_irb_tx_t *txirb = req->arg; + usbos_info_t *usbos_info = req->usbinfo; + unsigned long flags; + int status = DBUS_OK; + int txposted; + + USB_AUTOPM_PUT_INTERFACE_ASYNC(g_probe_info.intf); + + spin_lock_irqsave(&usbos_info->txlock, flags); + + dbus_usbos_req_del(req, &usbos_info->txposted_lock); + txposted = atomic_dec_return(&usbos_info->txposted); + if (unlikely (txposted < 0)) { + DBUSERR(("%s ERROR: txposted is negative (%d)!!\n", __FUNCTION__, txposted)); + } + spin_unlock_irqrestore(&usbos_info->txlock, flags); + + if (unlikely (urb->status)) { + status = DBUS_ERR_TXFAIL; + DBUSTRACE(("txfail status %d\n", urb->status)); + } + +#if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_RXNOCOPY) + /* sending req should not be assigned pkt buffer */ + ASSERT(req->pkt == NULL); +#endif + /* txirb should always be set, except for ZLP. ZLP is reusing this callback function. */ + if (txirb != NULL) { + if (txirb->send_buf != NULL) { + MFREE(usbos_info->pub->osh, txirb->send_buf, req->buf_len); + txirb->send_buf = NULL; + req->buf_len = 0; + } + if (likely (usbos_info->cbarg && usbos_info->cbs)) { + if (likely (usbos_info->cbs->send_irb_complete != NULL)) + usbos_info->cbs->send_irb_complete(usbos_info->cbarg, txirb, status); + } + } + + dbus_usbos_qenq(&usbos_info->req_txfreeq, req, &usbos_info->txfree_lock); +} /* dbus_usbos_send_complete */ + +/** + * In order to receive USB traffic from the dongle, we need to supply the Linux kernel with a free + * URB that is going to contain received data. + */ +static int BCMFASTPATH +dbus_usbos_recv_urb_submit(usbos_info_t *usbos_info, dbus_irb_rx_t *rxirb, uint32 ep_idx) +{ + urb_req_t *req; + int ret = DBUS_OK; + unsigned long flags; + void *p; + uint rx_pipe; + int rxposted; + + BCM_REFERENCE(rxposted); + + if (!(req = dbus_usbos_qdeq(&usbos_info->req_rxfreeq, &usbos_info->rxfree_lock))) { + DBUSTRACE(("%s No free URB!\n", __FUNCTION__)); + return DBUS_ERR_RXDROP; + } + + spin_lock_irqsave(&usbos_info->rxlock, flags); + +#if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_RXNOCOPY) + req->pkt = rxirb->pkt = PKTGET(usbos_info->pub->osh, req->buf_len, FALSE); + if (!rxirb->pkt) { + DBUSERR(("%s: PKTGET failed\n", __FUNCTION__)); + dbus_usbos_qenq(&usbos_info->req_rxfreeq, req, &usbos_info->rxfree_lock); + ret = DBUS_ERR_RXDROP; + goto fail; + } + /* consider the packet "native" so we don't count it as MALLOCED in the osl */ + PKTTONATIVE(usbos_info->pub->osh, req->pkt); + rxirb->buf = NULL; + p = PKTDATA(usbos_info->pub->osh, req->pkt); +#else + if (req->buf_len != usbos_info->rxbuf_len) { + ASSERT(req->pkt); + MFREE(usbos_info->pub->osh, req->pkt, req->buf_len); + DBUSTRACE(("%s: replace rx buff: old len %d, new len %d\n", __FUNCTION__, + req->buf_len, usbos_info->rxbuf_len)); + req->buf_len = 0; + req->pkt = MALLOC(usbos_info->pub->osh, usbos_info->rxbuf_len); + if (req->pkt == NULL) { + DBUSERR(("%s: MALLOC req->pkt failed\n", __FUNCTION__)); + ret = DBUS_ERR_NOMEM; + goto fail; + } + req->buf_len = usbos_info->rxbuf_len; + } + rxirb->buf = req->pkt; + p = rxirb->buf; +#endif /* defined(BCM_RPC_NOCOPY) */ + rxirb->buf_len = req->buf_len; + req->usbinfo = usbos_info; + req->arg = rxirb; + if (ep_idx == 0) { + rx_pipe = usbos_info->rx_pipe; + } else { + rx_pipe = usbos_info->rx_pipe2; + ASSERT(usbos_info->rx_pipe2); + } + /* Prepare the URB */ + usb_fill_bulk_urb(req->urb, usbos_info->usb, rx_pipe, + p, + rxirb->buf_len, + (usb_complete_t)dbus_usbos_recv_complete, req); + req->urb->transfer_flags |= URB_QUEUE_BULK; + + if ((ret = USB_SUBMIT_URB(req->urb))) { + DBUSERR(("%s USB_SUBMIT_URB failed. status %d\n", __FUNCTION__, ret)); + dbus_usbos_qenq(&usbos_info->req_rxfreeq, req, &usbos_info->rxfree_lock); + ret = DBUS_ERR_RXFAIL; + goto fail; + } + rxposted = atomic_inc_return(&usbos_info->rxposted); + + dbus_usbos_qenq(&usbos_info->req_rxpostedq, req, &usbos_info->rxposted_lock); +fail: + spin_unlock_irqrestore(&usbos_info->rxlock, flags); + return ret; +} /* dbus_usbos_recv_urb_submit */ + + +/** + * Called by worked thread when a 'receive URB' completed or Linux kernel when it returns a URB to + * this driver. + */ +static void BCMFASTPATH +dbus_usbos_recv_complete_handle(urb_req_t *req, int len, int status) +{ + dbus_irb_rx_t *rxirb = req->arg; + usbos_info_t *usbos_info = req->usbinfo; + unsigned long flags; + int rxallocated, rxposted; + int dbus_status = DBUS_OK; + bool killed = (g_probe_info.suspend_state == USBOS_SUSPEND_STATE_SUSPEND_PENDING) ? 1 : 0; + + spin_lock_irqsave(&usbos_info->rxlock, flags); + dbus_usbos_req_del(req, &usbos_info->rxposted_lock); + rxposted = atomic_dec_return(&usbos_info->rxposted); + rxallocated = atomic_read(&usbos_info->rxallocated); + spin_unlock_irqrestore(&usbos_info->rxlock, flags); + + if ((rxallocated < usbos_info->pub->nrxq) && (!status) && + (rxposted == DBUS_USB_RXQUEUE_LOWER_WATERMARK)) { + DBUSTRACE(("%s: need more rx buf: rxallocated %d rxposted %d!\n", + __FUNCTION__, rxallocated, rxposted)); + dbus_usbos_urbreqs_alloc(usbos_info, + MIN(DBUS_USB_RXQUEUE_BATCH_ADD, + usbos_info->pub->nrxq - rxallocated), TRUE); + } + + /* Handle errors */ + if (status) { + /* + * Linux 2.4 disconnect: -ENOENT or -EILSEQ for CRC error; rmmod: -ENOENT + * Linux 2.6 disconnect: -EPROTO, rmmod: -ESHUTDOWN + */ + if ((status == -ENOENT && (!killed))|| status == -ESHUTDOWN) { + /* NOTE: unlink() can not be called from URB callback(). + * Do not call dbusos_stop() here. + */ + DBUSTRACE(("%s rx error %d\n", __FUNCTION__, status)); + dbus_usbos_state_change(usbos_info, DBUS_STATE_DOWN); + } else if (status == -EPROTO) { + DBUSTRACE(("%s rx error %d\n", __FUNCTION__, status)); + } else if (killed && (status == -EHOSTUNREACH || status == -ENOENT)) { + /* Device is suspended */ + } else { + DBUSTRACE(("%s rx error %d\n", __FUNCTION__, status)); + dbus_usbos_errhandler(usbos_info, DBUS_ERR_RXFAIL); + } + + /* On error, don't submit more URBs yet */ + rxirb->buf = NULL; + rxirb->actual_len = 0; + dbus_status = DBUS_ERR_RXFAIL; + goto fail; + } + + /* Make the skb represent the received urb */ + rxirb->actual_len = len; + + if (rxirb->actual_len < sizeof(uint32)) { + DBUSTRACE(("small pkt len %d, process as ZLP\n", rxirb->actual_len)); + dbus_status = DBUS_ERR_RXZLP; + } + +fail: +#if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_RXNOCOPY) + /* detach the packet from the queue */ + req->pkt = NULL; +#endif /* BCM_RPC_NOCOPY || BCM_RPC_RXNOCOPY */ + + if (usbos_info->cbarg && usbos_info->cbs) { + if (usbos_info->cbs->recv_irb_complete) { + usbos_info->cbs->recv_irb_complete(usbos_info->cbarg, rxirb, dbus_status); + } + } + + dbus_usbos_qenq(&usbos_info->req_rxfreeq, req, &usbos_info->rxfree_lock); + + /* Mark the interface as busy to reset USB autosuspend timer */ + USB_MARK_LAST_BUSY(usbos_info->usb); +} /* dbus_usbos_recv_complete_handle */ + +/** called by Linux kernel when it returns a URB to this driver */ +static void +dbus_usbos_recv_complete(CALLBACK_ARGS) +{ +#ifdef USBOS_THREAD + dbus_usbos_dispatch_schedule(CALLBACK_ARGS_DATA); +#else /* !USBOS_THREAD */ + dbus_usbos_recv_complete_handle(urb->context, urb->actual_length, urb->status); +#endif /* USBOS_THREAD */ +} + + +/** + * If Linux notifies our driver that a control read or write URB has completed, we should notify + * the DBUS layer above us (dbus_usb.c in this case). + */ +static void +dbus_usbos_ctl_complete(usbos_info_t *usbos_info, int type, int urbstatus) +{ + int status = DBUS_ERR; + + if (usbos_info == NULL) + return; + + switch (urbstatus) { + case 0: + status = DBUS_OK; + break; + case -EINPROGRESS: + case -ENOENT: + default: +#ifdef INTR_EP_ENABLE + DBUSERR(("%s:%d fail status %d bus:%d susp:%d intr:%d ctli:%d ctlo:%d\n", + __FUNCTION__, type, urbstatus, + usbos_info->pub->busstate, g_probe_info.suspend_state, + usbos_info->intr_urb_submitted, usbos_info->ctlin_urb_submitted, + usbos_info->ctlout_urb_submitted)); +#else + DBUSERR(("%s: failed with status %d\n", __FUNCTION__, urbstatus)); + status = DBUS_ERR; + break; +#endif /* INTR_EP_ENABLE */ + } + + if (usbos_info->cbarg && usbos_info->cbs) { + if (usbos_info->cbs->ctl_complete) + usbos_info->cbs->ctl_complete(usbos_info->cbarg, type, status); + } +} + +/** called by Linux */ +static void +dbus_usbos_ctlread_complete(CALLBACK_ARGS) +{ + usbos_info_t *usbos_info = (usbos_info_t *)urb->context; + + ASSERT(urb); + usbos_info = (usbos_info_t *)urb->context; + + dbus_usbos_ctl_complete(usbos_info, DBUS_CBCTL_READ, urb->status); + +#ifdef USBOS_THREAD + if (usbos_info->rxctl_deferrespok) { + usbos_info->ctl_read.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | + USB_RECIP_INTERFACE; + usbos_info->ctl_read.bRequest = 1; + } +#endif + + up(&usbos_info->ctl_lock); + + USB_AUTOPM_PUT_INTERFACE_ASYNC(g_probe_info.intf); +} + +/** called by Linux */ +static void +dbus_usbos_ctlwrite_complete(CALLBACK_ARGS) +{ + usbos_info_t *usbos_info = (usbos_info_t *)urb->context; + + ASSERT(urb); + usbos_info = (usbos_info_t *)urb->context; + + dbus_usbos_ctl_complete(usbos_info, DBUS_CBCTL_WRITE, urb->status); + +#ifdef USBOS_TX_THREAD + usbos_info->ctl_state = USBOS_REQUEST_STATE_UNSCHEDULED; +#endif /* USBOS_TX_THREAD */ + + up(&usbos_info->ctl_lock); + + USB_AUTOPM_PUT_INTERFACE_ASYNC(g_probe_info.intf); +} + +#ifdef INTR_EP_ENABLE +/** called by Linux */ +static void +dbus_usbos_intr_complete(CALLBACK_ARGS) +{ + usbos_info_t *usbos_info = (usbos_info_t *)urb->context; + bool killed = (g_probe_info.suspend_state == USBOS_SUSPEND_STATE_SUSPEND_PENDING) ? 1 : 0; + + if (usbos_info == NULL || usbos_info->pub == NULL) + return; + if ((urb->status == -ENOENT && (!killed)) || urb->status == -ESHUTDOWN || + urb->status == -ENODEV) { + dbus_usbos_state_change(usbos_info, DBUS_STATE_DOWN); + } + + if (usbos_info->pub->busstate == DBUS_STATE_DOWN) { + DBUSERR(("%s: intr cb when DBUS down, ignoring\n", __FUNCTION__)); + return; + } + dbus_usbos_ctl_complete(usbos_info, DBUS_CBINTR_POLL, urb->status); +} +#endif /* INTR_EP_ENABLE */ + +/** + * when the bus is going to sleep or halt, the Linux kernel requires us to take ownership of our + * URBs again. Multiple code paths in this file require a list of URBs to be cancelled in a + * concurrency save manner. + */ +static void +dbus_usbos_unlink(struct list_head *urbreq_q, spinlock_t *lock) +{ + urb_req_t *req; + + /* dbus_usbos_recv_complete() adds req back to req_freeq */ + while ((req = dbus_usbos_qdeq(urbreq_q, lock)) != NULL) { + ASSERT(req->urb != NULL); + USB_UNLINK_URB(req->urb); + } +} + +/** multiple code paths in this file require the bus to stop */ +static void +dbus_usbos_cancel_all_urbs(usbos_info_t *usbos_info) +{ + int rxposted, txposted; + + DBUSTRACE(("%s: unlink all URBs\n", __FUNCTION__)); + +#ifdef USBOS_TX_THREAD + usbos_info->ctl_state = USBOS_REQUEST_STATE_UNSCHEDULED; + + /* Yield the CPU to TX thread so all pending requests are submitted */ + while (!list_empty(&usbos_info->usbos_tx_list)) { + wake_up_interruptible(&usbos_info->usbos_tx_queue_head); + OSL_SLEEP(10); + } +#endif /* USBOS_TX_THREAD */ + + /* tell Linux kernel to cancel a single intr, ctl and blk URB */ + if (usbos_info->intr_urb) + USB_UNLINK_URB(usbos_info->intr_urb); + if (usbos_info->ctl_urb) + USB_UNLINK_URB(usbos_info->ctl_urb); + if (usbos_info->blk_urb) + USB_UNLINK_URB(usbos_info->blk_urb); + + dbus_usbos_unlink(&usbos_info->req_txpostedq, &usbos_info->txposted_lock); + dbus_usbos_unlink(&usbos_info->req_rxpostedq, &usbos_info->rxposted_lock); + + /* Wait until the callbacks for all submitted URBs have been called, because the + * handler needs to know is an USB suspend is in progress. + */ + SPINWAIT((atomic_read(&usbos_info->txposted) != 0 || + atomic_read(&usbos_info->rxposted) != 0), 10000); + + txposted = atomic_read(&usbos_info->txposted); + rxposted = atomic_read(&usbos_info->rxposted); + if (txposted != 0 || rxposted != 0) { + DBUSERR(("%s ERROR: REQs posted, rx=%d tx=%d!\n", + __FUNCTION__, rxposted, txposted)); + } +} /* dbus_usbos_cancel_all_urbs */ + +/** multiple code paths require the bus to stop */ +static void +dbusos_stop(usbos_info_t *usbos_info) +{ + urb_req_t *req; + int rxposted; + req = NULL; + BCM_REFERENCE(req); + + ASSERT(usbos_info); + + dbus_usbos_state_change(usbos_info, DBUS_STATE_DOWN); + + dbus_usbos_cancel_all_urbs(usbos_info); + +#ifdef USBOS_THREAD + /* yield the CPU to rx packet thread */ + while (1) { + if (atomic_read(&usbos_info->usbos_list_cnt) <= 0) break; + wake_up_interruptible(&usbos_info->usbos_queue_head); + OSL_SLEEP(3); + } +#endif /* USBOS_THREAD */ + + rxposted = atomic_read(&usbos_info->rxposted); + if (rxposted > 0) { + DBUSERR(("%s ERROR: rx REQs posted=%d in stop!\n", __FUNCTION__, + rxposted)); + } + + ASSERT(atomic_read(&usbos_info->txposted) == 0 && rxposted == 0); + +} /* dbusos_stop */ + +#if defined(USB_SUSPEND_AVAILABLE) + +/** + * Linux kernel sports a 'USB auto suspend' feature. See: http://lwn.net/Articles/373550/ + * The suspend method is called by the Linux kernel to warn the driver that the device is going to + * be suspended. If the driver returns a negative error code, the suspend will be aborted. If the + * driver returns 0, it must cancel all outstanding URBs (usb_kill_urb()) and not submit any more. + */ +static int +dbus_usbos_suspend(struct usb_interface *intf, + pm_message_t message) +{ + DBUSERR(("%s suspend state: %d\n", __FUNCTION__, g_probe_info.suspend_state)); + /* DHD for full dongle model */ + g_probe_info.suspend_state = USBOS_SUSPEND_STATE_SUSPEND_PENDING; + dbus_usbos_state_change((usbos_info_t*)g_probe_info.usbos_info, DBUS_STATE_SLEEP); + dbus_usbos_cancel_all_urbs((usbos_info_t*)g_probe_info.usbos_info); + g_probe_info.suspend_state = USBOS_SUSPEND_STATE_SUSPENDED; + + return 0; +} + +/** + * The resume method is called to tell the driver that the device has been resumed and the driver + * can return to normal operation. URBs may once more be submitted. + */ +static int dbus_usbos_resume(struct usb_interface *intf) +{ + DBUSERR(("%s Device resumed\n", __FUNCTION__)); + + dbus_usbos_state_change((usbos_info_t*)g_probe_info.usbos_info, DBUS_STATE_UP); + g_probe_info.suspend_state = USBOS_SUSPEND_STATE_DEVICE_ACTIVE; + return 0; +} + +/** +* This function is directly called by the Linux kernel, when the suspended device has been reset +* instead of being resumed +*/ +static int dbus_usbos_reset_resume(struct usb_interface *intf) +{ + DBUSERR(("%s Device reset resumed\n", __FUNCTION__)); + + /* The device may have lost power, so a firmware download may be required */ + dbus_usbos_state_change((usbos_info_t*)g_probe_info.usbos_info, DBUS_STATE_DL_NEEDED); + g_probe_info.suspend_state = USBOS_SUSPEND_STATE_DEVICE_ACTIVE; + return 0; +} + +#endif /* USB_SUSPEND_AVAILABLE */ + +/** + * Called by Linux kernel at initialization time, kernel wants to know if our driver will accept the + * caller supplied USB interface. Note that USB drivers are bound to interfaces, and not to USB + * devices. + */ +#ifdef KERNEL26 +#define DBUS_USBOS_PROBE() static int dbus_usbos_probe(struct usb_interface *intf, const struct usb_device_id *id) +#define DBUS_USBOS_DISCONNECT() static void dbus_usbos_disconnect(struct usb_interface *intf) +#else +#define DBUS_USBOS_PROBE() static void * dbus_usbos_probe(struct usb_device *usb, unsigned int ifnum, const struct usb_device_id *id) +#define DBUS_USBOS_DISCONNECT() static void dbus_usbos_disconnect(struct usb_device *usb, void *ptr) +#endif /* KERNEL26 */ + +DBUS_USBOS_PROBE() +{ + int ep; + struct usb_endpoint_descriptor *endpoint; + int ret = 0; +#ifdef KERNEL26 + struct usb_device *usb = interface_to_usbdev(intf); +#else + int claimed = 0; +#endif + int num_of_eps; +#ifdef BCMUSBDEV_COMPOSITE + int wlan_if = -1; + bool intr_ep = FALSE; +#endif /* BCMUSBDEV_COMPOSITE */ + wifi_adapter_info_t *adapter; + + DHD_MUTEX_LOCK(); + + DBUSERR(("%s: bus num(busnum)=%d, slot num (portnum)=%d\n", __FUNCTION__, + usb->bus->busnum, usb->portnum)); + adapter = dhd_wifi_platform_attach_adapter(USB_BUS, usb->bus->busnum, + usb->portnum, WIFI_STATUS_POWER_ON); + if (adapter == NULL) { + DBUSERR(("%s: can't find adapter info for this chip\n", __FUNCTION__)); + goto fail; + } + +#ifdef BCMUSBDEV_COMPOSITE + wlan_if = dbus_usbos_intf_wlan(usb); +#ifdef KERNEL26 + if ((wlan_if >= 0) && (IFPTR(usb, wlan_if) == intf)) +#else + if (wlan_if == ifnum) +#endif /* KERNEL26 */ + { +#endif /* BCMUSBDEV_COMPOSITE */ + g_probe_info.usb = usb; + g_probe_info.dldone = TRUE; +#ifdef BCMUSBDEV_COMPOSITE + } else { + DBUSTRACE(("dbus_usbos_probe: skip probe for non WLAN interface\n")); + ret = BCME_UNSUPPORTED; + goto fail; + } +#endif /* BCMUSBDEV_COMPOSITE */ + +#ifdef KERNEL26 + g_probe_info.intf = intf; +#endif /* KERNEL26 */ + +#ifdef BCMUSBDEV_COMPOSITE + if (IFDESC(usb, wlan_if).bInterfaceNumber > USB_COMPIF_MAX) +#else + if (IFDESC(usb, CONTROL_IF).bInterfaceNumber) +#endif /* BCMUSBDEV_COMPOSITE */ + { + ret = -1; + goto fail; + } + if (id != NULL) { + g_probe_info.vid = id->idVendor; + g_probe_info.pid = id->idProduct; + } + +#ifdef KERNEL26 + usb_set_intfdata(intf, &g_probe_info); +#endif + + /* Check that the device supports only one configuration */ + if (usb->descriptor.bNumConfigurations != 1) { + ret = -1; + goto fail; + } + + if (usb->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) { +#ifdef BCMUSBDEV_COMPOSITE + if ((usb->descriptor.bDeviceClass != USB_CLASS_MISC) && + (usb->descriptor.bDeviceClass != USB_CLASS_WIRELESS)) { +#endif /* BCMUSBDEV_COMPOSITE */ + ret = -1; + goto fail; +#ifdef BCMUSBDEV_COMPOSITE + } +#endif /* BCMUSBDEV_COMPOSITE */ + } + + /* + * Only the BDC interface configuration is supported: + * Device class: USB_CLASS_VENDOR_SPEC + * if0 class: USB_CLASS_VENDOR_SPEC + * if0/ep0: control + * if0/ep1: bulk in + * if0/ep2: bulk out (ok if swapped with bulk in) + */ + if (CONFIGDESC(usb)->bNumInterfaces != 1) { +#ifdef BCMUSBDEV_COMPOSITE + if (CONFIGDESC(usb)->bNumInterfaces > USB_COMPIF_MAX) { +#endif /* BCMUSBDEV_COMPOSITE */ + ret = -1; + goto fail; +#ifdef BCMUSBDEV_COMPOSITE + } +#endif /* BCMUSBDEV_COMPOSITE */ + } + + /* Check interface */ +#ifndef KERNEL26 +#ifdef BCMUSBDEV_COMPOSITE + if (usb_interface_claimed(IFPTR(usb, wlan_if))) +#else + if (usb_interface_claimed(IFPTR(usb, CONTROL_IF))) +#endif /* BCMUSBDEV_COMPOSITE */ + { + ret = -1; + goto fail; + } +#endif /* !KERNEL26 */ + +#ifdef BCMUSBDEV_COMPOSITE + if ((IFDESC(usb, wlan_if).bInterfaceClass != USB_CLASS_VENDOR_SPEC || + IFDESC(usb, wlan_if).bInterfaceSubClass != 2 || + IFDESC(usb, wlan_if).bInterfaceProtocol != 0xff) && + (IFDESC(usb, wlan_if).bInterfaceClass != USB_CLASS_MISC || + IFDESC(usb, wlan_if).bInterfaceSubClass != USB_SUBCLASS_COMMON || + IFDESC(usb, wlan_if).bInterfaceProtocol != USB_PROTO_IAD)) +#else + if (IFDESC(usb, CONTROL_IF).bInterfaceClass != USB_CLASS_VENDOR_SPEC || + IFDESC(usb, CONTROL_IF).bInterfaceSubClass != 2 || + IFDESC(usb, CONTROL_IF).bInterfaceProtocol != 0xff) +#endif /* BCMUSBDEV_COMPOSITE */ + { +#ifdef BCMUSBDEV_COMPOSITE + DBUSERR(("%s: invalid control interface: class %d, subclass %d, proto %d\n", + __FUNCTION__, + IFDESC(usb, wlan_if).bInterfaceClass, + IFDESC(usb, wlan_if).bInterfaceSubClass, + IFDESC(usb, wlan_if).bInterfaceProtocol)); +#else + DBUSERR(("%s: invalid control interface: class %d, subclass %d, proto %d\n", + __FUNCTION__, + IFDESC(usb, CONTROL_IF).bInterfaceClass, + IFDESC(usb, CONTROL_IF).bInterfaceSubClass, + IFDESC(usb, CONTROL_IF).bInterfaceProtocol)); +#endif /* BCMUSBDEV_COMPOSITE */ + ret = -1; + goto fail; + } + + /* Check control endpoint */ +#ifdef BCMUSBDEV_COMPOSITE + endpoint = &IFEPDESC(usb, wlan_if, 0); +#else + endpoint = &IFEPDESC(usb, CONTROL_IF, 0); +#endif /* BCMUSBDEV_COMPOSITE */ + if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) { +#ifdef BCMUSBDEV_COMPOSITE + if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != + USB_ENDPOINT_XFER_BULK) { +#endif /* BCMUSBDEV_COMPOSITE */ + DBUSERR(("%s: invalid control endpoint %d\n", + __FUNCTION__, endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)); + ret = -1; + goto fail; +#ifdef BCMUSBDEV_COMPOSITE + } +#endif /* BCMUSBDEV_COMPOSITE */ + } + +#ifdef BCMUSBDEV_COMPOSITE + if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) { +#endif /* BCMUSBDEV_COMPOSITE */ + g_probe_info.intr_pipe = + usb_rcvintpipe(usb, endpoint->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); +#ifdef BCMUSBDEV_COMPOSITE + intr_ep = TRUE; + } +#endif /* BCMUSBDEV_COMPOSITE */ + +#ifndef KERNEL26 + /* Claim interface */ +#ifdef BCMUSBDEV_COMPOSITE + usb_driver_claim_interface(&dbus_usbdev, IFPTR(usb, wlan_if), &g_probe_info); +#else + usb_driver_claim_interface(&dbus_usbdev, IFPTR(usb, CONTROL_IF), &g_probe_info); +#endif /* BCMUSBDEV_COMPOSITE */ + claimed = 1; +#endif /* !KERNEL26 */ + g_probe_info.rx_pipe = 0; + g_probe_info.rx_pipe2 = 0; + g_probe_info.tx_pipe = 0; +#ifdef BCMUSBDEV_COMPOSITE + if (intr_ep) + ep = 1; + else + ep = 0; + num_of_eps = IFDESC(usb, wlan_if).bNumEndpoints - 1; +#else + num_of_eps = IFDESC(usb, BULK_IF).bNumEndpoints - 1; +#endif /* BCMUSBDEV_COMPOSITE */ + + if ((num_of_eps != 2) && (num_of_eps != 3)) { +#ifdef BCMUSBDEV_COMPOSITE + if (num_of_eps > 7) +#endif /* BCMUSBDEV_COMPOSITE */ + ASSERT(0); + } + /* Check data endpoints and get pipes */ +#ifdef BCMUSBDEV_COMPOSITE + for (; ep <= num_of_eps; ep++) +#else + for (ep = 1; ep <= num_of_eps; ep++) +#endif /* BCMUSBDEV_COMPOSITE */ + { +#ifdef BCMUSBDEV_COMPOSITE + endpoint = &IFEPDESC(usb, wlan_if, ep); +#else + endpoint = &IFEPDESC(usb, BULK_IF, ep); +#endif /* BCMUSBDEV_COMPOSITE */ + if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != + USB_ENDPOINT_XFER_BULK) { + DBUSERR(("%s: invalid data endpoint %d\n", + __FUNCTION__, ep)); + ret = -1; + goto fail; + } + + if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { + /* direction: dongle->host */ + if (!g_probe_info.rx_pipe) { + g_probe_info.rx_pipe = usb_rcvbulkpipe(usb, + (endpoint->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK)); + } else { + g_probe_info.rx_pipe2 = usb_rcvbulkpipe(usb, + (endpoint->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK)); + } + + } else + g_probe_info.tx_pipe = usb_sndbulkpipe(usb, (endpoint->bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK)); + } + + /* Allocate interrupt URB and data buffer */ + /* RNDIS says 8-byte intr, our old drivers used 4-byte */ +#ifdef BCMUSBDEV_COMPOSITE + g_probe_info.intr_size = (IFEPDESC(usb, wlan_if, 0).wMaxPacketSize == 16) ? 8 : 4; + g_probe_info.interval = IFEPDESC(usb, wlan_if, 0).bInterval; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 21)) + usb->quirks |= USB_QUIRK_NO_SET_INTF; +#endif +#else + g_probe_info.intr_size = (IFEPDESC(usb, CONTROL_IF, 0).wMaxPacketSize == 16) ? 8 : 4; + g_probe_info.interval = IFEPDESC(usb, CONTROL_IF, 0).bInterval; +#endif /* BCMUSBDEV_COMPOSITE */ + +#ifndef KERNEL26 + /* usb_fill_int_urb does the interval decoding in 2.6 */ + if (usb->speed == USB_SPEED_HIGH) + g_probe_info.interval = 1 << (g_probe_info.interval - 1); +#endif + if (usb->speed == USB_SPEED_SUPER) { + g_probe_info.device_speed = SUPER_SPEED; + DBUSERR(("super speed device detected\n")); + } else if (usb->speed == USB_SPEED_HIGH) { + g_probe_info.device_speed = HIGH_SPEED; + DBUSERR(("high speed device detected\n")); + } else { + g_probe_info.device_speed = FULL_SPEED; + DBUSERR(("full speed device detected\n")); + } + if (g_probe_info.dereged == FALSE && probe_cb) { + disc_arg = probe_cb(probe_arg, "", USB_BUS, usb->bus->busnum, usb->portnum, 0); + } + + g_probe_info.disc_cb_done = FALSE; + +#ifdef KERNEL26 + intf->needs_remote_wakeup = 1; +#endif /* KERNEL26 */ + DHD_MUTEX_UNLOCK(); + + /* Success */ +#ifdef KERNEL26 + return DBUS_OK; +#else + usb_inc_dev_use(usb); + return &g_probe_info; +#endif + +fail: + printf("%s: Exit ret=%d\n", __FUNCTION__, ret); +#ifdef BCMUSBDEV_COMPOSITE + if (ret != BCME_UNSUPPORTED) +#endif /* BCMUSBDEV_COMPOSITE */ + DBUSERR(("%s: failed with errno %d\n", __FUNCTION__, ret)); +#ifndef KERNEL26 + if (claimed) +#ifdef BCMUSBDEV_COMPOSITE + usb_driver_release_interface(&dbus_usbdev, IFPTR(usb, wlan_if)); +#else + usb_driver_release_interface(&dbus_usbdev, IFPTR(usb, CONTROL_IF)); +#endif /* BCMUSBDEV_COMPOSITE */ +#endif /* !KERNEL26 */ + + DHD_MUTEX_UNLOCK(); +#ifdef KERNEL26 + usb_set_intfdata(intf, NULL); + return ret; +#else + return NULL; +#endif +} /* dbus_usbos_probe */ + +/** Called by Linux kernel, is the counter part of dbus_usbos_probe() */ +DBUS_USBOS_DISCONNECT() +{ +#ifdef KERNEL26 + struct usb_device *usb = interface_to_usbdev(intf); + probe_info_t *probe_usb_init_data = usb_get_intfdata(intf); +#else + probe_info_t *probe_usb_init_data = (probe_info_t *) ptr; +#endif + usbos_info_t *usbos_info; + + DHD_MUTEX_LOCK(); + + DBUSERR(("%s: bus num(busnum)=%d, slot num (portnum)=%d\n", __FUNCTION__, + usb->bus->busnum, usb->portnum)); + + if (probe_usb_init_data) { + usbos_info = (usbos_info_t *) probe_usb_init_data->usbos_info; + if (usbos_info) { + if ((probe_usb_init_data->dereged == FALSE) && disconnect_cb && disc_arg) { + disconnect_cb(disc_arg); + disc_arg = NULL; + probe_usb_init_data->disc_cb_done = TRUE; + } + } + } + + if (usb) { +#ifndef KERNEL26 +#ifdef BCMUSBDEV_COMPOSITE + usb_driver_release_interface(&dbus_usbdev, IFPTR(usb, wlan_if)); +#else + usb_driver_release_interface(&dbus_usbdev, IFPTR(usb, CONTROL_IF)); +#endif /* BCMUSBDEV_COMPOSITE */ + usb_dec_dev_use(usb); +#endif /* !KERNEL26 */ + } + DHD_MUTEX_UNLOCK(); +} /* dbus_usbos_disconnect */ + +#define LOOPBACK_PKT_START 0xBABE1234 + +bool is_loopback_pkt(void *buf) +{ + + uint32 *buf_ptr = (uint32 *) buf; + + if (*buf_ptr == LOOPBACK_PKT_START) + return TRUE; + return FALSE; + +} + +int matches_loopback_pkt(void *buf) +{ + int i, j; + unsigned char *cbuf = (unsigned char *) buf; + + for (i = 4; i < loopback_size; i++) { + if (cbuf[i] != (i % 256)) { + printf("%s: mismatch at i=%d %d : ", __FUNCTION__, i, cbuf[i]); + for (j = i; ((j < i+ 16) && (j < loopback_size)); j++) { + printf("%d ", cbuf[j]); + } + printf("\n"); + return 0; + } + } + loopback_rx_cnt++; + return 1; +} + +int dbus_usbos_loopback_tx(void *usbos_info_ptr, int cnt, int size) +{ + usbos_info_t *usbos_info = (usbos_info_t *) usbos_info_ptr; + unsigned char *buf; + int j; + void* p = NULL; + int rc, last_rx_cnt; + int tx_failed_cnt; + int max_size = 1650; + int usb_packet_size = 512; + int min_packet_size = 10; + + if (size % usb_packet_size == 0) { + size = size - 1; + DBUSERR(("%s: overriding size=%d \n", __FUNCTION__, size)); + } + + if (size < min_packet_size) { + size = min_packet_size; + DBUSERR(("%s: overriding size=%d\n", __FUNCTION__, min_packet_size)); + } + if (size > max_size) { + size = max_size; + DBUSERR(("%s: overriding size=%d\n", __FUNCTION__, max_size)); + } + + loopback_tx_cnt = 0; + loopback_rx_cnt = 0; + tx_failed_cnt = 0; + loopback_size = size; + + while (loopback_tx_cnt < cnt) { + uint32 *x; + int pkt_size = loopback_size; + + p = PKTGET(usbos_info->pub->osh, pkt_size, TRUE); + if (p == NULL) { + DBUSERR(("%s:%d Failed to allocate packet sz=%d\n", + __FUNCTION__, __LINE__, pkt_size)); + return BCME_ERROR; + } + x = (uint32*) PKTDATA(usbos_info->pub->osh, p); + *x = LOOPBACK_PKT_START; + buf = (unsigned char*) x; + for (j = 4; j < pkt_size; j++) { + buf[j] = j % 256; + } + rc = dbus_send_buf(usbos_info->pub, buf, pkt_size, p); + if (rc != BCME_OK) { + DBUSERR(("%s:%d Freeing packet \n", __FUNCTION__, __LINE__)); + PKTFREE(usbos_info->pub->osh, p, TRUE); + dbus_usbos_wait(usbos_info, 1); + tx_failed_cnt++; + } else { + loopback_tx_cnt++; + tx_failed_cnt = 0; + } + if (tx_failed_cnt == 5) { + DBUSERR(("%s : Failed to send loopback packets cnt=%d loopback_tx_cnt=%d\n", + __FUNCTION__, cnt, loopback_tx_cnt)); + break; + } + } + printf("Transmitted %d loopback packets of size %d\n", loopback_tx_cnt, loopback_size); + + last_rx_cnt = loopback_rx_cnt; + while (loopback_rx_cnt < loopback_tx_cnt) { + dbus_usbos_wait(usbos_info, 1); + if (loopback_rx_cnt <= last_rx_cnt) { + DBUSERR(("%s: Matched rx cnt stuck at %d \n", __FUNCTION__, last_rx_cnt)); + return BCME_ERROR; + } + last_rx_cnt = loopback_rx_cnt; + } + printf("Received %d loopback packets of size %d\n", loopback_tx_cnt, loopback_size); + + return BCME_OK; +} /* dbus_usbos_loopback_tx */ + +/** + * Higher layer (dbus_usb.c) wants to transmit an I/O Request Block + * @param[in] txirb txirb->pkt, if non-zero, contains a single or a chain of packets + */ +static int +dbus_usbos_intf_send_irb(void *bus, dbus_irb_tx_t *txirb) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + urb_req_t *req, *req_zlp = NULL; + int ret = DBUS_OK; + unsigned long flags; + void *pkt; + uint32 buffer_length; + uint8 *buf; + + if ((usbos_info == NULL) || !usbos_info->tx_pipe) { + return DBUS_ERR; + } + + if (txirb->pkt != NULL) { + buffer_length = pkttotlen(usbos_info->pub->osh, txirb->pkt); + /* In case of multiple packets the values below may be overwritten */ + txirb->send_buf = NULL; + buf = PKTDATA(usbos_info->pub->osh, txirb->pkt); + } else { /* txirb->buf != NULL */ + ASSERT(txirb->buf != NULL); + ASSERT(txirb->send_buf == NULL); + buffer_length = txirb->len; + buf = txirb->buf; + } + + if (!(req = dbus_usbos_qdeq(&usbos_info->req_txfreeq, &usbos_info->txfree_lock))) { + DBUSERR(("%s No free URB!\n", __FUNCTION__)); + return DBUS_ERR_TXDROP; + } + + /* If not using standard Linux kernel functionality for handling Zero Length Packet(ZLP), + * the dbus needs to generate ZLP when length is multiple of MaxPacketSize. + */ +#ifndef WL_URB_ZPKT + if (!(buffer_length % usbos_info->maxps)) { + if (!(req_zlp = + dbus_usbos_qdeq(&usbos_info->req_txfreeq, &usbos_info->txfree_lock))) { + DBUSERR(("%s No free URB for ZLP!\n", __FUNCTION__)); + dbus_usbos_qenq(&usbos_info->req_txfreeq, req, &usbos_info->txfree_lock); + return DBUS_ERR_TXDROP; + } + + /* No txirb, so that dbus_usbos_send_complete can differentiate between + * DATA and ZLP. + */ + req_zlp->arg = NULL; + req_zlp->usbinfo = usbos_info; + req_zlp->buf_len = 0; + + usb_fill_bulk_urb(req_zlp->urb, usbos_info->usb, usbos_info->tx_pipe, NULL, + 0, (usb_complete_t)dbus_usbos_send_complete, req_zlp); + + req_zlp->urb->transfer_flags |= URB_QUEUE_BULK; + } +#endif /* !WL_URB_ZPKT */ + +#ifndef USBOS_TX_THREAD + /* Disable USB autosuspend until this request completes, request USB resume if needed. + * Because this call runs asynchronously, there is no guarantee the bus is resumed before + * the URB is submitted, and the URB might be dropped. Use USBOS_TX_THREAD to avoid + * this. + */ + USB_AUTOPM_GET_INTERFACE_ASYNC(g_probe_info.intf); +#endif /* !USBOS_TX_THREAD */ + + spin_lock_irqsave(&usbos_info->txlock, flags); + + req->arg = txirb; + req->usbinfo = usbos_info; + req->buf_len = 0; + + /* Prepare the URB */ + if (txirb->pkt != NULL) { + uint32 pktlen; + uint8 *transfer_buf; + + /* For multiple packets, allocate contiguous buffer and copy packet data to it */ + if (PKTNEXT(usbos_info->pub->osh, txirb->pkt)) { + transfer_buf = MALLOC(usbos_info->pub->osh, buffer_length); + if (!transfer_buf) { + ret = DBUS_ERR_TXDROP; + DBUSERR(("fail to alloc to usb buffer\n")); + goto fail; + } + + pkt = txirb->pkt; + txirb->send_buf = transfer_buf; + req->buf_len = buffer_length; + + while (pkt) { + pktlen = PKTLEN(usbos_info->pub->osh, pkt); + bcopy(PKTDATA(usbos_info->pub->osh, pkt), transfer_buf, pktlen); + transfer_buf += pktlen; + pkt = PKTNEXT(usbos_info->pub->osh, pkt); + } + + ASSERT(((uint8 *) txirb->send_buf + buffer_length) == transfer_buf); + + /* Overwrite buf pointer with pointer to allocated contiguous transfer_buf + */ + buf = txirb->send_buf; + } + } + + usb_fill_bulk_urb(req->urb, usbos_info->usb, usbos_info->tx_pipe, buf, + buffer_length, (usb_complete_t)dbus_usbos_send_complete, req); + + req->urb->transfer_flags |= URB_QUEUE_BULK; + +#ifdef USBOS_TX_THREAD + /* Enqueue TX request, the TX thread will resume the bus if needed and submit + * it asynchronously + */ + dbus_usbos_qenq(&usbos_info->usbos_tx_list, req, &usbos_info->usbos_tx_list_lock); + if (req_zlp != NULL) { + dbus_usbos_qenq(&usbos_info->usbos_tx_list, req_zlp, + &usbos_info->usbos_tx_list_lock); + } + spin_unlock_irqrestore(&usbos_info->txlock, flags); + + wake_up_interruptible(&usbos_info->usbos_tx_queue_head); + return DBUS_OK; +#else + if ((ret = USB_SUBMIT_URB(req->urb))) { + ret = DBUS_ERR_TXDROP; + goto fail; + } + + dbus_usbos_qenq(&usbos_info->req_txpostedq, req, &usbos_info->txposted_lock); + atomic_inc(&usbos_info->txposted); + + if (req_zlp != NULL) { + if ((ret = USB_SUBMIT_URB(req_zlp->urb))) { + DBUSERR(("failed to submit ZLP URB!\n")); + ASSERT(0); + ret = DBUS_ERR_TXDROP; + goto fail2; + } + + dbus_usbos_qenq(&usbos_info->req_txpostedq, req_zlp, &usbos_info->txposted_lock); + /* Also increment txposted for zlp packet, as it will be decremented in + * dbus_usbos_send_complete() + */ + atomic_inc(&usbos_info->txposted); + } + + spin_unlock_irqrestore(&usbos_info->txlock, flags); + return DBUS_OK; +#endif /* USBOS_TX_THREAD */ + +fail: + if (txirb->send_buf != NULL) { + MFREE(usbos_info->pub->osh, txirb->send_buf, req->buf_len); + txirb->send_buf = NULL; + req->buf_len = 0; + } + dbus_usbos_qenq(&usbos_info->req_txfreeq, req, &usbos_info->txfree_lock); +#ifndef USBOS_TX_THREAD +fail2: +#endif + if (req_zlp != NULL) { + dbus_usbos_qenq(&usbos_info->req_txfreeq, req_zlp, &usbos_info->txfree_lock); + } + + spin_unlock_irqrestore(&usbos_info->txlock, flags); + +#ifndef USBOS_TX_THREAD + USB_AUTOPM_PUT_INTERFACE_ASYNC(g_probe_info.intf); +#endif /* !USBOS_TX_THREAD */ + + return ret; +} /* dbus_usbos_intf_send_irb */ + +/** Higher layer (dbus_usb.c) recycles a received (and used) packet. */ +static int +dbus_usbos_intf_recv_irb(void *bus, dbus_irb_rx_t *rxirb) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + int ret = DBUS_OK; + + if (usbos_info == NULL) + return DBUS_ERR; + + ret = dbus_usbos_recv_urb_submit(usbos_info, rxirb, 0); + return ret; +} + +static int +dbus_usbos_intf_recv_irb_from_ep(void *bus, dbus_irb_rx_t *rxirb, uint32 ep_idx) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + int ret = DBUS_OK; + + if (usbos_info == NULL) + return DBUS_ERR; + +#ifdef INTR_EP_ENABLE + /* By specifying the ep_idx value of 0xff, the cdc layer is asking to + * submit an interrupt URB + */ + if (rxirb == NULL && ep_idx == 0xff) { + /* submit intr URB */ + if ((ret = USB_SUBMIT_URB(usbos_info->intr_urb)) < 0) { + DBUSERR(("%s intr USB_SUBMIT_URB failed, status %d\n", + __FUNCTION__, ret)); + } + return ret; + } +#else + if (rxirb == NULL) { + return DBUS_ERR; + } +#endif /* INTR_EP_ENABLE */ + + ret = dbus_usbos_recv_urb_submit(usbos_info, rxirb, ep_idx); + return ret; +} + +/** Higher layer (dbus_usb.c) want to cancel an IRB */ +static int +dbus_usbos_intf_cancel_irb(void *bus, dbus_irb_tx_t *txirb) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + + if (usbos_info == NULL) + return DBUS_ERR; + + return DBUS_ERR; +} + +/** Only one CTL transfer can be pending at any time. This function may block. */ +static int +dbus_usbos_intf_send_ctl(void *bus, uint8 *buf, int len) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + uint16 size; +#ifndef USBOS_TX_THREAD + int status; +#endif /* USBOS_TX_THREAD */ + + if ((usbos_info == NULL) || (buf == NULL) || (len == 0)) + return DBUS_ERR; + + if (usbos_info->ctl_urb == NULL) + return DBUS_ERR; + + /* Block until a pending CTL transfer has completed */ + if (down_interruptible(&usbos_info->ctl_lock) != 0) { + return DBUS_ERR_TXCTLFAIL; + } + +#ifdef USBOS_TX_THREAD + ASSERT(usbos_info->ctl_state == USBOS_REQUEST_STATE_UNSCHEDULED); +#else + /* Disable USB autosuspend until this request completes, request USB resume if needed. + * Because this call runs asynchronously, there is no guarantee the bus is resumed before + * the URB is submitted, and the URB might be dropped. Use USBOS_TX_THREAD to avoid + * this. + */ + USB_AUTOPM_GET_INTERFACE_ASYNC(g_probe_info.intf); +#endif /* USBOS_TX_THREAD */ + + size = len; + usbos_info->ctl_write.wLength = cpu_to_le16p(&size); + usbos_info->ctl_urb->transfer_buffer_length = size; + + usb_fill_control_urb(usbos_info->ctl_urb, + usbos_info->usb, + usb_sndctrlpipe(usbos_info->usb, 0), + (unsigned char *) &usbos_info->ctl_write, + buf, size, (usb_complete_t)dbus_usbos_ctlwrite_complete, usbos_info); + +#ifdef USBOS_TX_THREAD + /* Enqueue CTRL request for transmission by the TX thread. The + * USB bus will first be resumed if needed. + */ + usbos_info->ctl_state = USBOS_REQUEST_STATE_SCHEDULED; + wake_up_interruptible(&usbos_info->usbos_tx_queue_head); +#else + status = USB_SUBMIT_URB(usbos_info->ctl_urb); + if (status < 0) { + DBUSERR(("%s: usb_submit_urb failed %d\n", __FUNCTION__, status)); + up(&usbos_info->ctl_lock); + + USB_AUTOPM_PUT_INTERFACE_ASYNC(g_probe_info.intf); + + return DBUS_ERR_TXCTLFAIL; + } +#endif /* USBOS_TX_THREAD */ + + return DBUS_OK; +} /* dbus_usbos_intf_send_ctl */ + +/** This function does not seem to be called by anyone, including dbus_usb.c */ +static int +dbus_usbos_intf_recv_ctl(void *bus, uint8 *buf, int len) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + int status; + uint16 size; + + if ((usbos_info == NULL) || (buf == NULL) || (len == 0)) + return DBUS_ERR; + + if (usbos_info->ctl_urb == NULL) + return DBUS_ERR; + + /* Block until a pending CTRL transfer has completed */ + if (down_interruptible(&usbos_info->ctl_lock) != 0) { + return DBUS_ERR_TXCTLFAIL; + } + + /* Disable USB autosuspend until this request completes, request USB resume if needed. */ + USB_AUTOPM_GET_INTERFACE_ASYNC(g_probe_info.intf); + + size = len; + usbos_info->ctl_read.wLength = cpu_to_le16p(&size); + usbos_info->ctl_urb->transfer_buffer_length = size; + + if (usbos_info->rxctl_deferrespok) { + /* BMAC model */ + usbos_info->ctl_read.bRequestType = USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE; + usbos_info->ctl_read.bRequest = DL_DEFER_RESP_OK; + } else { + /* full dongle model */ + usbos_info->ctl_read.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | + USB_RECIP_INTERFACE; + usbos_info->ctl_read.bRequest = 1; + } + + usb_fill_control_urb(usbos_info->ctl_urb, + usbos_info->usb, + usb_rcvctrlpipe(usbos_info->usb, 0), + (unsigned char *) &usbos_info->ctl_read, + buf, size, (usb_complete_t)dbus_usbos_ctlread_complete, usbos_info); + + status = USB_SUBMIT_URB(usbos_info->ctl_urb); + if (status < 0) { + DBUSERR(("%s: usb_submit_urb failed %d\n", __FUNCTION__, status)); + up(&usbos_info->ctl_lock); + + USB_AUTOPM_PUT_INTERFACE_ASYNC(g_probe_info.intf); + + return DBUS_ERR_RXCTLFAIL; + } + + return DBUS_OK; +} + +static int +dbus_usbos_intf_get_attrib(void *bus, dbus_attrib_t *attrib) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + + if ((usbos_info == NULL) || (attrib == NULL)) + return DBUS_ERR; + + attrib->bustype = DBUS_USB; + attrib->vid = g_probe_info.vid; + attrib->pid = g_probe_info.pid; + attrib->devid = 0x4322; + + attrib->nchan = 1; + + /* MaxPacketSize for USB hi-speed bulk out is 512 bytes + * and 64-bytes for full-speed. + * When sending pkt > MaxPacketSize, Host SW breaks it + * up into multiple packets. + */ + attrib->mtu = usbos_info->maxps; + + return DBUS_OK; +} + +/** Called by higher layer (dbus_usb.c) when it wants to 'up' the USB interface to the dongle */ +static int +dbus_usbos_intf_up(void *bus) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + uint16 ifnum; +#ifdef BCMUSBDEV_COMPOSITE + int wlan_if = 0; +#endif + if (usbos_info == NULL) + return DBUS_ERR; + + if (usbos_info->usb == NULL) + return DBUS_ERR; + +#if defined(INTR_EP_ENABLE) + /* full dongle use intr EP, bmac doesn't use it */ + if (usbos_info->intr_urb) { + int ret; + + usb_fill_int_urb(usbos_info->intr_urb, usbos_info->usb, + usbos_info->intr_pipe, &usbos_info->intr, + usbos_info->intr_size, (usb_complete_t)dbus_usbos_intr_complete, + usbos_info, usbos_info->interval); + + if ((ret = USB_SUBMIT_URB(usbos_info->intr_urb))) { + DBUSERR(("%s USB_SUBMIT_URB failed with status %d\n", __FUNCTION__, ret)); + return DBUS_ERR; + } + } +#endif + + if (usbos_info->ctl_urb) { + usbos_info->ctl_in_pipe = usb_rcvctrlpipe(usbos_info->usb, 0); + usbos_info->ctl_out_pipe = usb_sndctrlpipe(usbos_info->usb, 0); + +#ifdef BCMUSBDEV_COMPOSITE + wlan_if = dbus_usbos_intf_wlan(usbos_info->usb); + ifnum = cpu_to_le16(IFDESC(usbos_info->usb, wlan_if).bInterfaceNumber); +#else + ifnum = cpu_to_le16(IFDESC(usbos_info->usb, CONTROL_IF).bInterfaceNumber); +#endif /* BCMUSBDEV_COMPOSITE */ + /* CTL Write */ + usbos_info->ctl_write.bRequestType = + USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; + usbos_info->ctl_write.bRequest = 0; + usbos_info->ctl_write.wValue = cpu_to_le16(0); + usbos_info->ctl_write.wIndex = cpu_to_le16p(&ifnum); + + /* CTL Read */ + usbos_info->ctl_read.bRequestType = + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE; + usbos_info->ctl_read.bRequest = 1; + usbos_info->ctl_read.wValue = cpu_to_le16(0); + usbos_info->ctl_read.wIndex = cpu_to_le16p(&ifnum); + } + + /* Success, indicate usbos_info is fully up */ + dbus_usbos_state_change(usbos_info, DBUS_STATE_UP); + + return DBUS_OK; +} /* dbus_usbos_intf_up */ + +static int +dbus_usbos_intf_down(void *bus) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + + if (usbos_info == NULL) + return DBUS_ERR; + + dbusos_stop(usbos_info); + return DBUS_OK; +} + +static int +dbus_usbos_intf_stop(void *bus) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + + if (usbos_info == NULL) + return DBUS_ERR; + + dbusos_stop(usbos_info); + return DBUS_OK; +} + + +/** Called by higher layer (dbus_usb.c) */ +static int +dbus_usbos_intf_set_config(void *bus, dbus_config_t *config) +{ + int err = DBUS_ERR; + usbos_info_t* usbos_info = bus; + + if (config->config_id == DBUS_CONFIG_ID_RXCTL_DEFERRES) { + usbos_info->rxctl_deferrespok = config->rxctl_deferrespok; + err = DBUS_OK; + } else if (config->config_id == DBUS_CONFIG_ID_AGGR_LIMIT) { + /* DBUS_CONFIG_ID_AGGR_LIMIT shouldn't be called after probe stage */ + ASSERT(disc_arg == NULL); + ASSERT(config->aggr_param.maxrxsf > 0); + ASSERT(config->aggr_param.maxrxsize > 0); + if (config->aggr_param.maxrxsize > usbos_info->rxbuf_len) { + int state = usbos_info->pub->busstate; + dbus_usbos_unlink(&usbos_info->req_rxpostedq, &usbos_info->rxposted_lock); + while (atomic_read(&usbos_info->rxposted)) { + DBUSTRACE(("%s rxposted is %d, delay 1 ms\n", __FUNCTION__, + atomic_read(&usbos_info->rxposted))); + dbus_usbos_wait(usbos_info, 1); + } + usbos_info->rxbuf_len = config->aggr_param.maxrxsize; + dbus_usbos_state_change(usbos_info, state); + } + err = DBUS_OK; + } + + return err; +} + + +/** Called by dbus_usb.c when it wants to download firmware into the dongle */ +bool +dbus_usbos_dl_cmd(usbos_info_t *usbinfo, uint8 cmd, void *buffer, int buflen) +{ + int transferred; + int index = 0; + char *tmpbuf; + + if ((usbinfo == NULL) || (buffer == NULL) || (buflen == 0)) + return FALSE; + + tmpbuf = (char *) MALLOC(usbinfo->pub->osh, buflen); + if (!tmpbuf) { + DBUSERR(("%s: Unable to allocate memory \n", __FUNCTION__)); + return FALSE; + } + +#ifdef BCM_REQUEST_FW + if (cmd == DL_GO) { + index = 1; + } +#endif + + /* Disable USB autosuspend until this request completes, request USB resume if needed. */ + USB_AUTOPM_GET_INTERFACE(g_probe_info.intf); + + transferred = USB_CONTROL_MSG(usbinfo->usb, usb_rcvctrlpipe(usbinfo->usb, 0), + cmd, (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE), + 0, index, + (void*) tmpbuf, buflen, USB_CTRL_EP_TIMEOUT); + if (transferred == buflen) { + memcpy(buffer, tmpbuf, buflen); + } else { + DBUSERR(("%s: usb_control_msg failed %d\n", __FUNCTION__, transferred)); + } + + USB_AUTOPM_PUT_INTERFACE(g_probe_info.intf); + + MFREE(usbinfo->pub->osh, tmpbuf, buflen); + return (transferred == buflen); +} + +/** + * Called by dbus_usb.c when it wants to download a buffer into the dongle (e.g. as part of the + * download process, when writing nvram variables). + */ +int +dbus_write_membytes(usbos_info_t* usbinfo, bool set, uint32 address, uint8 *data, uint size) +{ + hwacc_t hwacc; + int write_bytes = 4; + int status; + int retval = 0; + + DBUSTRACE(("Enter:%s\n", __FUNCTION__)); + + /* Read is not supported */ + if (set == 0) { + DBUSERR(("Currently read is not supported!!\n")); + return -1; + } + + USB_AUTOPM_GET_INTERFACE(g_probe_info.intf); + + hwacc.cmd = DL_CMD_WRHW; + hwacc.addr = address; + + DBUSTRACE(("Address:%x size:%d", hwacc.addr, size)); + do { + if (size >= 4) { + write_bytes = 4; + } else if (size >= 2) { + write_bytes = 2; + } else { + write_bytes = 1; + } + + hwacc.len = write_bytes; + + while (size >= write_bytes) { + hwacc.data = *((unsigned int*)data); + + status = USB_CONTROL_MSG(usbinfo->usb, usb_sndctrlpipe(usbinfo->usb, 0), + DL_WRHW, (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE), + 1, 0, (char *)&hwacc, sizeof(hwacc_t), USB_CTRL_EP_TIMEOUT); + + if (status < 0) { + retval = -1; + DBUSERR((" Ctrl write hwacc failed w/status %d @ address:%x \n", + status, hwacc.addr)); + goto err; + } + + hwacc.addr += write_bytes; + data += write_bytes; + size -= write_bytes; + } + } while (size > 0); + +err: + USB_AUTOPM_PUT_INTERFACE(g_probe_info.intf); + + return retval; +} + +int +dbus_usbos_readreg(void *bus, uint32 regaddr, int datalen, uint32 *value) +{ + usbos_info_t *usbinfo = (usbos_info_t *) bus; + int ret = DBUS_OK; + int transferred; + uint32 cmd; + hwacc_t hwacc; + + if (usbinfo == NULL) + return DBUS_ERR; + + if (datalen == 1) + cmd = DL_RDHW8; + else if (datalen == 2) + cmd = DL_RDHW16; + else + cmd = DL_RDHW32; + + USB_AUTOPM_GET_INTERFACE(g_probe_info.intf); + + transferred = USB_CONTROL_MSG(usbinfo->usb, usb_rcvctrlpipe(usbinfo->usb, 0), + cmd, (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE), + (uint16)(regaddr), (uint16)(regaddr >> 16), + (void *) &hwacc, sizeof(hwacc_t), USB_CTRL_EP_TIMEOUT); + + if (transferred >= sizeof(hwacc_t)) { + *value = hwacc.data; + } else { + DBUSERR(("%s: usb_control_msg failed %d\n", __FUNCTION__, transferred)); + ret = DBUS_ERR; + } + + USB_AUTOPM_PUT_INTERFACE(g_probe_info.intf); + + return ret; +} + +int +dbus_usbos_writereg(void *bus, uint32 regaddr, int datalen, uint32 data) +{ + usbos_info_t *usbinfo = (usbos_info_t *) bus; + int ret = DBUS_OK; + int transferred; + uint32 cmd = DL_WRHW; + hwacc_t hwacc; + + if (usbinfo == NULL) + return DBUS_ERR; + + USB_AUTOPM_GET_INTERFACE(g_probe_info.intf); + + hwacc.cmd = DL_WRHW; + hwacc.addr = regaddr; + hwacc.data = data; + hwacc.len = datalen; + + transferred = USB_CONTROL_MSG(usbinfo->usb, usb_sndctrlpipe(usbinfo->usb, 0), + cmd, (USB_DIR_OUT| USB_TYPE_VENDOR | USB_RECIP_INTERFACE), + 1, 0, + (void *) &hwacc, sizeof(hwacc_t), USB_CTRL_EP_TIMEOUT); + + if (transferred != sizeof(hwacc_t)) { + DBUSERR(("%s: usb_control_msg failed %d\n", __FUNCTION__, transferred)); + ret = DBUS_ERR; + } + + USB_AUTOPM_PUT_INTERFACE(g_probe_info.intf); + + return ret; +} + +int +dbus_usbos_wait(usbos_info_t *usbinfo, uint16 ms) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) + if (in_interrupt()) + mdelay(ms); + else + msleep_interruptible(ms); +#else + wait_ms(ms); +#endif + return DBUS_OK; +} + +/** Called by dbus_usb.c as part of the firmware download process */ +bool +dbus_usbos_dl_send_bulk(usbos_info_t *usbinfo, void *buffer, int len) +{ + bool ret = TRUE; + int status; + int transferred = 0; + + if (usbinfo == NULL) + return DBUS_ERR; + + USB_AUTOPM_GET_INTERFACE(g_probe_info.intf); + + status = USB_BULK_MSG(usbinfo->usb, usbinfo->tx_pipe, + buffer, len, + &transferred, USB_BULK_EP_TIMEOUT); + + if (status < 0) { + DBUSERR(("%s: usb_bulk_msg failed %d\n", __FUNCTION__, status)); + ret = FALSE; + } + + USB_AUTOPM_PUT_INTERFACE(g_probe_info.intf); + + return ret; +} + +static bool +dbus_usbos_intf_recv_needed(void *bus) +{ + return FALSE; +} + +/** + * Higher layer (dbus_usb.c) wants to execute a function on the condition that the rx spin lock has + * been acquired. + */ +static void* +dbus_usbos_intf_exec_rxlock(void *bus, exec_cb_t cb, struct exec_parms *args) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + void *ret; + unsigned long flags; + + if (usbos_info == NULL) + return NULL; + + spin_lock_irqsave(&usbos_info->rxlock, flags); + ret = cb(args); + spin_unlock_irqrestore(&usbos_info->rxlock, flags); + + return ret; +} + +static void* +dbus_usbos_intf_exec_txlock(void *bus, exec_cb_t cb, struct exec_parms *args) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + void *ret; + unsigned long flags; + + if (usbos_info == NULL) + return NULL; + + spin_lock_irqsave(&usbos_info->txlock, flags); + ret = cb(args); + spin_unlock_irqrestore(&usbos_info->txlock, flags); + + return ret; +} + +/** + * if an error condition was detected in this module, the higher DBUS layer (dbus_usb.c) has to + * be notified. + */ +int +dbus_usbos_errhandler(void *bus, int err) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + + if (usbos_info == NULL) + return DBUS_ERR; + + if (usbos_info->cbarg && usbos_info->cbs) { + if (usbos_info->cbs->errhandler) + usbos_info->cbs->errhandler(usbos_info->cbarg, err); + } + + return DBUS_OK; +} + +/** + * if a change in bus state was detected in this module, the higher DBUS layer (dbus_usb.c) has to + * be notified. + */ +int +dbus_usbos_state_change(void *bus, int state) +{ + usbos_info_t *usbos_info = (usbos_info_t *) bus; + + if (usbos_info == NULL) + return DBUS_ERR; + + if (usbos_info->cbarg && usbos_info->cbs) { + if (usbos_info->cbs->state_change) + usbos_info->cbs->state_change(usbos_info->cbarg, state); + } + + usbos_info->pub->busstate = state; + return DBUS_OK; +} + +int +dbus_bus_osl_register(int vid, int pid, probe_cb_t prcb, + disconnect_cb_t discb, void *prarg, dbus_intf_t **intf, void *param1, void *param2) +{ + bzero(&g_probe_info, sizeof(probe_info_t)); + + probe_cb = prcb; + disconnect_cb = discb; + probe_arg = prarg; + + devid_table[0].idVendor = vid; + devid_table[0].idProduct = pid; + + *intf = &dbus_usbos_intf; + + USB_REGISTER(); + + return DBUS_ERR_NODEVICE; +} + +int +dbus_bus_osl_deregister() +{ + g_probe_info.dereged = TRUE; + + DHD_MUTEX_LOCK(); + if (disconnect_cb && disc_arg && (g_probe_info.disc_cb_done == FALSE)) { + disconnect_cb(disc_arg); + disc_arg = NULL; + } + DHD_MUTEX_UNLOCK(); + + USB_DEREGISTER(); + + return DBUS_OK; +} + +void * +dbus_usbos_intf_attach(dbus_pub_t *pub, void *cbarg, dbus_intf_callbacks_t *cbs) +{ + usbos_info_t *usbos_info; + + if (g_probe_info.dldone == FALSE) { + DBUSERR(("%s: err device not downloaded!\n", __FUNCTION__)); + return NULL; + } + + /* Sanity check for BUS_INFO() */ + ASSERT(OFFSETOF(usbos_info_t, pub) == 0); + + usbos_info = MALLOC(pub->osh, sizeof(usbos_info_t)); + if (usbos_info == NULL) + return NULL; + + bzero(usbos_info, sizeof(usbos_info_t)); + + usbos_info->pub = pub; + usbos_info->cbarg = cbarg; + usbos_info->cbs = cbs; + + /* Needed for disconnect() */ + g_probe_info.usbos_info = usbos_info; + + /* Update USB Info */ + usbos_info->usb = g_probe_info.usb; + usbos_info->rx_pipe = g_probe_info.rx_pipe; + usbos_info->rx_pipe2 = g_probe_info.rx_pipe2; + usbos_info->tx_pipe = g_probe_info.tx_pipe; + usbos_info->intr_pipe = g_probe_info.intr_pipe; + usbos_info->intr_size = g_probe_info.intr_size; + usbos_info->interval = g_probe_info.interval; + usbos_info->pub->device_speed = g_probe_info.device_speed; + if (usbos_info->rx_pipe2) { + usbos_info->pub->attrib.has_2nd_bulk_in_ep = 1; + } else { + usbos_info->pub->attrib.has_2nd_bulk_in_ep = 0; + } + + if (usbos_info->tx_pipe) + usbos_info->maxps = usb_maxpacket(usbos_info->usb, + usbos_info->tx_pipe, usb_pipeout(usbos_info->tx_pipe)); + + INIT_LIST_HEAD(&usbos_info->req_rxfreeq); + INIT_LIST_HEAD(&usbos_info->req_txfreeq); + INIT_LIST_HEAD(&usbos_info->req_rxpostedq); + INIT_LIST_HEAD(&usbos_info->req_txpostedq); + spin_lock_init(&usbos_info->rxfree_lock); + spin_lock_init(&usbos_info->txfree_lock); + spin_lock_init(&usbos_info->rxposted_lock); + spin_lock_init(&usbos_info->txposted_lock); + spin_lock_init(&usbos_info->rxlock); + spin_lock_init(&usbos_info->txlock); + + atomic_set(&usbos_info->rxposted, 0); + atomic_set(&usbos_info->txposted, 0); + + +#ifdef USB_DISABLE_INT_EP + usbos_info->intr_urb = NULL; +#else + if (!(usbos_info->intr_urb = USB_ALLOC_URB())) { + DBUSERR(("%s: usb_alloc_urb (tx) failed\n", __FUNCTION__)); + goto fail; + } +#endif + + if (!(usbos_info->ctl_urb = USB_ALLOC_URB())) { + DBUSERR(("%s: usb_alloc_urb (tx) failed\n", __FUNCTION__)); + goto fail; + } + + init_waitqueue_head(&usbos_info->wait); + + if (!(usbos_info->blk_urb = USB_ALLOC_URB())) { /* for embedded image downloading */ + DBUSERR(("%s: usb_alloc_urb (tx) failed\n", __FUNCTION__)); + goto fail; + } + + usbos_info->rxbuf_len = (uint)usbos_info->pub->rxsize; + + + + atomic_set(&usbos_info->txallocated, 0); + if (DBUS_OK != dbus_usbos_urbreqs_alloc(usbos_info, + usbos_info->pub->ntxq, FALSE)) { + goto fail; + } + + atomic_set(&usbos_info->rxallocated, 0); + if (DBUS_OK != dbus_usbos_urbreqs_alloc(usbos_info, + MIN(DBUS_USB_RXQUEUE_BATCH_ADD, usbos_info->pub->nrxq), + TRUE)) { + goto fail; + } + + sema_init(&usbos_info->ctl_lock, 1); + +#ifdef USBOS_THREAD + if (dbus_usbos_thread_init(usbos_info) == NULL) + goto fail; +#endif /* USBOS_THREAD */ + +#ifdef USBOS_TX_THREAD + if (dbus_usbos_tx_thread_init(usbos_info) == NULL) + goto fail; +#endif /* USBOS_TX_THREAD */ + + pub->dev_info = g_probe_info.usb; + + + return (void *) usbos_info; +fail: + if (usbos_info->intr_urb) { + USB_FREE_URB(usbos_info->intr_urb); + usbos_info->intr_urb = NULL; + } + + if (usbos_info->ctl_urb) { + USB_FREE_URB(usbos_info->ctl_urb); + usbos_info->ctl_urb = NULL; + } + +#if defined(BCM_REQUEST_FW) + if (usbos_info->blk_urb) { + USB_FREE_URB(usbos_info->blk_urb); + usbos_info->blk_urb = NULL; + } +#endif + + dbus_usbos_urbreqs_free(usbos_info, TRUE); + atomic_set(&usbos_info->rxallocated, 0); + dbus_usbos_urbreqs_free(usbos_info, FALSE); + atomic_set(&usbos_info->txallocated, 0); + + g_probe_info.usbos_info = NULL; + + MFREE(pub->osh, usbos_info, sizeof(usbos_info_t)); + return NULL; +} /* dbus_usbos_intf_attach */ + +void +dbus_usbos_intf_detach(dbus_pub_t *pub, void *info) +{ + usbos_info_t *usbos_info = (usbos_info_t *) info; + osl_t *osh = pub->osh; + + if (usbos_info == NULL) { + return; + } + +#ifdef USBOS_TX_THREAD + dbus_usbos_tx_thread_deinit(usbos_info); +#endif /* USBOS_TX_THREAD */ + + /* Must unlink all URBs prior to driver unload; + * otherwise an URB callback can occur after driver + * has been de-allocated and rmmod'd + */ + dbusos_stop(usbos_info); + + if (usbos_info->intr_urb) { + USB_FREE_URB(usbos_info->intr_urb); + usbos_info->intr_urb = NULL; + } + + if (usbos_info->ctl_urb) { + USB_FREE_URB(usbos_info->ctl_urb); + usbos_info->ctl_urb = NULL; + } + + if (usbos_info->blk_urb) { + USB_FREE_URB(usbos_info->blk_urb); + usbos_info->blk_urb = NULL; + } + + dbus_usbos_urbreqs_free(usbos_info, TRUE); + atomic_set(&usbos_info->rxallocated, 0); + dbus_usbos_urbreqs_free(usbos_info, FALSE); + atomic_set(&usbos_info->txallocated, 0); + +#ifdef USBOS_THREAD + dbus_usbos_thread_deinit(usbos_info); +#endif /* USBOS_THREAD */ + + g_probe_info.usbos_info = NULL; + MFREE(osh, usbos_info, sizeof(usbos_info_t)); +} /* dbus_usbos_intf_detach */ + + +#ifdef USBOS_TX_THREAD + +void* +dbus_usbos_tx_thread_init(usbos_info_t *usbos_info) +{ + spin_lock_init(&usbos_info->usbos_tx_list_lock); + INIT_LIST_HEAD(&usbos_info->usbos_tx_list); + init_waitqueue_head(&usbos_info->usbos_tx_queue_head); + + usbos_info->usbos_tx_kt = kthread_create(dbus_usbos_tx_thread_func, + usbos_info, "usb-tx-thread"); + + if (IS_ERR(usbos_info->usbos_tx_kt)) { + DBUSERR(("Thread Creation failed\n")); + return (NULL); + } + + usbos_info->ctl_state = USBOS_REQUEST_STATE_UNSCHEDULED; + wake_up_process(usbos_info->usbos_tx_kt); + + return (usbos_info->usbos_tx_kt); +} + +void +dbus_usbos_tx_thread_deinit(usbos_info_t *usbos_info) +{ + urb_req_t *req; + + if (usbos_info->usbos_tx_kt) { + wake_up_interruptible(&usbos_info->usbos_tx_queue_head); + kthread_stop(usbos_info->usbos_tx_kt); + } + + /* Move pending requests to free queue so they can be freed */ + while ((req = dbus_usbos_qdeq( + &usbos_info->usbos_tx_list, &usbos_info->usbos_tx_list_lock)) != NULL) { + dbus_usbos_qenq(&usbos_info->req_txfreeq, req, &usbos_info->txfree_lock); + } +} + +/** + * Allow USB in-band resume to block by submitting CTRL and DATA URBs on a separate thread. + */ +int +dbus_usbos_tx_thread_func(void *data) +{ + usbos_info_t *usbos_info = (usbos_info_t *)data; + urb_req_t *req; + dbus_irb_tx_t *txirb; + int ret; + unsigned long flags; + +#ifdef WL_THREADNICE + set_user_nice(current, WL_THREADNICE); +#endif + + while (1) { + /* Wait until there are URBs to submit */ + wait_event_interruptible_timeout( + usbos_info->usbos_tx_queue_head, + !list_empty(&usbos_info->usbos_tx_list) || + usbos_info->ctl_state == USBOS_REQUEST_STATE_SCHEDULED, + 100); + + if (kthread_should_stop()) + break; + + /* Submit CTRL URB if needed */ + if (usbos_info->ctl_state == USBOS_REQUEST_STATE_SCHEDULED) { + + /* Disable USB autosuspend until this request completes. If the + * interface was suspended, this call blocks until it has been resumed. + */ + USB_AUTOPM_GET_INTERFACE(g_probe_info.intf); + + usbos_info->ctl_state = USBOS_REQUEST_STATE_SUBMITTED; + + ret = USB_SUBMIT_URB(usbos_info->ctl_urb); + if (ret != 0) { + DBUSERR(("%s CTRL USB_SUBMIT_URB failed, status %d\n", + __FUNCTION__, ret)); + + usbos_info->ctl_state = USBOS_REQUEST_STATE_UNSCHEDULED; + up(&usbos_info->ctl_lock); + + USB_AUTOPM_PUT_INTERFACE_ASYNC(g_probe_info.intf); + } + } + + /* Submit all available TX URBs */ + while ((req = dbus_usbos_qdeq(&usbos_info->usbos_tx_list, + &usbos_info->usbos_tx_list_lock)) != NULL) { + + /* Disable USB autosuspend until this request completes. If the + * interface was suspended, this call blocks until it has been resumed. + */ + USB_AUTOPM_GET_INTERFACE(g_probe_info.intf); + + spin_lock_irqsave(&usbos_info->txlock, flags); + + ret = USB_SUBMIT_URB(req->urb); + if (ret == 0) { + /* URB submitted successfully */ + dbus_usbos_qenq(&usbos_info->req_txpostedq, req, + &usbos_info->txposted_lock); + atomic_inc(&usbos_info->txposted); + } else { + /* Submitting the URB failed. */ + DBUSERR(("%s TX USB_SUBMIT_URB failed, status %d\n", + __FUNCTION__, ret)); + + USB_AUTOPM_PUT_INTERFACE_ASYNC(g_probe_info.intf); + } + + spin_unlock_irqrestore(&usbos_info->txlock, flags); + + if (ret != 0) { + /* Cleanup and notify higher layers */ + dbus_usbos_qenq(&usbos_info->req_txfreeq, req, + &usbos_info->txfree_lock); + + txirb = req->arg; + if (txirb->send_buf) { + MFREE(usbos_info->pub->osh, txirb->send_buf, req->buf_len); + txirb->send_buf = NULL; + req->buf_len = 0; + } + + if (likely (usbos_info->cbarg && usbos_info->cbs)) { + if (likely (usbos_info->cbs->send_irb_complete != NULL)) + usbos_info->cbs->send_irb_complete( + usbos_info->cbarg, txirb, DBUS_ERR_TXDROP); + } + } + } + } + + return 0; +} /* dbus_usbos_tx_thread_func */ + +#endif /* USBOS_TX_THREAD */ + +#ifdef USBOS_THREAD + +/** + * Increase system performance by creating a USB thread that runs parallel to other system + * activity. + */ +static void* +dbus_usbos_thread_init(usbos_info_t *usbos_info) +{ + usbos_list_entry_t *entry; + unsigned long flags, ii; + + spin_lock_init(&usbos_info->usbos_list_lock); + spin_lock_init(&usbos_info->ctrl_lock); + INIT_LIST_HEAD(&usbos_info->usbos_list); + INIT_LIST_HEAD(&usbos_info->usbos_free_list); + init_waitqueue_head(&usbos_info->usbos_queue_head); + atomic_set(&usbos_info->usbos_list_cnt, 0); + + + for (ii = 0; ii < (usbos_info->pub->nrxq + usbos_info->pub->ntxq); ii++) { + entry = MALLOC(usbos_info->pub->osh, sizeof(usbos_list_entry_t)); + if (entry) { + spin_lock_irqsave(&usbos_info->usbos_list_lock, flags); + list_add_tail((struct list_head*) entry, &usbos_info->usbos_free_list); + spin_unlock_irqrestore(&usbos_info->usbos_list_lock, flags); + } else { + DBUSERR(("Failed to create list\n")); + } + } + + usbos_info->usbos_kt = kthread_create(dbus_usbos_thread_func, + usbos_info, "usb-thread"); + + if (IS_ERR(usbos_info->usbos_kt)) { + DBUSERR(("Thread Creation failed\n")); + return (NULL); + } + + wake_up_process(usbos_info->usbos_kt); + + return (usbos_info->usbos_kt); +} + +static void +dbus_usbos_thread_deinit(usbos_info_t *usbos_info) +{ + struct list_head *cur, *next; + usbos_list_entry_t *entry; + unsigned long flags; + + if (usbos_info->usbos_kt) { + wake_up_interruptible(&usbos_info->usbos_queue_head); + kthread_stop(usbos_info->usbos_kt); + } +#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + list_for_each_safe(cur, next, &usbos_info->usbos_list) + { + entry = list_entry(cur, struct usbos_list_entry, list); + /* detach this entry from the list and then free the entry */ + spin_lock_irqsave(&usbos_info->usbos_list_lock, flags); + list_del(cur); + MFREE(usbos_info->pub->osh, entry, sizeof(usbos_list_entry_t)); + spin_unlock_irqrestore(&usbos_info->usbos_list_lock, flags); + } + + list_for_each_safe(cur, next, &usbos_info->usbos_free_list) + { + entry = list_entry(cur, struct usbos_list_entry, list); + /* detach this entry from the list and then free the entry */ + spin_lock_irqsave(&usbos_info->usbos_list_lock, flags); + list_del(cur); + MFREE(usbos_info->pub->osh, entry, sizeof(usbos_list_entry_t)); + spin_unlock_irqrestore(&usbos_info->usbos_list_lock, flags); + } +#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) +#pragma GCC diagnostic pop +#endif +} + +/** Process completed URBs in a worker thread */ +static int +dbus_usbos_thread_func(void *data) +{ + usbos_info_t *usbos_info = (usbos_info_t *)data; + usbos_list_entry_t *entry; + struct list_head *cur, *next; + unsigned long flags; + +#ifdef WL_THREADNICE + set_user_nice(current, WL_THREADNICE); +#endif + + while (1) { + /* If the list is empty, then go to sleep */ + wait_event_interruptible_timeout + (usbos_info->usbos_queue_head, + atomic_read(&usbos_info->usbos_list_cnt) > 0, + 100); + + if (kthread_should_stop()) + break; + + spin_lock_irqsave(&usbos_info->usbos_list_lock, flags); + + /* For each entry on the list, process it. Remove the entry from + * the list when done. + */ +#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + list_for_each_safe(cur, next, &usbos_info->usbos_list) + { + urb_req_t *req; + int len; + int stat; + usbos_info_t *usbos_info_local; + + entry = list_entry(cur, struct usbos_list_entry, list); + if (entry == NULL) + break; + + req = entry->urb_context; + len = entry->urb_length; + stat = entry->urb_status; + usbos_info_local = req->usbinfo; + + /* detach this entry from the list and attach it to the free list */ + list_del_init(cur); + spin_unlock_irqrestore(&usbos_info_local->usbos_list_lock, flags); + + dbus_usbos_recv_complete_handle(req, len, stat); + + spin_lock_irqsave(&usbos_info_local->usbos_list_lock, flags); + + list_add_tail(cur, &usbos_info_local->usbos_free_list); + + atomic_dec(&usbos_info_local->usbos_list_cnt); + } + + spin_unlock_irqrestore(&usbos_info->usbos_list_lock, flags); + + } +#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + + return 0; +} /* dbus_usbos_thread_func */ + +/** Called on Linux calling URB callback, see dbus_usbos_recv_complete() */ +static void +dbus_usbos_dispatch_schedule(CALLBACK_ARGS) +{ + urb_req_t *req = urb->context; + usbos_info_t *usbos_info = req->usbinfo; + usbos_list_entry_t *entry; + unsigned long flags; + struct list_head *cur; + + spin_lock_irqsave(&usbos_info->usbos_list_lock, flags); + + cur = usbos_info->usbos_free_list.next; +#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + entry = list_entry(cur, struct usbos_list_entry, list); +#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + + /* detach this entry from the free list and prepare it insert it to use list */ + list_del_init(cur); + + if (entry) { + entry->urb_context = urb->context; + entry->urb_length = urb->actual_length; + entry->urb_status = urb->status; + + atomic_inc(&usbos_info->usbos_list_cnt); + list_add_tail(cur, &usbos_info->usbos_list); + } else { + DBUSERR(("!!!!!!OUT OF MEMORY!!!!!!!\n")); + } + + spin_unlock_irqrestore(&usbos_info->usbos_list_lock, flags); + + /* thread */ + wake_up_interruptible(&usbos_info->usbos_queue_head); +} /* dbus_usbos_dispatch_schedule */ + +#endif /* USBOS_THREAD */ + + + + +#ifdef BCM_REQUEST_FW + +struct request_fw_context { + const struct firmware *firmware; + struct semaphore lock; +}; + +/* + * Callback for dbus_request_firmware(). + */ +static void +dbus_request_firmware_done(const struct firmware *firmware, void *ctx) +{ + struct request_fw_context *context = (struct request_fw_context*)ctx; + + /* Store the received firmware handle in the context and wake requester */ + context->firmware = firmware; + up(&context->lock); +} + +/* + * Send a firmware request and wait for completion. + * + * The use of the asynchronous version of request_firmware() is needed to avoid + * kernel oopses when we just come out of system hibernate. + */ +static int +dbus_request_firmware(const char *name, const struct firmware **firmware) +{ + struct request_fw_context *context; + int ret; + + context = kzalloc(sizeof(*context), GFP_KERNEL); + if (!context) + return -ENOMEM; + + sema_init(&context->lock, 0); + + ret = request_firmware_nowait(THIS_MODULE, true, name, &g_probe_info.usb->dev, + GFP_KERNEL, context, dbus_request_firmware_done); + if (ret) { + kfree(context); + return ret; + } + + /* Wait for completion */ + if (down_interruptible(&context->lock) != 0) { + kfree(context); + return -ERESTARTSYS; + } + + *firmware = context->firmware; + kfree(context); + + return *firmware != NULL ? 0 : -ENOENT; +} + +static void * +dbus_get_fwfile(int devid, int chiprev, uint8 **fw, int *fwlen, uint16 boardtype, uint16 boardrev) +{ + const struct firmware *firmware = NULL; +#ifndef OEM_ANDROID + s8 *device_id = NULL; + s8 *chip_rev = ""; +#endif /* OEM_ANDROID */ + s8 file_name[64]; + int ret; + +#ifndef OEM_ANDROID + switch (devid) { + case BCM4350_CHIP_ID: + case BCM4354_CHIP_ID: + case BCM43556_CHIP_ID: + case BCM43558_CHIP_ID: + case BCM43566_CHIP_ID: + case BCM43568_CHIP_ID: + case BCM43570_CHIP_ID: + case BCM4358_CHIP_ID: + device_id = "4350"; + break; + case BCM43143_CHIP_ID: + device_id = "43143"; + break; + case BCM43234_CHIP_ID: + case BCM43235_CHIP_ID: + case BCM43236_CHIP_ID: + device_id = "43236"; + break; + case BCM43242_CHIP_ID: + device_id = "43242"; + break; + case BCM43238_CHIP_ID: + device_id = "43238"; + break; + case BCM43526_CHIP_ID: + device_id = "43526"; + break; + case BCM43569_CHIP_ID: + device_id = "43569"; + switch (chiprev) { + case 0: + chip_rev = "a0"; + break; + case 2: + chip_rev = "a2"; + break; + default: + break; + } + break; + default: + DBUSERR(("unsupported device %x\n", devid)); + return NULL; + } + + /* Load firmware */ + snprintf(file_name, sizeof(file_name), "brcm/bcm%s%s-firmware.bin", device_id, chip_rev); +#else + snprintf(file_name, sizeof(file_name), "%s", CONFIG_ANDROID_BCMDHD_FW_PATH); +#endif /* OEM_ANDROID */ + + ret = dbus_request_firmware(file_name, &firmware); + if (ret) { + DBUSERR(("fail to request firmware %s\n", file_name)); + return NULL; + } + + *fwlen = firmware->size; + *fw = (uint8 *)firmware->data; + return (void *)firmware; + +} + +static void * +dbus_get_nvfile(int devid, int chiprev, uint8 **fw, int *fwlen, uint16 boardtype, uint16 boardrev) +{ + const struct firmware *firmware = NULL; +#ifndef OEM_ANDROID + s8 *device_id = NULL; + s8 *chip_rev = ""; +#endif /* OEM_ANDROID */ + s8 file_name[64]; + int ret; + +#ifndef OEM_ANDROID + switch (devid) { + case BCM4350_CHIP_ID: + case BCM4354_CHIP_ID: + case BCM43556_CHIP_ID: + case BCM43558_CHIP_ID: + case BCM43566_CHIP_ID: + case BCM43568_CHIP_ID: + case BCM43570_CHIP_ID: + case BCM4358_CHIP_ID: + device_id = "4350"; + break; + case BCM43143_CHIP_ID: + device_id = "43143"; + break; + case BCM43234_CHIP_ID: + device_id = "43234"; + break; + case BCM43235_CHIP_ID: + device_id = "43235"; + break; + case BCM43236_CHIP_ID: + device_id = "43236"; + break; + case BCM43238_CHIP_ID: + device_id = "43238"; + break; + case BCM43242_CHIP_ID: + device_id = "43242"; + break; + case BCM43526_CHIP_ID: + device_id = "43526"; + break; + case BCM43569_CHIP_ID: + device_id = "43569"; + switch (chiprev) { + case 0: + chip_rev = "a0"; + break; + case 2: + chip_rev = "a2"; + break; + default: + break; + } + break; + default: + DBUSERR(("unsupported device %x\n", devid)); + return NULL; + } + + /* Load board specific nvram file */ + snprintf(file_name, sizeof(file_name), "brcm/bcm%s%s-%2x-%2x.nvm", + device_id, chip_rev, boardtype, boardrev); +#else + snprintf(file_name, sizeof(file_name), "%s", CONFIG_ANDROID_BCMDHD_NVRAM_PATH); +#endif /* OEM_ANDROID */ + + ret = dbus_request_firmware(file_name, &firmware); + if (ret) { + DBUSERR(("fail to request nvram %s\n", file_name)); + +#ifndef OEM_ANDROID + /* Load generic nvram file */ + snprintf(file_name, sizeof(file_name), "brcm/bcm%s%s.nvm", + device_id, chip_rev); + + ret = dbus_request_firmware(file_name, &firmware); +#endif /* OEM_ANDROID */ + + if (ret) { + DBUSERR(("fail to request nvram %s\n", file_name)); + return NULL; + } + } + + *fwlen = firmware->size; + *fw = (uint8 *)firmware->data; + return (void *)firmware; +} + +void * +dbus_get_fw_nvfile(int devid, int chiprev, uint8 **fw, int *fwlen, int type, uint16 boardtype, + uint16 boardrev) +{ + switch (type) { + case DBUS_FIRMWARE: + return dbus_get_fwfile(devid, chiprev, fw, fwlen, boardtype, boardrev); + case DBUS_NVFILE: + return dbus_get_nvfile(devid, chiprev, fw, fwlen, boardtype, boardrev); + default: + return NULL; + } +} + +void +dbus_release_fw_nvfile(void *firmware) +{ + release_firmware((struct firmware *)firmware); +} +#endif /* BCM_REQUEST_FW */ + +#ifdef BCMUSBDEV_COMPOSITE +/** + * For a composite device the interface order is not guaranteed, scan the device struct for the WLAN + * interface. + */ +static int +dbus_usbos_intf_wlan(struct usb_device *usb) +{ + int i, num_of_eps, ep, intf_wlan = -1; + int num_intf = CONFIGDESC(usb)->bNumInterfaces; + struct usb_endpoint_descriptor *endpoint; + + for (i = 0; i < num_intf; i++) { + if (IFDESC(usb, i).bInterfaceClass != USB_CLASS_VENDOR_SPEC) + continue; + num_of_eps = IFDESC(usb, i).bNumEndpoints; + + for (ep = 0; ep < num_of_eps; ep++) { + endpoint = &IFEPDESC(usb, i, ep); + if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_BULK) { + intf_wlan = i; + break; + } + } + if (ep < num_of_eps) + break; + } + + return intf_wlan; +} +#endif /* BCMUSBDEV_COMPOSITE */ diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd.h index 6b413b57..eb6edcb9 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd.h +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd.h @@ -51,6 +51,9 @@ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_HAS_WAKELOCK) #include #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined (CONFIG_HAS_WAKELOCK) */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) +#include +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) */ /* The kernel threading is sdio-specific */ struct task_struct; struct sched_param; @@ -106,6 +109,7 @@ enum dhd_bus_state { DHD_BUS_DATA, /* Ready for frame transfers */ DHD_BUS_SUSPEND, /* Bus has been suspended */ DHD_BUS_DOWN_IN_PROGRESS, /* Bus going Down */ + DHD_BUS_REMOVE, /* Bus has been removed */ }; /* @@ -217,7 +221,11 @@ enum dhd_bus_state { DHD_BUS_BUSY_CHECK_RPM_SUSPEND_IN_PROGRESS(dhdp)) #define DHD_BUS_CHECK_DOWN_OR_DOWN_IN_PROGRESS(dhdp) \ - ((dhdp)->busstate == DHD_BUS_DOWN || (dhdp)->busstate == DHD_BUS_DOWN_IN_PROGRESS) + ((dhdp)->busstate == DHD_BUS_DOWN || (dhdp)->busstate == DHD_BUS_DOWN_IN_PROGRESS || \ + (dhdp)->busstate == DHD_BUS_REMOVE) + +#define DHD_BUS_CHECK_REMOVE(dhdp) \ + ((dhdp)->busstate == DHD_BUS_REMOVE) /* Macro to print Ethernet Address as String * expects both arguements as (char *) @@ -667,6 +675,9 @@ typedef struct dhd_pub { * please do NOT merge it back from other branches !!! */ +#ifdef BCMDBUS + struct dbus_pub *dbus; +#endif /* BCMDBUS */ /* Internal dhd items */ bool up; /* Driver up/down (to OS) */ @@ -1028,6 +1039,10 @@ typedef struct dhd_pub { char *clm_path; /* module_param: path to clm vars file */ char *conf_path; /* module_param: path to config vars file */ struct dhd_conf *conf; /* Bus module handle */ + void *adapter; /* adapter information, interrupt, fw path etc. */ +#ifdef BCMDBUS + bool dhd_remove; +#endif /* BCMDBUS */ } dhd_pub_t; typedef struct { @@ -1347,12 +1362,36 @@ typedef enum dhd_ioctl_recieved_status */ void dhd_net_if_lock(struct net_device *dev); void dhd_net_if_unlock(struct net_device *dev); - #if defined(MULTIPLE_SUPPLICANT) -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 -extern struct mutex _dhd_sdio_mutex_lock_; +extern void wl_android_post_init(void); // terence 20120530: fix critical section in dhd_open and dhdsdio_probe +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && defined(MULTIPLE_SUPPLICANT) +extern struct mutex _dhd_mutex_lock_; +#define DHD_MUTEX_IS_LOCK_RETURN() \ + if (mutex_is_locked(&_dhd_mutex_lock_) != 0) { \ + printf("%s : probe is already running! return.\n", __FUNCTION__); \ + return 0; \ + } +#define DHD_MUTEX_LOCK() \ + do { \ + if (mutex_is_locked(&_dhd_mutex_lock_) == 0) { \ + printf("%s : no mutex held. set lock\n", __FUNCTION__); \ + } else { \ + printf("%s : mutex is locked!. wait for unlocking\n", __FUNCTION__); \ + } \ + mutex_lock(&_dhd_mutex_lock_); \ + } while (0) +#define DHD_MUTEX_UNLOCK() \ + do { \ + mutex_unlock(&_dhd_mutex_lock_); \ + printf("%s : the lock is released.\n", __FUNCTION__); \ + } while (0) +#else +#define DHD_MUTEX_IS_LOCK_RETURN(a) do {} while (0) +#define DHD_MUTEX_LOCK(a) do {} while (0) +#define DHD_MUTEX_UNLOCK(a) do {} while (0) #endif -#endif /* MULTIPLE_SUPPLICANT */ typedef enum dhd_attach_states { @@ -1386,7 +1425,11 @@ typedef enum dhd_attach_states * Returned structure should have bus and prot pointers filled in. * bus_hdrlen specifies required headroom for bus module header. */ -extern dhd_pub_t *dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen); +extern dhd_pub_t *dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen +#ifdef BCMDBUS + , void *adapter +#endif +); #if defined(WLP2P) && defined(WL_CFG80211) /* To allow attach/detach calls corresponding to p2p0 interface */ extern int dhd_attach_p2p(dhd_pub_t *); @@ -1482,7 +1525,7 @@ extern void dhd_os_dhdiovar_lock(dhd_pub_t *pub); extern void dhd_os_dhdiovar_unlock(dhd_pub_t *pub); extern int dhd_os_proto_block(dhd_pub_t * pub); extern int dhd_os_proto_unblock(dhd_pub_t * pub); -extern int dhd_os_ioctl_resp_wait(dhd_pub_t * pub, uint * condition); +extern int dhd_os_ioctl_resp_wait(dhd_pub_t * pub, uint * condition, bool resched); extern int dhd_os_ioctl_resp_wake(dhd_pub_t * pub); extern unsigned int dhd_os_get_ioctl_resp_timeout(void); extern void dhd_os_set_ioctl_resp_timeout(unsigned int timeout_msec); @@ -1700,6 +1743,9 @@ extern int dhd_event_ifdel(struct dhd_info *dhd, struct wl_event_data_if *ifeven char *name, uint8 *mac); extern int dhd_event_ifchange(struct dhd_info *dhd, struct wl_event_data_if *ifevent, char *name, uint8 *mac); +#ifdef DHD_UPDATE_INTF_MAC +extern int dhd_op_if_update(dhd_pub_t *dhdpub, int ifidx); +#endif /* DHD_UPDATE_INTF_MAC */ extern struct net_device* dhd_allocate_if(dhd_pub_t *dhdpub, int ifidx, const char *name, uint8 *mac, uint8 bssidx, bool need_rtnl_lock, const char *dngl_name); extern int dhd_remove_if(dhd_pub_t *dhdpub, int ifidx, bool need_rtnl_lock); @@ -1821,6 +1867,9 @@ extern uint dhd_console_ms; extern uint android_msg_level; extern uint config_msg_level; extern uint sd_msglevel; +#ifdef BCMDBUS +extern uint dbus_msglevel; +#endif /* BCMDBUS */ #ifdef WL_WIRELESS_EXT extern uint iw_msg_level; #endif @@ -2031,7 +2080,9 @@ extern char fw_path2[MOD_PARAM_PATHLEN]; /* Flag to indicate if we should download firmware on driver load */ extern uint dhd_download_fw_on_driverload; +#ifndef BCMDBUS extern int allow_delay_fwdl; +#endif /* !BCMDBUS */ extern int dhd_process_cid_mac(dhd_pub_t *dhdp, bool prepost); extern int dhd_write_file(const char *filepath, char *buf, int buf_len); @@ -2226,6 +2277,12 @@ extern void dhd_os_general_spin_unlock(dhd_pub_t *pub, unsigned long flags); extern void dhd_dump_to_kernelog(dhd_pub_t *dhdp); +#ifdef BCMDBUS +extern uint dhd_get_rxsz(dhd_pub_t *pub); +extern void dhd_set_path(dhd_pub_t *pub); +extern void dhd_bus_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf); +extern void dhd_bus_clearcounts(dhd_pub_t *dhdp); +#endif /* BCMDBUS */ #ifdef DHD_L2_FILTER extern int dhd_get_parp_status(dhd_pub_t *dhdp, uint32 idx); diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_bus.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_bus.h index c785f121..e0f04833 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_bus.h +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_bus.h @@ -33,6 +33,10 @@ #ifndef _dhd_bus_h_ #define _dhd_bus_h_ +extern int dbus_up(struct dhd_bus *pub); +extern int dbus_stop(struct dhd_bus *pub); +extern int dbus_send_ctl(struct dhd_bus *pub, uint8 *buf, int len); +extern int dbus_recv_ctl(struct dhd_bus *pub, uint8 *buf, int len); /* * Exported from dhd bus module (dhd_usb, dhd_sdio) */ diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_cdc.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_cdc.c old mode 100755 new mode 100644 index 11344de2..3fb5e457 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_cdc.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_cdc.c @@ -49,6 +49,9 @@ #include #include #endif +#ifdef BCMDBUS +#include +#endif /* BCMDBUS */ #ifdef DHD_ULP #include @@ -68,15 +71,20 @@ typedef struct dhd_prot { uint16 reqid; uint8 pending; uint32 lastcmd; +#ifdef BCMDBUS + uint ctl_completed; +#endif /* BCMDBUS */ uint8 bus_header[BUS_HEADER_LEN]; cdc_ioctl_t msg; unsigned char buf[WLC_IOCTL_MAXLEN + ROUND_UP_MARGIN]; } dhd_prot_t; - static int dhdcdc_msg(dhd_pub_t *dhd) { +#ifdef BCMDBUS + int timeout = 0; +#endif /* BCMDBUS */ int err = 0; dhd_prot_t *prot = dhd->prot; int len = ltoh32(prot->msg.len) + sizeof(cdc_ioctl_t); @@ -93,8 +101,51 @@ dhdcdc_msg(dhd_pub_t *dhd) len = CDC_MAX_MSG_SIZE; /* Send request */ +#ifdef BCMDBUS + DHD_OS_IOCTL_RESP_LOCK(dhd); + prot->ctl_completed = FALSE; + err = dbus_send_ctl(dhd->bus, (void *)&prot->msg, len); + if (err) { + DHD_ERROR(("dbus_send_ctl error=%d\n", err)); + DHD_OS_IOCTL_RESP_UNLOCK(dhd); + DHD_OS_WAKE_UNLOCK(dhd); + return err; + } +#else err = dhd_bus_txctl(dhd->bus, (uchar*)&prot->msg, len); - +#endif /* BCMDBUS */ + +#ifdef BCMDBUS + timeout = dhd_os_ioctl_resp_wait(dhd, &prot->ctl_completed, false); + if ((!timeout) || (!prot->ctl_completed)) { + DHD_ERROR(("Txctl timeout %d ctl_completed %d\n", + timeout, prot->ctl_completed)); + DHD_ERROR(("Txctl wait timed out\n")); + err = -1; + } + DHD_OS_IOCTL_RESP_UNLOCK(dhd); +#endif /* BCMDBUS */ +#if defined(BCMDBUS) && defined(INTR_EP_ENABLE) + /* If the ctl write is successfully completed, wait for an acknowledgement + * that indicates that it is now ok to do ctl read from the dongle + */ + if (err != -1) { + DHD_OS_IOCTL_RESP_LOCK(dhd); + prot->ctl_completed = FALSE; + if (dbus_poll_intr(dhd->dbus)) { + DHD_ERROR(("dbus_poll_intr not submitted\n")); + } else { + /* interrupt polling is sucessfully submitted. Wait for dongle to send + * interrupt + */ + timeout = dhd_os_ioctl_resp_wait(dhd, &prot->ctl_completed, false); + if (!timeout) { + DHD_ERROR(("intr poll wait timed out\n")); + } + } + DHD_OS_IOCTL_RESP_UNLOCK(dhd); + } +#endif /* defined(BCMDBUS) && defined(INTR_EP_ENABLE) */ DHD_OS_WAKE_UNLOCK(dhd); return err; } @@ -102,6 +153,9 @@ dhdcdc_msg(dhd_pub_t *dhd) static int dhdcdc_cmplt(dhd_pub_t *dhd, uint32 id, uint32 len) { +#ifdef BCMDBUS + int timeout = 0; +#endif /* BCMDBUS */ int ret; int cdc_len = len + sizeof(cdc_ioctl_t); dhd_prot_t *prot = dhd->prot; @@ -109,11 +163,37 @@ dhdcdc_cmplt(dhd_pub_t *dhd, uint32 id, uint32 len) DHD_TRACE(("%s: Enter\n", __FUNCTION__)); do { +#ifdef BCMDBUS + DHD_OS_IOCTL_RESP_LOCK(dhd); + prot->ctl_completed = FALSE; + ret = dbus_recv_ctl(dhd->bus, (uchar*)&prot->msg, cdc_len); + if (ret) { + DHD_ERROR(("dbus_recv_ctl error=0x%x(%d)\n", ret, ret)); + DHD_OS_IOCTL_RESP_UNLOCK(dhd); + goto done; + } + timeout = dhd_os_ioctl_resp_wait(dhd, &prot->ctl_completed, false); + if ((!timeout) || (!prot->ctl_completed)) { + DHD_ERROR(("Rxctl timeout %d ctl_completed %d\n", + timeout, prot->ctl_completed)); + ret = -1; + DHD_OS_IOCTL_RESP_UNLOCK(dhd); + + goto done; + } + DHD_OS_IOCTL_RESP_UNLOCK(dhd); + + ret = cdc_len; +#else ret = dhd_bus_rxctl(dhd->bus, (uchar*)&prot->msg, cdc_len); +#endif /* BCMDBUS */ if (ret < 0) break; } while (CDC_IOC_ID(ltoh32(prot->msg.flags)) != id); +#ifdef BCMDBUS +done: +#endif /* BCMDBUS */ return ret; } @@ -286,6 +366,25 @@ dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len, uint8 return ret; } +#ifdef BCMDBUS +int +dhd_prot_ctl_complete(dhd_pub_t *dhd) +{ + dhd_prot_t *prot; + + if (dhd == NULL) + return BCME_ERROR; + + prot = dhd->prot; + + ASSERT(prot); + DHD_OS_IOCTL_RESP_LOCK(dhd); + prot->ctl_completed = TRUE; + dhd_os_ioctl_resp_wake(dhd); + DHD_OS_IOCTL_RESP_UNLOCK(dhd); + return 0; +} +#endif /* BCMDBUS */ int dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t * ioc, void * buf, int len) @@ -487,6 +586,12 @@ dhd_prot_hdrpull(dhd_pub_t *dhd, int *ifidx, void *pktbuf, uchar *reorder_buf_in dhd_wlfc_parse_header_info(dhd, pktbuf, (data_offset << 2), reorder_buf_info, reorder_info_len); +#ifdef BCMDBUS +#ifndef DHD_WLFC_THREAD + dhd_wlfc_commit_packets(dhd, + (f_commitpkt_t)dhd_bus_txdata, dhd->bus, NULL, FALSE); +#endif /* DHD_WLFC_THREAD */ +#endif /* BCMDBUS */ } #endif /* PROP_TXSTATUS */ @@ -572,6 +677,14 @@ dhd_sync_with_dongle(dhd_pub_t *dhd) ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_REVINFO, &revinfo, sizeof(revinfo), FALSE, 0); if (ret < 0) goto done; +#if defined(BCMDBUS) + if (dhd_download_fw_on_driverload) { + dhd_conf_reset(dhd); + dhd_conf_set_chiprev(dhd, revinfo.chipnum, revinfo.chiprev); + dhd_conf_preinit(dhd); + dhd_conf_read_config(dhd, dhd->conf_path); + } +#endif /* BCMDBUS */ DHD_SSSR_DUMP_INIT(dhd); diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_cfg80211.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_cfg80211.c old mode 100755 new mode 100644 index d01e7680..b98fcd36 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_cfg80211.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_cfg80211.c @@ -161,8 +161,10 @@ void dhd_netdev_free(struct net_device *ndev) #ifdef WL_CFG80211 ndev = dhd_cfg80211_netdev_free(ndev); #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) if (ndev) free_netdev(ndev); +#endif } static s32 diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_common.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_common.c index 485594e9..bbab84ae 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_common.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_common.c @@ -650,7 +650,11 @@ void* dhd_get_fwdump_buf(dhd_pub_t *dhd_pub, uint32 length) int dhd_common_socram_dump(dhd_pub_t *dhdp) { +#ifdef BCMDBUS + return 0; +#else return dhd_socram_dump(dhdp->bus); +#endif /* BCMDBUS */ } static int @@ -1038,7 +1042,7 @@ dhd_iovar_parse_bssidx(dhd_pub_t *dhd_pub, const char *params, uint32 *idx, cons return BCME_OK; } -#if defined(DHD_DEBUG) && defined(BCMDHDUSB) +#if defined(DHD_DEBUG) && defined(BCMDBUS) /* USB Device console input function */ int dhd_bus_console_in(dhd_pub_t *dhd, uchar *msg, uint msglen) { @@ -1047,7 +1051,7 @@ int dhd_bus_console_in(dhd_pub_t *dhd, uchar *msg, uint msglen) return dhd_iovar(dhd, 0, "cons", msg, msglen, NULL, 0, TRUE); } -#endif /* DHD_DEBUG && BCMDHDUSB */ +#endif /* DHD_DEBUG && BCMDBUS */ #ifdef DHD_DEBUG int @@ -1263,10 +1267,12 @@ dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const ch bcopy(&int_val, arg, val_size); break; +#ifndef BCMDBUS case IOV_GVAL(IOV_WDTICK): int_val = (int32)dhd_watchdog_ms; bcopy(&int_val, arg, val_size); break; +#endif /* !BCMDBUS */ case IOV_SVAL(IOV_WDTICK): if (!dhd_pub->up) { @@ -1285,6 +1291,7 @@ dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const ch bcmerror = dhd_dump(dhd_pub, arg, len); break; +#ifndef BCMDBUS case IOV_GVAL(IOV_DCONSOLE_POLL): int_val = (int32)dhd_console_ms; bcopy(&int_val, arg, val_size); @@ -1298,6 +1305,7 @@ dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const ch if (len > 0) bcmerror = dhd_bus_console_in(dhd_pub, arg, len - 1); break; +#endif /* !BCMDBUS */ case IOV_SVAL(IOV_CLEARCOUNTS): dhd_pub->tx_packets = dhd_pub->rx_packets = 0; @@ -1423,9 +1431,9 @@ dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const ch case IOV_GVAL(IOV_BUS_TYPE): /* The dhd application queries the driver to check if its usb or sdio. */ -#ifdef BCMDHDUSB +#ifdef BCMDBUS int_val = BUS_TYPE_USB; -#endif +#endif /* BCMDBUS */ #ifdef BCMSDIO int_val = BUS_TYPE_SDIO; #endif @@ -1952,6 +1960,8 @@ dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const ch break; } #endif /* REPORT_FATAL_TIMEOUTS */ +#ifdef DHD_DEBUG +#if defined(BCMSDIO) || defined(BCMPCIE) case IOV_GVAL(IOV_DONGLE_TRAP_TYPE): if (dhd_pub->dongle_trap_occured) int_val = ltoh32(dhd_pub->last_trap_info.type); @@ -1971,8 +1981,6 @@ dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const ch dhd_bus_dump_trap_info(dhd_pub->bus, &strbuf); break; } -#ifdef DHD_DEBUG -#if defined(BCMSDIO) || defined(BCMPCIE) case IOV_GVAL(IOV_BPADDR): { @@ -2820,12 +2828,14 @@ dngl_host_event_process(dhd_pub_t *dhdp, bcm_dngl_event_t *event, #ifdef DHD_FW_COREDUMP dhdp->memdump_type = DUMP_TYPE_DONGLE_HOST_EVENT; #endif /* DHD_FW_COREDUMP */ +#ifndef BCMDBUS if (dhd_socram_dump(dhdp->bus)) { DHD_ERROR(("%s: socram dump failed\n", __FUNCTION__)); } else { /* Notify framework */ dhd_dbg_send_urgent_evt(dhdp, p, datalen); } +#endif /* !BCMDBUS */ } #endif /* DNGL_EVENT_SUPPORT */ @@ -3113,6 +3123,7 @@ wl_process_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata, uint pktlen dhd_ifname2idx(dhd_pub->info, event->ifname), &event->addr.octet); break; +#ifndef BCMDBUS #if defined(DHD_FW_COREDUMP) case WLC_E_PSM_WATCHDOG: DHD_ERROR(("%s: WLC_E_PSM_WATCHDOG event received : \n", __FUNCTION__)); @@ -3121,6 +3132,7 @@ wl_process_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata, uint pktlen } break; #endif +#endif /* !BCMDBUS */ #ifdef DHD_WMF case WLC_E_PSTA_PRIMARY_INTF_IND: dhd_update_psta_interface_for_sta(dhd_pub, event->ifname, @@ -3187,6 +3199,14 @@ wl_process_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata, uint pktlen default: *ifidx = dhd_ifname2idx(dhd_pub->info, event->ifname); +#ifdef DHD_UPDATE_INTF_MAC + if ((WLC_E_LINK==type)&&(WLC_EVENT_MSG_LINK&flags)) { + dhd_event_ifchange(dhd_pub->info, + (struct wl_event_data_if *)event, + event->ifname, + event->addr.octet); + } +#endif /* DHD_UPDATE_INTF_MAC */ /* push up to external supp/auth */ dhd_event(dhd_pub->info, (char *)pvt_data, evlen, *ifidx); DHD_TRACE(("%s: MAC event %d, flags %x, status %x\n", @@ -3580,7 +3600,7 @@ dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg) htod16(WL_PKT_FILTER_MFLAG_NEG); (argv[i])++; } - if (strlen(argv[i]) == 0) { + if (*argv[i] == '\0') { printf("Pattern not provided\n"); goto fail; } @@ -4271,6 +4291,8 @@ dhd_get_suspend_bcn_li_dtim(dhd_pub_t *dhd) } } + if (dhd->conf->suspend_bcn_li_dtim >= 0) + bcn_li_dtim = dhd->conf->suspend_bcn_li_dtim; DHD_ERROR(("%s beacon=%d bcn_li_dtim=%d DTIM=%d Listen=%d\n", __FUNCTION__, ap_beacon, bcn_li_dtim, dtim_period, CUSTOM_LISTEN_INTERVAL)); @@ -4865,7 +4887,7 @@ dhd_apply_default_clm(dhd_pub_t *dhd, char *clm_path) char iovbuf[WLC_IOCTL_SMLEN] = {0}; int status = FALSE; - if (clm_path[0] != '\0') { + if (clm_path && clm_path[0] != '\0') { if (strlen(clm_path) > MOD_PARAM_PATHLEN) { DHD_ERROR(("clm path exceeds max len\n")); return BCME_ERROR; diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_config.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_config.c index 47480b26..4f333a46 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_config.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_config.c @@ -1,5 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - +/* SPDX-License-Identifier: GPL-2.0 */ #include #include @@ -40,66 +39,6 @@ uint config_msg_level = CONFIG_ERROR_LEVEL; #define MAXSZ_BUF 1000 #define MAXSZ_CONFIG 4096 -#define FW_TYPE_STA 0 -#define FW_TYPE_APSTA 1 -#define FW_TYPE_P2P 2 -#define FW_TYPE_ES 3 -#define FW_TYPE_MFG 4 -#define FW_TYPE_G 0 -#define FW_TYPE_AG 1 - -#ifdef CONFIG_PATH_AUTO_SELECT -#ifdef BCMSDIO -#define CONFIG_BCM4330B2 "config_40183b2.txt" -#define CONFIG_BCM43362A0 "config_40181a0.txt" -#define CONFIG_BCM43362A2 "config_40181a2.txt" -#define CONFIG_BCM43438A0 "config_43438a0.txt" -#define CONFIG_BCM43438A1 "config_43438a1.txt" -#define CONFIG_BCM43436B0 "config_43436b0.txt" -#define CONFIG_BCM4334B1 "config_4334b1.txt" -#define CONFIG_BCM43341B0 "config_43341b0.txt" -#define CONFIG_BCM43241B4 "config_43241b4.txt" -#define CONFIG_BCM4339A0 "config_4339a0.txt" -#define CONFIG_BCM43454C0 "config_43454c0.txt" -#define CONFIG_BCM43455C0 "config_43455c0.txt" -#define CONFIG_BCM43456C5 "config_43456c5.txt" -#define CONFIG_BCM4354A1 "config_4354a1.txt" -#endif -#define CONFIG_BCM4356A2 "config_4356a2.txt" -#define CONFIG_BCM4358A3 "config_4358a3.txt" -#define CONFIG_BCM4359B1 "config_4359b1.txt" -#define CONFIG_BCM4359C0 "config_4359c0.txt" -#endif - -#ifdef BCMSDIO -#define SBSDIO_CIS_SIZE_LIMIT 0x200 - -#define FW_BCM4330B2 "fw_RK903b2" -#define FW_BCM4330B2_AG "fw_RK903_ag" -#define FW_BCM43362A0 "fw_RK901a0" -#define FW_BCM43362A2 "fw_RK901a2" -#define FW_BCM4334B1 "fw_bcm4334b1_ag" -#define FW_BCM43438A0 "fw_bcm43438a0" -#define FW_BCM43438A1 "fw_bcm43438a1" -#define FW_BCM43436B0 "fw_bcm43436b0" -#define FW_BCM43012B0 "fw_bcm43012b0" -#define FW_BCM43341B1 "fw_bcm43341b0_ag" -#define FW_BCM43241B4 "fw_bcm43241b4_ag" -#define FW_BCM4339A0 "fw_bcm4339a0_ag" -#define FW_BCM43455C0 "fw_bcm43455c0_ag" -#define FW_BCM43456C5 "fw_bcm43456c5_ag" -#define FW_BCM4354A1 "fw_bcm4354a1_ag" -#define FW_BCM4356A2 "fw_bcm4356a2_ag" -#define FW_BCM4358A3 "fw_bcm4358a3_ag" -#define FW_BCM4359B1 "fw_bcm4359b1_ag" -#define FW_BCM4359C0 "fw_bcm4359c0_ag" - -#define CLM_BCM43012B0 "clm_bcm43012b0" -#endif -#ifdef BCMPCIE -#define FW_BCM4356A2 "fw_bcm4356a2_pcie_ag" -#endif - #define htod32(i) i #define htod16(i) i #define dtoh32(i) i @@ -107,6 +46,61 @@ uint config_msg_level = CONFIG_ERROR_LEVEL; #define htodchanspec(i) i #define dtohchanspec(i) i +typedef struct cihp_name_map_t { + uint chip; + uint chiprev; + uint ag_type; + bool clm; + char *chip_name; + char *module_name; +} cihp_name_map_t; + +/* Map of WLC_E events to connection failure strings */ +#define DONT_CARE 9999 +const cihp_name_map_t chip_name_map [] = { + /* ChipID Chiprev AG CLM ChipName ModuleName */ +#ifdef BCMSDIO + {BCM43362_CHIP_ID, 0, DONT_CARE, FALSE, "RK901a0", ""}, + //{BCM43362_CHIP_ID, 1, DONT_CARE, FALSE, "RK901a2", "nvram_AP6210.txt"}, + {BCM43362_CHIP_ID, 1, DONT_CARE, FALSE, "bcm40181a2", "nvram_ap6181.txt"}, + {BCM4330_CHIP_ID, 4, FW_TYPE_G, FALSE, "RK903b2", ""}, + {BCM4330_CHIP_ID, 4, FW_TYPE_AG, FALSE, "RK903_ag", "nvram_AP6330.txt"}, + {BCM43430_CHIP_ID, 0, DONT_CARE, FALSE, "bcm43438a0", "nvram_ap6212.txt"}, + {BCM43430_CHIP_ID, 1, DONT_CARE, FALSE, "bcm43438a1", "nvram_ap6212a.txt"}, + {BCM43430_CHIP_ID, 2, DONT_CARE, FALSE, "bcm43436b0", "nvram_ap6236.txt"}, + {BCM43012_CHIP_ID, 1, DONT_CARE, TRUE, "bcm43013b0", ""}, + {BCM4334_CHIP_ID, 3, DONT_CARE, FALSE, "bcm4334b1_ag", ""}, + {BCM43340_CHIP_ID, 2, DONT_CARE, FALSE, "bcm43341b0_ag", ""}, + {BCM43341_CHIP_ID, 2, DONT_CARE, FALSE, "bcm43341b0_ag", ""}, + {BCM4324_CHIP_ID, 5, DONT_CARE, FALSE, "bcm43241b4_ag", "nvram_ap62x2.txt"}, + {BCM4335_CHIP_ID, 2, DONT_CARE, FALSE, "bcm4339a0_ag", "nvram_AP6335.txt"}, + {BCM4339_CHIP_ID, 1, DONT_CARE, FALSE, "bcm4339a0_ag", "nvram_AP6335.txt"}, + {BCM4345_CHIP_ID, 6, DONT_CARE, FALSE, "bcm43455c0_ag", "nvram_ap6255.txt"}, + {BCM43454_CHIP_ID, 6, DONT_CARE, FALSE, "bcm43455c0_ag", ""}, + {BCM4345_CHIP_ID, 9, DONT_CARE, FALSE, "bcm43456c5_ag", "nvram_ap6256.txt"}, + {BCM43454_CHIP_ID, 9, DONT_CARE, FALSE, "bcm43456c5_ag", ""}, + {BCM4354_CHIP_ID, 1, DONT_CARE, FALSE, "bcm4354a1_ag", "nvram_ap6354.txt"}, + {BCM4354_CHIP_ID, 2, DONT_CARE, FALSE, "bcm4356a2_ag", "nvram_ap6356.txt"}, + {BCM4356_CHIP_ID, 2, DONT_CARE, FALSE, "bcm4356a2_ag", "nvram_ap6356.txt"}, + {BCM4371_CHIP_ID, 2, DONT_CARE, FALSE, "bcm4356a2_ag", ""}, + {BCM43569_CHIP_ID, 3, DONT_CARE, FALSE, "bcm4358a3_ag", ""}, + {BCM4359_CHIP_ID, 5, DONT_CARE, FALSE, "bcm4359b1_ag", ""}, + {BCM4359_CHIP_ID, 9, DONT_CARE, FALSE, "bcm4359c0_ag", "nvram_ap6398s.txt"}, + {BCM4362_CHIP_ID, 0, DONT_CARE, TRUE, "bcm43752a0_ag", ""}, +#endif +#ifdef BCMPCIE + {BCM4354_CHIP_ID, 2, DONT_CARE, FALSE, "bcm4356a2_pcie_ag", ""}, + {BCM4356_CHIP_ID, 2, DONT_CARE, FALSE, "bcm4356a2_pcie_ag", ""}, + {BCM4359_CHIP_ID, 9, DONT_CARE, FALSE, "bcm4359c0_pcie_ag", ""}, + {BCM4362_CHIP_ID, 0, DONT_CARE, TRUE, "bcm43752a0_pcie_ag", ""}, +#endif +#ifdef BCMDBUS + {BCM43143_CHIP_ID, 2, DONT_CARE, FALSE, "bcm43143b0", ""}, + {BCM43242_CHIP_ID, 1, DONT_CARE, FALSE, "bcm43242a1_ag", ""}, + {BCM43569_CHIP_ID, 2, DONT_CARE, FALSE, "bcm4358u_ag", ""}, +#endif +}; + #ifdef BCMSDIO void dhd_conf_free_mac_list(wl_mac_list_ctrl_t *mac_list) @@ -158,6 +152,7 @@ dhd_conf_set_hw_oob_intr(bcmsdh_info_t *sdh, uint chip) } #endif +#define SBSDIO_CIS_SIZE_LIMIT 0x200 #define F0_BLOCK_SIZE 32 int dhd_conf_set_blksize(bcmsdh_info_t *sdh) @@ -168,7 +163,7 @@ dhd_conf_set_blksize(bcmsdh_info_t *sdh) uint8 cisd; numfn = bcmsdh_query_iofnum(sdh); - + for (fn = 0; fn <= numfn; fn++) { if (!fn) blksize = F0_BLOCK_SIZE; @@ -290,8 +285,8 @@ dhd_conf_set_fw_name_by_mac(dhd_pub_t *dhd, bcmsdh_info_t *sdh, char *fw_path) uint32 oui, nic; wl_mac_list_t *mac_list; wl_mac_range_t *mac_range; - char *pfw_name; int fw_type, fw_type_new; + char *name_ptr; mac_list = dhd->conf->fw_by_mac.m_mac_list_head; fw_num = dhd->conf->fw_by_mac.count; @@ -308,22 +303,42 @@ dhd_conf_set_fw_name_by_mac(dhd_pub_t *dhd, bcmsdh_info_t *sdh, char *fw_path) /* find out the last '/' */ i = strlen(fw_path); while (i > 0) { - if (fw_path[i] == '/') break; + if (fw_path[i] == '/') { + i++; + break; + } i--; } - pfw_name = &fw_path[i+1]; - fw_type = (strstr(pfw_name, "_mfg") ? - FW_TYPE_MFG : (strstr(pfw_name, "_apsta") ? - FW_TYPE_APSTA : (strstr(pfw_name, "_p2p") ? - FW_TYPE_P2P : FW_TYPE_STA))); + name_ptr = &fw_path[i]; + + if (strstr(name_ptr, "_apsta")) + fw_type = FW_TYPE_APSTA; + else if (strstr(name_ptr, "_p2p")) + fw_type = FW_TYPE_P2P; + else if (strstr(name_ptr, "_mesh")) + fw_type = FW_TYPE_MESH; + else if (strstr(name_ptr, "_es")) + fw_type = FW_TYPE_ES; + else if (strstr(name_ptr, "_mfg")) + fw_type = FW_TYPE_MFG; + else + fw_type = FW_TYPE_STA; for (i=0; i= mac_range[j].nic_start && nic <= mac_range[j].nic_end) { - strcpy(pfw_name, mac_list[i].name); + strcpy(name_ptr, mac_list[i].name); printf("%s: matched oui=0x%06X, nic=0x%06X\n", __FUNCTION__, oui, nic); printf("%s: fw_path=%s\n", __FUNCTION__, fw_path); @@ -392,12 +407,27 @@ dhd_conf_set_nv_name_by_mac(dhd_pub_t *dhd, bcmsdh_info_t *sdh, char *nv_path) #endif void -dhd_conf_set_fw_name_by_chip(dhd_pub_t *dhd, char *fw_path, char *nv_path) +dhd_conf_free_country_list(conf_country_list_t *country_list) +{ + int i; + + CONFIG_TRACE(("%s called\n", __FUNCTION__)); + for (i=0; icount; i++) { + if (country_list->cspec[i]) { + CONFIG_TRACE(("%s Free cspec %p\n", __FUNCTION__, country_list->cspec[i])); + kfree(country_list->cspec[i]); + } + } + country_list->count = 0; +} + +void +dhd_conf_set_fw_name_by_chip(dhd_pub_t *dhd, char *fw_path) { int fw_type, ag_type; uint chip, chiprev; - int i, j; - char fw_tail[20]; + int i; + char *name_ptr; chip = dhd->conf->chip; chiprev = dhd->conf->chiprev; @@ -419,143 +449,53 @@ dhd_conf_set_fw_name_by_chip(dhd_pub_t *dhd, char *fw_path, char *nv_path) /* find out the last '/' */ i = strlen(fw_path); while (i > 0) { - if (fw_path[i] == '/') break; - i--; - } - j = strlen(nv_path); - while (j > 0) { - if (nv_path[j] == '/') + if (fw_path[i] == '/') { + i++; break; - j--; + } + i--; } + name_ptr = &fw_path[i]; #ifdef BAND_AG ag_type = FW_TYPE_AG; #else - ag_type = strstr(&fw_path[i], "_ag") ? FW_TYPE_AG : FW_TYPE_G; + ag_type = strstr(name_ptr, "_ag") ? FW_TYPE_AG : FW_TYPE_G; #endif - fw_type = (strstr(&fw_path[i], "_mfg") ? FW_TYPE_MFG : - (strstr(&fw_path[i], "_apsta") ? FW_TYPE_APSTA : - (strstr(&fw_path[i], "_p2p") ? FW_TYPE_P2P : - (strstr(&fw_path[i], "_es") ? FW_TYPE_ES : - FW_TYPE_STA)))); - - if (fw_type == FW_TYPE_STA) - strcpy(fw_tail, ".bin"); - else if (fw_type == FW_TYPE_APSTA) - strcpy(fw_tail, "_apsta.bin"); - else if (fw_type == FW_TYPE_P2P) - strcpy(fw_tail, "_p2p.bin"); - else if (fw_type == FW_TYPE_ES) - strcpy(fw_tail, "_es.bin"); - else if (fw_type == FW_TYPE_MFG) - strcpy(fw_tail, "_mfg.bin"); - - switch (chip) { -#ifdef BCMSDIO - case BCM4330_CHIP_ID: - if (ag_type == FW_TYPE_G) { - if (chiprev == BCM4330B2_CHIP_REV) - strcpy(&fw_path[i+1], FW_BCM4330B2); - } else { - if (chiprev == BCM4330B2_CHIP_REV) - strcpy(&fw_path[i+1], FW_BCM4330B2_AG); - strcpy(&nv_path[j + 1], "nvram_AP6330.txt"); - } - break; - case BCM43362_CHIP_ID: - if (chiprev == BCM43362A0_CHIP_REV) - strcpy(&fw_path[i+1], FW_BCM43362A0); + if (strstr(name_ptr, "_apsta")) + fw_type = FW_TYPE_APSTA; + else if (strstr(name_ptr, "_p2p")) + fw_type = FW_TYPE_P2P; + else if (strstr(name_ptr, "_mesh")) + fw_type = FW_TYPE_MESH; + else if (strstr(name_ptr, "_es")) + fw_type = FW_TYPE_ES; + else if (strstr(name_ptr, "_mfg")) + fw_type = FW_TYPE_MFG; + else + fw_type = FW_TYPE_STA; + + for (i = 0; i < sizeof(chip_name_map)/sizeof(chip_name_map[0]); i++) { + const cihp_name_map_t* row = &chip_name_map[i]; + if (row->chip == chip && row->chiprev == chiprev && + (row->ag_type == ag_type || row->ag_type == DONT_CARE)) { + strcpy(name_ptr, "fw_"); + strcat(fw_path, row->chip_name); + if (fw_type == FW_TYPE_APSTA) + strcat(fw_path, "_apsta.bin"); + else if (fw_type == FW_TYPE_P2P) + strcat(fw_path, "_p2p.bin"); + else if (fw_type == FW_TYPE_MESH) + strcat(fw_path, "_mesh.bin"); + else if (fw_type == FW_TYPE_ES) + strcat(fw_path, "_es.bin"); + else if (fw_type == FW_TYPE_MFG) + strcat(fw_path, "_mfg.bin"); else - strcpy(&fw_path[i+1], FW_BCM43362A2); - if (!strstr(nv_path, "6476")) - strcpy(&nv_path[j + 1], "nvram_AP6210.txt"); - break; - case BCM43430_CHIP_ID: - if (chiprev == BCM43430A0_CHIP_REV) { - strcpy(&fw_path[i+1], FW_BCM43438A0); - strcpy(&nv_path[j + 1], "nvram_ap6212.txt"); - } else if (chiprev == BCM43430A1_CHIP_REV) { - strcpy(&fw_path[i+1], FW_BCM43438A1); - strcpy(&nv_path[j + 1], "nvram_ap6212a.txt"); - } else if (chiprev == BCM43430A2_CHIP_REV) { - strcpy(&fw_path[i+1], FW_BCM43436B0); - strcpy(&nv_path[j + 1], "nvram_ap6236.txt"); - } - break; - case BCM43012_CHIP_ID: - if (chiprev == BCM43012B0_CHIP_REV) - strcpy(&fw_path[i+1], FW_BCM43012B0); - break; - case BCM4334_CHIP_ID: - if (chiprev == BCM4334B1_CHIP_REV) - strcpy(&fw_path[i+1], FW_BCM4334B1); - break; - case BCM43340_CHIP_ID: - case BCM43341_CHIP_ID: - if (chiprev == BCM43341B0_CHIP_REV) - strcpy(&fw_path[i+1], FW_BCM43341B1); - break; - case BCM4324_CHIP_ID: - if (chiprev == BCM43241B4_CHIP_REV) - strcpy(&fw_path[i+1], FW_BCM43241B4); - strcpy(&nv_path[j + 1], "nvram_ap62x2.txt"); - break; - case BCM4335_CHIP_ID: - if (chiprev == BCM4335A0_CHIP_REV) - strcpy(&fw_path[i+1], FW_BCM4339A0); - strcpy(&nv_path[j + 1], "nvram_AP6335.txt"); - break; - case BCM4339_CHIP_ID: - if (chiprev == BCM4339A0_CHIP_REV) - strcpy(&fw_path[i+1], FW_BCM4339A0); - strcpy(&nv_path[j + 1], "nvram_AP6335.txt"); - break; - case BCM4345_CHIP_ID: - case BCM43454_CHIP_ID: - if (chiprev == BCM43455C0_CHIP_REV) { - strcpy(&fw_path[i+1], FW_BCM43455C0); - strcpy(&nv_path[j + 1], "nvram_ap6255.txt"); - } else if (chiprev == BCM43456C5_CHIP_REV) { - strcpy(&fw_path[i+1], FW_BCM43456C5); - } - break; - case BCM4354_CHIP_ID: - if (chiprev == BCM4354A1_CHIP_REV) { - strcpy(&fw_path[i+1], FW_BCM4354A1); - strcpy(&nv_path[j + 1], "nvram_ap6354.txt"); - } else if (chiprev == BCM4356A2_CHIP_REV) { - strcpy(&fw_path[i+1], FW_BCM4356A2); - strcpy(&nv_path[j + 1], "nvram_ap6356.txt"); - } - break; - case BCM4356_CHIP_ID: - case BCM4371_CHIP_ID: - if (chiprev == BCM4356A2_CHIP_REV) - strcpy(&fw_path[i+1], FW_BCM4356A2); - strcpy(&nv_path[j + 1], "nvram_ap6356.txt"); - break; - case BCM43569_CHIP_ID: - if (chiprev == BCM4358A3_CHIP_REV) - strcpy(&fw_path[i+1], FW_BCM4358A3); - break; - case BCM4359_CHIP_ID: - if (chiprev == BCM4359B1_CHIP_REV) - strcpy(&fw_path[i+1], FW_BCM4359B1); - else if (chiprev == BCM4359C0_CHIP_REV) - strcpy(&fw_path[i+1], FW_BCM4359C0); - break; -#endif -#ifdef BCMPCIE - case BCM4354_CHIP_ID: - case BCM4356_CHIP_ID: - if (chiprev == BCM4356A2_CHIP_REV) - strcpy(&fw_path[i+1], FW_BCM4356A2); - break; -#endif - default: - strcpy(&fw_path[i+1], "fw_bcmdhd"); + strcat(fw_path, ".bin"); + } } - strcat(fw_path, fw_tail); + + dhd->conf->fw_type = fw_type; CONFIG_TRACE(("%s: firmware_path=%s\n", __FUNCTION__, fw_path)); } @@ -565,7 +505,7 @@ dhd_conf_set_clm_name_by_chip(dhd_pub_t *dhd, char *clm_path) { uint chip, chiprev; int i; - char fw_tail[20]; + char *name_ptr; chip = dhd->conf->chip; chiprev = dhd->conf->chiprev; @@ -578,23 +518,22 @@ dhd_conf_set_clm_name_by_chip(dhd_pub_t *dhd, char *clm_path) /* find out the last '/' */ i = strlen(clm_path); while (i > 0) { - if (clm_path[i] == '/') break; + if (clm_path[i] == '/') { + i++; + break; + } i--; } + name_ptr = &clm_path[i]; - strcpy(fw_tail, ".blob"); - - switch (chip) { -#ifdef BCMSDIO - case BCM43012_CHIP_ID: - if (chiprev == BCM43012B0_CHIP_REV) - strcpy(&clm_path[i+1], CLM_BCM43012B0); - break; -#endif - default: - strcpy(&clm_path[i+1], "clm_bcmdhd"); + for (i = 0; i < sizeof(chip_name_map)/sizeof(chip_name_map[0]); i++) { + const cihp_name_map_t* row = &chip_name_map[i]; + if (row->chip == chip && row->chiprev == chiprev && row->clm) { + strcpy(name_ptr, "clm_"); + strcat(clm_path, row->chip_name); + strcat(clm_path, ".blob"); + } } - strcat(clm_path, fw_tail); CONFIG_TRACE(("%s: clm_path=%s\n", __FUNCTION__, clm_path)); } @@ -602,23 +541,13 @@ dhd_conf_set_clm_name_by_chip(dhd_pub_t *dhd, char *clm_path) void dhd_conf_set_nv_name_by_chip(dhd_pub_t *dhd, char *nv_path) { - int matched=-1; uint chip, chiprev; int i; + char *name_ptr; chip = dhd->conf->chip; chiprev = dhd->conf->chiprev; - for (i=0; iconf->nv_by_chip.count; i++) { - if (chip==dhd->conf->nv_by_chip.m_chip_nv_path_head[i].chip && - chiprev==dhd->conf->nv_by_chip.m_chip_nv_path_head[i].chiprev) { - matched = i; - break; - } - } - if (matched < 0) - return; - if (nv_path[0] == '\0') { #ifdef CONFIG_BCMDHD_NVRAM_PATH bcm_strncpy_s(nv_path, MOD_PARAM_PATHLEN-1, CONFIG_BCMDHD_NVRAM_PATH, MOD_PARAM_PATHLEN-1); @@ -633,11 +562,28 @@ dhd_conf_set_nv_name_by_chip(dhd_pub_t *dhd, char *nv_path) /* find out the last '/' */ i = strlen(nv_path); while (i > 0) { - if (nv_path[i] == '/') break; + if (nv_path[i] == '/') { + i++; + break; + } i--; } + name_ptr = &nv_path[i]; + + for (i = 0; i < sizeof(chip_name_map)/sizeof(chip_name_map[0]); i++) { + const cihp_name_map_t* row = &chip_name_map[i]; + if (row->chip == chip && row->chiprev == chiprev && strlen(row->module_name)) { + strcpy(name_ptr, row->module_name); + } + } - strcpy(&nv_path[i+1], dhd->conf->nv_by_chip.m_chip_nv_path_head[matched].name); + for (i=0; iconf->nv_by_chip.count; i++) { + if (chip==dhd->conf->nv_by_chip.m_chip_nv_path_head[i].chip && + chiprev==dhd->conf->nv_by_chip.m_chip_nv_path_head[i].chiprev) { + strcpy(name_ptr, dhd->conf->nv_by_chip.m_chip_nv_path_head[i].name); + break; + } + } CONFIG_TRACE(("%s: nvram_path=%s\n", __FUNCTION__, nv_path)); } @@ -656,10 +602,13 @@ dhd_conf_set_path(dhd_pub_t *dhd, char *dst_name, char *dst_path, char *src_path /* find out the last '/' */ i = strlen(dst_path); while (i > 0) { - if (dst_path[i] == '/') break; + if (dst_path[i] == '/') { + i++; + break; + } i--; } - strcpy(&dst_path[i+1], dst_name); + strcpy(&dst_path[i], dst_name); CONFIG_TRACE(("%s: dst_path=%s\n", __FUNCTION__, dst_path)); } @@ -670,6 +619,7 @@ dhd_conf_set_conf_name_by_chip(dhd_pub_t *dhd, char *conf_path) { uint chip, chiprev; int i; + char *name_ptr; chip = dhd->conf->chip; chiprev = dhd->conf->chiprev; @@ -682,90 +632,21 @@ dhd_conf_set_conf_name_by_chip(dhd_pub_t *dhd, char *conf_path) /* find out the last '/' */ i = strlen(conf_path); while (i > 0) { - if (conf_path[i] == '/') break; + if (conf_path[i] == '/') { + i++; + break; + } i--; } + name_ptr = conf_path[i]; - switch (chip) { -#ifdef BCMSDIO - case BCM4330_CHIP_ID: - if (chiprev == BCM4330B2_CHIP_REV) - strcpy(&conf_path[i+1], CONFIG_BCM4330B2); - break; - case BCM43362_CHIP_ID: - if (chiprev == BCM43362A0_CHIP_REV) - strcpy(&conf_path[i+1], CONFIG_BCM43362A0); - else - strcpy(&conf_path[i+1], CONFIG_BCM43362A2); - break; - case BCM43430_CHIP_ID: - if (chiprev == BCM43430A0_CHIP_REV) - strcpy(&conf_path[i+1], CONFIG_BCM43438A0); - else if (chiprev == BCM43430A1_CHIP_REV) - strcpy(&conf_path[i+1], CONFIG_BCM43438A1); - else if (chiprev == BCM43430A2_CHIP_REV) - strcpy(&conf_path[i+1], CONFIG_BCM43436B0); - break; - case BCM4334_CHIP_ID: - if (chiprev == BCM4334B1_CHIP_REV) - strcpy(&conf_path[i+1], CONFIG_BCM4334B1); - break; - case BCM43340_CHIP_ID: - case BCM43341_CHIP_ID: - if (chiprev == BCM43341B0_CHIP_REV) - strcpy(&conf_path[i+1], CONFIG_BCM43341B0); - break; - case BCM4324_CHIP_ID: - if (chiprev == BCM43241B4_CHIP_REV) - strcpy(&conf_path[i+1], CONFIG_BCM43241B4); - break; - case BCM4335_CHIP_ID: - if (chiprev == BCM4335A0_CHIP_REV) - strcpy(&conf_path[i+1], CONFIG_BCM4339A0); - break; - case BCM43454_CHIP_ID: - if (chiprev == BCM43455C0_CHIP_REV) - strcpy(&conf_path[i+1], CONFIG_BCM43454C0); - break; - case BCM4345_CHIP_ID: - if (chiprev == BCM43455C0_CHIP_REV) - strcpy(&conf_path[i+1], CONFIG_BCM43455C0); - else if (chiprev == BCM43456C5_CHIP_REV) - strcpy(&conf_path[i+1], CONFIG_BCM43456C5); - break; - case BCM4339_CHIP_ID: - if (chiprev == BCM4339A0_CHIP_REV) - strcpy(&conf_path[i+1], CONFIG_BCM4339A0); - break; - case BCM4354_CHIP_ID: - if (chiprev == BCM4354A1_CHIP_REV) - strcpy(&conf_path[i+1], CONFIG_BCM4354A1); - else if (chiprev == BCM4356A2_CHIP_REV) - strcpy(&conf_path[i+1], CONFIG_BCM4356A2); - break; - case BCM4356_CHIP_ID: - case BCM4371_CHIP_ID: - if (chiprev == BCM4356A2_CHIP_REV) - strcpy(&conf_path[i+1], CONFIG_BCM4356A2); - break; - case BCM43569_CHIP_ID: - if (chiprev == BCM4358A3_CHIP_REV) - strcpy(&conf_path[i+1], CONFIG_BCM4358A3); - break; - case BCM4359_CHIP_ID: - if (chiprev == BCM4359B1_CHIP_REV) - strcpy(&conf_path[i+1], CONFIG_BCM4359B1); - else if (chiprev == BCM4359C0_CHIP_REV) - strcpy(&conf_path[i+1], CONFIG_BCM4359C0); - break; -#endif -#ifdef BCMPCIE - case BCM4354_CHIP_ID: - case BCM4356_CHIP_ID: - if (chiprev == BCM4356A2_CHIP_REV) - strcpy(&conf_path[i+1], CONFIG_BCM4356A2); - break; -#endif + for (i = 0; i < sizeof(chip_name_map)/sizeof(chip_name_map[0]); i++) { + const cihp_name_map_t* row = &chip_name_map[i]; + if (row->chip == chip && row->chiprev == chiprev) { + strcpy(name_ptr, "config_"); + strcat(conf_path, row->chip_name); + strcat(conf_path, ".txt"); + } } CONFIG_TRACE(("%s: config_path=%s\n", __FUNCTION__, conf_path)); @@ -785,12 +666,12 @@ dhd_conf_set_intiovar(dhd_pub_t *dhd, uint cmd, char *name, int val, CONFIG_ERROR(("%s: WLC_DOWN setting failed %d\n", __FUNCTION__, ret)); } if (cmd == WLC_SET_VAR) { - printf("%s: set %s %d\n", __FUNCTION__, name, val); + CONFIG_TRACE(("%s: set %s %d\n", __FUNCTION__, name, val)); bcm_mkiovar(name, (char *)&val, sizeof(val), iovbuf, sizeof(iovbuf)); if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) CONFIG_ERROR(("%s: %s setting failed %d\n", __FUNCTION__, name, ret)); } else { - printf("%s: set %s %d %d\n", __FUNCTION__, name, cmd, val); + CONFIG_TRACE(("%s: set %s %d %d\n", __FUNCTION__, name, cmd, val)); if ((ret = dhd_wl_ioctl_cmd(dhd, cmd, &val, sizeof(val), TRUE, 0)) < 0) CONFIG_ERROR(("%s: %s setting failed %d\n", __FUNCTION__, name, ret)); } @@ -852,7 +733,7 @@ dhd_conf_get_iovar(dhd_pub_t *dhd, int cmd, char *name, char *buf, int len, int uint dhd_conf_get_band(dhd_pub_t *dhd) { - uint band = WLC_BAND_AUTO; + int band = -1; if (dhd && dhd->conf) band = dhd->conf->band; @@ -862,19 +743,6 @@ dhd_conf_get_band(dhd_pub_t *dhd) return band; } -int -dhd_conf_set_country(dhd_pub_t *dhd) -{ - int bcmerror = -1; - - memset(&dhd->dhd_cspec, 0, sizeof(wl_country_t)); - printf("%s: set country %s, revision %d\n", __FUNCTION__, - dhd->conf->cspec.ccode, dhd->conf->cspec.rev); - dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "country", (char *)&dhd->conf->cspec, sizeof(wl_country_t), FALSE); - - return bcmerror; -} - int dhd_conf_get_country(dhd_pub_t *dhd, wl_country_t *cspec) { @@ -884,23 +752,28 @@ dhd_conf_get_country(dhd_pub_t *dhd, wl_country_t *cspec) bcm_mkiovar("country", NULL, 0, (char*)cspec, sizeof(wl_country_t)); if ((bcmerror = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, cspec, sizeof(wl_country_t), FALSE, 0)) < 0) CONFIG_ERROR(("%s: country code getting failed %d\n", __FUNCTION__, bcmerror)); - else - printf("Country code: %s (%s/%d)\n", cspec->country_abbrev, cspec->ccode, cspec->rev); return bcmerror; } int -dhd_conf_get_country_from_config(dhd_pub_t *dhd, wl_country_t *cspec) +dhd_conf_map_country_list(dhd_pub_t *dhd, wl_country_t *cspec, int nodfs) { int bcmerror = -1, i; struct dhd_conf *conf = dhd->conf; + conf_country_list_t *country_list; - for (i = 0; i < conf->country_list.count; i++) { - if (!strncmp(cspec->country_abbrev, conf->country_list.cspec[i].country_abbrev, 2)) { - memcpy(cspec->ccode, - conf->country_list.cspec[i].ccode, WLC_CNTRY_BUF_SZ); - cspec->rev = conf->country_list.cspec[i].rev; + if ((nodfs > 0 || dhd->op_mode & DHD_FLAG_HOSTAP_MODE) && + conf->country_list_nodfs.count > 0) { + country_list = &conf->country_list_nodfs; + } else { + country_list = &conf->country_list; + } + + for (i = 0; i < country_list->count; i++) { + if (!strncmp(cspec->country_abbrev, country_list->cspec[i]->country_abbrev, 2)) { + memcpy(cspec->ccode, country_list->cspec[i]->ccode, WLC_CNTRY_BUF_SZ); + cspec->rev = country_list->cspec[i]->rev; printf("%s: %s/%d\n", __FUNCTION__, cspec->ccode, cspec->rev); return 0; } @@ -909,6 +782,21 @@ dhd_conf_get_country_from_config(dhd_pub_t *dhd, wl_country_t *cspec) return bcmerror; } +int +dhd_conf_set_country(dhd_pub_t *dhd, wl_country_t *cspec) +{ + int bcmerror = -1; + + memset(&dhd->dhd_cspec, 0, sizeof(wl_country_t)); + + printf("%s: set country %s, revision %d\n", __FUNCTION__, cspec->ccode, cspec->rev); + dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "country", (char *)cspec, sizeof(wl_country_t), FALSE); + dhd_conf_get_country(dhd, cspec); + printf("Country code: %s (%s/%d)\n", cspec->country_abbrev, cspec->ccode, cspec->rev); + + return bcmerror; +} + int dhd_conf_fix_country(dhd_pub_t *dhd) { @@ -916,6 +804,7 @@ dhd_conf_fix_country(dhd_pub_t *dhd) uint band; wl_uint32_list_t *list; u8 valid_chan_list[sizeof(u32)*(WL_NUMCHANNELS + 1)]; + wl_country_t cspec; if (!(dhd && dhd->conf)) { return bcmerror; @@ -934,11 +823,13 @@ dhd_conf_fix_country(dhd_pub_t *dhd) dtoh32(list->count)<11)) { CONFIG_ERROR(("%s: bcmerror=%d, # of channels %d\n", __FUNCTION__, bcmerror, dtoh32(list->count))); - if ((bcmerror = dhd_conf_set_country(dhd)) < 0) { - strcpy(dhd->conf->cspec.country_abbrev, "US"); - dhd->conf->cspec.rev = 0; - strcpy(dhd->conf->cspec.ccode, "US"); - dhd_conf_set_country(dhd); + dhd_conf_map_country_list(dhd, &dhd->conf->cspec, 0); + if ((bcmerror = dhd_conf_set_country(dhd, &dhd->conf->cspec)) < 0) { + strcpy(cspec.country_abbrev, "US"); + cspec.rev = 0; + strcpy(cspec.ccode, "US"); + dhd_conf_map_country_list(dhd, &cspec, 0); + dhd_conf_set_country(dhd, &cspec); } } @@ -1002,17 +893,19 @@ dhd_conf_set_bw_cap(dhd_pub_t *dhd) u32 bw_cap; } param = {0, 0}; - if (dhd->conf->bw_cap_2g >= 0) { + if (dhd->conf->bw_cap[0] >= 0) { + memset(¶m, 0, sizeof(param)); param.band = WLC_BAND_2G; - param.bw_cap = (uint)dhd->conf->bw_cap_2g; - printf("%s: set bw_cap 2g %d\n", __FUNCTION__, param.bw_cap); + param.bw_cap = (uint)dhd->conf->bw_cap[0]; + printf("%s: set bw_cap 2g 0x%x\n", __FUNCTION__, param.bw_cap); dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "bw_cap", (char *)¶m, sizeof(param), TRUE); } - if (dhd->conf->bw_cap_5g >= 0) { + if (dhd->conf->bw_cap[1] >= 0) { + memset(¶m, 0, sizeof(param)); param.band = WLC_BAND_5G; - param.bw_cap = (uint)dhd->conf->bw_cap_5g; - printf("%s: set bw_cap 5g %d\n", __FUNCTION__, param.bw_cap); + param.bw_cap = (uint)dhd->conf->bw_cap[1]; + printf("%s: set bw_cap 5g 0x%x\n", __FUNCTION__, param.bw_cap); dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "bw_cap", (char *)¶m, sizeof(param), TRUE); } } @@ -1155,6 +1048,26 @@ dhd_conf_set_wme(dhd_pub_t *dhd, int mode) return; } +void +dhd_conf_set_mchan_bw(dhd_pub_t *dhd, int p2p_mode, int miracast_mode) +{ + int i; + struct dhd_conf *conf = dhd->conf; + bool set = true; + + for (i=0; imchan[i].bw >= 0); + set &= ((conf->mchan[i].p2p_mode == -1) | (conf->mchan[i].p2p_mode == p2p_mode)); + set &= ((conf->mchan[i].miracast_mode == -1) | (conf->mchan[i].miracast_mode == miracast_mode)); + if (set) { + dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "mchan_bw", conf->mchan[i].bw, 0, FALSE); + } + } + + return; +} + #ifdef PKT_FILTER_SUPPORT void dhd_conf_add_pkt_filter(dhd_pub_t *dhd) @@ -1164,16 +1077,11 @@ dhd_conf_add_pkt_filter(dhd_pub_t *dhd) #define MACS "%02x%02x%02x%02x%02x%02x" /* - * 1. Filter out all pkt: actually not to enable this since 4-way handshake will be filter out as well. - * 1) dhd_master_mode=0 - * 2) pkt_filter_add=99 0 0 0 0x000000000000 0x000000000000 - * 2. Filter in less pkt: ARP(0x0806, ID is 105), BRCM(0x886C), 802.1X(0x888E) + * Filter in less pkt: ARP(0x0806, ID is 105), BRCM(0x886C), 802.1X(0x888E) * 1) dhd_master_mode=1 * 2) pkt_filter_del=100, 102, 103, 104, 105 * 3) pkt_filter_add=131 0 0 12 0xFFFF 0x886C, 132 0 0 12 0xFFFF 0x888E - * 3. magic pkt: magic_pkt_filter_add=141 0 1 12 - * 4. Filter out netbios pkt: - * Netbios: 121 0 0 12 0xFFFF000000000000000000FF000000000000000000000000FFFF 0x0800000000000000000000110000000000000000000000000089 + * 4) magic_pkt_filter_add=141 0 1 12 */ for(i=0; iconf->pkt_filter_add.count; i++) { dhd->pktfilter[i+dhd->pktfilter_count] = dhd->conf->pkt_filter_add.filter[i]; @@ -1244,8 +1152,9 @@ dhd_conf_discard_pkt_filter(dhd_pub_t *dhd) int dhd_conf_get_pm(dhd_pub_t *dhd) { - if (dhd && dhd->conf) + if (dhd && dhd->conf) { return dhd->conf->pm; + } return -1; } @@ -1408,8 +1317,6 @@ pick_config_vars(char *varbuf, uint len, uint start_pos, char *pickbuf) if (pick) { if (varbuf[n] == 0x9) continue; - if (pick_column>0 && pickbuf[pick_column-1]==' ' && varbuf[n]==' ') - continue; pickbuf[pick_column] = varbuf[n]; pick_column++; } @@ -1432,6 +1339,12 @@ dhd_conf_read_log_level(dhd_pub_t *dhd, char *full_param, uint len_param) sd_msglevel = (int)simple_strtol(data, NULL, 0); printf("%s: sd_msglevel = 0x%X\n", __FUNCTION__, sd_msglevel); } +#endif +#ifdef BCMDBUS + else if (!strncmp("dbus_msglevel=", full_param, len_param)) { + dbus_msglevel = (int)simple_strtol(data, NULL, 0); + printf("%s: dbus_msglevel = 0x%X\n", __FUNCTION__, dbus_msglevel); + } #endif else if (!strncmp("android_msg_level=", full_param, len_param)) { android_msg_level = (int)simple_strtol(data, NULL, 0); @@ -1751,9 +1664,11 @@ bool dhd_conf_read_country_list(dhd_pub_t *dhd, char *full_param, uint len_param) { int i; - char *pch, *pick_tmp; + char *pch, *pick_tmp, *pick_tmp2; struct dhd_conf *conf = dhd->conf; char *data = full_param+len_param; + wl_country_t *cspec; + conf_country_list_t *country_list = NULL; /* Process country_list: * country_list=[country1]:[ccode1]/[regrev1], @@ -1761,28 +1676,115 @@ dhd_conf_read_country_list(dhd_pub_t *dhd, char *full_param, uint len_param) * Ex: country_list=US:US/0, TW:TW/1 */ if (!strncmp("country_list=", full_param, len_param)) { + country_list = &dhd->conf->country_list; + } else if (!strncmp("country_list_nodfs=", full_param, len_param)) { + country_list = &dhd->conf->country_list_nodfs; + } + if (country_list) { pick_tmp = data; for (i=0; icountry_list.cspec[i].country_abbrev, pch); - pch = bcmstrtok(&pick_tmp, "/", 0); + pch = bcmstrtok(&pick_tmp2, ":", 0); if (!pch) break; - memcpy(conf->country_list.cspec[i].ccode, pch, 2); - pch = bcmstrtok(&pick_tmp, ", ", 0); - if (!pch) + cspec = NULL; + if (!(cspec = kmalloc(sizeof(wl_country_t), GFP_KERNEL))) { + CONFIG_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); break; - conf->country_list.cspec[i].rev = (int32)simple_strtol(pch, NULL, 10); - conf->country_list.count ++; + } + memset(cspec, 0, sizeof(wl_country_t)); + + strcpy(cspec->country_abbrev, pch); + pch = bcmstrtok(&pick_tmp2, "/", 0); + if (!pch) { + kfree(cspec); + break; + } + memcpy(cspec->ccode, pch, 2); + pch = bcmstrtok(&pick_tmp2, "/", 0); + if (!pch) { + kfree(cspec); + break; + } + cspec->rev = (int32)simple_strtol(pch, NULL, 10); + country_list->count++; + country_list->cspec[i] = cspec; CONFIG_TRACE(("%s: country_list abbrev=%s, ccode=%s, regrev=%d\n", __FUNCTION__, - conf->country_list.cspec[i].country_abbrev, - conf->country_list.cspec[i].ccode, - conf->country_list.cspec[i].rev)); + cspec->country_abbrev, cspec->ccode, cspec->rev)); + } + if (!strncmp("country_list=", full_param, len_param)) { + printf("%s: %d country in list\n", __FUNCTION__, conf->country_list.count); + } else if (!strncmp("country_list_nodfs=", full_param, len_param)) { + printf("%s: %d nodfs country in list\n", __FUNCTION__, conf->country_list.count); + } + } + else + return false; + + return true; +} + +bool +dhd_conf_read_mchan_params(dhd_pub_t *dhd, char *full_param, uint len_param) +{ + int i; + char *pch, *pick_tmp, *pick_tmp2; + struct dhd_conf *conf = dhd->conf; + char *data = full_param+len_param; + + /* Process mchan_bw: + * mchan_bw=[val]/[any/go/gc]/[any/source/sink] + * Ex: mchan_bw=80/go/source, 30/gc/sink + */ + if (!strncmp("mchan_bw=", full_param, len_param)) { + pick_tmp = data; + for (i=0; imchan[i].bw = (int)simple_strtol(pch, NULL, 0); + if (conf->mchan[i].bw < 0 || conf->mchan[i].bw > 100) { + CONFIG_ERROR(("%s: wrong bw %d\n", __FUNCTION__, conf->mchan[i].bw)); + conf->mchan[i].bw = 0; + break; + } + } + pch = bcmstrtok(&pick_tmp2, "/", 0); + if (!pch) { + break; + } else { + if (bcmstrstr(pch, "any")) { + conf->mchan[i].p2p_mode = -1; + } else if (bcmstrstr(pch, "go")) { + conf->mchan[i].p2p_mode = WL_P2P_IF_GO; + } else if (bcmstrstr(pch, "gc")) { + conf->mchan[i].p2p_mode = WL_P2P_IF_CLIENT; + } + } + pch = bcmstrtok(&pick_tmp2, "/", 0); + if (!pch) { + break; + } else { + if (bcmstrstr(pch, "any")) { + conf->mchan[i].miracast_mode = -1; + } else if (bcmstrstr(pch, "source")) { + conf->mchan[i].miracast_mode = MIRACAST_SOURCE; + } else if (bcmstrstr(pch, "sink")) { + conf->mchan[i].miracast_mode = MIRACAST_SINK; + } + } + } + for (i=0; imchan[i].bw >= 0) + printf("%s: mchan_bw=%d/%d/%d\n", __FUNCTION__, + conf->mchan[i].bw, conf->mchan[i].p2p_mode, conf->mchan[i].miracast_mode); } - printf("%s: %d country in list\n", __FUNCTION__, conf->country_list.count); } else return false; @@ -1842,6 +1844,7 @@ dhd_conf_read_pkt_filter(dhd_pub_t *dhd, char *full_param, uint len_param) if (!(conf->magic_pkt_filter_add = kmalloc(MAGIC_PKT_FILTER_LEN, GFP_KERNEL))) { CONFIG_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); } else { + memset(conf->magic_pkt_filter_add, 0, MAGIC_PKT_FILTER_LEN); strcpy(conf->magic_pkt_filter_add, data); printf("%s: magic_pkt_filter_add = %s\n", __FUNCTION__, conf->magic_pkt_filter_add); } @@ -1853,33 +1856,33 @@ dhd_conf_read_pkt_filter(dhd_pub_t *dhd, char *full_param, uint len_param) } #endif -#ifdef IAPSTA_PREINIT +#ifdef ISAM_PREINIT /* - * iapsta_init=mode [sta|ap|apsta|dualap] vifname [wlan1] - * iapsta_config=ifname [wlan0|wlan1] ssid [xxx] chan [x] + * isam_init=mode [sta|ap|apsta|dualap] vifname [wlan1] + * isam_config=ifname [wlan0|wlan1] ssid [xxx] chan [x] hidden [y|n] maxassoc [x] amode [open|shared|wpapsk|wpa2psk|wpawpa2psk] emode [none|wep|tkip|aes|tkipaes] key [xxxxx] - * iapsta_enable=ifname [wlan0|wlan1] + * isam_enable=ifname [wlan0|wlan1] */ bool -dhd_conf_read_iapsta(dhd_pub_t *dhd, char *full_param, uint len_param) +dhd_conf_read_isam(dhd_pub_t *dhd, char *full_param, uint len_param) { struct dhd_conf *conf = dhd->conf; char *data = full_param+len_param; - if (!strncmp("iapsta_init=", full_param, len_param)) { - sprintf(conf->iapsta_init, "iapsta_init %s", data); - printf("%s: iapsta_init=%s\n", __FUNCTION__, conf->iapsta_init); + if (!strncmp("isam_init=", full_param, len_param)) { + sprintf(conf->isam_init, "isam_init %s", data); + printf("%s: isam_init=%s\n", __FUNCTION__, conf->isam_init); } - else if (!strncmp("iapsta_config=", full_param, len_param)) { - sprintf(conf->iapsta_config, "iapsta_config %s", data); - printf("%s: iapsta_config=%s\n", __FUNCTION__, conf->iapsta_config); + else if (!strncmp("isam_config=", full_param, len_param)) { + sprintf(conf->isam_config, "isam_config %s", data); + printf("%s: isam_config=%s\n", __FUNCTION__, conf->isam_config); } - else if (!strncmp("iapsta_enable=", full_param, len_param)) { - sprintf(conf->iapsta_enable, "iapsta_enable %s", data); - printf("%s: iapsta_enable=%s\n", __FUNCTION__, conf->iapsta_enable); + else if (!strncmp("isam_enable=", full_param, len_param)) { + sprintf(conf->isam_enable, "isam_enable %s", data); + printf("%s: isam_enable=%s\n", __FUNCTION__, conf->isam_enable); } else return false; @@ -1949,24 +1952,14 @@ dhd_conf_read_sdio_params(dhd_pub_t *dhd, char *full_param, uint len_param) dhd_doflow = TRUE; printf("%s: dhd_doflow = %d\n", __FUNCTION__, dhd_doflow); } - else if (!strncmp("dhd_slpauto=", full_param, len_param)) { - if (!strncmp(data, "0", 1)) - dhd_slpauto = FALSE; - else - dhd_slpauto = TRUE; - printf("%s: dhd_slpauto = %d\n", __FUNCTION__, dhd_slpauto); - } - else if (!strncmp("kso_enable=", full_param, len_param)) { + else if (!strncmp("dhd_slpauto=", full_param, len_param) || + !strncmp("kso_enable=", full_param, len_param)) { if (!strncmp(data, "0", 1)) dhd_slpauto = FALSE; else dhd_slpauto = TRUE; printf("%s: dhd_slpauto = %d\n", __FUNCTION__, dhd_slpauto); } - else if (!strncmp("bus:txglom=", full_param, len_param)) { - conf->bus_txglom = (int)simple_strtol(data, NULL, 10); - printf("%s: bus:txglom = %d\n", __FUNCTION__, conf->bus_txglom); - } else if (!strncmp("use_rxchain=", full_param, len_param)) { conf->use_rxchain = (int)simple_strtol(data, NULL, 10); printf("%s: use_rxchain = %d\n", __FUNCTION__, conf->use_rxchain); @@ -1998,6 +1991,10 @@ dhd_conf_read_sdio_params(dhd_pub_t *dhd, char *full_param, uint len_param) conf->rxf_cpucore = (int)simple_strtol(data, NULL, 10); printf("%s: rxf_cpucore = %d\n", __FUNCTION__, conf->rxf_cpucore); } + else if (!strncmp("orphan_move=", full_param, len_param)) { + conf->orphan_move = (int)simple_strtol(data, NULL, 10); + printf("%s: orphan_move = %d\n", __FUNCTION__, conf->orphan_move); + } #if defined(BCMSDIOH_TXGLOM) else if (!strncmp("txglomsize=", full_param, len_param)) { conf->txglomsize = (uint)simple_strtol(data, NULL, 10); @@ -2027,13 +2024,6 @@ dhd_conf_read_sdio_params(dhd_pub_t *dhd, char *full_param, uint len_param) conf->bus_rxglom = TRUE; printf("%s: bus:rxglom = %d\n", __FUNCTION__, conf->bus_rxglom); } - else if (!strncmp("dhd_poll=", full_param, len_param)) { - if (!strncmp(data, "0", 1)) - conf->dhd_poll = 0; - else - conf->dhd_poll = 1; - printf("%s: dhd_poll = %d\n", __FUNCTION__, conf->dhd_poll); - } else if (!strncmp("deferred_tx_len=", full_param, len_param)) { conf->deferred_tx_len = (int)simple_strtol(data, NULL, 10); printf("%s: deferred_tx_len = %d\n", __FUNCTION__, conf->deferred_tx_len); @@ -2068,17 +2058,34 @@ dhd_conf_read_sdio_params(dhd_pub_t *dhd, char *full_param, uint len_param) } #endif +#ifdef BCMPCIE bool -dhd_conf_read_pm_params(dhd_pub_t *dhd, char *full_param, uint len_param) +dhd_conf_read_pcie_params(dhd_pub_t *dhd, char *full_param, uint len_param) { struct dhd_conf *conf = dhd->conf; char *data = full_param+len_param; - if (!strncmp("lpc=", full_param, len_param)) { - conf->lpc = (int)simple_strtol(data, NULL, 10); - printf("%s: lpc = %d\n", __FUNCTION__, conf->lpc); + if (!strncmp("bus:deepsleep_disable=", full_param, len_param)) { + if (!strncmp(data, "0", 1)) + conf->bus_deepsleep_disable = 0; + else + conf->bus_deepsleep_disable = 1; + printf("%s: bus:deepsleep_disable = %d\n", __FUNCTION__, conf->bus_deepsleep_disable); } - else if (!strncmp("deepsleep=", full_param, len_param)) { + else + return false; + + return true; +} +#endif + +bool +dhd_conf_read_pm_params(dhd_pub_t *dhd, char *full_param, uint len_param) +{ + struct dhd_conf *conf = dhd->conf; + char *data = full_param+len_param; + + if (!strncmp("deepsleep=", full_param, len_param)) { if (!strncmp(data, "1", 1)) conf->deepsleep = TRUE; else @@ -2093,9 +2100,9 @@ dhd_conf_read_pm_params(dhd_pub_t *dhd, char *full_param, uint len_param) conf->pm_in_suspend = (int)simple_strtol(data, NULL, 10); printf("%s: pm_in_suspend = %d\n", __FUNCTION__, conf->pm_in_suspend); } - else if (!strncmp("pm2_sleep_ret=", full_param, len_param)) { - conf->pm2_sleep_ret = (int)simple_strtol(data, NULL, 10); - printf("%s: pm2_sleep_ret = %d\n", __FUNCTION__, conf->pm2_sleep_ret); + else if (!strncmp("suspend_bcn_li_dtim=", full_param, len_param)) { + conf->suspend_bcn_li_dtim = (int)simple_strtol(data, NULL, 10); + printf("%s: suspend_bcn_li_dtim = %d\n", __FUNCTION__, conf->suspend_bcn_li_dtim); } else if (!strncmp("xmit_in_suspend=", full_param, len_param)) { if (!strncmp(data, "1", 1)) @@ -2108,6 +2115,15 @@ dhd_conf_read_pm_params(dhd_pub_t *dhd, char *full_param, uint len_param) conf->ap_in_suspend = (int)simple_strtol(data, NULL, 10); printf("%s: ap_in_suspend = %d\n", __FUNCTION__, conf->ap_in_suspend); } +#ifdef SUSPEND_EVENT + else if (!strncmp("suspend_eventmask_enable=", full_param, len_param)) { + if (!strncmp(data, "1", 1)) + conf->suspend_eventmask_enable = TRUE; + else + conf->suspend_eventmask_enable = FALSE; + printf("%s: suspend_eventmask_enable = %d\n", __FUNCTION__, conf->suspend_eventmask_enable); + } +#endif else return false; @@ -2123,7 +2139,18 @@ dhd_conf_read_others(dhd_pub_t *dhd, char *full_param, uint len_param) char *pch, *pick_tmp; int i; - if (!strncmp("band=", full_param, len_param)) { + if (!strncmp("dhd_poll=", full_param, len_param)) { + if (!strncmp(data, "0", 1)) + conf->dhd_poll = 0; + else + conf->dhd_poll = 1; + printf("%s: dhd_poll = %d\n", __FUNCTION__, conf->dhd_poll); + } + else if (!strncmp("dhd_watchdog_ms=", full_param, len_param)) { + dhd_watchdog_ms = (int)simple_strtol(data, NULL, 10); + printf("%s: dhd_watchdog_ms = %d\n", __FUNCTION__, dhd_watchdog_ms); + } + else if (!strncmp("band=", full_param, len_param)) { /* Process band: * band=a for 5GHz only and band=b for 2.4GHz only */ @@ -2135,17 +2162,26 @@ dhd_conf_read_others(dhd_pub_t *dhd, char *full_param, uint len_param) conf->band = WLC_BAND_AUTO; printf("%s: band = %d\n", __FUNCTION__, conf->band); } - else if (!strncmp("mimo_bw_cap=", full_param, len_param)) { - conf->mimo_bw_cap = (uint)simple_strtol(data, NULL, 10); - printf("%s: mimo_bw_cap = %d\n", __FUNCTION__, conf->mimo_bw_cap); - } else if (!strncmp("bw_cap_2g=", full_param, len_param)) { - conf->bw_cap_2g = (uint)simple_strtol(data, NULL, 0); - printf("%s: bw_cap_2g = %d\n", __FUNCTION__, conf->bw_cap_2g); + conf->bw_cap[0] = (uint)simple_strtol(data, NULL, 0); + printf("%s: bw_cap_2g = %d\n", __FUNCTION__, conf->bw_cap[0]); } else if (!strncmp("bw_cap_5g=", full_param, len_param)) { - conf->bw_cap_5g = (uint)simple_strtol(data, NULL, 0); - printf("%s: bw_cap_2g = %d\n", __FUNCTION__, conf->bw_cap_5g); + conf->bw_cap[1] = (uint)simple_strtol(data, NULL, 0); + printf("%s: bw_cap_5g = %d\n", __FUNCTION__, conf->bw_cap[1]); + } + else if (!strncmp("bw_cap=", full_param, len_param)) { + pick_tmp = data; + pch = bcmstrtok(&pick_tmp, " ,.-", 0); + if (pch != NULL) { + conf->bw_cap[0] = (uint32)simple_strtol(pch, NULL, 0); + printf("%s: bw_cap 2g = %d\n", __FUNCTION__, conf->bw_cap[0]); + } + pch = bcmstrtok(&pick_tmp, " ,.-", 0); + if (pch != NULL) { + conf->bw_cap[1] = (uint32)simple_strtol(pch, NULL, 0); + printf("%s: bw_cap 5g = %d\n", __FUNCTION__, conf->bw_cap[1]); + } } else if (!strncmp("ccode=", full_param, len_param)) { memset(&conf->cspec, 0, sizeof(wl_country_t)); @@ -2177,10 +2213,6 @@ dhd_conf_read_others(dhd_pub_t *dhd, char *full_param, uint len_param) printf("%s: keep_alive_period = %d\n", __FUNCTION__, conf->keep_alive_period); } - else if (!strncmp("stbc=", full_param, len_param)) { - conf->stbc = (int)simple_strtol(data, NULL, 10); - printf("%s: stbc = %d\n", __FUNCTION__, conf->stbc); - } else if (!strncmp("phy_oclscdenable=", full_param, len_param)) { conf->phy_oclscdenable = (int)simple_strtol(data, NULL, 10); printf("%s: phy_oclscdenable = %d\n", __FUNCTION__, conf->phy_oclscdenable); @@ -2197,18 +2229,6 @@ dhd_conf_read_others(dhd_pub_t *dhd, char *full_param, uint len_param) conf->bcn_timeout= (uint)simple_strtol(data, NULL, 10); printf("%s: bcn_timeout = %d\n", __FUNCTION__, conf->bcn_timeout); } - else if (!strncmp("ampdu_ba_wsize=", full_param, len_param)) { - conf->ampdu_ba_wsize = (int)simple_strtol(data, NULL, 10); - printf("%s: ampdu_ba_wsize = %d\n", __FUNCTION__, conf->ampdu_ba_wsize); - } - else if (!strncmp("ampdu_hostreorder=", full_param, len_param)) { - conf->ampdu_hostreorder = (int)simple_strtol(data, NULL, 10); - printf("%s: ampdu_hostreorder = %d\n", __FUNCTION__, conf->ampdu_hostreorder); - } - else if (!strncmp("spect=", full_param, len_param)) { - conf->spect = (int)simple_strtol(data, NULL, 10); - printf("%s: spect = %d\n", __FUNCTION__, conf->spect); - } else if (!strncmp("txbf=", full_param, len_param)) { conf->txbf = (int)simple_strtol(data, NULL, 10); printf("%s: txbf = %d\n", __FUNCTION__, conf->txbf); @@ -2231,6 +2251,7 @@ dhd_conf_read_others(dhd_pub_t *dhd, char *full_param, uint len_param) conf->pktprio8021x = (int)simple_strtol(data, NULL, 10); printf("%s: pktprio8021x = %d\n", __FUNCTION__, conf->pktprio8021x); } +#if defined(BCMSDIO) || defined(BCMPCIE) else if (!strncmp("dhd_txbound=", full_param, len_param)) { dhd_txbound = (uint)simple_strtol(data, NULL, 10); printf("%s: dhd_txbound = %d\n", __FUNCTION__, dhd_txbound); @@ -2239,29 +2260,32 @@ dhd_conf_read_others(dhd_pub_t *dhd, char *full_param, uint len_param) dhd_rxbound = (uint)simple_strtol(data, NULL, 10); printf("%s: dhd_rxbound = %d\n", __FUNCTION__, dhd_rxbound); } - else if (!strncmp("rsdb_mode=", full_param, len_param)) { - conf->rsdb_mode = (int)simple_strtol(data, NULL, 10); - printf("%s: rsdb_mode = %d\n", __FUNCTION__, conf->rsdb_mode); - } - else if (!strncmp("vhtmode=", full_param, len_param)) { - if (!strncmp(data, "0", 1)) - conf->vhtmode = 0; - else - conf->vhtmode = 1; - printf("%s: vhtmode = %d\n", __FUNCTION__, conf->vhtmode); - } +#endif else if (!strncmp("num_different_channels=", full_param, len_param)) { conf->num_different_channels = (int)simple_strtol(data, NULL, 10); printf("%s: num_different_channels = %d\n", __FUNCTION__, conf->num_different_channels); } - else if (!strncmp("autocountry=", full_param, len_param)) { - conf->autocountry = (int)simple_strtol(data, NULL, 10); - printf("%s: autocountry = %d\n", __FUNCTION__, conf->autocountry); - } else if (!strncmp("tsq=", full_param, len_param)) { conf->tsq = (int)simple_strtol(data, NULL, 10); printf("%s: tsq = %d\n", __FUNCTION__, conf->tsq); } + else if (!strncmp("ctrl_resched=", full_param, len_param)) { + conf->ctrl_resched = (int)simple_strtol(data, NULL, 10); + printf("%s: ctrl_resched = %d\n", __FUNCTION__, conf->ctrl_resched); + } + else if (!strncmp("dhd_ioctl_timeout_msec=", full_param, len_param)) { + conf->dhd_ioctl_timeout_msec = (int)simple_strtol(data, NULL, 10); + printf("%s: dhd_ioctl_timeout_msec = %d\n", __FUNCTION__, conf->dhd_ioctl_timeout_msec); + } + else if (!strncmp("wl_preinit=", full_param, len_param)) { + if (!(conf->wl_preinit = kmalloc(len_param+1, GFP_KERNEL))) { + CONFIG_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); + } else { + memset(conf->wl_preinit, 0, len_param+1); + strcpy(conf->wl_preinit, data); + printf("%s: wl_preinit = %s\n", __FUNCTION__, conf->wl_preinit); + } + } else return false; @@ -2272,7 +2296,7 @@ int dhd_conf_read_config(dhd_pub_t *dhd, char *conf_path) { int bcmerror = -1; - uint len, start_pos=0; + uint len = 0, start_pos=0; void * image = NULL; char * memblock = NULL; char *bufp, *pick = NULL, *pch; @@ -2344,14 +2368,16 @@ dhd_conf_read_config(dhd_pub_t *dhd, char *conf_path) continue; else if (dhd_conf_read_country_list(dhd, pick, len_param)) continue; + else if (dhd_conf_read_mchan_params(dhd, pick, len_param)) + continue; #ifdef PKT_FILTER_SUPPORT else if (dhd_conf_read_pkt_filter(dhd, pick, len_param)) continue; #endif /* PKT_FILTER_SUPPORT */ -#ifdef IAPSTA_PREINIT - else if (dhd_conf_read_iapsta(dhd, pick, len_param)) +#ifdef ISAM_PREINIT + else if (dhd_conf_read_isam(dhd, pick, len_param)) continue; -#endif /* IAPSTA_PREINIT */ +#endif /* ISAM_PREINIT */ #ifdef IDHCP else if (dhd_conf_read_dhcp_params(dhd, pick, len_param)) continue; @@ -2360,6 +2386,10 @@ dhd_conf_read_config(dhd_pub_t *dhd, char *conf_path) else if (dhd_conf_read_sdio_params(dhd, pick, len_param)) continue; #endif /* BCMSDIO */ +#ifdef BCMPCIE + else if (dhd_conf_read_pcie_params(dhd, pick, len_param)) + continue; +#endif /* BCMPCIE */ else if (dhd_conf_read_pm_params(dhd, pick, len_param)) continue; else if (dhd_conf_read_others(dhd, pick, len_param)) @@ -2432,7 +2462,7 @@ dhd_conf_set_txglom_params(dhd_pub_t *dhd, bool enable) #endif // other parameters set in preinit or config.txt } else { - // clear txglom parameters, but don't change swtxglom since it's possible enabled in config.txt + // clear txglom parameters conf->txglom_ext = FALSE; conf->txglom_bucket_size = 0; conf->txglomsize = 0; @@ -2441,8 +2471,10 @@ dhd_conf_set_txglom_params(dhd_pub_t *dhd, bool enable) if (conf->txglom_ext) printf("%s: txglom_ext=%d, txglom_bucket_size=%d\n", __FUNCTION__, conf->txglom_ext, conf->txglom_bucket_size); - printf("%s: txglomsize=%d, deferred_tx_len=%d, bus_txglom=%d\n", __FUNCTION__, - conf->txglomsize, conf->deferred_tx_len, conf->bus_txglom); + printf("%s: txglom_mode=%s\n", __FUNCTION__, + conf->txglom_mode==SDPCM_TXGLOM_MDESC?"multi-desc":"copy"); + printf("%s: txglomsize=%d, deferred_tx_len=%d\n", __FUNCTION__, + conf->txglomsize, conf->deferred_tx_len); printf("%s: tx_in_rx=%d, txinrx_thres=%d, dhd_txminmax=%d\n", __FUNCTION__, conf->tx_in_rx, conf->txinrx_thres, conf->dhd_txminmax); printf("%s: tx_max_offset=%d, txctl_tmo_fix=%d\n", __FUNCTION__, @@ -2451,10 +2483,151 @@ dhd_conf_set_txglom_params(dhd_pub_t *dhd, bool enable) } #endif +static int +dhd_conf_rsdb_mode(dhd_pub_t *dhd, char *buf) +{ + char *pch; + wl_config_t rsdb_mode_cfg = {1, 0}; + + pch = buf; + rsdb_mode_cfg.config = (int)simple_strtol(pch, NULL, 0); + + if (pch) { + dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "rsdb_mode", (char *)&rsdb_mode_cfg, + sizeof(rsdb_mode_cfg), TRUE); + printf("%s: rsdb_mode %d\n", __FUNCTION__, rsdb_mode_cfg.config); + } + + return 0; +} + +typedef int (tpl_parse_t)(dhd_pub_t *dhd, char *buf); + +typedef struct iovar_tpl_t { + int cmd; + char *name; + tpl_parse_t *parse; +} iovar_tpl_t; + +const iovar_tpl_t iovar_tpl_list[] = { + {WLC_SET_VAR, "rsdb_mode", dhd_conf_rsdb_mode}, +}; + +static int iovar_tpl_parse(const iovar_tpl_t *tpl, int tpl_count, + dhd_pub_t *dhd, int cmd, char *name, char *buf) +{ + int i, ret = 0; + + /* look for a matching code in the table */ + for (i = 0; i < tpl_count; i++, tpl++) { + if (tpl->cmd == cmd && !strcmp(tpl->name, name)) + break; + } + if (i < tpl_count && tpl->parse) { + ret = tpl->parse(dhd, buf); + } else { + ret = -1; + } + + return ret; +} + +bool +dhd_conf_set_wl_preinit(dhd_pub_t *dhd, char *data) +{ + int cmd, val, ret = 0; + char name[32], *pch, *pick_tmp, *pick_tmp2; + + /* Process wl_preinit: + * wl_preinit=[cmd]/[val], [cmd]/[val] \ + * Ex: wl_preinit=86/0, mpc/0 + */ + pick_tmp = data; + while (pick_tmp && (pick_tmp2 = bcmstrtok(&pick_tmp, ",", 0)) != NULL) { + pch = bcmstrtok(&pick_tmp2, "=", 0); + if (!pch) + break; + if (*pch == ' ') { + pch++; + } + memset(name, 0 , sizeof (name)); + cmd = (int)simple_strtol(pch, NULL, 0); + if (cmd == 0) { + cmd = WLC_SET_VAR; + strcpy(name, pch); + } + pch = bcmstrtok(&pick_tmp2, ",", 0); + if (!pch) { + break; + } + ret = iovar_tpl_parse(iovar_tpl_list, ARRAY_SIZE(iovar_tpl_list), + dhd, cmd, name, pch); + if (ret) { + val = (int)simple_strtol(pch, NULL, 0); + dhd_conf_set_intiovar(dhd, cmd, name, val, -1, TRUE); + } + } + + return true; +} + +void +dhd_conf_postinit_ioctls(dhd_pub_t *dhd) +{ + struct dhd_conf *conf = dhd->conf; + + dhd_conf_set_intiovar(dhd, WLC_UP, "up", 0, 0, FALSE); + dhd_conf_map_country_list(dhd, &conf->cspec, 0); + dhd_conf_set_country(dhd, &conf->cspec); + dhd_conf_fix_country(dhd); + dhd_conf_get_country(dhd, &dhd->dhd_cspec); + + dhd_conf_set_intiovar(dhd, WLC_SET_BAND, "WLC_SET_BAND", conf->band, 0, FALSE); + dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "bcn_timeout", conf->bcn_timeout, 0, FALSE); + if (conf->fw_type == FW_TYPE_MESH) + conf->pm = PM_OFF; + dhd_conf_set_intiovar(dhd, WLC_SET_PM, "PM", conf->pm, 0, FALSE); + dhd_conf_set_intiovar(dhd, WLC_SET_SRL, "WLC_SET_SRL", conf->srl, 0, TRUE); + dhd_conf_set_intiovar(dhd, WLC_SET_LRL, "WLC_SET_LRL", conf->lrl, 0, FALSE); + dhd_conf_set_bw_cap(dhd); + dhd_conf_set_roam(dhd); + +#if defined(BCMPCIE) + dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "bus:deepsleep_disable", + conf->bus_deepsleep_disable, 0, FALSE); +#endif /* defined(BCMPCIE) */ + +#ifdef IDHCP + dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "dhcpc_enable", conf->dhcpc_enable, 0, FALSE); + if (dhd->conf->dhcpd_enable >= 0) { + dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_addr", + (char *)&conf->dhcpd_ip_addr, sizeof(conf->dhcpd_ip_addr), FALSE); + dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_mask", + (char *)&conf->dhcpd_ip_mask, sizeof(conf->dhcpd_ip_mask), FALSE); + dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_start", + (char *)&conf->dhcpd_ip_start, sizeof(conf->dhcpd_ip_start), FALSE); + dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_end", + (char *)&conf->dhcpd_ip_end, sizeof(conf->dhcpd_ip_end), FALSE); + dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "dhcpd_enable", + conf->dhcpd_enable, 0, FALSE); + } +#endif + dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "txbf", conf->txbf, 0, FALSE); + dhd_conf_set_intiovar(dhd, WLC_SET_FAKEFRAG, "WLC_SET_FAKEFRAG", conf->frameburst, 0, FALSE); + + dhd_conf_set_wl_preinit(dhd, conf->wl_preinit); + +#ifndef WL_CFG80211 + dhd_conf_set_intiovar(dhd, WLC_UP, "up", 0, 0, FALSE); +#endif + +} + int dhd_conf_preinit(dhd_pub_t *dhd) { struct dhd_conf *conf = dhd->conf; + int i; CONFIG_TRACE(("%s: Enter\n", __FUNCTION__)); @@ -2463,13 +2636,19 @@ dhd_conf_preinit(dhd_pub_t *dhd) dhd_conf_free_mac_list(&conf->nv_by_mac); dhd_conf_free_chip_nv_path_list(&conf->nv_by_chip); #endif - if (conf->magic_pkt_filter_add) + dhd_conf_free_country_list(&conf->country_list); + dhd_conf_free_country_list(&conf->country_list_nodfs); + if (conf->magic_pkt_filter_add) { kfree(conf->magic_pkt_filter_add); + conf->magic_pkt_filter_add = NULL; + } + if (conf->wl_preinit) { + kfree(conf->wl_preinit); + conf->wl_preinit = NULL; + } memset(&conf->country_list, 0, sizeof(conf_country_list_t)); conf->band = -1; - conf->mimo_bw_cap = -1; - conf->bw_cap_2g = -1; - conf->bw_cap_5g = -1; + memset(&conf->bw_cap, -1, sizeof(conf->bw_cap)); if (conf->chip == BCM43362_CHIP_ID || conf->chip == BCM4330_CHIP_ID) { strcpy(conf->cspec.country_abbrev, "ALL"); strcpy(conf->cspec.ccode, "ALL"); @@ -2477,7 +2656,8 @@ dhd_conf_preinit(dhd_pub_t *dhd) } else if (conf->chip == BCM4335_CHIP_ID || conf->chip == BCM4339_CHIP_ID || conf->chip == BCM4354_CHIP_ID || conf->chip == BCM4356_CHIP_ID || conf->chip == BCM4345_CHIP_ID || conf->chip == BCM4371_CHIP_ID || - conf->chip == BCM43569_CHIP_ID || conf->chip == BCM4359_CHIP_ID) { + conf->chip == BCM43569_CHIP_ID || conf->chip == BCM4359_CHIP_ID || + conf->chip == BCM4362_CHIP_ID) { strcpy(conf->cspec.country_abbrev, "CN"); strcpy(conf->cspec.ccode, "CN"); conf->cspec.rev = 38; @@ -2516,7 +2696,6 @@ dhd_conf_preinit(dhd_pub_t *dhd) conf->force_wme_ac = 0; memset(&conf->wme_sta, 0, sizeof(wme_param_t)); memset(&conf->wme_ap, 0, sizeof(wme_param_t)); - conf->stbc = -1; conf->phy_oclscdenable = -1; #ifdef PKT_FILTER_SUPPORT memset(&conf->pkt_filter_add, 0, sizeof(conf_pkt_filter_add_t)); @@ -2525,19 +2704,16 @@ dhd_conf_preinit(dhd_pub_t *dhd) conf->srl = -1; conf->lrl = -1; conf->bcn_timeout = 16; - conf->spect = -1; conf->txbf = -1; - conf->lpc = -1; conf->disable_proptx = -1; + conf->dhd_poll = -1; #ifdef BCMSDIO - conf->bus_txglom = -1; conf->use_rxchain = 0; conf->bus_rxglom = TRUE; conf->txglom_ext = FALSE; conf->tx_max_offset = 0; conf->txglomsize = SDPCM_DEFGLOM_SIZE; - conf->dhd_poll = -1; - conf->txctl_tmo_fix = 5; + conf->txctl_tmo_fix = 300; conf->tx_in_rx = TRUE; conf->txglom_mode = SDPCM_TXGLOM_CPY; conf->deferred_tx_len = 0; @@ -2545,19 +2721,26 @@ dhd_conf_preinit(dhd_pub_t *dhd) conf->txinrx_thres = -1; conf->sd_f2_blocksize = 0; conf->oob_enabled_later = FALSE; + conf->orphan_move = 0; +#endif +#ifdef BCMPCIE + conf->bus_deepsleep_disable = 1; #endif - conf->ampdu_ba_wsize = 0; - conf->ampdu_hostreorder = -1; conf->dpc_cpucore = -1; conf->rxf_cpucore = -1; conf->frameburst = -1; conf->deepsleep = FALSE; conf->pm = -1; conf->pm_in_suspend = -1; - conf->pm2_sleep_ret = -1; + conf->suspend_bcn_li_dtim = -1; conf->num_different_channels = -1; conf->xmit_in_suspend = TRUE; conf->ap_in_suspend = 0; +#ifdef SUSPEND_EVENT + conf->suspend_eventmask_enable = FALSE; + memset(&conf->suspend_eventmask, 0, sizeof(conf->suspend_eventmask)); + memset(&conf->resume_eventmask, 0, sizeof(conf->resume_eventmask)); +#endif #ifdef IDHCP conf->dhcpc_enable = -1; conf->dhcpd_enable = -1; @@ -2568,25 +2751,35 @@ dhd_conf_preinit(dhd_pub_t *dhd) conf->tsq = 0; #endif #ifdef DHDTCPACK_SUPPRESS +#ifdef BCMPCIE + conf->tcpack_sup_mode = TCPACK_SUP_DEFAULT; +#else conf->tcpack_sup_mode = TCPACK_SUP_OFF; +#endif #endif conf->pktprio8021x = -1; - conf->rsdb_mode = -2; - conf->vhtmode = -1; - conf->autocountry = -1; -#ifdef IAPSTA_PREINIT - memset(conf->iapsta_init, 0, sizeof(conf->iapsta_init)); - memset(conf->iapsta_config, 0, sizeof(conf->iapsta_config)); - memset(conf->iapsta_enable, 0, sizeof(conf->iapsta_enable)); + conf->ctrl_resched = 2; + conf->dhd_ioctl_timeout_msec = 0; +#ifdef ISAM_PREINIT + memset(conf->isam_init, 0, sizeof(conf->isam_init)); + memset(conf->isam_config, 0, sizeof(conf->isam_config)); + memset(conf->isam_enable, 0, sizeof(conf->isam_enable)); #endif + for (i=0; imchan[i], -1, sizeof(mchan_params_t)); + } if (conf->chip == BCM4354_CHIP_ID || conf->chip == BCM4356_CHIP_ID || conf->chip == BCM4371_CHIP_ID || conf->chip == BCM43569_CHIP_ID || - conf->chip == BCM4359_CHIP_ID) { + conf->chip == BCM4359_CHIP_ID || conf->chip == BCM4362_CHIP_ID) { #ifdef DHDTCPACK_SUPPRESS +#ifdef BCMSDIO conf->tcpack_sup_mode = TCPACK_SUP_REPLACE; #endif +#endif +#if defined(BCMSDIO) || defined(BCMPCIE) dhd_rxbound = 128; dhd_txbound = 64; +#endif conf->txbf = 1; conf->frameburst = 1; #ifdef BCMSDIO @@ -2594,6 +2787,11 @@ dhd_conf_preinit(dhd_pub_t *dhd) conf->txinrx_thres = 128; conf->sd_f2_blocksize = CUSTOM_SDIO_F2_BLKSIZE; conf->oob_enabled_later = TRUE; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) + conf->orphan_move = 1; +#else + conf->orphan_move = 0; +#endif #endif } @@ -2603,9 +2801,6 @@ dhd_conf_preinit(dhd_pub_t *dhd) conf->chip == BCM43340_CHIP_ID || conf->chip == BCM43341_CHIP_ID || conf->chip == BCM4334_CHIP_ID || conf->chip == BCM4324_CHIP_ID) { conf->txglom_ext = TRUE; - conf->use_rxchain = 0; - conf->tx_in_rx = TRUE; - conf->tx_max_offset = 1; } else { conf->txglom_ext = FALSE; } @@ -2621,10 +2816,7 @@ dhd_conf_preinit(dhd_pub_t *dhd) #endif if (conf->txglomsize > SDPCM_MAXGLOM_SIZE) conf->txglomsize = SDPCM_MAXGLOM_SIZE; - conf->deferred_tx_len = conf->txglomsize; #endif - if (conf->chip == BCM4354_CHIP_ID && conf->chiprev == 1) - dhd_slpauto = 0; return 0; } @@ -2637,8 +2829,16 @@ dhd_conf_reset(dhd_pub_t *dhd) dhd_conf_free_mac_list(&dhd->conf->nv_by_mac); dhd_conf_free_chip_nv_path_list(&dhd->conf->nv_by_chip); #endif - if (dhd->conf->magic_pkt_filter_add) + dhd_conf_free_country_list(&dhd->conf->country_list); + dhd_conf_free_country_list(&dhd->conf->country_list_nodfs); + if (dhd->conf->magic_pkt_filter_add) { kfree(dhd->conf->magic_pkt_filter_add); + dhd->conf->magic_pkt_filter_add = NULL; + } + if (dhd->conf->wl_preinit) { + kfree(dhd->conf->wl_preinit); + dhd->conf->wl_preinit = NULL; + } memset(dhd->conf, 0, sizeof(dhd_conf_t)); return 0; } @@ -2682,8 +2882,16 @@ dhd_conf_detach(dhd_pub_t *dhd) dhd_conf_free_mac_list(&dhd->conf->nv_by_mac); dhd_conf_free_chip_nv_path_list(&dhd->conf->nv_by_chip); #endif - if (dhd->conf->magic_pkt_filter_add) + dhd_conf_free_country_list(&dhd->conf->country_list); + dhd_conf_free_country_list(&dhd->conf->country_list_nodfs); + if (dhd->conf->magic_pkt_filter_add) { kfree(dhd->conf->magic_pkt_filter_add); + dhd->conf->magic_pkt_filter_add = NULL; + } + if (dhd->conf->wl_preinit) { + kfree(dhd->conf->wl_preinit); + dhd->conf->wl_preinit = NULL; + } MFREE(dhd->osh, dhd->conf, sizeof(dhd_conf_t)); } dhd->conf = NULL; diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_config.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_config.h index bfa6a3ea..6adf2d07 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_config.h +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_config.h @@ -1,5 +1,4 @@ /* SPDX-License-Identifier: GPL-2.0 */ - #ifndef _dhd_config_ #define _dhd_config_ @@ -9,36 +8,27 @@ #include #include <802.11.h> +#define FW_TYPE_STA 0 +#define FW_TYPE_APSTA 1 +#define FW_TYPE_P2P 2 +#define FW_TYPE_MESH 3 +#define FW_TYPE_ES 4 +#define FW_TYPE_MFG 5 +#define FW_TYPE_G 0 +#define FW_TYPE_AG 1 + #define FW_PATH_AUTO_SELECT 1 //#define CONFIG_PATH_AUTO_SELECT extern char firmware_path[MOD_PARAM_PATHLEN]; +#if defined(BCMSDIO) || defined(BCMPCIE) extern uint dhd_rxbound; extern uint dhd_txbound; +#endif #ifdef BCMSDIO #define TXGLOM_RECV_OFFSET 8 extern uint dhd_doflow; extern uint dhd_slpauto; - -#define BCM43362A0_CHIP_REV 0 -#define BCM43362A2_CHIP_REV 1 -#define BCM43430A0_CHIP_REV 0 -#define BCM43430A1_CHIP_REV 1 -#define BCM43430A2_CHIP_REV 2 -#define BCM43012B0_CHIP_REV 1 -#define BCM4330B2_CHIP_REV 4 -#define BCM4334B1_CHIP_REV 3 -#define BCM43341B0_CHIP_REV 2 -#define BCM43241B4_CHIP_REV 5 -#define BCM4335A0_CHIP_REV 2 -#define BCM4339A0_CHIP_REV 1 -#define BCM43455C0_CHIP_REV 6 -#define BCM43456C5_CHIP_REV 9 -#define BCM4354A1_CHIP_REV 1 -#define BCM4359B1_CHIP_REV 5 -#define BCM4359C0_CHIP_REV 9 #endif -#define BCM4356A2_CHIP_REV 2 -#define BCM4358A3_CHIP_REV 3 typedef struct wl_mac_range { uint32 oui; @@ -98,25 +88,35 @@ typedef struct conf_pkt_filter_del { #define CONFIG_COUNTRY_LIST_SIZE 100 typedef struct conf_country_list { uint32 count; - wl_country_t cspec[CONFIG_COUNTRY_LIST_SIZE]; + wl_country_t *cspec[CONFIG_COUNTRY_LIST_SIZE]; } conf_country_list_t; +/* mchan_params */ +#define MCHAN_MAX_NUM 4 +#define MIRACAST_SOURCE 1 +#define MIRACAST_SINK 2 +typedef struct mchan_params { + int bw; + int p2p_mode; + int miracast_mode; +} mchan_params_t; + typedef struct dhd_conf { uint chip; uint chiprev; + int fw_type; wl_mac_list_ctrl_t fw_by_mac; wl_mac_list_ctrl_t nv_by_mac; wl_chip_nv_path_list_ctrl_t nv_by_chip; conf_country_list_t country_list; + conf_country_list_t country_list_nodfs; int band; - int mimo_bw_cap; - int bw_cap_2g; - int bw_cap_5g; + int bw_cap[2]; wl_country_t cspec; wl_channel_list_t channels; uint roam_off; uint roam_off_suspend; - int roam_trigger[2]; + int roam_trigger[2]; int roam_scan_period[2]; int roam_delta[2]; int fullroamperiod; @@ -124,7 +124,6 @@ typedef struct dhd_conf { int force_wme_ac; wme_param_t wme_sta; wme_param_t wme_ap; - int stbc; int phy_oclscdenable; #ifdef PKT_FILTER_SUPPORT conf_pkt_filter_add_t pkt_filter_add; @@ -134,12 +133,10 @@ typedef struct dhd_conf { int srl; int lrl; uint bcn_timeout; - int spect; int txbf; - int lpc; int disable_proptx; + int dhd_poll; #ifdef BCMSDIO - int bus_txglom; int use_rxchain; bool bus_rxglom; bool txglom_ext; /* Only for 43362/4330/43340/43341/43241 */ @@ -150,7 +147,6 @@ typedef struct dhd_conf { */ int tx_max_offset; uint txglomsize; - int dhd_poll; int txctl_tmo_fix; bool tx_in_rx; bool txglom_mode; @@ -164,22 +160,22 @@ typedef struct dhd_conf { int dhd_txminmax; // -1=DATABUFCNT(bus) uint sd_f2_blocksize; bool oob_enabled_later; + int orphan_move; +#endif +#ifdef BCMPCIE + int bus_deepsleep_disable; #endif - int ampdu_ba_wsize; - int ampdu_hostreorder; int dpc_cpucore; int rxf_cpucore; int frameburst; bool deepsleep; int pm; int pm_in_suspend; - int pm2_sleep_ret; + int suspend_bcn_li_dtim; #ifdef DHDTCPACK_SUPPRESS uint8 tcpack_sup_mode; #endif int pktprio8021x; - int rsdb_mode; - int vhtmode; int num_different_channels; int xmit_in_suspend; int ap_in_suspend; @@ -196,12 +192,15 @@ typedef struct dhd_conf { struct ipv4_addr dhcpd_ip_start; struct ipv4_addr dhcpd_ip_end; #endif -#ifdef IAPSTA_PREINIT - char iapsta_init[50]; - char iapsta_config[300]; - char iapsta_enable[50]; +#ifdef ISAM_PREINIT + char isam_init[50]; + char isam_config[300]; + char isam_enable[50]; #endif - int autocountry; + int ctrl_resched; + int dhd_ioctl_timeout_msec; + struct mchan_params mchan[MCHAN_MAX_NUM]; + char *wl_preinit; int tsq; } dhd_conf_t; @@ -215,7 +214,7 @@ void dhd_conf_set_hw_oob_intr(bcmsdh_info_t *sdh, uint chip); void dhd_conf_set_txglom_params(dhd_pub_t *dhd, bool enable); int dhd_conf_set_blksize(bcmsdh_info_t *sdh); #endif -void dhd_conf_set_fw_name_by_chip(dhd_pub_t *dhd, char *fw_path, char *nv_path); +void dhd_conf_set_fw_name_by_chip(dhd_pub_t *dhd, char *fw_path); void dhd_conf_set_clm_name_by_chip(dhd_pub_t *dhd, char *clm_path); void dhd_conf_set_nv_name_by_chip(dhd_pub_t *dhd, char *nv_path); void dhd_conf_set_path(dhd_pub_t *dhd, char *dst_name, char *dst_path, char *src_path); @@ -226,14 +225,13 @@ int dhd_conf_set_intiovar(dhd_pub_t *dhd, uint cmd, char *name, int val, int def int dhd_conf_get_iovar(dhd_pub_t *dhd, int cmd, char *name, char *buf, int len, int ifidx); int dhd_conf_set_bufiovar(dhd_pub_t *dhd, uint cmd, char *name, char *buf, int len, bool down); uint dhd_conf_get_band(dhd_pub_t *dhd); -int dhd_conf_set_country(dhd_pub_t *dhd); +int dhd_conf_set_country(dhd_pub_t *dhd, wl_country_t *cspec); int dhd_conf_get_country(dhd_pub_t *dhd, wl_country_t *cspec); -int dhd_conf_get_country_from_config(dhd_pub_t *dhd, wl_country_t *cspec); +int dhd_conf_map_country_list(dhd_pub_t *dhd, wl_country_t *cspec, int nodfs); int dhd_conf_fix_country(dhd_pub_t *dhd); bool dhd_conf_match_channel(dhd_pub_t *dhd, uint32 channel); -int dhd_conf_set_roam(dhd_pub_t *dhd); -void dhd_conf_set_bw_cap(dhd_pub_t *dhd); void dhd_conf_set_wme(dhd_pub_t *dhd, int mode); +void dhd_conf_set_mchan_bw(dhd_pub_t *dhd, int go, int source); void dhd_conf_add_pkt_filter(dhd_pub_t *dhd); bool dhd_conf_del_pkt_filter(dhd_pub_t *dhd, uint32 id); void dhd_conf_discard_pkt_filter(dhd_pub_t *dhd); @@ -247,6 +245,7 @@ int dhd_conf_get_disable_proptx(dhd_pub_t *dhd); #endif int dhd_conf_get_ap_mode_in_suspend(dhd_pub_t *dhd); int dhd_conf_set_ap_in_suspend(dhd_pub_t *dhd, int suspend); +void dhd_conf_postinit_ioctls(dhd_pub_t *dhd); int dhd_conf_preinit(dhd_pub_t *dhd); int dhd_conf_reset(dhd_pub_t *dhd); int dhd_conf_attach(dhd_pub_t *dhd); diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_gpio.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_gpio.c index a7ef7c84..dbdcbfa7 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_gpio.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_gpio.c @@ -1,5 +1,4 @@ /* SPDX-License-Identifier: GPL-2.0 */ - #include #include #include @@ -148,10 +147,10 @@ static int dhd_wlan_get_mac_addr(unsigned char *buf) bcopy((char *)&ea_example, buf, sizeof(struct ether_addr)); } #endif /* EXAMPLE_GET_MAC */ + err = rockchip_wifi_mac_addr(buf); #ifdef EXAMPLE_GET_MAC_VER2 /* EXAMPLE code */ { - char mac[6] = {0x00,0x11,0x22,0x33,0x44,0xFF}; char macpad[56]= { 0x00,0xaa,0x9c,0x84,0xc7,0xbc,0x9b,0xf6, 0x02,0x33,0xa9,0x4d,0x5c,0xb4,0x0a,0x5d, @@ -160,11 +159,9 @@ static int dhd_wlan_get_mac_addr(unsigned char *buf) 0x4a,0xeb,0xf6,0xe6,0x3c,0xe7,0x5f,0xfc, 0x0e,0xa7,0xb3,0x0f,0x00,0xe4,0x4a,0xaf, 0x87,0x08,0x16,0x6d,0x3a,0xe3,0xc7,0x80}; - bcopy(mac, buf, sizeof(mac)); bcopy(macpad, buf+6, sizeof(macpad)); } #endif /* EXAMPLE_GET_MAC_VER2 */ - err = rockchip_wifi_mac_addr(buf); return err; } @@ -255,39 +252,39 @@ int dhd_wlan_init_gpio(void) gpio_wl_host_wake = -1; #endif - printf("%s: GPIO(WL_REG_ON) = %d\n", __FUNCTION__, gpio_wl_reg_on); if (gpio_wl_reg_on >= 0) { err = gpio_request(gpio_wl_reg_on, "WL_REG_ON"); if (err < 0) { - printf("%s: Faiiled to request gpio %d for WL_REG_ON\n", + printf("%s: gpio_request(%d) for WL_REG_ON failed\n", __FUNCTION__, gpio_wl_reg_on); gpio_wl_reg_on = -1; } } #ifdef CUSTOMER_OOB - printf("%s: GPIO(WL_HOST_WAKE) = %d\n", __FUNCTION__, gpio_wl_host_wake); if (gpio_wl_host_wake >= 0) { err = gpio_request(gpio_wl_host_wake, "bcmdhd"); if (err < 0) { - printf("%s: gpio_request failed\n", __FUNCTION__); + printf("%s: gpio_request(%d) for WL_HOST_WAKE failed\n", + __FUNCTION__, gpio_wl_host_wake); return -1; } err = gpio_direction_input(gpio_wl_host_wake); if (err < 0) { - printf("%s: gpio_direction_input failed\n", __FUNCTION__); + printf("%s: gpio_direction_input(%d) for WL_HOST_WAKE failed\n", + __FUNCTION__, gpio_wl_host_wake); gpio_free(gpio_wl_host_wake); return -1; } host_oob_irq = gpio_to_irq(gpio_wl_host_wake); if (host_oob_irq < 0) { - printf("%s: gpio_to_irq failed\n", __FUNCTION__); + printf("%s: gpio_to_irq(%d) for WL_HOST_WAKE failed\n", + __FUNCTION__, gpio_wl_host_wake); gpio_free(gpio_wl_host_wake); return -1; } } host_oob_irq = rockchip_wifi_get_oob_irq(); - printf("%s: host_oob_irq: %d\n", __FUNCTION__, host_oob_irq); #ifdef HW_OOB host_oob_irq_flags = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE; @@ -304,7 +301,8 @@ int dhd_wlan_init_gpio(void) dhd_wlan_resources[0].start = dhd_wlan_resources[0].end = host_oob_irq; dhd_wlan_resources[0].flags = host_oob_irq_flags; - printf("%s: host_oob_irq_flags=0x%x\n", __FUNCTION__, host_oob_irq_flags); + printf("%s: WL_REG_ON=%d, WL_HOST_WAKE=%d\n", __FUNCTION__, gpio_wl_reg_on, gpio_wl_host_wake); + printf("%s: oob_irq=%d, oob_irq_flags=0x%x\n", __FUNCTION__, host_oob_irq, host_oob_irq_flags); #endif /* CUSTOMER_OOB */ return 0; diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_ip.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_ip.c old mode 100755 new mode 100644 index ee7d105d..d8be26cd --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_ip.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_ip.c @@ -287,10 +287,20 @@ static void _tdata_psh_info_pool_deinit(dhd_pub_t *dhdp, return; } -static void dhd_tcpack_send(ulong data) +static void dhd_tcpack_send( +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + struct timer_list *t +#else + ulong data +#endif +) { tcpack_sup_module_t *tcpack_sup_mod; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + tcpack_info_t *cur_tbl = from_timer(cur_tbl, t, timer); +#else tcpack_info_t *cur_tbl = (tcpack_info_t *)data; +#endif dhd_pub_t *dhdp; int ifidx; void* pkt; @@ -464,9 +474,13 @@ int dhd_tcpack_suppress_set(dhd_pub_t *dhdp, uint8 mode) tcpack_info_t *tcpack_info_tbl = &tcpack_sup_module->tcpack_info_tbl[i]; tcpack_info_tbl->dhdp = dhdp; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + timer_setup(&tcpack_info_tbl->timer, dhd_tcpack_send, 0); +#else init_timer(&tcpack_info_tbl->timer); tcpack_info_tbl->timer.data = (ulong)tcpack_info_tbl; tcpack_info_tbl->timer.function = dhd_tcpack_send; +#endif } break; } diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux.c index 233c1f77..87c5df2a 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux.c @@ -345,6 +345,12 @@ static void dhd_hang_process(void *dhd_info, void *event_data, u8 event); MODULE_LICENSE("GPL and additional rights"); #endif /* LinuxVer */ +#if defined(MULTIPLE_SUPPLICANT) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) +DEFINE_MUTEX(_dhd_mutex_lock_); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ +#endif + #ifdef CONFIG_BCM_DETECT_CONSECUTIVE_HANG #define MAX_CONSECUTIVE_HANG_COUNTS 5 #endif /* CONFIG_BCM_DETECT_CONSECUTIVE_HANG */ @@ -600,7 +606,7 @@ typedef struct dhd_info { dhd_pub_t pub; dhd_if_t *iflist[DHD_MAX_IFS]; /* for supporting multiple interfaces */ - void *adapter; /* adapter information, interrupt, fw path etc. */ + wifi_adapter_info_t *adapter; /* adapter information, interrupt, fw path etc. */ char fw_path[PATH_MAX]; /* path to firmware image */ char nv_path[PATH_MAX]; /* path to nvram vars file */ char clm_path[PATH_MAX]; /* path to clm vars file */ @@ -616,6 +622,10 @@ typedef struct dhd_info { #ifdef PROP_TXSTATUS spinlock_t wlfc_spinlock; +#ifdef BCMDBUS + ulong wlfc_lock_flags; + ulong wlfc_pub_lock_flags; +#endif /* BCMDBUS */ #endif /* PROP_TXSTATUS */ #ifdef WLMEDIA_HTSF htsf_t htsf; @@ -637,10 +647,14 @@ typedef struct dhd_info { spinlock_t txqlock; spinlock_t rxqlock; spinlock_t dhd_lock; +#ifdef BCMDBUS + ulong txqlock_flags; +#else struct semaphore sdsem; tsk_ctl_t thr_dpc_ctl; tsk_ctl_t thr_wdt_ctl; +#endif /* BCMDBUS */ tsk_ctl_t thr_rxf_ctl; spinlock_t rxf_lock; @@ -940,7 +954,7 @@ int op_mode = 0; int disable_proptx = 0; module_param(op_mode, int, 0644); extern int wl_control_wl_start(struct net_device *dev); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(BCMLXSDMMC) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && (defined(BCMLXSDMMC) || defined(BCMDBUS)) struct semaphore dhd_registration_sem; #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ @@ -950,6 +964,9 @@ static void dhd_ifdel_event_handler(void *handle, void *event_info, u8 event); static void dhd_set_mac_addr_handler(void *handle, void *event_info, u8 event); static void dhd_set_mcast_list_handler(void *handle, void *event_info, u8 event); +#ifdef DHD_UPDATE_INTF_MAC +static void dhd_ifupdate_event_handler(void *handle, void *event_info, u8 event); +#endif /* DHD_UPDATE_INTF_MAC */ #if defined(CONFIG_IPV6) && defined(IPV6_NDO_SUPPORT) static void dhd_inet6_work_handler(void *dhd_info, void *event_data, u8 event); #endif /* CONFIG_IPV6 && IPV6_NDO_SUPPORT */ @@ -1054,10 +1071,10 @@ module_param(dhd_dpc_prio, int, 0); int dhd_rxf_prio = CUSTOM_RXF_PRIO_SETTING; module_param(dhd_rxf_prio, int, 0); -#if !defined(BCMDHDUSB) +#if !defined(BCMDBUS) extern int dhd_dongle_ramsize; module_param(dhd_dongle_ramsize, int, 0); -#endif /* BCMDHDUSB */ +#endif /* !BCMDBUS */ #ifdef WL_CFG80211 int passive_channel_skip = 0; @@ -1903,9 +1920,11 @@ module_param(dhd_pktgen_len, uint, 0); +#ifndef BCMDBUS /* Allow delayed firmware download for debug purpose */ int allow_delay_fwdl = FALSE; module_param(allow_delay_fwdl, int, 0); +#endif /* !BCMDBUS */ extern char dhd_version[]; extern char fw_version[]; @@ -1938,7 +1957,9 @@ int dhd_monitor_uninit(void); struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev); #endif /* defined(WL_WIRELESS_EXT) */ +#ifndef BCMDBUS static void dhd_dpc(ulong data); +#endif /* !BCMDBUS */ /* forward decl */ extern int dhd_wait_pend8021x(struct net_device *dev); void dhd_os_wd_timer_extend(void *bus, bool extend); @@ -3208,8 +3229,8 @@ int dhd_process_cid_mac(dhd_pub_t *dhdp, bool prepost) } // terence 20160615: fix building error if ARP_OFFLOAD_SUPPORT removed -#if defined(PKT_FILTER_SUPPORT) && defined(ARP_OFFLOAD_SUPPORT) -#ifndef GAN_LITE_NAT_KEEPALIVE_FILTER +#if defined(PKT_FILTER_SUPPORT) +#if defined(ARP_OFFLOAD_SUPPORT) && !defined(GAN_LITE_NAT_KEEPALIVE_FILTER) static bool _turn_on_arp_filter(dhd_pub_t *dhd, int op_mode_param) { @@ -4354,6 +4375,87 @@ dhd_ifdel_event_handler(void *handle, void *event_info, u8 event) dhd_net_if_unlock_local(dhd); } +#ifdef DHD_UPDATE_INTF_MAC +static void +dhd_ifupdate_event_handler(void *handle, void *event_info, u8 event) +{ + dhd_info_t *dhd = handle; + int ifidx; + dhd_if_event_t *if_event = event_info; + + if (event != DHD_WQ_WORK_IF_UPDATE) { + DHD_ERROR(("%s: unexpected event \n", __FUNCTION__)); + return; + } + + if (!dhd) { + DHD_ERROR(("%s: dhd info not available \n", __FUNCTION__)); + return; + } + + if (!if_event) { + DHD_ERROR(("%s: event data is null \n", __FUNCTION__)); + return; + } + + dhd_net_if_lock_local(dhd); + DHD_OS_WAKE_LOCK(&dhd->pub); + + ifidx = if_event->event.ifidx; + DHD_TRACE(("%s: Update interface with idx %d\n", __FUNCTION__, ifidx)); + + dhd_op_if_update(&dhd->pub, ifidx); + + MFREE(dhd->pub.osh, if_event, sizeof(dhd_if_event_t)); + + DHD_OS_WAKE_UNLOCK(&dhd->pub); + dhd_net_if_unlock_local(dhd); +} + +int dhd_op_if_update(dhd_pub_t *dhdpub, int ifidx) +{ + dhd_info_t * dhdinfo = NULL; + dhd_if_t * ifp = NULL; + int ret = 0; + char buf[128]; + + if ((NULL==dhdpub)||(NULL==dhdpub->info)) { + DHD_ERROR(("%s: *** DHD handler is NULL!\n", __FUNCTION__)); + return -1; + } else { + dhdinfo = (dhd_info_t *)dhdpub->info; + ifp = dhdinfo->iflist[ifidx]; + if (NULL==ifp) { + DHD_ERROR(("%s: *** ifp handler is NULL!\n", __FUNCTION__)); + return -2; + } + } + + DHD_TRACE(("%s: idx %d\n", __FUNCTION__, ifidx)); + // Get MAC address + strcpy(buf, "cur_etheraddr"); + ret = dhd_wl_ioctl_cmd(&dhdinfo->pub, WLC_GET_VAR, buf, sizeof(buf), FALSE, ifp->idx); + if (0>ret) { + DHD_ERROR(("Failed to upudate the MAC address for itf=%s, ret=%d\n", ifp->name, ret)); + // avoid collision + dhdinfo->iflist[ifp->idx]->mac_addr[5] += 1; + // force locally administrate address + ETHER_SET_LOCALADDR(&dhdinfo->iflist[ifp->idx]->mac_addr); + } else { + DHD_EVENT(("Got mac for itf %s, idx %d, MAC=%02X:%02X:%02X:%02X:%02X:%02X\n", + ifp->name, ifp->idx, + (unsigned char)buf[0], (unsigned char)buf[1], (unsigned char)buf[2], + (unsigned char)buf[3], (unsigned char)buf[4], (unsigned char)buf[5])); + memcpy(dhdinfo->iflist[ifp->idx]->mac_addr, buf, ETHER_ADDR_LEN); + if (dhdinfo->iflist[ifp->idx]->net) { + memcpy(dhdinfo->iflist[ifp->idx]->net->dev_addr, buf, ETHER_ADDR_LEN); + } + } + + return ret; +} +#endif /* DHD_UPDATE_INTF_MAC */ + static void dhd_set_mac_addr_handler(void *handle, void *event_info, u8 event) { @@ -4536,7 +4638,11 @@ dhd_os_wlfc_block(dhd_pub_t *pub) /* terence 20161229: don't do spin lock if proptx not enabled */ if (disable_proptx) return 1; +#ifdef BCMDBUS + spin_lock_irqsave(&di->wlfc_spinlock, di->wlfc_lock_flags); +#else spin_lock_bh(&di->wlfc_spinlock); +#endif /* BCMDBUS */ return 1; } @@ -4549,7 +4655,11 @@ dhd_os_wlfc_unblock(dhd_pub_t *pub) /* terence 20161229: don't do spin lock if proptx not enabled */ if (disable_proptx) return 1; +#ifdef BCMDBUS + spin_unlock_irqrestore(&di->wlfc_spinlock, di->wlfc_lock_flags); +#else spin_unlock_bh(&di->wlfc_spinlock); +#endif /* BCMDBUS */ return 1; } @@ -4885,6 +4995,10 @@ __dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pktbuf) ret = dhd_bus_txdata(dhdp->bus, pktbuf); #endif /* BCMPCIE */ #endif /* PROP_TXSTATUS */ +#ifdef BCMDBUS + if (ret) + PKTCFREE(dhdp->osh, pktbuf, TRUE); +#endif /* BCMDBUS */ return ret; } @@ -5054,6 +5168,7 @@ dhd_start_xmit(struct sk_buff *skb, struct net_device *net) __FUNCTION__, dhd->pub.busstate, dhd->pub.dhd_bus_busy_state)); } #endif + DHD_OS_WAKE_LOCK(&dhd->pub); DHD_PERIM_LOCK_TRY(DHD_FWDER_UNIT(dhd), lock_taken); @@ -5070,7 +5185,7 @@ dhd_start_xmit(struct sk_buff *skb, struct net_device *net) __FUNCTION__, dhd->pub.up, dhd->pub.busstate)); netif_stop_queue(net); /* Send Event when bus down detected during data session */ - if (dhd->pub.up && !dhd->pub.hang_was_sent) { + if (dhd->pub.up && !dhd->pub.hang_was_sent && !DHD_BUS_CHECK_REMOVE(&dhd->pub)) { DHD_ERROR(("%s: Event HANG sent up\n", __FUNCTION__)); dhd->pub.hang_reason = HANG_REASON_BUS_DOWN; net_os_send_hang_message(net); @@ -5656,8 +5771,12 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) continue; } #ifdef DHD_WAKE_STATUS +#ifdef BCMDBUS + wcp = NULL; +#else pkt_wake = dhd_bus_get_bus_wake(dhdp); wcp = dhd_bus_get_wakecount(dhdp); +#endif /* BCMDBUS */ if (wcp == NULL) { /* If wakeinfo count buffer is null do not update wake count values */ pkt_wake = 0; @@ -6099,8 +6218,10 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) #endif /* DHD_WAKE_STATUS */ } +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) if (ifp->net) ifp->net->last_rx = jiffies; +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) */ if (ntoh16(skb->protocol) != ETHER_TYPE_BRCM) { dhdp->dstats.rx_bytes += skb->len; @@ -6247,6 +6368,7 @@ dhd_get_stats(struct net_device *net) return &net->stats; } +#ifndef BCMDBUS static int dhd_watchdog_thread(void *data) { @@ -6309,9 +6431,19 @@ dhd_watchdog_thread(void *data) complete_and_exit(&tsk->completed, 0); } -static void dhd_watchdog(ulong data) +static void dhd_watchdog( +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + struct timer_list *t +#else + ulong data +#endif +) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + dhd_info_t *dhd = from_timer(dhd, t, timer); +#else dhd_info_t *dhd = (dhd_info_t *)data; +#endif unsigned long flags; if (dhd->pub.dongle_reset) { @@ -6391,9 +6523,19 @@ dhd_rpm_state_thread(void *data) complete_and_exit(&tsk->completed, 0); } -static void dhd_runtimepm(ulong data) +static void dhd_runtimepm( +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + struct timer_list *t +#else + ulong data +#endif +) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + dhd_info_t *dhd = from_timer(dhd, t, rpm_timer); +#else dhd_info_t *dhd = (dhd_info_t *)data; +#endif if (dhd->pub.dongle_reset) { return; @@ -6457,6 +6599,7 @@ static int dhd_cpufreq_notifier(struct notifier_block *nb, unsigned long val, vo return 0; } #endif /* DEBUG_CPU_FREQ */ + static int dhd_dpc_thread(void *data) { @@ -6738,6 +6881,7 @@ dhd_sched_dpc(dhd_pub_t *dhdp) tasklet_schedule(&dhd->tasklet); } } +#endif /* BCMDBUS */ static void dhd_sched_rxf(dhd_pub_t *dhdp, void *skb) @@ -7008,12 +7152,12 @@ static bool dhd_check_hang(struct net_device *net, dhd_pub_t *dhdp, int error) if (!dhdp->up) return FALSE; -#if !defined(BCMPCIE) +#if !defined(BCMPCIE) && !defined(BCMDBUS) if (dhdp->info->thr_dpc_ctl.thr_pid < 0) { DHD_ERROR(("%s : skipped due to negative pid - unloading?\n", __FUNCTION__)); return FALSE; } -#endif +#endif /* !BCMPCIE && !BCMDBUS */ if ((error == -ETIMEDOUT) || (error == -EREMOTEIO) || ((dhdp->busstate == DHD_BUS_DOWN) && (!dhdp->dongle_reset))) { @@ -7387,6 +7531,7 @@ int dhd_ioctl_process(dhd_pub_t *pub, int ifidx, dhd_ioctl_t *ioc, void *data_bu if (data_buf) buflen = MIN(ioc->len, WLC_IOCTL_MAXLEN); +#ifndef BCMDBUS /* send to dongle (must be up, and wl). */ if (pub->busstate == DHD_BUS_DOWN || pub->busstate == DHD_BUS_LOAD) { if ((!pub->dongle_trap_occured) && allow_delay_fwdl) { @@ -7412,6 +7557,7 @@ int dhd_ioctl_process(dhd_pub_t *pub, int ifidx, dhd_ioctl_t *ioc, void *data_bu bcmerror = BCME_DONGLE_DOWN; goto done; } +#endif /* !BCMDBUS */ /* * Flush the TX queue if required for proper message serialization: @@ -8230,7 +8376,7 @@ dhd_stop(struct net_device *net) #else wl_android_wifi_off(net, TRUE); #ifdef WL_EXT_IAPSTA - wl_android_ext_dettach_netdev(); + wl_ext_iapsta_dettach_netdev(); #endif } else { if (dhd->pub.conf->deepsleep) @@ -8304,6 +8450,10 @@ dhd_open(struct net_device *net) uint32 slot_num = -1; wifi_adapter_info_t *adapter = NULL; #endif +#if defined(WL_EXT_IAPSTA) && defined(ISAM_PREINIT) + int bytes_written = 0; + struct dhd_conf *conf; +#endif if (!dhd_download_fw_on_driverload) { if (!dhd_driver_init_done) { @@ -8313,14 +8463,7 @@ dhd_open(struct net_device *net) } printf("%s: Enter %p\n", __FUNCTION__, net); -#if defined(MULTIPLE_SUPPLICANT) -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 - if (mutex_is_locked(&_dhd_sdio_mutex_lock_) != 0) { - DHD_ERROR(("%s : dhd_open: call dev open before insmod complete!\n", __FUNCTION__)); - } - mutex_lock(&_dhd_sdio_mutex_lock_); -#endif -#endif /* MULTIPLE_SUPPLICANT */ + DHD_MUTEX_LOCK(); /* Init wakelock */ if (!dhd_download_fw_on_driverload) { if (!(dhd->dhd_state & DHD_ATTACH_STATE_WAKELOCKS_INIT)) { @@ -8406,6 +8549,9 @@ dhd_open(struct net_device *net) atomic_set(&dhd->pend_8021x_cnt, 0); if (!dhd_download_fw_on_driverload) { DHD_ERROR(("\n%s\n", dhd_version)); +#ifdef WL_EXT_IAPSTA + wl_ext_iapsta_attach_netdev(net, ifidx); +#endif #if defined(USE_INITIAL_SHORT_DWELL_TIME) g_first_broadcast_scan = TRUE; #endif @@ -8421,6 +8567,14 @@ dhd_open(struct net_device *net) ret = -1; goto exit; } +#if defined(WL_EXT_IAPSTA) && defined(ISAM_PREINIT) + conf = dhd_get_conf(net); + if (conf) { + wl_android_ext_priv_cmd(net, conf->isam_init, 0, &bytes_written); + wl_android_ext_priv_cmd(net, conf->isam_config, 0, &bytes_written); + wl_android_ext_priv_cmd(net, conf->isam_enable, 0, &bytes_written); + } +#endif } #ifdef FIX_CPU_MIN_CLOCK if (dhd_get_fw_mode(dhd) == DHD_FLAG_HOSTAP_MODE) { @@ -8440,7 +8594,24 @@ dhd_open(struct net_device *net) #endif if (dhd->pub.busstate != DHD_BUS_DATA) { - +#ifdef BCMDBUS + dhd_set_path(&dhd->pub); + DHD_MUTEX_UNLOCK(); + wait_event_interruptible_timeout(dhd->adapter->status_event, + wifi_get_adapter_status(dhd->adapter, WIFI_STATUS_FW_READY), + msecs_to_jiffies(DHD_FW_READY_TIMEOUT)); + DHD_MUTEX_LOCK(); + if ((ret = dbus_up(dhd->pub.bus)) != 0) { + DHD_ERROR(("%s: failed to dbus_up with code %d\n", __FUNCTION__, ret)); + goto exit; + } else { + dhd->pub.busstate = DHD_BUS_DATA; + } + if ((ret = dhd_sync_with_dongle(&dhd->pub)) < 0) { + DHD_ERROR(("%s: failed with code %d\n", __FUNCTION__, ret)); + goto exit; + } +#else /* try to bring up bus */ DHD_PERIM_UNLOCK(&dhd->pub); ret = dhd_bus_start(&dhd->pub); @@ -8450,8 +8621,12 @@ dhd_open(struct net_device *net) ret = -1; goto exit; } +#endif /* !BCMDBUS */ } +#ifdef WL_EXT_IAPSTA + wl_ext_iapsta_attach_name(net, ifidx); +#endif if (dhd_download_fw_on_driverload) { if (dhd->pub.conf->deepsleep) dhd_deepsleep(dhd, 0); @@ -8560,9 +8735,6 @@ dhd_open(struct net_device *net) } argos_register_notifier_init(net); -#if defined(DHDTCPACK_SUPPRESS) - dhd_tcpack_suppress_set(&dhd->pub, TCPACK_SUP_DEFAULT); -#endif /* DHDTCPACK_SUPPRESS */ #if defined(NUM_SCB_MAX_PROBE) dhd_set_scb_probe(&dhd->pub); #endif /* NUM_SCB_MAX_PROBE */ @@ -8586,12 +8758,7 @@ dhd_open(struct net_device *net) DHD_PERIM_UNLOCK(&dhd->pub); DHD_OS_WAKE_UNLOCK(&dhd->pub); - -#if defined(MULTIPLE_SUPPLICANT) -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 - mutex_unlock(&_dhd_sdio_mutex_lock_); -#endif -#endif /* MULTIPLE_SUPPLICANT */ + DHD_MUTEX_UNLOCK(); printf("%s: Exit ret=%d\n", __FUNCTION__, ret); return ret; @@ -8606,14 +8773,7 @@ int dhd_do_driver_init(struct net_device *net) return -EINVAL; } -#ifdef MULTIPLE_SUPPLICANT -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 && defined(BCMSDIO) - if (mutex_is_locked(&_dhd_sdio_mutex_lock_) != 0) { - DHD_ERROR(("%s : dhdsdio_probe is already running!\n", __FUNCTION__)); - return 0; - } -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ -#endif /* MULTIPLE_SUPPLICANT */ + DHD_MUTEX_IS_LOCK_RETURN(); /* && defined(OEM_ANDROID) && defined(BCMSDIO) */ dhd = DHD_DEV_INFO(net); @@ -8700,10 +8860,36 @@ dhd_event_ifdel(dhd_info_t *dhdinfo, wl_event_data_if_t *ifevent, char *name, ui int dhd_event_ifchange(dhd_info_t *dhdinfo, wl_event_data_if_t *ifevent, char *name, uint8 *mac) { +#ifdef DHD_UPDATE_INTF_MAC + dhd_if_event_t *if_event; +#endif /* DHD_UPDATE_INTF_MAC */ + #ifdef WL_CFG80211 wl_cfg80211_notify_ifchange(dhd_linux_get_primary_netdev(&dhdinfo->pub), ifevent->ifidx, name, mac, ifevent->bssidx); #endif /* WL_CFG80211 */ + +#ifdef DHD_UPDATE_INTF_MAC + /* handle IF event caused by wl commands, SoftAP, WEXT, MBSS and + * anything else + */ + if_event = MALLOC(dhdinfo->pub.osh, sizeof(dhd_if_event_t)); + if (if_event == NULL) { + DHD_ERROR(("dhd_event_ifdel: malloc failed for if_event, malloced %d bytes", + MALLOCED(dhdinfo->pub.osh))); + return BCME_NOMEM; + } + memcpy(&if_event->event, ifevent, sizeof(if_event->event)); + // construct a change event + if_event->event.ifidx = dhd_ifname2idx(dhdinfo, name); + if_event->event.opcode = WLC_E_IF_CHANGE; + memcpy(if_event->mac, mac, ETHER_ADDR_LEN); + strncpy(if_event->name, name, IFNAMSIZ); + if_event->name[IFNAMSIZ - 1] = '\0'; + dhd_deferred_schedule_work(dhdinfo->dhd_deferred_wq, (void *)if_event, DHD_WQ_WORK_IF_UPDATE, + dhd_ifupdate_event_handler, DHD_WQ_WORK_PRIORITY_LOW); +#endif /* DHD_UPDATE_INTF_MAC */ + return BCME_OK; } @@ -8788,12 +8974,26 @@ dhd_allocate_if(dhd_pub_t *dhdpub, int ifidx, const char *name, } #ifdef WL_CFG80211 - if (ifidx == 0) + if (ifidx == 0) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) ifp->net->destructor = free_netdev; - else +#else + ifp->net->needs_free_netdev = true; +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) */ + } else { +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) ifp->net->destructor = dhd_netdev_free; #else + ifp->net->needs_free_netdev = true; + ifp->net->priv_destructor = dhd_netdev_free; +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) */ + } +#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) ifp->net->destructor = free_netdev; +#else + ifp->net->needs_free_netdev = true; +#endif #endif /* WL_CFG80211 */ strncpy(ifp->name, ifp->net->name, IFNAMSIZ); ifp->name[IFNAMSIZ - 1] = '\0'; @@ -9173,20 +9373,61 @@ dhd_init_static_strs_array(osl_t *osh, dhd_event_log_t *temp, char *str_file, ch #endif /* SHOW_LOGTRACE */ +#ifdef BCMDBUS +uint +dhd_get_rxsz(dhd_pub_t *pub) +{ + struct net_device *net = NULL; + dhd_info_t *dhd = NULL; + uint rxsz; + + /* Assign rxsz for dbus_attach */ + dhd = pub->info; + net = dhd->iflist[0]->net; + net->hard_header_len = ETH_HLEN + pub->hdrlen; + rxsz = DBUS_RX_BUFFER_SIZE_DHD(net); + + return rxsz; +} + +void +dhd_set_path(dhd_pub_t *pub) +{ + dhd_info_t *dhd = NULL; + + dhd = pub->info; + + /* try to download image and nvram to the dongle */ + if (dhd_update_fw_nv_path(dhd) && dhd->pub.bus) { + DHD_INFO(("%s: fw %s, nv %s, conf %s\n", + __FUNCTION__, dhd->fw_path, dhd->nv_path, dhd->conf_path)); + dhd_bus_update_fw_nv_path(dhd->pub.bus, + dhd->fw_path, dhd->nv_path, dhd->clm_path, dhd->conf_path); + } +} +#endif dhd_pub_t * -dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) +dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen +#ifdef BCMDBUS + , void *data +#endif +) { dhd_info_t *dhd = NULL; struct net_device *net = NULL; char if_name[IFNAMSIZ] = {'\0'}; - uint32 bus_type = -1; - uint32 bus_num = -1; - uint32 slot_num = -1; #ifdef SHOW_LOGTRACE int ret; #endif /* SHOW_LOGTRACE */ +#if defined(BCMSDIO) || defined(BCMPCIE) + uint32 bus_type = -1; + uint32 bus_num = -1; + uint32 slot_num = -1; wifi_adapter_info_t *adapter = NULL; +#elif defined(BCMDBUS) + wifi_adapter_info_t *adapter = data; +#endif dhd_attach_states_t dhd_state = DHD_ATTACH_STATE_INIT; DHD_TRACE(("%s: Enter\n", __FUNCTION__)); @@ -9198,7 +9439,9 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) #if defined(BCMSDIO) dhd_bus_get_ids(bus, &bus_type, &bus_num, &slot_num); #endif +#if defined(BCMSDIO) || defined(BCMPCIE) adapter = dhd_wifi_platform_get_adapter(bus_type, bus_num, slot_num); +#endif /* Allocate primary dhd_info */ dhd = wifi_platform_prealloc(adapter, DHD_PREALLOC_DHD_INFO, sizeof(dhd_info_t)); @@ -9219,6 +9462,7 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) dll_init(&(dhd->pub.dump_iovlist_head)); #endif /* DUMP_IOCTL_IOV_LIST */ dhd->adapter = adapter; + dhd->pub.adapter = (void *)adapter; #ifdef DHD_DEBUG dll_init(&(dhd->pub.mw_list_head)); #endif /* DHD_DEBUG */ @@ -9239,6 +9483,7 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) dhd->pub.dhd_cspec.country_abbrev, &dhd->pub.dhd_cspec, dhd->pub.dhd_cflags); #endif /* CUSTOM_COUNTRY_CODE */ +#ifndef BCMDBUS dhd->thr_dpc_ctl.thr_pid = DHD_PID_KT_TL_INVALID; dhd->thr_wdt_ctl.thr_pid = DHD_PID_KT_INVALID; #ifdef DHD_WET @@ -9246,6 +9491,7 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) #endif /* DHD_WET */ /* Initialize thread based operation and lock */ sema_init(&dhd->sdsem, 1); +#endif /* !BCMDBUS */ /* Link to info module */ dhd->pub.info = dhd; @@ -9262,9 +9508,11 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) DHD_ERROR(("dhd_conf_attach failed\n")); goto fail; } +#ifndef BCMDBUS dhd_conf_reset(&dhd->pub); dhd_conf_set_chiprev(&dhd->pub, dhd_bus_chip(bus), dhd_bus_chiprev(bus)); dhd_conf_preinit(&dhd->pub); +#endif /* !BCMDBUS */ /* Some DHD modules (e.g. cfg80211) configures operation mode based on firmware name. * This is indeed a hack but we have to make it work properly before we have a better @@ -9458,10 +9706,15 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) +#ifndef BCMDBUS /* Set up the watchdog timer */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + timer_setup(&dhd->timer, dhd_watchdog, 0); +#else init_timer(&dhd->timer); dhd->timer.data = (ulong)dhd; dhd->timer.function = dhd_watchdog; +#endif dhd->default_wd_interval = dhd_watchdog_ms; if (dhd_watchdog_prio >= 0) { @@ -9477,9 +9730,13 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) #ifdef DHD_PCIE_RUNTIMEPM /* Setup up the runtime PM Idlecount timer */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + timer_setup(&dhd->rpm_timer, dhd_runtimepm, 0); +#else init_timer(&dhd->rpm_timer); dhd->rpm_timer.data = (ulong)dhd; dhd->rpm_timer.function = dhd_runtimepm; +#endif dhd->rpm_timer_valid = FALSE; dhd->thr_rpm_ctl.thr_pid = DHD_PID_KT_INVALID; @@ -9492,9 +9749,6 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) #ifdef DEBUGGER debugger_init((void *) bus); #endif -#ifdef SHOW_LOGTRACE - skb_queue_head_init(&dhd->evt_trace_queue); -#endif /* SHOW_LOGTRACE */ /* Set up the bottom half handler */ if (dhd_dpc_prio >= 0) { @@ -9517,6 +9771,10 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) goto fail; } } +#endif /* !BCMDBUS */ +#ifdef SHOW_LOGTRACE + skb_queue_head_init(&dhd->evt_trace_queue); +#endif /* SHOW_LOGTRACE */ dhd_state |= DHD_ATTACH_STATE_THREADS_CREATED; @@ -10046,6 +10304,7 @@ int dhd_download_btfw(wlan_bt_handle_t handle, char* btfw_path) } EXPORT_SYMBOL(dhd_download_btfw); #endif /* defined (BT_OVER_SDIO) */ +#ifndef BCMDBUS int dhd_bus_start(dhd_pub_t *dhdp) { @@ -10227,6 +10486,7 @@ dhd_bus_start(dhd_pub_t *dhdp) DHD_PERIM_UNLOCK(dhdp); return 0; } +#endif /* !BCMDBUS */ #ifdef WLTDLS int _dhd_tdls_enable(dhd_pub_t *dhd, bool tdls_on, bool auto_on, struct ether_addr *mac) @@ -10582,9 +10842,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) char eventmask[WL_EVENTING_MASK_LEN]; char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ uint32 buf_key_b4_m4 = 1; -#ifndef WL_CFG80211 - u32 up = 0; -#endif uint8 msglen; eventmsgs_ext_t *eventmask_msg = NULL; char* iov_buf = NULL; @@ -10604,7 +10861,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #endif shub_control_t shub_ctl; -#if defined(BCMSDIO) +#if defined(BCMSDIO) || defined(BCMDBUS) #ifdef PROP_TXSTATUS int wlfc_enable = TRUE; #ifndef DISABLE_11N @@ -10612,7 +10869,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) uint wl_down = 1; #endif /* DISABLE_11N */ #endif /* PROP_TXSTATUS */ -#endif +#endif /* BCMSDIO || BCMDBUS */ #ifndef PCIE_FULL_DONGLE uint32 wl_ap_isolate; #endif /* PCIE_FULL_DONGLE */ @@ -10632,7 +10889,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #if defined(CUSTOMER_HW2) && defined(USE_WL_CREDALL) uint32 credall = 1; #endif - uint bcn_timeout = dhd->conf->bcn_timeout; + uint bcn_timeout = CUSTOM_BCN_TIMEOUT; uint scancache_enab = TRUE; #ifdef ENABLE_BCN_LI_BCN_WAKEUP uint32 bcn_li_bcn = 1; @@ -10744,7 +11001,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #endif /* CUSTOM_SET_OCLOFF */ DHD_TRACE(("Enter %s\n", __FUNCTION__)); - dhd_conf_set_intiovar(dhd, WLC_SET_BAND, "WLC_SET_BAND", dhd->conf->band, 0, FALSE); #ifdef DHDTCPACK_SUPPRESS printf("%s: Set tcpack_sup_mode %d\n", __FUNCTION__, dhd->conf->tcpack_sup_mode); dhd_tcpack_suppress_set(dhd, dhd->conf->tcpack_sup_mode); @@ -11021,17 +11277,11 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #endif /* Set Country code */ if (dhd->dhd_cspec.ccode[0] != 0) { - printf("Set country %s, revision %d\n", dhd->dhd_cspec.ccode, dhd->dhd_cspec.rev); ret = dhd_iovar(dhd, 0, "country", (char *)&dhd->dhd_cspec, sizeof(wl_country_t), NULL, 0, TRUE); if (ret < 0) - printf("%s: country code setting failed %d\n", __FUNCTION__, ret); - } else { - dhd_conf_set_country(dhd); - dhd_conf_fix_country(dhd); + DHD_ERROR(("%s: country code setting failed\n", __FUNCTION__)); } - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "autocountry", dhd->conf->autocountry, 0, FALSE); - dhd_conf_get_country(dhd, &dhd->dhd_cspec); /* Set Listen Interval */ @@ -11068,7 +11318,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) if (ret < 0) DHD_ERROR(("%s: roam fullscan period set failed %d\n", __FUNCTION__, ret)); #endif /* ROAM_ENABLE */ - dhd_conf_set_roam(dhd); #ifdef CUSTOM_EVENT_PM_WAKE ret = dhd_iovar(dhd, 0, "const_awake_thresh", (char *)&pm_awake_thresh, @@ -11104,7 +11353,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) } } #endif /* DHD_ENABLE_LPC */ - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "lpc", dhd->conf->lpc, 0, TRUE); #ifdef WLADPS #ifdef WLADPS_SEAK_AP_WAR @@ -11123,10 +11371,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #endif /* WLADPS */ /* Set PowerSave mode */ - if (dhd->conf->pm >= 0) - power_mode = dhd->conf->pm; (void) dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, sizeof(power_mode), TRUE, 0); - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "pm2_sleep_ret", dhd->conf->pm2_sleep_ret, 0, FALSE); #if defined(BCMSDIO) /* Match Host and Dongle rx alignment */ @@ -11159,27 +11404,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) dhd_iovar(dhd, 0, "apsta", (char *)&apsta, sizeof(apsta), NULL, 0, TRUE); #endif /* defined(AP) && !defined(WLP2P) */ - /* 0:HT20 in ALL, 1:HT40 in ALL, 2: HT20 in 2G HT40 in 5G */ - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "mimo_bw_cap", dhd->conf->mimo_bw_cap, 0, TRUE); - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "force_wme_ac", dhd->conf->force_wme_ac, 1, FALSE); - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "stbc_tx", dhd->conf->stbc, 0, FALSE); - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "stbc_rx", dhd->conf->stbc, 0, FALSE); - dhd_conf_set_intiovar(dhd, WLC_SET_SRL, "WLC_SET_SRL", dhd->conf->srl, 0, TRUE); - dhd_conf_set_intiovar(dhd, WLC_SET_LRL, "WLC_SET_LRL", dhd->conf->lrl, 0, FALSE); - dhd_conf_set_intiovar(dhd, WLC_SET_SPECT_MANAGMENT, "WLC_SET_SPECT_MANAGMENT", dhd->conf->spect, 0, FALSE); - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "rsdb_mode", dhd->conf->rsdb_mode, -1, TRUE); - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "vhtmode", dhd->conf->vhtmode, 0, TRUE); -#ifdef IDHCP - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "dhcpc_enable", dhd->conf->dhcpc_enable, 0, FALSE); - if(dhd->conf->dhcpd_enable >= 0){ - dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_addr", (char *)&dhd->conf->dhcpd_ip_addr, sizeof(dhd->conf->dhcpd_ip_addr), FALSE); - dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_mask", (char *)&dhd->conf->dhcpd_ip_mask, sizeof(dhd->conf->dhcpd_ip_mask), FALSE); - dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_start", (char *)&dhd->conf->dhcpd_ip_start, sizeof(dhd->conf->dhcpd_ip_start), FALSE); - dhd_conf_set_bufiovar(dhd, WLC_SET_VAR, "dhcpd_ip_end", (char *)&dhd->conf->dhcpd_ip_end, sizeof(dhd->conf->dhcpd_ip_end), FALSE); - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "dhcpd_enable", dhd->conf->dhcpd_enable, 0, FALSE); - } -#endif - dhd_conf_set_bw_cap(dhd); #ifdef MIMO_ANT_SETTING dhd_sel_ant_from_file(dhd); @@ -11214,7 +11438,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) DHD_ERROR(("%s Set txbf failed %d\n", __FUNCTION__, ret)); #endif /* USE_WL_TXBF */ - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "txbf", dhd->conf->txbf, 0, FALSE); ret = dhd_iovar(dhd, 0, "scancache", (char *)&scancache_enab, sizeof(scancache_enab), NULL, 0, TRUE); @@ -11251,7 +11474,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) sizeof(frameburst), TRUE, 0)) < 0) { DHD_INFO(("%s frameburst not supported %d\n", __FUNCTION__, ret)); } - dhd_conf_set_intiovar(dhd, WLC_SET_FAKEFRAG, "WLC_SET_FAKEFRAG", dhd->conf->frameburst, 0, FALSE); iov_buf = (char*)kmalloc(WLC_IOCTL_SMLEN, GFP_KERNEL); if (iov_buf == NULL) { @@ -11275,7 +11497,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) } } #endif - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "ampdu_ba_wsize", dhd->conf->ampdu_ba_wsize, 1, FALSE); #ifdef ENABLE_TEMP_THROTTLING if (dhd->op_mode & DHD_FLAG_STA_MODE) { @@ -11694,9 +11915,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) bcmstrtok(&ptr, "\n", 0); strncpy(fw_version, buf, FW_VER_STR_LEN); fw_version[FW_VER_STR_LEN-1] = '\0'; -#if defined(BCMSDIO) || defined(BCMPCIE) dhd_set_version_info(dhd, buf); -#endif /* BCMSDIO || BCMPCIE */ #ifdef WRITE_WLANINFO sec_save_wlinfo(buf, EPI_VERSION_STR, dhd->info->nv_path, clm_version); #endif /* WRITE_WLANINFO */ @@ -11707,11 +11926,9 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #if defined(BCMSDIO) dhd_txglom_enable(dhd, dhd->conf->bus_rxglom); - // terence 20151210: set bus:txglom after dhd_txglom_enable since it's possible changed in dhd_conf_set_txglom_params - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "bus:txglom", dhd->conf->bus_txglom, 0, FALSE); #endif /* defined(BCMSDIO) */ -#if defined(BCMSDIO) +#if defined(BCMSDIO) || defined(BCMDBUS) #ifdef PROP_TXSTATUS if (disable_proptx || #ifdef PROP_TXSTATUS_VSDB @@ -11784,8 +12001,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) printf("%s: not define PROP_TXSTATUS\n", __FUNCTION__); dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "ampdu_hostreorder", 0, 0, TRUE); #endif /* PROP_TXSTATUS */ - dhd_conf_set_intiovar(dhd, WLC_SET_VAR, "ampdu_hostreorder", dhd->conf->ampdu_hostreorder, 0, TRUE); -#endif /* BCMSDIO || BCMBUS */ +#endif /* BCMSDIO || BCMDBUS */ #ifndef PCIE_FULL_DONGLE /* For FD we need all the packets at DHD to handle intra-BSS forwarding */ if (FW_SUPPORTED(dhd, ap)) { @@ -11812,9 +12028,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #ifdef WL11U dhd_interworking_enable(dhd); #endif /* WL11U */ -#ifndef WL_CFG80211 - dhd_wl_ioctl_cmd(dhd, WLC_UP, (char *)&up, sizeof(up), TRUE, 0); -#endif #ifdef SUPPORT_SENSORHUB DHD_ERROR(("%s: SensorHub enabled %d\n", @@ -11897,6 +12110,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) DHD_ERROR(("failed to set WNM capabilities\n")); } + dhd_conf_postinit_ioctls(dhd); done: if (eventmask_msg) @@ -12448,13 +12662,27 @@ dhd_register_if(dhd_pub_t *dhdp, int ifidx, bool need_rtnl_lock) dhd->pub.rxsz = DBUS_RX_BUFFER_SIZE_DHD(net); +#ifdef WLMESH + if (ifidx >= 2 && dhdp->conf->fw_type == FW_TYPE_MESH) { + temp_addr[4] ^= 0x80; + temp_addr[4] += ifidx; + temp_addr[5] += ifidx; + } +#endif memcpy(net->dev_addr, temp_addr, ETHER_ADDR_LEN); if (ifidx == 0) printf("%s\n", dhd_version); #ifdef WL_EXT_IAPSTA - else if (!strncmp(net->name, "wl0.", strlen("wl0."))) { - wl_android_ext_attach_netdev(net, ifidx); + else + wl_ext_iapsta_attach_netdev(net, ifidx); +#endif +#ifdef WLMESH + if (ifidx != 0 && dhdp->conf->fw_type == FW_TYPE_MESH) { + if (_dhd_set_mac_address(dhd, ifidx, temp_addr) == 0) + DHD_INFO(("%s: MACID is overwritten\n", __FUNCTION__)); + else + DHD_ERROR(("%s: _dhd_set_mac_address() failed\n", __FUNCTION__)); } #endif @@ -12467,6 +12695,11 @@ dhd_register_if(dhd_pub_t *dhdp, int ifidx, bool need_rtnl_lock) DHD_ERROR(("couldn't register the net device [%s], err %d\n", net->name, err)); goto fail; } +#ifdef WL_EXT_IAPSTA + if (ifidx == 0) + wl_ext_iapsta_attach_netdev(net, ifidx); + wl_ext_iapsta_attach_name(net, ifidx); +#endif @@ -12482,7 +12715,7 @@ dhd_register_if(dhd_pub_t *dhdp, int ifidx, bool need_rtnl_lock) #endif #if (defined(BCMPCIE) || (defined(BCMLXSDMMC) && (LINUX_VERSION_CODE >= \ - KERNEL_VERSION(2, 6, 27)))) + KERNEL_VERSION(2, 6, 27))) || defined(BCMDBUS)) if (ifidx == 0) { #if defined(BCMLXSDMMC) && !defined(DHD_PRELOAD) up(&dhd_registration_sem); @@ -12549,7 +12782,16 @@ dhd_bus_detach(dhd_pub_t *dhdp) dhd_prot_stop(&dhd->pub); /* Stop the bus module */ +#ifdef BCMDBUS + /* Force Dongle terminated */ + if (dhd_wl_ioctl_cmd(dhdp, WLC_TERMINATED, NULL, 0, TRUE, 0) < 0) + DHD_ERROR(("%s Setting WLC_TERMINATED failed\n", + __FUNCTION__)); + dbus_stop(dhd->pub.bus); + dhd->pub.busstate = DHD_BUS_DOWN; +#else dhd_bus_stop(dhd->pub.bus, TRUE); +#endif /* BCMDBUS */ } #if defined(OOB_INTR_ONLY) || defined(BCMPCIE_OOB_HOST_WAKE) @@ -12702,9 +12944,6 @@ void dhd_detach(dhd_pub_t *dhdp) ASSERT(ifp); ASSERT(ifp->net); if (ifp && ifp->net) { - - - #ifdef WL_CFG80211 cfg = wl_get_cfg(ifp->net); #endif @@ -12754,6 +12993,9 @@ void dhd_detach(dhd_pub_t *dhdp) del_timer_sync(&dhd->timer); DHD_DISABLE_RUNTIME_PM(&dhd->pub); +#ifdef BCMDBUS + tasklet_kill(&dhd->tasklet); +#else if (dhd->dhd_state & DHD_ATTACH_STATE_THREADS_CREATED) { #ifdef DHD_PCIE_RUNTIMEPM if (dhd->thr_rpm_ctl.thr_pid >= 0) { @@ -12775,6 +13017,7 @@ void dhd_detach(dhd_pub_t *dhdp) tasklet_kill(&dhd->tasklet); } } +#endif /* BCMDBUS */ #ifdef DHD_LB if (dhd->dhd_state & DHD_ATTACH_STATE_LB_ATTACH_DONE) { @@ -12893,14 +13136,14 @@ void dhd_detach(dhd_pub_t *dhdp) dhd->new_freq = NULL; cpufreq_unregister_notifier(&dhd->freq_trans, CPUFREQ_TRANSITION_NOTIFIER); #endif + DHD_TRACE(("wd wakelock count:%d\n", dhd->wakelock_wd_counter)); #ifdef CONFIG_HAS_WAKELOCK dhd->wakelock_wd_counter = 0; wake_lock_destroy(&dhd->wl_wdwake); - // terence 20161023: can not destroy wl_wifi when wlan down, it will happen null pointer in dhd_ioctl_entry - wake_lock_destroy(&dhd->wl_wifi); + // terence 20161023: can not destroy wl_wifi when wlan down, it will happen null pointer in dhd_ioctl_entry + wake_lock_destroy(&dhd->wl_wifi); #endif /* CONFIG_HAS_WAKELOCK */ if (dhd->dhd_state & DHD_ATTACH_STATE_WAKELOCKS_INIT) { - DHD_TRACE(("wd wakelock count:%d\n", dhd->wakelock_wd_counter)); DHD_OS_WAKE_LOCK_DESTROY(dhd); } @@ -13264,10 +13507,15 @@ dhd_os_set_ioctl_resp_timeout(unsigned int timeout_msec) } int -dhd_os_ioctl_resp_wait(dhd_pub_t *pub, uint *condition) +dhd_os_ioctl_resp_wait(dhd_pub_t *pub, uint *condition, bool resched) { dhd_info_t * dhd = (dhd_info_t *)(pub->info); - int timeout; + int timeout, timeout_tmp = dhd_ioctl_timeout_msec; + + if (!resched && pub->conf->ctrl_resched>0 && pub->conf->dhd_ioctl_timeout_msec>0) { + timeout_tmp = dhd_ioctl_timeout_msec; + dhd_ioctl_timeout_msec = pub->conf->dhd_ioctl_timeout_msec; + } /* Convert timeout in millsecond to jiffies */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) @@ -13280,6 +13528,10 @@ dhd_os_ioctl_resp_wait(dhd_pub_t *pub, uint *condition) timeout = wait_event_timeout(dhd->ioctl_resp_wait, (*condition), timeout); + if (!resched && pub->conf->ctrl_resched>0 && pub->conf->dhd_ioctl_timeout_msec>0) { + dhd_ioctl_timeout_msec = timeout_tmp; + } + DHD_PERIM_LOCK(pub); return timeout; @@ -13422,6 +13674,7 @@ dhd_os_busbusy_wake(dhd_pub_t *pub) void dhd_os_wd_timer_extend(void *bus, bool extend) { +#ifndef BCMDBUS dhd_pub_t *pub = bus; dhd_info_t *dhd = (dhd_info_t *)pub->info; @@ -13429,12 +13682,14 @@ dhd_os_wd_timer_extend(void *bus, bool extend) dhd_os_wd_timer(bus, WATCHDOG_EXTEND_INTERVAL); else dhd_os_wd_timer(bus, dhd->default_wd_interval); +#endif /* !BCMDBUS */ } void dhd_os_wd_timer(void *bus, uint wdtick) { +#ifndef BCMDBUS dhd_pub_t *pub = bus; dhd_info_t *dhd = (dhd_info_t *)pub->info; unsigned long flags; @@ -13469,6 +13724,7 @@ dhd_os_wd_timer(void *bus, uint wdtick) dhd->wd_timer_valid = TRUE; } DHD_GENERAL_UNLOCK(pub, flags); +#endif /* !BCMDBUS */ } #ifdef DHD_PCIE_RUNTIMEPM @@ -13570,15 +13826,21 @@ dhd_os_get_image_block(char *buf, int len, void *image) } size = i_size_read(file_inode(fp)); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + rdlen = kernel_read(fp, buf, MIN(len, size), &fp->f_pos); +#else rdlen = kernel_read(fp, fp->f_pos, buf, MIN(len, size)); +#endif if (len >= size && size != rdlen) { return -EIO; } +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) if (rdlen > 0) { fp->f_pos += rdlen; } +#endif return rdlen; } @@ -13609,7 +13871,11 @@ dhd_os_gets_image(dhd_pub_t *pub, char *str, int len, void *image) if (!image) return 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + rd_len = kernel_read(fp, str, len, &fp->f_pos); +#else rd_len = kernel_read(fp, fp->f_pos, str, len); +#endif str_end = strnchr(str, len, '\n'); if (str_end == NULL) { goto err; @@ -13640,10 +13906,14 @@ dhd_os_sdlock(dhd_pub_t *pub) dhd = (dhd_info_t *)(pub->info); +#ifdef BCMDBUS + spin_lock_bh(&dhd->sdlock); +#else if (dhd_dpc_prio >= 0) down(&dhd->sdsem); else spin_lock_bh(&dhd->sdlock); +#endif /* !BCMDBUS */ } void @@ -13653,10 +13923,14 @@ dhd_os_sdunlock(dhd_pub_t *pub) dhd = (dhd_info_t *)(pub->info); +#ifdef BCMDBUS + spin_unlock_bh(&dhd->sdlock); +#else if (dhd_dpc_prio >= 0) up(&dhd->sdsem); else spin_unlock_bh(&dhd->sdlock); +#endif /* !BCMDBUS */ } void @@ -13665,7 +13939,11 @@ dhd_os_sdlock_txq(dhd_pub_t *pub) dhd_info_t *dhd; dhd = (dhd_info_t *)(pub->info); +#ifdef BCMDBUS + spin_lock_irqsave(&dhd->txqlock, dhd->txqlock_flags); +#else spin_lock_bh(&dhd->txqlock); +#endif /* BCMDBUS */ } void @@ -13674,17 +13952,33 @@ dhd_os_sdunlock_txq(dhd_pub_t *pub) dhd_info_t *dhd; dhd = (dhd_info_t *)(pub->info); +#ifdef BCMDBUS + spin_unlock_irqrestore(&dhd->txqlock, dhd->txqlock_flags); +#else spin_unlock_bh(&dhd->txqlock); +#endif /* BCMDBUS */ } void dhd_os_sdlock_rxq(dhd_pub_t *pub) { +#if 0 + dhd_info_t *dhd; + + dhd = (dhd_info_t *)(pub->info); + spin_lock_bh(&dhd->rxqlock); +#endif } void dhd_os_sdunlock_rxq(dhd_pub_t *pub) { +#if 0 + dhd_info_t *dhd; + + dhd = (dhd_info_t *)(pub->info); + spin_unlock_bh(&dhd->rxqlock); +#endif } static void @@ -13804,6 +14098,9 @@ dhd_wl_host_event(dhd_info_t *dhd, int ifidx, void *pktdata, uint16 pktlen, if (bcmerror != BCME_OK) return (bcmerror); +#if defined(WL_EXT_IAPSTA) + wl_ext_iapsta_event(dhd->iflist[ifidx]->net, event, *data); +#endif /* defined(WL_EXT_IAPSTA) */ #if defined(WL_WIRELESS_EXT) if (event->bsscfgidx == 0) { /* @@ -13929,7 +14226,7 @@ void dhd_wait_event_wakeup(dhd_pub_t *dhd) return; } -#if defined(BCMSDIO) || defined(BCMPCIE) +#if defined(BCMSDIO) || defined(BCMPCIE) || defined(BCMDBUS) int dhd_net_bus_devreset(struct net_device *dev, uint8 flag) { @@ -13997,7 +14294,7 @@ dhd_net_bus_resume(struct net_device *dev, uint8 stage) } #endif /* BCMSDIO */ -#endif /* BCMSDIO || BCMPCIE */ +#endif /* BCMSDIO || BCMPCIE || BCMDBUS */ int net_os_set_suspend_disable(struct net_device *dev, int val) { @@ -14167,6 +14464,9 @@ dhd_dev_get_feature_set(struct net_device *dev) if (dhd_is_pno_supported(dhd)) { feature_set |= WIFI_FEATURE_PNO; #ifdef GSCAN_SUPPORT + /* terence 20171115: remove to get GTS PASS + * com.google.android.gts.wifi.WifiHostTest#testWifiScannerBatchTimestamp + */ // feature_set |= WIFI_FEATURE_GSCAN; // feature_set |= WIFI_FEATURE_HAL_EPNO; #endif /* GSCAN_SUPPORT */ @@ -15718,6 +16018,7 @@ void dhd_get_customized_country_code(struct net_device *dev, char *country_iso_c BCM_REFERENCE(dhd); } + void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec, bool notify) { dhd_info_t *dhd = DHD_DEV_INFO(dev); @@ -15923,7 +16224,11 @@ int write_file(const char * file_name, uint32 flags, uint8 *buf, int size) } /* Write buf to file */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = kernel_write(fp, buf, size, &pos); +#else ret = vfs_write(fp, buf, size, &pos); +#endif if (ret < 0) { DHD_ERROR(("write file error, err = %d\n", ret)); goto exit; @@ -16848,7 +17153,6 @@ bool dhd_os_check_if_up(dhd_pub_t *pub) return pub->up; } -#if defined(BCMSDIO) || defined(BCMPCIE) /* function to collect firmware, chip id and chip version info */ void dhd_set_version_info(dhd_pub_t *dhdp, char *fw) { @@ -16862,10 +17166,9 @@ void dhd_set_version_info(dhd_pub_t *dhdp, char *fw) return; i = snprintf(&info_string[i], sizeof(info_string) - i, - "\n Chip: %x Rev %x Pkg %x", dhd_bus_chip_id(dhdp), - dhd_bus_chiprev_id(dhdp), dhd_bus_chippkg_id(dhdp)); + "\n Chip: %x Rev %x", dhd_conf_get_chip(dhdp), + dhd_conf_get_chiprev(dhdp)); } -#endif /* BCMSDIO || BCMPCIE */ int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc, int cmd) { @@ -17571,7 +17874,11 @@ void dhd_get_memdump_info(dhd_pub_t *dhd) } /* Handle success case */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = kernel_read(fp, (char *)&mem_val, 4, NULL); +#else ret = kernel_read(fp, 0, (char *)&mem_val, 4); +#endif if (ret < 0) { DHD_ERROR(("%s: File read error, ret=%d\n", __FUNCTION__, ret)); filp_close(fp, NULL); @@ -17810,7 +18117,11 @@ do_dhd_log_dump(dhd_pub_t *dhdp) goto exit; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = kernel_write(fp, pre_strs, strlen(pre_strs), &pos); +#else ret = vfs_write(fp, pre_strs, strlen(pre_strs), &pos); +#endif if (ret < 0) { DHD_ERROR(("write file error, err = %d\n", ret)); goto exit; @@ -17828,7 +18139,11 @@ do_dhd_log_dump(dhd_pub_t *dhdp) wr_size = (unsigned int)(dld_buf->present - dld_buf->front); } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = kernel_write(fp, dld_buf->buffer, wr_size, &pos); +#else ret = vfs_write(fp, dld_buf->buffer, wr_size, &pos); +#endif if (ret < 0) { DHD_ERROR(("write file error, err = %d\n", ret)); goto exit; @@ -17849,7 +18164,11 @@ do_dhd_log_dump(dhd_pub_t *dhdp) break; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = kernel_write(fp, post_strs, strlen(post_strs), &pos); +#else ret = vfs_write(fp, post_strs, strlen(post_strs), &pos); +#endif if (ret < 0) { DHD_ERROR(("write file error, err = %d\n", ret)); goto exit; @@ -17898,7 +18217,11 @@ void dhd_get_assert_info(dhd_pub_t *dhd) if (IS_ERR(fp)) { DHD_ERROR(("%s: File [%s] doesn't exist\n", __FUNCTION__, filepath)); } else { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ssize_t ret = kernel_read(fp, (char *)&mem_val, 4, NULL); +#else int ret = kernel_read(fp, 0, (char *)&mem_val, 4); +#endif if (ret < 0) { DHD_ERROR(("%s: File read error, ret=%d\n", __FUNCTION__, ret)); } else { @@ -18809,7 +19132,11 @@ dhd_write_file(const char *filepath, char *buf, int buf_len) ret = BCME_ERROR; } else { if (fp->f_mode & FMODE_WRITE) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = kernel_write(fp, buf, buf_len, &fp->f_pos); +#else ret = vfs_write(fp, buf, buf_len, &fp->f_pos); +#endif if (ret < 0) { DHD_ERROR(("%s: Couldn't write file '%s'\n", __FUNCTION__, filepath)); @@ -18845,7 +19172,11 @@ dhd_read_file(const char *filepath, char *buf, int buf_len) return BCME_ERROR; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = kernel_read(fp, buf, buf_len, NULL); +#else ret = kernel_read(fp, 0, buf, buf_len); +#endif filp_close(fp, NULL); /* restore previous address limit */ @@ -19444,7 +19775,11 @@ dhd_make_hang_with_reason(struct net_device *dev, const char *string_num) wake_counts_t* dhd_get_wakecount(dhd_pub_t *dhdp) { +#ifdef BCMDBUS + return NULL; +#else return dhd_bus_get_wakecount(dhdp); +#endif /* BCMDBUS */ } #endif /* DHD_WAKE_STATUS */ diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux.h index 4651dc51..248f5271 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux.h +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux.h @@ -51,7 +51,21 @@ #include #endif /* defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) */ +/* dongle status */ +enum wifi_adapter_status { + WIFI_STATUS_POWER_ON = 0, + WIFI_STATUS_ATTACH, + WIFI_STATUS_FW_READY, + WIFI_STATUS_DETTACH +}; +#define wifi_chk_adapter_status(adapter, stat) (test_bit(stat, &(adapter)->status)) +#define wifi_get_adapter_status(adapter, stat) (test_bit(stat, &(adapter)->status)) +#define wifi_set_adapter_status(adapter, stat) (set_bit(stat, &(adapter)->status)) +#define wifi_clr_adapter_status(adapter, stat) (clear_bit(stat, &(adapter)->status)) +#define wifi_chg_adapter_status(adapter, stat) (change_bit(stat, &(adapter)->status)) + #define DHD_REGISTRATION_TIMEOUT 12000 /* msec : allowed time to finished dhd registration */ +#define DHD_FW_READY_TIMEOUT 5000 /* msec : allowed time to finished fw download */ typedef struct wifi_adapter_info { const char *name; @@ -65,6 +79,8 @@ typedef struct wifi_adapter_info { uint bus_type; uint bus_num; uint slot_num; + wait_queue_head_t status_event; + unsigned long status; #if defined(BT_OVER_SDIO) const char *btfw_path; #endif /* defined (BT_OVER_SDIO) */ @@ -120,6 +136,8 @@ typedef dhd_sta_t dhd_sta_pool_t; int dhd_wifi_platform_register_drv(void); void dhd_wifi_platform_unregister_drv(void); +wifi_adapter_info_t* dhd_wifi_platform_attach_adapter(uint32 bus_type, + uint32 bus_num, uint32 slot_num, unsigned long status); wifi_adapter_info_t* dhd_wifi_platform_get_adapter(uint32 bus_type, uint32 bus_num, uint32 slot_num); int wifi_platform_set_power(wifi_adapter_info_t *adapter, bool on, unsigned long msec); diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux_platdev.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux_platdev.c old mode 100755 new mode 100644 index 7be2fa30..98592283 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux_platdev.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux_platdev.c @@ -58,6 +58,7 @@ extern void dhd_wlan_deinit_plat_data(wifi_adapter_info_t *adapter); #ifdef CONFIG_DTS struct regulator *wifi_regulator = NULL; +extern struct wifi_platform_data dhd_wlan_control; #endif /* CONFIG_DTS */ bool cfg_multichip = FALSE; @@ -93,6 +94,30 @@ extern void bcm_bt_unlock(int cookie); static int lock_cookie_wifi = 'W' | 'i'<<8 | 'F'<<16 | 'i'<<24; /* cookie is "WiFi" */ #endif /* ENABLE_4335BT_WAR */ +wifi_adapter_info_t* dhd_wifi_platform_attach_adapter(uint32 bus_type, + uint32 bus_num, uint32 slot_num, unsigned long status) +{ + int i; + + if (dhd_wifi_platdata == NULL) + return NULL; + + for (i = 0; i < dhd_wifi_platdata->num_adapters; i++) { + wifi_adapter_info_t *adapter = &dhd_wifi_platdata->adapters[i]; + if ((adapter->bus_type == -1 || adapter->bus_type == bus_type) && + (adapter->bus_num == -1 || adapter->bus_num == bus_num) && + (adapter->slot_num == -1 || adapter->slot_num == slot_num) +#if defined(ENABLE_INSMOD_NO_FW_LOAD) + && (wifi_chk_adapter_status(adapter, status)) +#endif + ) { + DHD_ERROR(("attach adapter info '%s'\n", adapter->name)); + return adapter; + } + } + return NULL; +} + wifi_adapter_info_t* dhd_wifi_platform_get_adapter(uint32 bus_type, uint32 bus_num, uint32 slot_num) { int i; @@ -165,20 +190,31 @@ int wifi_platform_set_power(wifi_adapter_info_t *adapter, bool on, unsigned long return -EINVAL; } #endif /* BT_OVER_SDIO */ + if (on) { + wifi_set_adapter_status(adapter, WIFI_STATUS_POWER_ON); + } else { + wifi_clr_adapter_status(adapter, WIFI_STATUS_POWER_ON); + } #ifdef CONFIG_DTS if (on) { + printf("======== PULL WL_REG_ON HIGH! ========\n"); err = regulator_enable(wifi_regulator); is_power_on = TRUE; } else { + printf("======== PULL WL_REG_ON LOW! ========\n"); err = regulator_disable(wifi_regulator); is_power_on = FALSE; } - if (err < 0) + if (err < 0) { DHD_ERROR(("%s: regulator enable/disable failed", __FUNCTION__)); + goto fail; + } #else - if (!adapter || !adapter->wifi_plat_data) - return -EINVAL; + if (!adapter || !adapter->wifi_plat_data) { + err = -EINVAL; + goto fail; + } plat_data = adapter->wifi_plat_data; DHD_ERROR(("%s = %d\n", __FUNCTION__, on)); @@ -213,6 +249,13 @@ int wifi_platform_set_power(wifi_adapter_info_t *adapter, bool on, unsigned long #endif /* CONFIG_DTS */ + return err; +fail: + if (on) { + wifi_clr_adapter_status(adapter, WIFI_STATUS_POWER_ON); + } else { + wifi_set_adapter_status(adapter, WIFI_STATUS_POWER_ON); + } return err; } @@ -280,7 +323,7 @@ static int wifi_plat_dev_drv_probe(struct platform_device *pdev) { struct resource *resource; wifi_adapter_info_t *adapter; -#ifdef CONFIG_DTS +#if defined(CONFIG_DTS) && defined(CUSTOMER_OOB) int irq, gpio; #endif /* CONFIG_DTS */ @@ -290,7 +333,8 @@ static int wifi_plat_dev_drv_probe(struct platform_device *pdev) ASSERT(dhd_wifi_platdata != NULL); ASSERT(dhd_wifi_platdata->num_adapters == 1); adapter = &dhd_wifi_platdata->adapters[0]; - adapter->wifi_plat_data = (struct wifi_platform_data *)(pdev->dev.platform_data); + adapter->wifi_plat_data = (void *)&dhd_wlan_control; +// adapter->wifi_plat_data = (struct wifi_platform_data *)(pdev->dev.platform_data); resource = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "bcmdhd_wlan_irq"); if (resource == NULL) @@ -310,6 +354,7 @@ static int wifi_plat_dev_drv_probe(struct platform_device *pdev) return -1; } +#if defined(CUSTOMER_OOB) /* This is to get the irq for the OOB */ gpio = of_get_gpio(pdev->dev.of_node, 0); @@ -327,6 +372,7 @@ static int wifi_plat_dev_drv_probe(struct platform_device *pdev) /* need to change the flags according to our requirement */ adapter->intr_flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE; +#endif #endif /* CONFIG_DTS */ wifi_plat_dev_probe_ret = dhd_wifi_platform_load(); @@ -469,6 +515,7 @@ static int wifi_ctrlfunc_register_drv(void) dhd_wifi_platdata = kzalloc(sizeof(bcmdhd_wifi_platdata_t), GFP_KERNEL); dhd_wifi_platdata->num_adapters = 1; dhd_wifi_platdata->adapters = adapter; + init_waitqueue_head(&adapter->status_event); #ifndef CUSTOMER_HW if (dev1) { @@ -519,7 +566,9 @@ static int wifi_ctrlfunc_register_drv(void) void wifi_ctrlfunc_unregister_drv(void) { +#ifndef CONFIG_DTS wifi_adapter_info_t *adapter; +#endif #if defined(CONFIG_DTS) && !defined(CUSTOMER_HW) DHD_ERROR(("unregister wifi platform drivers\n")); @@ -730,7 +779,7 @@ void dhd_wifi_platform_unregister_drv(void) extern int dhd_watchdog_prio; extern int dhd_dpc_prio; extern uint dhd_deferred_tx; -#if defined(BCMLXSDMMC) +#if defined(BCMLXSDMMC) || defined(BCMDBUS) extern struct semaphore dhd_registration_sem; #endif @@ -854,10 +903,67 @@ static int dhd_wifi_platform_load_sdio(void) } #endif /* BCMSDIO */ +#ifdef BCMDBUS +static int dhd_wifi_platform_load_usb(void) +{ + wifi_adapter_info_t *adapter; + s32 timeout = -1; + int i; + int err = 0; + enum wifi_adapter_status wait_status; + + err = dhd_bus_register(); + if (err) { + DHD_ERROR(("%s: usb_register failed\n", __FUNCTION__)); + goto exit; + } + + /* power up all adapters */ + for (i = 0; i < dhd_wifi_platdata->num_adapters; i++) { + adapter = &dhd_wifi_platdata->adapters[i]; + DHD_ERROR(("Power-up adapter '%s'\n", adapter->name)); + DHD_INFO((" - irq %d [flags %d], firmware: %s, nvram: %s\n", + adapter->irq_num, adapter->intr_flags, adapter->fw_path, adapter->nv_path)); + DHD_INFO((" - bus type %d, bus num %d, slot num %d\n\n", + adapter->bus_type, adapter->bus_num, adapter->slot_num)); + err = wifi_platform_set_power(adapter, TRUE, WIFI_TURNON_DELAY); + if (err) { + DHD_ERROR(("failed to wifi_platform_set_power on %s\n", adapter->name)); + goto fail; + } + if (dhd_download_fw_on_driverload) + wait_status = WIFI_STATUS_ATTACH; + else + wait_status = WIFI_STATUS_DETTACH; + timeout = wait_event_interruptible_timeout(adapter->status_event, + wifi_get_adapter_status(adapter, wait_status), + msecs_to_jiffies(DHD_REGISTRATION_TIMEOUT)); + if (timeout <= 0) { + err = -1; + DHD_ERROR(("%s: usb_register_driver timeout\n", __FUNCTION__)); + goto fail; + } + } + +exit: + return err; + +fail: + dhd_bus_unregister(); + /* power down all adapters */ + for (i = 0; i < dhd_wifi_platdata->num_adapters; i++) { + adapter = &dhd_wifi_platdata->adapters[i]; + wifi_platform_set_power(adapter, FALSE, WIFI_TURNOFF_DELAY); + } + + return err; +} +#else /* BCMDBUS */ static int dhd_wifi_platform_load_usb(void) { return 0; } +#endif /* BCMDBUS */ static int dhd_wifi_platform_load() { diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux_wq.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux_wq.h old mode 100755 new mode 100644 index 6dc41a5d..9c51d066 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux_wq.h +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux_wq.h @@ -47,6 +47,9 @@ enum _wq_event { DHD_WQ_WORK_DEBUG_UART_DUMP, DHD_WQ_WORK_SSSR_DUMP, DHD_WQ_WORK_PKTLOG_DUMP, +#ifdef DHD_UPDATE_INTF_MAC + DHD_WQ_WORK_IF_UPDATE, +#endif /* DHD_UPDATE_INTF_MAC */ DHD_MAX_WQ_EVENTS }; diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_msgbuf.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_msgbuf.c old mode 100755 new mode 100644 index e602d24f..455f125a --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_msgbuf.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_msgbuf.c @@ -52,6 +52,7 @@ #include #include #include +#include #ifdef DHD_TIMESYNC #include #endif /* DHD_TIMESYNC */ @@ -6013,6 +6014,7 @@ dhd_msgbuf_wait_ioctl_cmplt(dhd_pub_t *dhd, uint32 len, void *buf) int timeleft; unsigned long flags; int ret = 0; + static uint cnt = 0; DHD_TRACE(("%s: Enter\n", __FUNCTION__)); @@ -6021,7 +6023,7 @@ dhd_msgbuf_wait_ioctl_cmplt(dhd_pub_t *dhd, uint32 len, void *buf) goto out; } - timeleft = dhd_os_ioctl_resp_wait(dhd, (uint *)&prot->ioctl_received); + timeleft = dhd_os_ioctl_resp_wait(dhd, (uint *)&prot->ioctl_received, false); #ifdef DHD_RECOVER_TIMEOUT if (prot->ioctl_received == 0) { @@ -6053,6 +6055,25 @@ dhd_msgbuf_wait_ioctl_cmplt(dhd_pub_t *dhd, uint32 len, void *buf) } #endif /* DHD_RECOVER_TIMEOUT */ + if (dhd->conf->ctrl_resched > 0 && timeleft == 0 && (!dhd_query_bus_erros(dhd))) { + cnt++; + if (cnt <= dhd->conf->ctrl_resched) { + uint32 intstatus = 0, intmask = 0; + intstatus = si_corereg(dhd->bus->sih, dhd->bus->sih->buscoreidx, PCIMailBoxInt, 0, 0); + intmask = si_corereg(dhd->bus->sih, dhd->bus->sih->buscoreidx, PCIMailBoxMask, 0, 0); + if (intstatus) { + DHD_ERROR(("%s: reschedule dhd_dpc, cnt=%d, intstatus=0x%x, intmask=0x%x\n", + __FUNCTION__, cnt, intstatus, intmask)); + dhd->bus->ipend = TRUE; + dhd->bus->dpc_sched = TRUE; + dhd_sched_dpc(dhd); + timeleft = dhd_os_ioctl_resp_wait(dhd, &prot->ioctl_received, true); + } + } + } else { + cnt = 0; + } + if (timeleft == 0 && (!dhd_query_bus_erros(dhd))) { uint32 intstatus; diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie.c index 91a1203e..a785fe52 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie.c @@ -497,7 +497,9 @@ uint32 dhdpcie_bus_intstatus(dhd_bus_t *bus) { uint32 intstatus = 0; +#ifndef DHD_READ_INTSTATUS_IN_DPC uint32 intmask = 0; +#endif /* DHD_READ_INTSTATUS_IN_DPC */ if ((bus->dhd->busstate == DHD_BUS_SUSPEND || bus->d3_suspend_pending) && bus->wait_for_d3_ack) { @@ -521,10 +523,12 @@ dhdpcie_bus_intstatus(dhd_bus_t *bus) /* this is a PCIE core register..not a config register... */ intstatus = si_corereg(bus->sih, bus->sih->buscoreidx, PCIMailBoxInt, 0, 0); +#ifndef DHD_READ_INTSTATUS_IN_DPC /* this is a PCIE core register..not a config register... */ intmask = si_corereg(bus->sih, bus->sih->buscoreidx, PCIMailBoxMask, 0, 0); intstatus &= intmask; +#endif /* DHD_READ_INTSTATUS_IN_DPC */ /* Is device removed. intstatus & intmask read 0xffffffff */ if (intstatus == (uint32)-1) { DHD_ERROR(("%s: Device is removed or Link is down.\n", __FUNCTION__)); @@ -600,6 +604,7 @@ dhdpcie_bus_isr(dhd_bus_t *bus) } } +#ifndef DHD_READ_INTSTATUS_IN_DPC intstatus = dhdpcie_bus_intstatus(bus); /* Check if the interrupt is ours or not */ @@ -627,6 +632,7 @@ dhdpcie_bus_isr(dhd_bus_t *bus) /* Count the interrupt call */ bus->intrcount++; +#endif /* DHD_READ_INTSTATUS_IN_DPC */ bus->ipend = TRUE; @@ -988,6 +994,9 @@ dhdpcie_dongle_attach(dhd_bus_t *bus) case BCM4347_CHIP_GRPID: bus->dongle_ram_base = CR4_4347_RAM_BASE; break; + case BCM4362_CHIP_ID: + bus->dongle_ram_base = CR4_4362_RAM_BASE; + break; default: bus->dongle_ram_base = 0; DHD_ERROR(("%s: WARNING: Using default ram base at 0x%x\n", @@ -1008,6 +1017,8 @@ dhdpcie_dongle_attach(dhd_bus_t *bus) /* Set the poll and/or interrupt flags */ bus->intr = (bool)dhd_intr; + if ((bus->poll = (bool)dhd_poll)) + bus->pollrate = 1; bus->wait_for_d3_ack = 1; #ifdef PCIE_OOB @@ -1116,6 +1127,27 @@ dhdpcie_advertise_bus_cleanup(dhd_pub_t *dhdp) return; } +static void +dhdpcie_advertise_bus_remove(dhd_pub_t *dhdp) +{ + unsigned long flags; + int timeleft; + + DHD_GENERAL_LOCK(dhdp, flags); + dhdp->busstate = DHD_BUS_REMOVE; + DHD_GENERAL_UNLOCK(dhdp, flags); + + timeleft = dhd_os_busbusy_wait_negation(dhdp, &dhdp->dhd_bus_busy_state); + if ((timeleft == 0) || (timeleft == 1)) { + DHD_ERROR(("%s : Timeout due to dhd_bus_busy_state=0x%x\n", + __FUNCTION__, dhdp->dhd_bus_busy_state)); + ASSERT(0); + } + + return; +} + + static void dhdpcie_bus_remove_prep(dhd_bus_t *bus) { @@ -1169,7 +1201,7 @@ dhdpcie_bus_release(dhd_bus_t *bus) ASSERT(osh); if (bus->dhd) { - dhdpcie_advertise_bus_cleanup(bus->dhd); + dhdpcie_advertise_bus_remove(bus->dhd); dongle_isolation = bus->dhd->dongle_isolation; bus->dhd->is_pcie_watchdog_reset = FALSE; dhdpcie_bus_remove_prep(bus); @@ -1509,6 +1541,14 @@ bool dhd_bus_watchdog(dhd_pub_t *dhd) } } +#ifdef DHD_READ_INTSTATUS_IN_DPC + if (bus->poll) { + bus->ipend = TRUE; + bus->dpc_sched = TRUE; + dhd_sched_dpc(bus->dhd); /* queue DPC now!! */ + } +#endif /* DHD_READ_INTSTATUS_IN_DPC */ + #if defined(PCIE_OOB) || defined(PCIE_INB_DW) /* If haven't communicated with device for a while, deassert the Device_Wake GPIO */ if (dhd_doorbell_timeout != 0 && dhd->busstate == DHD_BUS_DATA && @@ -1618,6 +1658,17 @@ dhd_set_path_params(struct dhd_bus *bus) } +void +dhd_set_bus_params(struct dhd_bus *bus) +{ + if (bus->dhd->conf->dhd_poll >= 0) { + bus->poll = bus->dhd->conf->dhd_poll; + if (!bus->pollrate) + bus->pollrate = 1; + printf("%s: set polling mode %d\n", __FUNCTION__, bus->dhd->conf->dhd_poll); + } +} + static int dhdpcie_download_firmware(struct dhd_bus *bus, osl_t *osh) { @@ -1659,6 +1710,7 @@ dhdpcie_download_firmware(struct dhd_bus *bus, osl_t *osh) DHD_OS_WAKE_LOCK(bus->dhd); dhd_set_path_params(bus); + dhd_set_bus_params(bus); ret = _dhdpcie_download_firmware(bus); @@ -6045,10 +6097,24 @@ dhd_bus_dpc(struct dhd_bus *bus) DHD_BUS_BUSY_SET_IN_DPC(bus->dhd); DHD_GENERAL_UNLOCK(bus->dhd, flags); +#ifdef DHD_READ_INTSTATUS_IN_DPC + if (bus->ipend) { + bus->ipend = FALSE; + bus->intstatus = dhdpcie_bus_intstatus(bus); + /* Check if the interrupt is ours or not */ + if (bus->intstatus == 0) { + goto INTR_ON; + } + bus->intrcount++; + } +#endif /* DHD_READ_INTSTATUS_IN_DPC */ resched = dhdpcie_bus_process_mailbox_intr(bus, bus->intstatus); if (!resched) { bus->intstatus = 0; +#ifdef DHD_READ_INTSTATUS_IN_DPC +INTR_ON: +#endif /* DHD_READ_INTSTATUS_IN_DPC */ bus->dpc_intr_enable_count++; dhdpcie_bus_intr_enable(bus); /* Enable back interrupt using Intmask!! */ } @@ -7025,6 +7091,11 @@ dhdpcie_chipmatch(uint16 vendor, uint16 device) if ((device == BCM4361_D11AC_ID) || (device == BCM4361_D11AC2G_ID) || (device == BCM4361_D11AC5G_ID) || (device == BCM4361_CHIP_ID)) return 0; + + if ((device == BCM4362_D11AX_ID) || (device == BCM4362_D11AX2G_ID) || + (device == BCM4362_D11AX5G_ID) || (device == BCM4362_CHIP_ID)) { + return 0; + } if ((device == BCM4365_D11AC_ID) || (device == BCM4365_D11AC2G_ID) || (device == BCM4365_D11AC5G_ID) || (device == BCM4365_CHIP_ID)) diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie.h old mode 100755 new mode 100644 index eb8de629..92b07c6e --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie.h +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie.h @@ -259,6 +259,7 @@ typedef struct dhd_bus { struct pktq txq; /* Queue length used for flow-control */ bool intr; /* Use interrupts */ + bool poll; /* Use polling */ bool ipend; /* Device interrupt is pending */ bool intdis; /* Interrupts disabled by isr */ uint intrcount; /* Count of device interrupt callbacks */ diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie_linux.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie_linux.c old mode 100755 new mode 100644 index 51664a7e..ecaed3e9 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie_linux.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pcie_linux.c @@ -178,12 +178,6 @@ static int dhdpcie_init(struct pci_dev *pdev); static irqreturn_t dhdpcie_isr(int irq, void *arg); /* OS Routine functions for PCI suspend/resume */ -#if defined(MULTIPLE_SUPPLICANT) -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) -DEFINE_MUTEX(_dhd_sdio_mutex_lock_); -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ -#endif - static int dhdpcie_set_suspend_resume(dhd_bus_t *bus, bool state); static int dhdpcie_resume_host_dev(dhd_bus_t *bus); static int dhdpcie_suspend_host_dev(dhd_bus_t *bus); @@ -890,9 +884,7 @@ dhdpcie_bus_unregister(void) int __devinit dhdpcie_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { -#ifdef BUS_POWER_RESTORE - wifi_adapter_info_t *adapter = NULL; -#endif + DHD_MUTEX_LOCK(); if (dhdpcie_chipmatch (pdev->vendor, pdev->device)) { DHD_ERROR(("%s: chipmatch failed!!\n", __FUNCTION__)); @@ -912,15 +904,8 @@ dhdpcie_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) device_disable_async_suspend(&pdev->dev); #endif /* BCMPCIE_DISABLE_ASYNC_SUSPEND */ -#ifdef BUS_POWER_RESTORE - adapter = dhd_wifi_platform_get_adapter(PCI_BUS, pdev->bus->number, - PCI_SLOT(pdev->devfn)); - - if (adapter != NULL) - adapter->pci_dev = pdev; -#endif - DHD_TRACE(("%s: PCIe Enumeration done!!\n", __FUNCTION__)); + DHD_MUTEX_UNLOCK(); return 0; } @@ -948,17 +933,7 @@ dhdpcie_pci_remove(struct pci_dev *pdev) DHD_TRACE(("%s Enter\n", __FUNCTION__)); -#if defined(MULTIPLE_SUPPLICANT) -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) - if (mutex_is_locked(&_dhd_sdio_mutex_lock_) == 0) { - DHD_ERROR(("%s : no mutex held. set lock\n", __FUNCTION__)); - } - else { - DHD_ERROR(("%s : mutex is locked!. wait for unlocking\n", __FUNCTION__)); - } - mutex_lock(&_dhd_sdio_mutex_lock_); -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ -#endif + DHD_MUTEX_LOCK(); pch = pci_get_drvdata(pdev); bus = pch->bus; @@ -1006,12 +981,7 @@ dhdpcie_pci_remove(struct pci_dev *pdev) dhdpcie_init_succeeded = FALSE; -#if defined(MULTIPLE_SUPPLICANT) -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) - mutex_unlock(&_dhd_sdio_mutex_lock_); - DHD_ERROR(("%s : the lock is released.\n", __FUNCTION__)); -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ -#endif /* LINUX */ + DHD_MUTEX_UNLOCK(); DHD_TRACE(("%s Exit\n", __FUNCTION__)); @@ -1029,10 +999,22 @@ dhdpcie_request_irq(dhdpcie_info_t *dhdpcie_info) if (!bus->irq_registered) { snprintf(dhdpcie_info->pciname, sizeof(dhdpcie_info->pciname), "dhdpcie:%s", pci_name(pdev)); +#ifdef DHD_USE_MSI + printf("%s: MSI enabled\n", __FUNCTION__); + err = pci_enable_msi(pdev); + if (err < 0) { + DHD_ERROR(("%s: pci_enable_msi() failed, %d, fall back to INTx\n", __FUNCTION__, err)); + } +#else + printf("%s: MSI not enabled\n", __FUNCTION__); +#endif /* DHD_USE_MSI */ err = request_irq(pdev->irq, dhdpcie_isr, IRQF_SHARED, dhdpcie_info->pciname, bus); if (err) { DHD_ERROR(("%s: request_irq() failed\n", __FUNCTION__)); +#ifdef DHD_USE_MSI + pci_disable_msi(pdev); +#endif /* DHD_USE_MSI */ return -1; } else { bus->irq_registered = TRUE; @@ -1226,10 +1208,6 @@ void dhdpcie_linkdown_cb(struct_pcie_notify *noti) */ #endif /* SUPPORT_LINKDOWN_RECOVERY */ -#if defined(MULTIPLE_SUPPLICANT) -extern void wl_android_post_init(void); // terence 20120530: fix critical section in dhd_open and dhdsdio_probe -#endif - int dhdpcie_init(struct pci_dev *pdev) { @@ -1244,18 +1222,6 @@ int dhdpcie_init(struct pci_dev *pdev) dhdpcie_smmu_info_t *dhdpcie_smmu_info = NULL; #endif /* USE_SMMU_ARCH_MSM */ -#if defined(MULTIPLE_SUPPLICANT) -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) - if (mutex_is_locked(&_dhd_sdio_mutex_lock_) == 0) { - DHD_ERROR(("%s : no mutex held. set lock\n", __FUNCTION__)); - } - else { - DHD_ERROR(("%s : mutex is locked!. wait for unlocking\n", __FUNCTION__)); - } - mutex_lock(&_dhd_sdio_mutex_lock_); -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ -#endif - do { /* osl attach */ if (!(osh = osl_attach(pdev, PCI_BUS, FALSE))) { @@ -1266,9 +1232,12 @@ int dhdpcie_init(struct pci_dev *pdev) /* initialize static buffer */ adapter = dhd_wifi_platform_get_adapter(PCI_BUS, pdev->bus->number, PCI_SLOT(pdev->devfn)); - if (adapter != NULL) + if (adapter != NULL) { DHD_ERROR(("%s: found adapter info '%s'\n", __FUNCTION__, adapter->name)); - else +#ifdef BUS_POWER_RESTORE + adapter->pci_dev = pdev; +#endif + } else DHD_ERROR(("%s: can't find adapter info for this chip\n", __FUNCTION__)); osl_static_mem_init(osh, adapter); @@ -1438,11 +1407,7 @@ int dhdpcie_init(struct pci_dev *pdev) #if defined(MULTIPLE_SUPPLICANT) wl_android_post_init(); // terence 20120530: fix critical section in dhd_open and dhdsdio_probe -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) - mutex_unlock(&_dhd_sdio_mutex_lock_); - DHD_ERROR(("%s : the lock is released.\n", __FUNCTION__)); -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ -#endif +#endif /* MULTIPLE_SUPPLICANT */ DHD_TRACE(("%s:Exit - SUCCESS \n", __FUNCTION__)); return 0; /* return SUCCESS */ @@ -1473,12 +1438,6 @@ int dhdpcie_init(struct pci_dev *pdev) osl_detach(osh); dhdpcie_init_succeeded = FALSE; -#if defined(MULTIPLE_SUPPLICANT) -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) - mutex_unlock(&_dhd_sdio_mutex_lock_); - DHD_ERROR(("%s : the lock is released.\n", __FUNCTION__)); -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ -#endif DHD_TRACE(("%s:Exit - FAILURE \n", __FUNCTION__)); @@ -1497,6 +1456,9 @@ dhdpcie_free_irq(dhd_bus_t *bus) if (bus->irq_registered) { free_irq(pdev->irq, bus); bus->irq_registered = FALSE; +#ifdef DHD_USE_MSI + pci_disable_msi(pdev); +#endif /* DHD_USE_MSI */ } else { DHD_ERROR(("%s: PCIe IRQ is not registered\n", __FUNCTION__)); } @@ -2296,6 +2258,7 @@ bool dhdpcie_is_resume_done(dhd_pub_t *dhdp) return bus->runtime_resume_done; } #endif /* DHD_PCIE_RUNTIMEPM */ + struct device * dhd_bus_to_dev(dhd_bus_t *bus) { struct pci_dev *pdev; @@ -2306,6 +2269,7 @@ struct device * dhd_bus_to_dev(dhd_bus_t *bus) else return NULL; } + #ifdef HOFFLOAD_MODULES void dhd_free_module_memory(struct dhd_bus *bus, struct module_metadata *hmem) diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pno.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pno.c old mode 100755 new mode 100644 index c553733f..570e75ec --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pno.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_pno.c @@ -910,6 +910,7 @@ _dhd_pno_convert_format(dhd_pub_t *dhd, struct dhd_pno_batch_params *params_batc bytes_written = (int32)(bp - buf); return bytes_written; } + static int _dhd_pno_clear_all_batch_results(dhd_pub_t *dhd, struct list_head *head, bool only_last) { @@ -992,6 +993,7 @@ _dhd_pno_cfg(dhd_pub_t *dhd, uint16 *channel_list, int nchan) exit: return err; } + static int _dhd_pno_reinitialize_prof(dhd_pub_t *dhd, dhd_pno_params_t *params, dhd_pno_mode_t mode) { @@ -1084,6 +1086,7 @@ _dhd_pno_reinitialize_prof(dhd_pub_t *dhd, dhd_pno_params_t *params, dhd_pno_mod mutex_unlock(&_pno_state->pno_mutex); return err; } + static int _dhd_pno_add_bssid(dhd_pub_t *dhd, wl_pfn_bssid_t *p_pfn_bssid, int nbssid) { @@ -2763,6 +2766,7 @@ _dhd_pno_get_gscan_batch_from_fw(dhd_pub_t *dhd) return err; } #endif /* GSCAN_SUPPORT */ + #if defined(GSCAN_SUPPORT) || defined(DHD_GET_VALID_CHANNELS) void * dhd_pno_get_gscan(dhd_pub_t *dhd, dhd_pno_gscan_cmd_cfg_t type, @@ -4007,6 +4011,7 @@ int dhd_pno_init(dhd_pub_t *dhd) kfree(buf); return err; } + int dhd_pno_deinit(dhd_pub_t *dhd) { int err = BCME_OK; diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_sdio.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_sdio.c index a4b20b94..342a53a4 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_sdio.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_sdio.c @@ -68,10 +68,6 @@ #include #include -#include -#include -#include "bcmsdh_sdmmc.h" - #ifdef PROP_TXSTATUS #include #endif @@ -83,6 +79,10 @@ #include #endif /* BT_OVER_SDIO */ +#include +#include +#include "bcmsdh_sdmmc.h" + bool dhd_mp_halting(dhd_pub_t *dhdp); extern void bcmsdh_waitfor_iodrain(void *sdh); extern void bcmsdh_reject_ioreqs(void *sdh, bool reject); @@ -186,12 +186,6 @@ DHD_SPINWAIT_SLEEP_INIT(sdioh_spinwait_sleep); pkt_statics_t tx_statics = {0}; #endif -#if defined(MULTIPLE_SUPPLICANT) -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) -DEFINE_MUTEX(_dhd_sdio_mutex_lock_); -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ -#endif - #ifdef SUPPORT_MULTIPLE_BOARD_REV_FROM_HW extern unsigned int system_hw_rev; #endif /* SUPPORT_MULTIPLE_BOARD_REV_FROM_HW */ @@ -345,6 +339,8 @@ typedef struct dhd_bus { #if defined(SUPPORT_P2P_GO_PS) wait_queue_head_t bus_sleep; #endif /* LINUX && SUPPORT_P2P_GO_PS */ + bool ctrl_wait; + wait_queue_head_t ctrl_tx_wait; uint rxflow_mode; /* Rx flow control mode */ bool rxflow; /* Is rx flow control on */ uint prev_rxlim_hit; /* Is prev rx limit exceeded (per dpc schedule) */ @@ -700,7 +696,7 @@ static int dhdsdio_txpkt_preprocess(dhd_bus_t *bus, void *pkt, int chan, int txs int prev_chain_total_len, bool last_chained_pkt, int *pad_pkt_len, void **new_pkt #if defined(BCMSDIOH_TXGLOM_EXT) - , int frist_frame + , int first_frame #endif ); static int dhdsdio_txpkt_postprocess(dhd_bus_t *bus, void *pkt); @@ -724,6 +720,7 @@ static int dhd_bcmsdh_send_buffer(void *bus, uint8 *frame, uint16 len); static int dhdsdio_set_sdmode(dhd_bus_t *bus, int32 sd_mode); static int dhdsdio_sdclk(dhd_bus_t *bus, bool on); static void dhdsdio_advertise_bus_cleanup(dhd_pub_t *dhdp); +static void dhdsdio_advertise_bus_remove(dhd_pub_t *dhdp); #ifdef SUPPORT_MULTIPLE_BOARD_REV_FROM_DT int dhd_get_system_rev(void); #endif /* SUPPORT_MULTIPLE_BOARD_REV_FROM_DT */ @@ -884,7 +881,8 @@ dhdsdio_sr_cap(dhd_bus_t *bus) (bus->sih->chip == BCM4371_CHIP_ID) || (BCM4349_CHIP(bus->sih->chip)) || (bus->sih->chip == BCM4350_CHIP_ID) || - (bus->sih->chip == BCM43012_CHIP_ID)) { + (bus->sih->chip == BCM43012_CHIP_ID) || + (bus->sih->chip == BCM4362_CHIP_ID)) { core_capext = TRUE; } else { core_capext = bcmsdh_reg_read(bus->sdh, @@ -980,7 +978,8 @@ dhdsdio_sr_init(dhd_bus_t *bus) if (CHIPID(bus->sih->chip) == BCM43430_CHIP_ID || CHIPID(bus->sih->chip) == BCM43018_CHIP_ID || CHIPID(bus->sih->chip) == BCM4339_CHIP_ID || - CHIPID(bus->sih->chip) == BCM43012_CHIP_ID) + CHIPID(bus->sih->chip) == BCM43012_CHIP_ID || + CHIPID(bus->sih->chip) == BCM4362_CHIP_ID) dhdsdio_devcap_set(bus, SDIOD_CCCR_BRCM_CARDCAP_CMD_NODEC); if (bus->sih->chip == BCM43012_CHIP_ID) { @@ -1043,29 +1042,26 @@ dhdsdio_clk_kso_enab(dhd_bus_t *bus, bool on) uint8 wr_val = 0, rd_val, cmp_val, bmask; int err = 0; int try_cnt = 0; + struct mmc_host *host; + struct sdioh_info *sd = (struct sdioh_info *)(bus->sdh->sdioh); + struct sdio_func *func = sd->func[SDIO_FUNC_0]; + + host = func->card->host; + mmc_retune_disable(host); KSO_DBG(("%s> op:%s\n", __FUNCTION__, (on ? "KSO_SET" : "KSO_CLR"))); wr_val |= (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT); - { - struct mmc_host *host; - struct sdioh_info *sd = (struct sdioh_info *)(bus->sdh->sdioh); - struct sdio_func *func = sd->func[SDIO_FUNC_0]; - - host = func->card->host; + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SLEEPCSR, wr_val, &err); - mmc_retune_disable(host); - bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SLEEPCSR, - wr_val, &err); - mmc_retune_enable(host); - } /* In case of 43012 chip, the chip could go down immediately after KSO bit is cleared. * So the further reads of KSO register could fail. Thereby just bailing out immediately * after clearing KSO bit, to avoid polling of KSO bit. */ if ((!on) && (bus->sih->chip == BCM43012_CHIP_ID)) { + mmc_retune_enable(host); return err; } @@ -1106,6 +1102,8 @@ dhdsdio_clk_kso_enab(dhd_bus_t *bus, bool on) __FUNCTION__, (on ? "KSO_SET" : "KSO_CLR"), try_cnt, rd_val, err)); } + mmc_retune_enable(host); + return err; } @@ -1994,12 +1992,16 @@ dhd_bus_txdata(struct dhd_bus *bus, void *pkt) prec = PRIO2PREC((PKTPRIO(pkt) & PRIOMASK)); + /* move from dhdsdio_sendfromq(), try to orphan skb early */ + if (bus->dhd->conf->orphan_move) + PKTORPHAN(pkt, bus->dhd->conf->tsq); + /* Check for existing queue, current flow-control, pending event, or pending clock */ if (dhd_deferred_tx || bus->fcstate || pktq_len(&bus->txq) || bus->dpc_sched || (!DATAOK(bus)) || (bus->flowcontrol & NBITVAL(prec)) || (bus->clkstate != CLK_AVAIL)) { bool deq_ret; - int pkq_len; + int pkq_len = 0; DHD_TRACE(("%s: deferring pktq len %d\n", __FUNCTION__, pktq_len(&bus->txq))); bus->fcqueued++; @@ -2028,10 +2030,12 @@ dhd_bus_txdata(struct dhd_bus *bus, void *pkt) } else ret = BCME_OK; - dhd_os_sdlock_txq(bus->dhd); - pkq_len = pktq_len(&bus->txq); - dhd_os_sdunlock_txq(bus->dhd); - if (pkq_len >= FCHI) { + if (dhd_doflow) { + dhd_os_sdlock_txq(bus->dhd); + pkq_len = pktq_len(&bus->txq); + dhd_os_sdunlock_txq(bus->dhd); + } + if (dhd_doflow && pkq_len >= FCHI) { bool wlfc_enabled = FALSE; #ifdef PROP_TXSTATUS wlfc_enabled = (dhd_wlfc_flowcontrol(bus->dhd, ON, FALSE) != @@ -2635,7 +2639,8 @@ dhdsdio_sendfromq(dhd_bus_t *bus, uint maxframes) } } #endif /* DHD_LOSSLESS_ROAMING */ - PKTORPHAN(pkts[i], bus->dhd->conf->tsq); + if (!bus->dhd->conf->orphan_move) + PKTORPHAN(pkts[i], bus->dhd->conf->tsq); datalen += PKTLEN(osh, pkts[i]); } dhd_os_sdunlock_txq(bus->dhd); @@ -2672,9 +2677,11 @@ dhdsdio_sendfromq(dhd_bus_t *bus, uint maxframes) } - dhd_os_sdlock_txq(bus->dhd); - txpktqlen = pktq_len(&bus->txq); - dhd_os_sdunlock_txq(bus->dhd); + if (dhd_doflow) { + dhd_os_sdlock_txq(bus->dhd); + txpktqlen = pktq_len(&bus->txq); + dhd_os_sdunlock_txq(bus->dhd); + } /* Do flow-control if needed */ if (dhd->up && (dhd->busstate == DHD_BUS_DATA) && (txpktqlen < FCLOW)) { @@ -2727,7 +2734,6 @@ dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen) uint8 doff = 0; int ret = -1; uint8 sdpcm_hdrlen = bus->txglom_enable ? SDPCM_HDRLEN_TXGLOM : SDPCM_HDRLEN; - int cnt = 0; DHD_TRACE(("%s: Enter\n", __FUNCTION__)); @@ -2767,17 +2773,13 @@ dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen) /* Need to lock here to protect txseq and SDIO tx calls */ -retry: - dhd_os_sdlock(bus->dhd); - if (cnt < bus->dhd->conf->txctl_tmo_fix && !TXCTLOK(bus)) { - cnt++; - dhd_os_sdunlock(bus->dhd); - OSL_SLEEP(1); - if (cnt >= (bus->dhd->conf->txctl_tmo_fix)) - DHD_ERROR(("%s: No bus credit bus->tx_max %d, bus->tx_seq %d, last retry cnt %d\n", - __FUNCTION__, bus->tx_max, bus->tx_seq, cnt)); - goto retry; + if (bus->dhd->conf->txctl_tmo_fix > 0 && !TXCTLOK(bus)) { + bus->ctrl_wait = TRUE; + wait_event_interruptible_timeout(bus->ctrl_tx_wait, TXCTLOK(bus), + msecs_to_jiffies(bus->dhd->conf->txctl_tmo_fix)); + bus->ctrl_wait = FALSE; } + dhd_os_sdlock(bus->dhd); BUS_WAKE(bus); @@ -2923,6 +2925,7 @@ dhd_bus_rxctl(struct dhd_bus *bus, uchar *msg, uint msglen) { int timeleft; uint rxlen = 0; + static uint cnt = 0; DHD_TRACE(("%s: Enter\n", __FUNCTION__)); @@ -2930,7 +2933,7 @@ dhd_bus_rxctl(struct dhd_bus *bus, uchar *msg, uint msglen) return -EIO; /* Wait until control frame is available */ - timeleft = dhd_os_ioctl_resp_wait(bus->dhd, &bus->rxlen); + timeleft = dhd_os_ioctl_resp_wait(bus->dhd, &bus->rxlen, false); dhd_os_sdlock(bus->dhd); rxlen = bus->rxlen; @@ -2938,6 +2941,32 @@ dhd_bus_rxctl(struct dhd_bus *bus, uchar *msg, uint msglen) bus->rxlen = 0; dhd_os_sdunlock(bus->dhd); + if (bus->dhd->conf->ctrl_resched > 0 && !rxlen && timeleft == 0) { + cnt++; + if (cnt <= bus->dhd->conf->ctrl_resched) { + uint32 status, retry = 0; + R_SDREG(status, &bus->regs->intstatus, retry); + if ((status & I_HMB_HOST_INT) || PKT_AVAILABLE(bus, status)) { + DHD_ERROR(("%s: reschedule dhd_dpc, cnt=%d, status=0x%x\n", + __FUNCTION__, cnt, status)); + bus->ipend = TRUE; + bus->dpc_sched = TRUE; + dhd_sched_dpc(bus->dhd); + + /* Wait until control frame is available */ + timeleft = dhd_os_ioctl_resp_wait(bus->dhd, &bus->rxlen, true); + + dhd_os_sdlock(bus->dhd); + rxlen = bus->rxlen; + bcopy(bus->rxctl, msg, MIN(msglen, rxlen)); + bus->rxlen = 0; + dhd_os_sdunlock(bus->dhd); + } + } + } else { + cnt = 0; + } + if (rxlen) { DHD_CTL(("%s: resumed on rxctl frame, got %d expected %d\n", __FUNCTION__, rxlen, msglen)); @@ -6832,6 +6861,8 @@ dhdsdio_dpc(dhd_bus_t *bus) } } + if (bus->ctrl_wait && TXCTLOK(bus)) + wake_up_interruptible(&bus->ctrl_tx_wait); dhd_os_sdunlock(bus->dhd); #ifdef DEBUG_DPC_THREAD_WATCHDOG if (bus->dhd->dhd_bug_on) { @@ -7631,14 +7662,12 @@ dhdsdio_chipmatch(uint16 chipid) if (chipid == BCM43012_CHIP_ID) return TRUE; + if (chipid == BCM4362_CHIP_ID) + return TRUE; return FALSE; } -#if defined(MULTIPLE_SUPPLICANT) -extern void wl_android_post_init(void); // terence 20120530: fix critical section in dhd_open and dhdsdio_probe -#endif - static void * dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot, uint16 func, uint bustype, void *regsva, osl_t * osh, void *sdh) @@ -7649,17 +7678,7 @@ dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot, struct ether_addr ea_addr; #endif -#if defined(MULTIPLE_SUPPLICANT) -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) - if (mutex_is_locked(&_dhd_sdio_mutex_lock_) == 0) { - DHD_ERROR(("%s : no mutex held. set lock\n", __FUNCTION__)); - } - else { - DHD_ERROR(("%s : mutex is locked!. wait for unlocking\n", __FUNCTION__)); - } - mutex_lock(&_dhd_sdio_mutex_lock_); -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ -#endif + DHD_MUTEX_LOCK(); /* Init global variables at run-time, not as part of the declaration. * This is required to support init/de-init of the driver. Initialization @@ -7741,6 +7760,7 @@ dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot, #if defined(SUPPORT_P2P_GO_PS) init_waitqueue_head(&bus->bus_sleep); #endif /* LINUX && SUPPORT_P2P_GO_PS */ + init_waitqueue_head(&bus->ctrl_tx_wait); /* attempt to attach to the dongle */ if (!(dhdsdio_probe_attach(bus, osh, sdh, regsva, devid))) { @@ -7839,11 +7859,8 @@ dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot, #if defined(MULTIPLE_SUPPLICANT) wl_android_post_init(); // terence 20120530: fix critical section in dhd_open and dhdsdio_probe -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) - mutex_unlock(&_dhd_sdio_mutex_lock_); - DHD_ERROR(("%s : the lock is released.\n", __FUNCTION__)); -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ -#endif +#endif /* MULTIPLE_SUPPLICANT */ + DHD_MUTEX_UNLOCK(); return bus; @@ -7851,12 +7868,7 @@ dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot, dhdsdio_release(bus, osh); forcereturn: -#if defined(MULTIPLE_SUPPLICANT) -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) - mutex_unlock(&_dhd_sdio_mutex_lock_); - DHD_ERROR(("%s : the lock is released.\n", __FUNCTION__)); -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ -#endif + DHD_MUTEX_UNLOCK(); return NULL; } @@ -7910,7 +7922,7 @@ dhdsdio_probe_attach(struct dhd_bus *bus, osl_t *osh, void *sdh, void *regsva, bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, DHD_INIT_CLKCTL2, &err); OSL_DELAY(200); - + if (DHD_INFO_ON()) { for (fn = 0; fn <= numfn; fn++) { if (!(cis[fn] = MALLOC(osh, SBSDIO_CIS_SIZE_LIMIT))) { @@ -8051,6 +8063,9 @@ dhdsdio_probe_attach(struct dhd_bus *bus, osl_t *osh, void *sdh, void *regsva, case BCM4347_CHIP_GRPID: bus->dongle_ram_base = CR4_4347_RAM_BASE; break; + case BCM4362_CHIP_ID: + bus->dongle_ram_base = CR4_4362_RAM_BASE; + break; default: bus->dongle_ram_base = 0; DHD_ERROR(("%s: WARNING: Using default ram base at 0x%x\n", @@ -8284,7 +8299,7 @@ dhd_set_path_params(struct dhd_bus *bus) dhd_conf_read_config(bus->dhd, bus->dhd->conf_path); - dhd_conf_set_fw_name_by_chip(bus->dhd, bus->fw_path, bus->nv_path); + dhd_conf_set_fw_name_by_chip(bus->dhd, bus->fw_path); dhd_conf_set_nv_name_by_chip(bus->dhd, bus->nv_path); dhd_conf_set_clm_name_by_chip(bus->dhd, bus->dhd->clm_path); @@ -8309,15 +8324,12 @@ dhd_set_bus_params(struct dhd_bus *bus) } if (bus->dhd->conf->use_rxchain >= 0) { bus->use_rxchain = (bool)bus->dhd->conf->use_rxchain; - printf("%s: set use_rxchain %d\n", __FUNCTION__, bus->dhd->conf->use_rxchain); } if (bus->dhd->conf->txinrx_thres >= 0) { bus->txinrx_thres = bus->dhd->conf->txinrx_thres; - printf("%s: set txinrx_thres %d\n", __FUNCTION__, bus->txinrx_thres); } if (bus->dhd->conf->txglomsize >= 0) { bus->txglomsize = bus->dhd->conf->txglomsize; - printf("%s: set txglomsize %d\n", __FUNCTION__, bus->dhd->conf->txglomsize); } } @@ -8462,33 +8474,14 @@ dhdsdio_disconnect(void *ptr) DHD_TRACE(("%s: Enter\n", __FUNCTION__)); -#if defined(MULTIPLE_SUPPLICANT) -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) - if (mutex_is_locked(&_dhd_sdio_mutex_lock_) == 0) { - DHD_ERROR(("%s : no mutex held. set lock\n", __FUNCTION__)); - } - else { - DHD_ERROR(("%s : mutex is locked!. wait for unlocking\n", __FUNCTION__)); - } - mutex_lock(&_dhd_sdio_mutex_lock_); -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ -#endif - - + DHD_MUTEX_LOCK(); if (bus) { ASSERT(bus->dhd); - /* Advertise bus cleanup during rmmod */ - dhdsdio_advertise_bus_cleanup(bus->dhd); + /* Advertise bus remove during rmmod */ + dhdsdio_advertise_bus_remove(bus->dhd); dhdsdio_release(bus, bus->dhd->osh); } - -#if defined(MULTIPLE_SUPPLICANT) -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) - mutex_unlock(&_dhd_sdio_mutex_lock_); - DHD_ERROR(("%s : the lock is released.\n", __FUNCTION__)); -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ -#endif /* LINUX */ - + DHD_MUTEX_UNLOCK(); DHD_TRACE(("%s: Disconnected\n", __FUNCTION__)); } @@ -9262,6 +9255,27 @@ dhdsdio_advertise_bus_cleanup(dhd_pub_t *dhdp) return; } +static void +dhdsdio_advertise_bus_remove(dhd_pub_t *dhdp) +{ + unsigned long flags; + int timeleft; + + DHD_LINUX_GENERAL_LOCK(dhdp, flags); + dhdp->busstate = DHD_BUS_REMOVE; + DHD_LINUX_GENERAL_UNLOCK(dhdp, flags); + + timeleft = dhd_os_busbusy_wait_negation(dhdp, &dhdp->dhd_bus_busy_state); + if ((timeleft == 0) || (timeleft == 1)) { + DHD_ERROR(("%s : Timeout due to dhd_bus_busy_state=0x%x\n", + __FUNCTION__, dhdp->dhd_bus_busy_state)); + ASSERT(0); + } + + return; +} + + int dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag) { diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_static_buf.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_static_buf.c index c615ba04..188a0ac0 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_static_buf.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_static_buf.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 */ +/* SPDX-License-Identifier: GPL-2.0 */ #include #include #include @@ -7,7 +7,7 @@ #include #include -#define DHD_STATIC_VERSION_STR "1.579.77.41.1" +#define DHD_STATIC_VERSION_STR "1.579.77.41.9" #define BCMDHD_SDIO #define BCMDHD_PCIE @@ -53,7 +53,7 @@ enum dhd_prealloc_index { #define DHD_PREALLOC_OSL_BUF_SIZE (STATIC_BUF_MAX_NUM * STATIC_BUF_SIZE) #define DHD_PREALLOC_WIPHY_ESCAN0_SIZE (64 * 1024) #define DHD_PREALLOC_DHD_INFO_SIZE (30 * 1024) -#define DHD_PREALLOC_MEMDUMP_RAM_SIZE (770 * 1024) +#define DHD_PREALLOC_MEMDUMP_RAM_SIZE (810 * 1024) #define DHD_PREALLOC_DHD_WLFC_HANGER_SIZE (73 * 1024) #define DHD_PREALLOC_WL_ESCAN_INFO_SIZE (66 * 1024) #ifdef CONFIG_64BIT @@ -327,6 +327,8 @@ static int dhd_init_wlan_mem(void) wlan_static_if_flow_lkup = kmalloc(DHD_PREALLOC_IF_FLOW_LKUP_SIZE, GFP_KERNEL); if (!wlan_static_if_flow_lkup) goto err_mem_alloc; + pr_err("%s: sectoin %d, size=%d\n", __func__, + DHD_PREALLOC_IF_FLOW_LKUP, DHD_PREALLOC_IF_FLOW_LKUP_SIZE); #endif /* BCMDHD_PCIE */ wlan_static_dhd_memdump_ram_buf = kmalloc(DHD_PREALLOC_MEMDUMP_RAM_SIZE, GFP_KERNEL); @@ -347,31 +349,29 @@ static int dhd_init_wlan_mem(void) pr_err("%s: sectoin %d, size=%d\n", __func__, DHD_PREALLOC_WL_ESCAN_INFO, DHD_PREALLOC_WL_ESCAN_INFO_SIZE); - wlan_static_fw_verbose_ring_buf = kmalloc( - DHD_PREALLOC_WIPHY_ESCAN0_SIZE, - GFP_KERNEL); + wlan_static_fw_verbose_ring_buf = kmalloc(FW_VERBOSE_RING_SIZE, GFP_KERNEL); if (!wlan_static_fw_verbose_ring_buf) goto err_mem_alloc; pr_err("%s: sectoin %d, size=%d\n", __func__, - DHD_PREALLOC_FW_VERBOSE_RING, DHD_PREALLOC_WL_ESCAN_INFO_SIZE); + DHD_PREALLOC_FW_VERBOSE_RING, FW_VERBOSE_RING_SIZE); - wlan_static_fw_event_ring_buf = kmalloc(DHD_PREALLOC_WIPHY_ESCAN0_SIZE, GFP_KERNEL); + wlan_static_fw_event_ring_buf = kmalloc(FW_EVENT_RING_SIZE, GFP_KERNEL); if (!wlan_static_fw_event_ring_buf) goto err_mem_alloc; pr_err("%s: sectoin %d, size=%d\n", __func__, - DHD_PREALLOC_FW_EVENT_RING, DHD_PREALLOC_WL_ESCAN_INFO_SIZE); + DHD_PREALLOC_FW_EVENT_RING, FW_EVENT_RING_SIZE); - wlan_static_dhd_event_ring_buf = kmalloc(DHD_PREALLOC_WIPHY_ESCAN0_SIZE, GFP_KERNEL); + wlan_static_dhd_event_ring_buf = kmalloc(DHD_EVENT_RING_SIZE, GFP_KERNEL); if (!wlan_static_dhd_event_ring_buf) goto err_mem_alloc; pr_err("%s: sectoin %d, size=%d\n", __func__, - DHD_PREALLOC_DHD_EVENT_RING, DHD_PREALLOC_WL_ESCAN_INFO_SIZE); + DHD_PREALLOC_DHD_EVENT_RING, DHD_EVENT_RING_SIZE); - wlan_static_nan_event_ring_buf = kmalloc(DHD_PREALLOC_WIPHY_ESCAN0_SIZE, GFP_KERNEL); + wlan_static_nan_event_ring_buf = kmalloc(NAN_EVENT_RING_SIZE, GFP_KERNEL); if (!wlan_static_nan_event_ring_buf) goto err_mem_alloc; pr_err("%s: sectoin %d, size=%d\n", __func__, - DHD_PREALLOC_NAN_EVENT_RING, DHD_PREALLOC_WL_ESCAN_INFO_SIZE); + DHD_PREALLOC_NAN_EVENT_RING, NAN_EVENT_RING_SIZE); return 0; diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_wlfc.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_wlfc.c index 678dbc38..442af325 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_wlfc.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_wlfc.c @@ -1617,7 +1617,7 @@ _dhd_wlfc_pktq_flush(athost_wl_status_info_t* ctx, struct pktq *pq, ASSERT(pq->len == 0); } /* _dhd_wlfc_pktq_flush */ - +#ifndef BCMDBUS /** !BCMDBUS specific function. Dequeues a packet from the caller supplied queue. */ static void* _dhd_wlfc_pktq_pdeq_with_fn(struct pktq *pq, int prec, f_processpkt_t fn, void *arg) @@ -1723,6 +1723,7 @@ _dhd_wlfc_cleanup_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg) PKTFREE(wlfc->osh, pkt, TRUE); } } /* _dhd_wlfc_cleanup_txq */ +#endif /* !BCMDBUS */ /** called during eg detach */ void @@ -1741,8 +1742,10 @@ _dhd_wlfc_cleanup(dhd_pub_t *dhd, f_processpkt_t fn, void *arg) /* * flush sequence should be txq -> psq -> hanger/afq, hanger has to be last one */ +#ifndef BCMDBUS /* flush bus->txq */ _dhd_wlfc_cleanup_txq(dhd, fn, arg); +#endif /* !BCMDBUS */ /* flush psq, search all entries, include nodes as well as interfaces */ total_entries = sizeof(wlfc->destination_entries)/sizeof(wlfc_mac_descriptor_t); @@ -2465,7 +2468,7 @@ _dhd_wlfc_fifocreditback_indicate(dhd_pub_t *dhd, uint8* credits) return BCME_OK; } /* _dhd_wlfc_fifocreditback_indicate */ - +#ifndef BCMDBUS /** !BCMDBUS specific function */ static void _dhd_wlfc_suppress_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg) @@ -2544,6 +2547,7 @@ _dhd_wlfc_suppress_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg) _dhd_wlfc_fifocreditback_indicate(dhd, credits); } } /* _dhd_wlfc_suppress_txq */ +#endif /* !BCMDBUS */ static int _dhd_wlfc_dbg_senum_check(dhd_pub_t *dhd, uint8 *value) @@ -3072,10 +3076,12 @@ dhd_wlfc_parse_header_info(dhd_pub_t *dhd, void* pktbuf, int tlv_hdr_len, uchar _dhd_wlfc_interface_update(dhd, value, type); } +#ifndef BCMDBUS if (entry && WLFC_GET_REORDERSUPP(dhd->wlfc_mode)) { /* suppress all packets for this mac entry from bus->txq */ _dhd_wlfc_suppress_txq(dhd, _dhd_wlfc_entrypkt_fn, entry); } +#endif /* !BCMDBUS */ } /* while */ if (remainder != 0 && wlfc) { @@ -3407,6 +3413,15 @@ dhd_wlfc_commit_packets(dhd_pub_t *dhdp, f_commitpkt_t fcommit, void* commit_ctx ctx = (athost_wl_status_info_t*)dhdp->wlfc_state; +#ifdef BCMDBUS + if (!dhdp->up || (dhdp->busstate == DHD_BUS_DOWN)) { + if (pktbuf) { + PKTFREE(ctx->osh, pktbuf, TRUE); + rc = BCME_OK; + } + goto exit; + } +#endif /* BCMDBUS */ if (dhdp->proptxstatus_module_ignore) { if (pktbuf) { @@ -3593,10 +3608,17 @@ dhd_wlfc_init(dhd_pub_t *dhd) DHD_ERROR(("%s: query wlfc_mode succeed, fw_caps=0x%x\n", __FUNCTION__, fw_caps)); if (WLFC_IS_OLD_DEF(fw_caps)) { +#ifdef BCMDBUS + mode = WLFC_MODE_HANGER; +#else /* enable proptxtstatus v2 by default */ mode = WLFC_MODE_AFQ; +#endif /* BCMDBUS */ } else { WLFC_SET_AFQ(mode, WLFC_GET_AFQ(fw_caps)); +#ifdef BCMDBUS + WLFC_SET_AFQ(mode, 0); +#endif /* BCMDBUS */ WLFC_SET_REUSESEQ(mode, WLFC_GET_REUSESEQ(fw_caps)); WLFC_SET_REORDERSUPP(mode, WLFC_GET_REORDERSUPP(fw_caps)); } @@ -3679,7 +3701,9 @@ dhd_wlfc_cleanup_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg) return WLFC_UNSUPPORTED; } +#ifndef BCMDBUS _dhd_wlfc_cleanup_txq(dhd, fn, arg); +#endif /* !BCMDBUS */ dhd_os_wlfc_unblock(dhd); diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_wlfc.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_wlfc.h old mode 100755 new mode 100644 index 1e8b01f9..54c6b3b4 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_wlfc.h +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_wlfc.h @@ -111,8 +111,13 @@ typedef struct wlfc_hanger { #define WLFC_PSQ_LEN (4096 * 8) +#ifdef BCMDBUS +#define WLFC_FLOWCONTROL_HIWATER 512 +#define WLFC_FLOWCONTROL_LOWATER (WLFC_FLOWCONTROL_HIWATER / 4) +#else #define WLFC_FLOWCONTROL_HIWATER ((4096 * 8) - 256) #define WLFC_FLOWCONTROL_LOWATER 256 +#endif #if (WLFC_FLOWCONTROL_HIWATER >= (WLFC_PSQ_LEN - 256)) #undef WLFC_FLOWCONTROL_HIWATER diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/bcmdevs.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/bcmdevs.h old mode 100755 new mode 100644 index 70ef4678..5437c8f2 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/bcmdevs.h +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/bcmdevs.h @@ -280,6 +280,10 @@ #define BCM4361_D11AC2G_ID 0x4420 /* 4361 802.11ac 2.4G device */ #define BCM4361_D11AC5G_ID 0x4421 /* 4361 802.11ac 5G device */ +#define BCM4362_D11AX_ID 0x4490 /* 4362 802.11ax dualband device */ +#define BCM4362_D11AX2G_ID 0x4491 /* 4362 802.11ax 2.4G device */ +#define BCM4362_D11AX5G_ID 0x4492 /* 4362 802.11ax 5G device */ + #define BCM4364_D11AC_ID 0x4464 /* 4364 802.11ac dualband device */ #define BCM4364_D11AC2G_ID 0x446a /* 4364 802.11ac 2.4G device */ #define BCM4364_D11AC5G_ID 0x446b /* 4364 802.11ac 5G device */ @@ -501,6 +505,7 @@ #define BCM4347_CHIP_ID 0x4347 /* 4347 chipcommon chipid */ #define BCM4357_CHIP_ID 0x4357 /* 4357 chipcommon chipid */ #define BCM4361_CHIP_ID 0x4361 /* 4361 chipcommon chipid */ +#define BCM4362_CHIP_ID 0x4362 /* 4362 chipcommon chipid */ #define BCM4347_CHIP(chipid) ((CHIPID(chipid) == BCM4347_CHIP_ID) || \ (CHIPID(chipid) == BCM4357_CHIP_ID) || \ (CHIPID(chipid) == BCM4361_CHIP_ID)) diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/dbus.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/dbus.h index c926ba77..f4dec0d9 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/dbus.h +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/dbus.h @@ -32,11 +32,22 @@ #define __DBUS_H__ #include "typedefs.h" - -#define DBUSTRACE(args) +#include + +extern uint dbus_msglevel; +#define DBUS_ERROR_VAL 0x0001 +#define DBUS_TRACE_VAL 0x0002 +#define DBUS_INFO_VAL 0x0004 + +#if defined(DHD_DEBUG) +#define DBUSERR(args) do {if (dbus_msglevel & DBUS_ERROR_VAL) printf args;} while (0) +#define DBUSTRACE(args) do {if (dbus_msglevel & DBUS_TRACE_VAL) printf args;} while (0) +#define DBUSINFO(args) do {if (dbus_msglevel & DBUS_INFO_VAL) printf args;} while (0) +#else /* defined(DHD_DEBUG) */ #define DBUSERR(args) +#define DBUSTRACE(args) #define DBUSINFO(args) -#define DBUSDBGLOCK(args) +#endif enum { DBUS_OK = 0, @@ -181,7 +192,8 @@ typedef struct dbus_extdl { struct dbus_callbacks; struct exec_parms; -typedef void *(*probe_cb_t)(void *arg, const char *desc, uint32 bustype, uint32 hdrlen); +typedef void *(*probe_cb_t)(void *arg, const char *desc, uint32 bustype, + uint16 bus_no, uint16 slot, uint32 hdrlen); typedef void (*disconnect_cb_t)(void *arg); typedef void *(*exec_cb_t)(struct exec_parms *args); @@ -237,7 +249,7 @@ typedef struct { int (*get_config)(void *bus, dbus_config_t *config); bool (*device_exists)(void *bus); - bool (*dlneeded)(void *bus); + int (*dlneeded)(void *bus); int (*dlstart)(void *bus, uint8 *fw, int len); int (*dlrun)(void *bus); bool (*recv_needed)(void *bus); @@ -299,26 +311,21 @@ extern int dbus_register(int vid, int pid, probe_cb_t prcb, disconnect_cb_t disc void *param1, void *param2); extern int dbus_deregister(void); -extern dbus_pub_t *dbus_attach(struct osl_info *osh, int rxsize, int nrxq, int ntxq, - void *cbarg, dbus_callbacks_t *cbs, dbus_extdl_t *extdl, struct shared_info *sh); -extern void dbus_detach(dbus_pub_t *pub); - -extern int dbus_download_firmware(dbus_pub_t *pub); -extern int dbus_up(dbus_pub_t *pub); +//extern int dbus_download_firmware(dbus_pub_t *pub); +//extern int dbus_up(struct dhd_bus *pub); extern int dbus_down(dbus_pub_t *pub); -extern int dbus_stop(dbus_pub_t *pub); +//extern int dbus_stop(struct dhd_bus *pub); extern int dbus_shutdown(dbus_pub_t *pub); extern void dbus_flowctrl_rx(dbus_pub_t *pub, bool on); extern int dbus_send_txdata(dbus_pub_t *dbus, void *pktbuf); extern int dbus_send_buf(dbus_pub_t *pub, uint8 *buf, int len, void *info); extern int dbus_send_pkt(dbus_pub_t *pub, void *pkt, void *info); -extern int dbus_send_ctl(dbus_pub_t *pub, uint8 *buf, int len); -extern int dbus_recv_ctl(dbus_pub_t *pub, uint8 *buf, int len); +//extern int dbus_send_ctl(struct dhd_bus *pub, uint8 *buf, int len); +//extern int dbus_recv_ctl(struct dhd_bus *pub, uint8 *buf, int len); extern int dbus_recv_bulk(dbus_pub_t *pub, uint32 ep_idx); extern int dbus_poll_intr(dbus_pub_t *pub); extern int dbus_get_stats(dbus_pub_t *pub, dbus_stats_t *stats); -extern int dbus_get_attrib(dbus_pub_t *pub, dbus_attrib_t *attrib); extern int dbus_get_device_speed(dbus_pub_t *pub); extern int dbus_set_config(dbus_pub_t *pub, dbus_config_t *config); extern int dbus_get_config(dbus_pub_t *pub, dbus_config_t *config); @@ -332,8 +339,8 @@ extern int dbus_pnp_sleep(dbus_pub_t *pub); extern int dbus_pnp_resume(dbus_pub_t *pub, int *fw_reload); extern int dbus_pnp_disconnect(dbus_pub_t *pub); -extern int dbus_iovar_op(dbus_pub_t *pub, const char *name, - void *params, int plen, void *arg, int len, bool set); +//extern int dhd_bus_iovar_op(dhd_pub_t *dhdp, const char *name, +// void *params, int plen, void *arg, int len, bool set); extern void *dhd_dbus_txq(const dbus_pub_t *pub); extern uint dhd_dbus_hdrlen(const dbus_pub_t *pub); diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/epivers.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/epivers.h index 4cf4c703..c014bb62 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/epivers.h +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/epivers.h @@ -46,6 +46,6 @@ #define EPI_VERSION_DEV 1.579.77.41 /* Driver Version String, ASCII, 32 chars max */ -#define EPI_VERSION_STR "1.579.77.41.2 (r)" +#define EPI_VERSION_STR "1.579.77.41.9 (r)" #endif /* _epivers_h_ */ diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/linux_osl.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/linux_osl.h old mode 100755 new mode 100644 index 3dd51bc3..b40ec111 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/linux_osl.h +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/linux_osl.h @@ -1145,6 +1145,7 @@ typedef struct sk_buff_head PKT_LIST; #define PKTLIST_UNLINK(x, y) skb_unlink((struct sk_buff *)(y), (struct sk_buff_head *)(x)) #define PKTLIST_FINI(x) skb_queue_purge((struct sk_buff_head *)(x)) +#ifdef REPORT_FATAL_TIMEOUTS typedef struct osl_timer { struct timer_list *timer; bool set; @@ -1156,5 +1157,6 @@ extern osl_timer_t * osl_timer_init(osl_t *osh, const char *name, void (*fn)(voi extern void osl_timer_add(osl_t *osh, osl_timer_t *t, uint32 ms, bool periodic); extern void osl_timer_update(osl_t *osh, osl_timer_t *t, uint32 ms, bool periodic); extern bool osl_timer_del(osl_t *osh, osl_timer_t *t); +#endif #endif /* _linux_osl_h_ */ diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/sbchipc.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/sbchipc.h index ffec624c..cbc75b2f 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/sbchipc.h +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/sbchipc.h @@ -3343,6 +3343,7 @@ typedef volatile struct { #define CA7_4365_RAM_BASE (0x200000) #define CR4_4347_RAM_BASE (0x170000) +#define CR4_4362_RAM_BASE (0x170000) /* 4335 chip OTP present & OTP select bits. */ #define SPROM4335_OTP_SELECT 0x00000010 diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/usbrdl.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/usbrdl.h new file mode 100644 index 00000000..f15fbd69 --- /dev/null +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/usbrdl.h @@ -0,0 +1,135 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Broadcom USB remote download definitions + * + * Copyright (C) 1999-2016, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * + * <> + * + * $Id: usbrdl.h 597933 2015-11-06 18:52:06Z $ + */ + +#ifndef _USB_RDL_H +#define _USB_RDL_H + +/* Control messages: bRequest values */ +#define DL_GETSTATE 0 /* returns the rdl_state_t struct */ +#define DL_CHECK_CRC 1 /* currently unused */ +#define DL_GO 2 /* execute downloaded image */ +#define DL_START 3 /* initialize dl state */ +#define DL_REBOOT 4 /* reboot the device in 2 seconds */ +#define DL_GETVER 5 /* returns the bootrom_id_t struct */ +#define DL_GO_PROTECTED 6 /* execute the downloaded code and set reset event + * to occur in 2 seconds. It is the responsibility + * of the downloaded code to clear this event + */ +#define DL_EXEC 7 /* jump to a supplied address */ +#define DL_RESETCFG 8 /* To support single enum on dongle + * - Not used by bootloader + */ +#define DL_DEFER_RESP_OK 9 /* Potentially defer the response to setup + * if resp unavailable + */ +#define DL_CHGSPD 0x0A + +#define DL_HWCMD_MASK 0xfc /* Mask for hardware read commands: */ +#define DL_RDHW 0x10 /* Read a hardware address (Ctl-in) */ +#define DL_RDHW32 0x10 /* Read a 32 bit word */ +#define DL_RDHW16 0x11 /* Read 16 bits */ +#define DL_RDHW8 0x12 /* Read an 8 bit byte */ +#define DL_WRHW 0x14 /* Write a hardware address (Ctl-out) */ +#define DL_WRHW_BLK 0x13 /* Block write to hardware access */ + +#define DL_CMD_WRHW 2 + + +/* states */ +#define DL_WAITING 0 /* waiting to rx first pkt that includes the hdr info */ +#define DL_READY 1 /* hdr was good, waiting for more of the compressed image */ +#define DL_BAD_HDR 2 /* hdr was corrupted */ +#define DL_BAD_CRC 3 /* compressed image was corrupted */ +#define DL_RUNNABLE 4 /* download was successful, waiting for go cmd */ +#define DL_START_FAIL 5 /* failed to initialize correctly */ +#define DL_NVRAM_TOOBIG 6 /* host specified nvram data exceeds DL_NVRAM value */ +#define DL_IMAGE_TOOBIG 7 /* download image too big (exceeds DATA_START for rdl) */ + +#define TIMEOUT 5000 /* Timeout for usb commands */ + +struct bcm_device_id { + char *name; + uint32 vend; + uint32 prod; +}; + +typedef struct { + uint32 state; + uint32 bytes; +} rdl_state_t; + +typedef struct { + uint32 chip; /* Chip id */ + uint32 chiprev; /* Chip rev */ + uint32 ramsize; /* Size of RAM */ + uint32 remapbase; /* Current remap base address */ + uint32 boardtype; /* Type of board */ + uint32 boardrev; /* Board revision */ +} bootrom_id_t; + +/* struct for backplane & jtag accesses */ +typedef struct { + uint32 cmd; /* tag to identify the cmd */ + uint32 addr; /* backplane address for write */ + uint32 len; /* length of data: 1, 2, 4 bytes */ + uint32 data; /* data to write */ +} hwacc_t; + + +/* struct for querying nvram params from bootloader */ +#define QUERY_STRING_MAX 32 +typedef struct { + uint32 cmd; /* tag to identify the cmd */ + char var[QUERY_STRING_MAX]; /* param name */ +} nvparam_t; + +typedef void (*exec_fn_t)(void *sih); + +#define USB_CTRL_IN (USB_TYPE_VENDOR | 0x80 | USB_RECIP_INTERFACE) +#define USB_CTRL_OUT (USB_TYPE_VENDOR | 0 | USB_RECIP_INTERFACE) + +#define USB_CTRL_EP_TIMEOUT 500 /* Timeout used in USB control_msg transactions. */ +#define USB_BULK_EP_TIMEOUT 500 /* Timeout used in USB bulk transactions. */ + +#define RDL_CHUNK_MAX (64 * 1024) /* max size of each dl transfer */ +#define RDL_CHUNK 1500 /* size of each dl transfer */ + +/* bootloader makes special use of trx header "offsets" array */ +#define TRX_OFFSETS_DLFWLEN_IDX 0 /* Size of the fw; used in uncompressed case */ +#define TRX_OFFSETS_JUMPTO_IDX 1 /* RAM address for jumpto after download */ +#define TRX_OFFSETS_NVM_LEN_IDX 2 /* Length of appended NVRAM data */ +#ifdef BCMTRXV2 +#define TRX_OFFSETS_DSG_LEN_IDX 3 /* Length of digital signature for the first image */ +#define TRX_OFFSETS_CFG_LEN_IDX 4 /* Length of config region, which is not digitally signed */ +#endif /* BCMTRXV2 */ + +#define TRX_OFFSETS_DLBASE_IDX 0 /* RAM start address for download */ + +#endif /* _USB_RDL_H */ diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/wlioctl.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/wlioctl.h index 1e6a3a28..812182af 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/wlioctl.h +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/include/wlioctl.h @@ -11548,6 +11548,15 @@ typedef enum wl_interface_type { */ #define WL_INTERFACE_BSSID_INDEX_USE (1 << 4) +#ifdef WLMESH +typedef struct wl_interface_info { + uint16 ver; /* version of this struct */ + struct ether_addr mac_addr; /* MAC address of the interface */ + char ifname[BCM_MSG_IFNAME_MAX]; /* name of interface */ + uint8 bsscfgidx; /* source bsscfg index */ +} wl_interface_info_t; +#endif + typedef struct wl_interface_create { uint16 ver; /* version of this struct */ uint32 flags; /* flags that defines the operation */ @@ -12462,6 +12471,12 @@ enum wl_mesh_cmd_xtlv_id { }; /* endif WLMESH */ +#ifdef WLMESH +#ifndef SAE_MAX_PASSWD_LEN +#define SAE_MAX_PASSWD_LEN 32 +#endif +#endif + /* Fast BSS Transition parameter configuration */ #define FBT_PARAM_CURRENT_VERSION 0 diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/linux_osl.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/linux_osl.c old mode 100755 new mode 100644 index efbcf36e..ee07bd32 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/linux_osl.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/linux_osl.c @@ -2148,9 +2148,13 @@ osl_os_get_image_block(char *buf, int len, void *image) if (!image) return 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + rdlen = kernel_read(fp, buf, len, &fp->f_pos); +#else rdlen = kernel_read(fp, fp->f_pos, buf, len); if (rdlen > 0) fp->f_pos += rdlen; +#endif return rdlen; } @@ -2677,13 +2681,19 @@ osl_pkt_orphan_partial(struct sk_buff *skb, int tsq) */ fraction = skb->truesize * (tsq - 1) / tsq; skb->truesize -= fraction; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) + atomic_sub(fraction, &skb->sk->sk_wmem_alloc.refs); +#else atomic_sub(fraction, &skb->sk->sk_wmem_alloc); +#endif /* LINUX_VERSION >= 4.13.0 */ + skb_orphan(skb); } #endif /* LINUX_VERSION >= 3.6.0 && TSQ_MULTIPLIER */ /* timer apis */ /* Note: All timer api's are thread unsafe and should be protected with locks by caller */ +#ifdef REPORT_FATAL_TIMEOUTS osl_timer_t * osl_timer_init(osl_t *osh, const char *name, void (*fn)(void *arg), void *arg) { @@ -2768,3 +2778,4 @@ osl_timer_del(osl_t *osh, osl_timer_t *t) } return (TRUE); } +#endif diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/siutils.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/siutils.c index 00ae5869..74cdcfaf 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/siutils.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/siutils.c @@ -604,7 +604,6 @@ si_doattach(si_info_t *sii, uint devid, osl_t *osh, volatile void *regs, } sih->bustype = bustype; - #ifdef BCMBUSTYPE if (bustype != BUSTYPE(bustype)) { SI_ERROR(("si_doattach: bus type %d does not match configured bus type %d\n", @@ -636,7 +635,7 @@ si_doattach(si_info_t *sii, uint devid, osl_t *osh, volatile void *regs, sih->chiprev = (w & CID_REV_MASK) >> CID_REV_SHIFT; sih->chippkg = (w & CID_PKG_MASK) >> CID_PKG_SHIFT; -#if defined(HW_OOB) || defined(FORCE_WOWLAN) +#if defined(BCMSDIO) && (defined(HW_OOB) || defined(FORCE_WOWLAN)) dhd_conf_set_hw_oob_intr(sdh, sih->chip); #endif diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_android.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_android.c index 42afe36f..a73c28df 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_android.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_android.c @@ -104,6 +104,10 @@ uint android_msg_level = ANDROID_ERROR_LEVEL; #define CMD_SETBAND "SETBAND" #define CMD_GETBAND "GETBAND" #define CMD_COUNTRY "COUNTRY" +#ifdef WLMESH +#define CMD_SAE_SET_PASSWORD "SAE_SET_PASSWORD" +#define CMD_SET_RSDB_MODE "RSDB_MODE" +#endif #define CMD_P2P_SET_NOA "P2P_SET_NOA" #if !defined WL_ENABLE_P2P_IF #define CMD_P2P_GET_NOA "P2P_GET_NOA" @@ -1068,7 +1072,7 @@ wl_cfg80211_get_sta_info(struct net_device *dev, char* command, int total_len) error: return bytes_written; } -#endif /* CUSTOMER_HW4_PRIVATE_CMD */ +#endif #ifdef WBTEXT static int wl_android_wbtext(struct net_device *dev, char *command, int total_len) @@ -1179,6 +1183,7 @@ static int wl_cfg80211_wbtext_btm_delta(struct net_device *dev, #define PNO_PARAM_SIZE 50 #define VALUE_SIZE 50 #define LIMIT_STR_FMT ("%50s %50s") + static int wls_parse_batching_cmd(struct net_device *dev, char *command, int total_len) { @@ -1187,7 +1192,8 @@ wls_parse_batching_cmd(struct net_device *dev, char *command, int total_len) char *pos, *pos2, *token, *token2, *delim; char param[PNO_PARAM_SIZE+1], value[VALUE_SIZE+1]; struct dhd_pno_batch_params batch_params; - DHD_PNO(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); + + ANDROID_INFO(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); if (total_len < strlen(CMD_WLS_BATCHING)) { ANDROID_ERROR(("%s argument=%d less min size\n", __FUNCTION__, total_len)); err = BCME_ERROR; @@ -1212,13 +1218,13 @@ wls_parse_batching_cmd(struct net_device *dev, char *command, int total_len) tokens = sscanf(token, LIMIT_STR_FMT, param, value); if (!strncmp(param, PNO_PARAM_SCANFREQ, strlen(PNO_PARAM_SCANFREQ))) { batch_params.scan_fr = simple_strtol(value, NULL, 0); - DHD_PNO(("scan_freq : %d\n", batch_params.scan_fr)); + ANDROID_INFO(("scan_freq : %d\n", batch_params.scan_fr)); } else if (!strncmp(param, PNO_PARAM_BESTN, strlen(PNO_PARAM_BESTN))) { batch_params.bestn = simple_strtol(value, NULL, 0); - DHD_PNO(("bestn : %d\n", batch_params.bestn)); + ANDROID_INFO(("bestn : %d\n", batch_params.bestn)); } else if (!strncmp(param, PNO_PARAM_MSCAN, strlen(PNO_PARAM_MSCAN))) { batch_params.mscan = simple_strtol(value, NULL, 0); - DHD_PNO(("mscan : %d\n", batch_params.mscan)); + ANDROID_INFO(("mscan : %d\n", batch_params.mscan)); } else if (!strncmp(param, PNO_PARAM_CHANNEL, strlen(PNO_PARAM_CHANNEL))) { i = 0; pos2 = value; @@ -1238,7 +1244,7 @@ wls_parse_batching_cmd(struct net_device *dev, char *command, int total_len) if (*token2 == 'A' || *token2 == 'B') { batch_params.band = (*token2 == 'A')? WLC_BAND_5G : WLC_BAND_2G; - DHD_PNO(("band : %s\n", + ANDROID_INFO(("band : %s\n", (*token2 == 'A')? "A" : "B")); } else { if ((batch_params.nchan >= WL_NUMCHANNELS) || @@ -1251,13 +1257,13 @@ wls_parse_batching_cmd(struct net_device *dev, char *command, int total_len) batch_params.chan_list[i++] = simple_strtol(token2, NULL, 0); batch_params.nchan++; - DHD_PNO(("channel :%d\n", + ANDROID_INFO(("channel :%d\n", batch_params.chan_list[i-1])); } } } else if (!strncmp(param, PNO_PARAM_RTT, strlen(PNO_PARAM_RTT))) { batch_params.rtt = simple_strtol(value, NULL, 0); - DHD_PNO(("rtt : %d\n", batch_params.rtt)); + ANDROID_INFO(("rtt : %d\n", batch_params.rtt)); } else { ANDROID_ERROR(("%s : unknown param: %s\n", __FUNCTION__, param)); err = BCME_ERROR; @@ -1294,6 +1300,7 @@ wls_parse_batching_cmd(struct net_device *dev, char *command, int total_len) exit: return err; } + #ifndef WL_SCHED_SCAN static int wl_android_set_pno_setup(struct net_device *dev, char *command, int total_len) { @@ -1327,7 +1334,7 @@ static int wl_android_set_pno_setup(struct net_device *dev, char *command, int t 0x00 }; #endif /* PNO_SET_DEBUG */ - DHD_PNO(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); + ANDROID_INFO(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); if (total_len < (strlen(CMD_PNOSETUP_SET) + sizeof(cmd_tlv_t))) { ANDROID_ERROR(("%s argument=%d less min size\n", __FUNCTION__, total_len)); @@ -1362,7 +1369,7 @@ static int wl_android_set_pno_setup(struct net_device *dev, char *command, int t } str_ptr++; pno_time = simple_strtoul(str_ptr, &str_ptr, 16); - DHD_PNO(("%s: pno_time=%d\n", __FUNCTION__, pno_time)); + ANDROID_INFO(("%s: pno_time=%d\n", __FUNCTION__, pno_time)); if (str_ptr[0] != 0) { if ((str_ptr[0] != PNO_TLV_FREQ_REPEAT)) { @@ -1372,7 +1379,7 @@ static int wl_android_set_pno_setup(struct net_device *dev, char *command, int t } str_ptr++; pno_repeat = simple_strtoul(str_ptr, &str_ptr, 16); - DHD_PNO(("%s :got pno_repeat=%d\n", __FUNCTION__, pno_repeat)); + ANDROID_INFO(("%s :got pno_repeat=%d\n", __FUNCTION__, pno_repeat)); if (str_ptr[0] != PNO_TLV_FREQ_EXPO_MAX) { ANDROID_ERROR(("%s FREQ_EXPO_MAX corrupted field size\n", __FUNCTION__)); @@ -1380,7 +1387,7 @@ static int wl_android_set_pno_setup(struct net_device *dev, char *command, int t } str_ptr++; pno_freq_expo_max = simple_strtoul(str_ptr, &str_ptr, 16); - DHD_PNO(("%s: pno_freq_expo_max=%d\n", + ANDROID_INFO(("%s: pno_freq_expo_max=%d\n", __FUNCTION__, pno_freq_expo_max)); } } @@ -1552,10 +1559,6 @@ int wl_android_wifi_on(struct net_device *dev) { int ret = 0; int retry = POWERUP_MAX_RETRY; -#ifdef IAPSTA_PREINIT - int bytes_written = 0; - struct dhd_conf *conf; -#endif if (!dev) { ANDROID_ERROR(("%s: dev is null\n", __FUNCTION__)); @@ -1589,30 +1592,22 @@ int wl_android_wifi_on(struct net_device *dev) ANDROID_ERROR(("\nfailed to power up wifi chip, max retry reached **\n\n")); goto exit; } -#ifdef BCMSDIO +#if defined(BCMSDIO) || defined(BCMDBUS) ret = dhd_net_bus_devreset(dev, FALSE); if (ret) goto err; +#ifdef BCMSDIO dhd_net_bus_resume(dev, 1); #endif /* BCMSDIO */ - -#ifndef BCMPCIE +#endif /* BCMSDIO || BCMDBUS */ +#if defined(BCMSDIO) || defined(BCMDBUS) if (!ret) { if (dhd_dev_init_ioctl(dev) < 0) { ret = -EFAULT; goto err; } } -#endif /* !BCMPCIE */ - -#ifdef IAPSTA_PREINIT - conf = dhd_get_conf(dev); - if (conf) { - wl_android_ext_priv_cmd(dev, conf->iapsta_init, 0, &bytes_written); - wl_android_ext_priv_cmd(dev, conf->iapsta_config, 0, &bytes_written); - wl_android_ext_priv_cmd(dev, conf->iapsta_enable, 0, &bytes_written); - } -#endif +#endif /* BCMSDIO || BCMDBUS */ g_wifi_on = TRUE; } @@ -1621,15 +1616,17 @@ int wl_android_wifi_on(struct net_device *dev) dhd_net_if_unlock(dev); return ret; -#ifdef BCMSDIO +#if defined(BCMSDIO) || defined(BCMDBUS) err: dhd_net_bus_devreset(dev, TRUE); +#ifdef BCMSDIO dhd_net_bus_suspend(dev); +#endif /* BCMSDIO */ dhd_net_wifi_platform_set_power(dev, FALSE, WIFI_TURNOFF_DELAY); printf("%s: Failed\n", __FUNCTION__); dhd_net_if_unlock(dev); return ret; -#endif +#endif /* BCMSDIO || BCMDBUS */ } int wl_android_wifi_off(struct net_device *dev, bool on_failure) @@ -1652,12 +1649,12 @@ int wl_android_wifi_off(struct net_device *dev, bool on_failure) dhd_net_if_lock(dev); printf("%s in 2: g_wifi_on=%d, on_failure=%d\n", __FUNCTION__, g_wifi_on, on_failure); if (g_wifi_on || on_failure) { -#if defined(BCMSDIO) || defined(BCMPCIE) +#if defined(BCMSDIO) || defined(BCMPCIE) || defined(BCMDBUS) ret = dhd_net_bus_devreset(dev, TRUE); #if defined(BCMSDIO) dhd_net_bus_suspend(dev); #endif /* BCMSDIO */ -#endif /* BCMSDIO || BCMPCIE */ +#endif /* BCMSDIO || BCMPCIE || BCMDBUS */ dhd_net_wifi_platform_set_power(dev, FALSE, WIFI_TURNOFF_DELAY); g_wifi_on = FALSE; } @@ -2497,6 +2494,7 @@ wl_android_set_auto_channel(struct net_device *dev, const char* cmd_str, uint32 band = WLC_BAND_2G; uint32 buf_size; char *pos = command; + int band_new, band_cur; if (cmd_str) { ANDROID_INFO(("Command: %s len:%d \n", cmd_str, (int)strlen(cmd_str))); @@ -2516,20 +2514,22 @@ wl_android_set_auto_channel(struct net_device *dev, const char* cmd_str, (channel == APCS_BAND_2G_LEGACY2)) { band = WLC_BAND_2G; } else { - ANDROID_ERROR(("Invalid argument\n")); + ANDROID_ERROR(("%s: Invalid argument\n", __FUNCTION__)); return -EINVAL; } } } else { /* If no argument is provided, default to 2G */ - ANDROID_ERROR(("No argument given default to 2.4G scan\n")); + ANDROID_ERROR(("%s: No argument given default to 2.4G scan\n", __FUNCTION__)); band = WLC_BAND_2G; } - ANDROID_INFO(("HAPD_AUTO_CHANNEL = %d, band=%d \n", channel, band)); + ANDROID_INFO(("%s : HAPD_AUTO_CHANNEL = %d, band=%d \n", __FUNCTION__, channel, band)); + + ret = wldev_ioctl_set(dev, WLC_GET_BAND, &band_cur, sizeof(band_cur)); if ((ret = wldev_ioctl_get(dev, WLC_GET_SPECT_MANAGMENT, &spect, sizeof(spect))) < 0) { - ANDROID_ERROR(("ACS: error getting the spect\n")); + ANDROID_ERROR(("%s: ACS: error getting the spect\n", __FUNCTION__)); goto done; } @@ -2551,15 +2551,19 @@ wl_android_set_auto_channel(struct net_device *dev, const char* cmd_str, reqbuf = kzalloc(CHANSPEC_BUF_SIZE, GFP_KERNEL); if (reqbuf == NULL) { - ANDROID_ERROR(("failed to allocate chanspec buffer\n")); + ANDROID_ERROR(("%s: failed to allocate chanspec buffer\n", __FUNCTION__)); return -ENOMEM; } if (band == WLC_BAND_AUTO) { - ANDROID_INFO(("ACS full channel scan \n")); + ANDROID_INFO(("%s: ACS full channel scan \n", __func__)); reqbuf[0] = htod32(0); } else if (band == WLC_BAND_5G) { - ANDROID_INFO(("ACS 5G band scan \n")); + band_new = band_cur==WLC_BAND_2G ? band_cur : WLC_BAND_5G; + ret = wldev_ioctl_set(dev, WLC_SET_BAND, &band_new, sizeof(band_new)); + if (ret < 0) + WL_ERR(("WLC_SET_BAND error %d\n", ret)); + ANDROID_INFO(("%s: ACS 5G band scan \n", __func__)); if ((ret = wl_cfg80211_get_chanspecs_5g(dev, reqbuf, CHANSPEC_BUF_SIZE)) < 0) { ANDROID_ERROR(("ACS 5g chanspec retreival failed! \n")); goto done; @@ -2569,7 +2573,7 @@ wl_android_set_auto_channel(struct net_device *dev, const char* cmd_str, * If channel argument is not provided/ argument 20 is provided, * Restrict channel to 2GHz, 20MHz BW, No SB */ - ANDROID_INFO(("ACS 2G band scan \n")); + ANDROID_INFO(("%s: ACS 2G band scan \n", __func__)); if ((ret = wl_cfg80211_get_chanspecs_2g(dev, reqbuf, CHANSPEC_BUF_SIZE)) < 0) { ANDROID_ERROR(("ACS 2g chanspec retreival failed! \n")); goto done; @@ -2579,11 +2583,12 @@ wl_android_set_auto_channel(struct net_device *dev, const char* cmd_str, goto done2; } - buf_size = (band == WLC_BAND_AUTO) ? sizeof(int) : CHANSPEC_BUF_SIZE; + buf_size = CHANSPEC_BUF_SIZE; ret = wldev_ioctl_set(dev, WLC_START_CHANNEL_SEL, (void *)reqbuf, buf_size); if (ret < 0) { - ANDROID_ERROR(("can't start auto channel scan, err = %d\n", ret)); + ANDROID_ERROR(("%s: can't start auto channel scan, err = %d\n", + __FUNCTION__, ret)); channel = 0; goto done; } @@ -2609,6 +2614,18 @@ wl_android_set_auto_channel(struct net_device *dev, const char* cmd_str, chosen = dtoh32(chosen); } + if ((ret == 0) && (dtoh32(chosen) != 0)) { + uint chip; + chip = dhd_conf_get_chip(dhd_get_pub(dev)); + if (chip != BCM43143_CHIP_ID) { + u32 chanspec = 0; + chanspec = wl_chspec_driver_to_host(chosen); + ANDROID_INFO(("%s: selected chanspec = 0x%x\n", __FUNCTION__, chanspec)); + chosen = wf_chspec_ctlchan(chanspec); + ANDROID_INFO(("%s: selected chosen = 0x%x\n", __FUNCTION__, chosen)); + } + } + if (chosen) { int chosen_band; int apcs_band; @@ -2623,12 +2640,15 @@ wl_android_set_auto_channel(struct net_device *dev, const char* cmd_str, #endif /* D11AC_IOTYPES */ apcs_band = (band == WLC_BAND_AUTO) ? WLC_BAND_2G : band; chosen_band = (channel <= CH_MAX_2G_CHANNEL) ? WLC_BAND_2G : WLC_BAND_5G; - if (apcs_band == chosen_band) { + if (band == WLC_BAND_AUTO) { + printf("%s: selected channel = %d\n", __FUNCTION__, channel); + break; + } else if (apcs_band == chosen_band) { printf("%s: selected channel = %d\n", __FUNCTION__, channel); break; } } - ANDROID_INFO(("%d tried, ret = %d, chosen = 0x%x\n", + ANDROID_INFO(("%s: %d tried, ret = %d, chosen = 0x%x\n", __FUNCTION__, (APCS_MAX_RETRY - retry), ret, chosen)); OSL_SLEEP(250); } @@ -2641,12 +2661,16 @@ wl_android_set_auto_channel(struct net_device *dev, const char* cmd_str, } else { channel = APCS_DEFAULT_2G_CH; } - ANDROID_ERROR(("ACS failed. Fall back to default channel (%d) \n", channel)); + ANDROID_ERROR(("%s: ACS failed." + " Fall back to default channel (%d) \n", __FUNCTION__, channel)); } done2: + ret = wldev_ioctl_set(dev, WLC_SET_BAND, &band_cur, sizeof(band_cur)); + if (ret < 0) + WL_ERR(("WLC_SET_BAND error %d\n", ret)); if (spect > 0) { if ((ret = wl_cfg80211_set_spect(dev, spect) < 0)) { - ANDROID_ERROR(("ACS: error while setting spect\n")); + ANDROID_ERROR(("%s: ACS: error while setting spect\n", __FUNCTION__)); } } @@ -2660,7 +2684,7 @@ wl_android_set_auto_channel(struct net_device *dev, const char* cmd_str, else pos += snprintf(pos, total_len, "5g="); pos += snprintf(pos, total_len, "%d", channel); - ANDROID_INFO(("command result is %s \n", command)); + ANDROID_INFO(("%s: command result is %s \n", __FUNCTION__, command)); return strlen(command); } else { return ret; @@ -3754,8 +3778,7 @@ wl_cfg80211_p2plo_offload(struct net_device *dev, char *cmd, char* buf, int len) } #endif /* P2P_LISTEN_OFFLOADING */ -#ifdef WL_CFG80211 -#ifdef BCM4359_CHIP +#if defined(BCM4359_CHIP) && defined(WL_CFG80211) int wl_android_murx_bfe_cap(struct net_device *dev, int val) { @@ -3797,7 +3820,6 @@ wl_android_murx_bfe_cap(struct net_device *dev, int val) return err; } #endif /* BCM4359_CHIP */ -#endif #ifdef SUPPORT_AP_HIGHER_BEACONRATE int @@ -4203,6 +4225,40 @@ wl_android_make_hang_with_reason(struct net_device *dev, const char *string_num) } #endif /* DHD_HANG_SEND_UP_TEST */ +#ifdef WL_CFG80211 +#ifdef WLMESH +static int +wl_android_set_rsdb_mode(struct net_device *dev, char *command, int total_len) +{ + int ret; + wl_config_t rsdb_mode_cfg = {-1, 0}; + char smbuf[WLC_IOCTL_SMLEN]; + s32 val = 1; + + if (sscanf(command, "%*s %d", &rsdb_mode_cfg.config) != 1) { + DHD_ERROR(("%s: Failed to get Parameter\n", __FUNCTION__)); + return -1; + } + DHD_INFO(("%s : RSDB_MODE = %d\n", __FUNCTION__, rsdb_mode_cfg.config)); + + ret = wldev_ioctl_set(dev, WLC_DOWN, &val, sizeof(s32)); + if (ret < 0) + DHD_ERROR(("WLC_DOWN error %d\n", ret)); + + ret = wldev_iovar_setbuf(dev, "rsdb_mode", &rsdb_mode_cfg, sizeof(rsdb_mode_cfg), + smbuf, sizeof(smbuf), NULL); + if (ret < 0) + DHD_ERROR(("%s : set rsdb_mode error=%d\n", __FUNCTION__, ret)); + + ret = wldev_ioctl_set(dev, WLC_UP, &val, sizeof(s32)); + if (ret < 0) + DHD_ERROR(("WLC_UP error %d\n", ret)); + + return ret; +} +#endif /* WLMESH */ +#endif /* WL_CFG80211 */ + #ifdef SUPPORT_LQCM static int wl_android_lqcm_enable(struct net_device *net, int lqcm_enable) @@ -4813,7 +4869,7 @@ wl_handle_private_cmd(struct net_device *net, char *command, u32 cmd_len) bytes_written = BCME_DISABLED; #else /* DISABLE_SETBAND */ uint band = *(command + strlen(CMD_SETBAND) + 1) - '0'; - if (dhd_conf_get_band(dhd_get_pub(net)) != WLC_BAND_AUTO) { + if (dhd_conf_get_band(dhd_get_pub(net)) >= WLC_BAND_AUTO) { printf("%s: Band is fixed in config.txt\n", __FUNCTION__); } else bytes_written = wl_cfg80211_set_if_band(net, band); @@ -4824,6 +4880,14 @@ wl_handle_private_cmd(struct net_device *net, char *command, u32 cmd_len) bytes_written = wl_android_get_band(net, command, priv_cmd.total_len); } #ifdef WL_CFG80211 + else if (strnicmp(command, CMD_SET_CSA, strlen(CMD_SET_CSA)) == 0) { + bytes_written = wl_android_set_csa(net, command, priv_cmd.total_len); + } else if (strnicmp(command, CMD_80211_MODE, strlen(CMD_80211_MODE)) == 0) { + bytes_written = wl_android_get_80211_mode(net, command, priv_cmd.total_len); + } else if (strnicmp(command, CMD_CHANSPEC, strlen(CMD_CHANSPEC)) == 0) { + bytes_written = wl_android_get_chanspec(net, command, priv_cmd.total_len); + } +#endif /* WL_CFG80211 */ /* CUSTOMER_SET_COUNTRY feature is define for only GGSM model */ else if (strnicmp(command, CMD_COUNTRY, strlen(CMD_COUNTRY)) == 0) { /* @@ -4858,14 +4922,6 @@ wl_handle_private_cmd(struct net_device *net, char *command, u32 cmd_len) #endif /* FCC_PWR_LIMIT_2G */ #endif /* CUSTOMER_HW4_PRIVATE_CMD */ } - else if (strnicmp(command, CMD_SET_CSA, strlen(CMD_SET_CSA)) == 0) { - bytes_written = wl_android_set_csa(net, command, priv_cmd.total_len); - } else if (strnicmp(command, CMD_80211_MODE, strlen(CMD_80211_MODE)) == 0) { - bytes_written = wl_android_get_80211_mode(net, command, priv_cmd.total_len); - } else if (strnicmp(command, CMD_CHANSPEC, strlen(CMD_CHANSPEC)) == 0) { - bytes_written = wl_android_get_chanspec(net, command, priv_cmd.total_len); - } -#endif /* WL_CFG80211 */ else if (strnicmp(command, CMD_DATARATE, strlen(CMD_DATARATE)) == 0) { bytes_written = wl_android_get_datarate(net, command, priv_cmd.total_len); } else if (strnicmp(command, CMD_ASSOC_CLIENTS, strlen(CMD_ASSOC_CLIENTS)) == 0) { @@ -4900,6 +4956,18 @@ wl_handle_private_cmd(struct net_device *net, char *command, u32 cmd_len) else if (strnicmp(command, CMD_P2P_DEV_ADDR, strlen(CMD_P2P_DEV_ADDR)) == 0) { bytes_written = wl_android_get_p2p_dev_addr(net, command, priv_cmd.total_len); } +#ifdef WL_CFG80211 +#ifdef WLMESH + else if (strnicmp(command, CMD_SAE_SET_PASSWORD, strlen(CMD_SAE_SET_PASSWORD)) == 0) { + int skip = strlen(CMD_SAE_SET_PASSWORD) + 1; + bytes_written = wl_cfg80211_set_sae_password(net, command + skip, + priv_cmd.total_len - skip); + } + else if (strnicmp(command, CMD_SET_RSDB_MODE, strlen(CMD_SET_RSDB_MODE)) == 0) { + bytes_written = wl_android_set_rsdb_mode(net, command, priv_cmd.total_len); + } +#endif +#endif /* WL_CFG80211 */ else if (strnicmp(command, CMD_P2P_SET_NOA, strlen(CMD_P2P_SET_NOA)) == 0) { int skip = strlen(CMD_P2P_SET_NOA) + 1; bytes_written = wl_cfg80211_set_p2p_noa(net, command + skip, @@ -5165,17 +5233,15 @@ wl_handle_private_cmd(struct net_device *net, char *command, u32 cmd_len) bytes_written = wl_cfg80211_get_sta_info(net, command, priv_cmd.total_len); } #endif /* CUSTOMER_HW4_PRIVATE_CMD */ -#ifdef WL_CFG80211 else if (strnicmp(command, CMD_MURX_BFE_CAP, strlen(CMD_MURX_BFE_CAP)) == 0) { -#ifdef BCM4359_CHIP +#if defined(BCM4359_CHIP) && defined(WL_CFG80211) uint val = *(command + strlen(CMD_MURX_BFE_CAP) + 1) - '0'; bytes_written = wl_android_murx_bfe_cap(net, val); #else return BCME_UNSUPPORTED; #endif /* BCM4359_CHIP */ } -#endif #ifdef SUPPORT_AP_HIGHER_BEACONRATE else if (strnicmp(command, CMD_GET_AP_BASICRATE, strlen(CMD_GET_AP_BASICRATE)) == 0) { bytes_written = wl_android_get_ap_basicrate(net, command, priv_cmd.total_len); diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_android.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_android.h index fe333040..39fd6ff8 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_android.h +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_android.h @@ -104,9 +104,14 @@ int wl_handle_private_cmd(struct net_device *net, char *command, u32 cmd_len); s32 wl_netlink_send_msg(int pid, int type, int seq, const void *data, size_t size); #ifdef WL_EXT_IAPSTA -int wl_android_ext_attach_netdev(struct net_device *net, uint8 bssidx); -int wl_android_ext_dettach_netdev(void); -void wl_android_ext_iapsta_disconnect_sta(struct net_device *dev, u32 channel); +int wl_ext_iapsta_attach_netdev(struct net_device *net, uint8 bssidx); +int wl_ext_iapsta_attach_name(struct net_device *net, uint8 bssidx); +int wl_ext_iapsta_dettach_netdev(void); +u32 wl_ext_iapsta_disconnect_sta(struct net_device *dev, u32 channel); +int wl_ext_iapsta_alive_preinit(struct net_device *dev); +int wl_ext_iapsta_alive_postinit(struct net_device *dev); +int wl_ext_iapsta_event(struct net_device *dev, wl_event_msg_t *e, void* data); +extern int op_mode; #endif int wl_android_ext_priv_cmd(struct net_device *net, char *command, int total_len, int *bytes_written); @@ -114,76 +119,6 @@ int wl_android_ext_priv_cmd(struct net_device *net, char *command, int total_len #define strnicmp(str1, str2, len) strncasecmp((str1), (str2), (len)) #endif -typedef enum IF_STATE { - IF_STATE_INIT = 1, - IF_STATE_DISALBE, - IF_STATE_ENABLE -} if_state_t; - -typedef enum APSTAMODE { - ISTAONLY_MODE = 1, - IAPONLY_MODE, - IAPSTA_MODE, - IDUALAP_MODE, - IGOSTA_MODE, - IGCSTA_MODE -} apstamode_t; - -typedef enum IFMODE { - ISTA_MODE = 1, - IAP_MODE -} ifmode_t; - -typedef enum BGNMODE { - IEEE80211B = 1, - IEEE80211G, - IEEE80211BG, - IEEE80211BGN, - IEEE80211BGNAC -} bgnmode_t; - -typedef enum AUTHMODE { - AUTH_OPEN, - AUTH_SHARED, - AUTH_WPAPSK, - AUTH_WPA2PSK, - AUTH_WPAWPA2PSK -} authmode_t; - -typedef enum ENCMODE { - ENC_NONE, - ENC_WEP, - ENC_TKIP, - ENC_AES, - ENC_TKIPAES -} encmode_t; - -/* i/f query */ -typedef struct wl_if_info { - struct net_device *dev; - if_state_t ifstate; - ifmode_t ifmode; - uint bssidx; - char ifname[IFNAMSIZ+1]; - char ssid[DOT11_MAX_SSID_LEN]; - struct ether_addr bssid; - bgnmode_t bgnmode; - int hidden; - int maxassoc; - uint16 channel; - authmode_t amode; - encmode_t emode; - char key[100]; -} wl_apsta_if_t; - -typedef struct wl_apsta_params { - struct wl_if_info pif; // primary device - struct wl_if_info vif; // virtual device - int ioctl_ver; - bool init; - apstamode_t apstamode; -} wl_apsta_params_t; - /* hostap mac mode */ #define MACLIST_MODE_DISABLED 0 #define MACLIST_MODE_DENY 1 @@ -224,6 +159,10 @@ int wl_android_set_ap_mac_list(struct net_device *dev, int macmode, struct macli #define REPEATED_SCAN_RESULT_CNT 1 #endif +#if defined(RSSIAVG) || defined(RSSIOFFSET) +extern int g_wifi_on; +#endif + #if defined(RSSIAVG) #define RSSIAVG_LEN (4*REPEATED_SCAN_RESULT_CNT) #define RSSICACHE_TIMEOUT 15 @@ -286,4 +225,12 @@ void wl_update_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl, wl_scan_results_t *ss_list); void wl_release_bss_cache_ctrl(wl_bss_cache_ctrl_t *bss_cache_ctrl); #endif +int wl_ext_get_best_channel(struct net_device *net, +#if defined(BSSCACHE) + wl_bss_cache_ctrl_t *bss_cache_ctrl, +#else + struct wl_scan_results *bss_list, +#endif + int *best_2g_ch, int *best_5g_ch +); #endif /* _wl_android_ */ diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_android_ext.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_android_ext.c index e510c241..6e7ad348 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_android_ext.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_android_ext.c @@ -1,151 +1,279 @@ /* SPDX-License-Identifier: GPL-2.0 */ - - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define htod32(i) i -#define htod16(i) i -#define dtoh32(i) i -#define dtoh16(i) i -#define htodchanspec(i) i -#define dtohchanspec(i) i -#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base)) - -#define CMD_CHANNEL "CHANNEL" -#define CMD_CHANNELS "CHANNELS" -#define CMD_ROAM_TRIGGER "ROAM_TRIGGER" -#define CMD_KEEP_ALIVE "KEEP_ALIVE" -#define CMD_PM "PM" -#define CMD_MONITOR "MONITOR" -#define CMD_SET_SUSPEND_BCN_LI_DTIM "SET_SUSPEND_BCN_LI_DTIM" - -#ifdef WL_EXT_IAPSTA -#define CMD_IAPSTA_INIT "IAPSTA_INIT" -#define CMD_IAPSTA_CONFIG "IAPSTA_CONFIG" -#define CMD_IAPSTA_ENABLE "IAPSTA_ENABLE" -#define CMD_IAPSTA_DISABLE "IAPSTA_DISABLE" -#ifdef PROP_TXSTATUS -#ifdef PROP_TXSTATUS_VSDB -#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef WL_CFG80211 +#include +#endif +#ifdef WL_ESCAN +#include +#endif + +#ifndef WL_CFG80211 +#define htod32(i) i +#define htod16(i) i +#define dtoh32(i) i +#define dtoh16(i) i +#define htodchanspec(i) i +#define dtohchanspec(i) i +#define IEEE80211_BAND_2GHZ 0 +#define IEEE80211_BAND_5GHZ 1 +#define WL_SCAN_JOIN_PROBE_INTERVAL_MS 20 +#define WL_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320 +#define WL_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400 +#endif +#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base)) + +#ifndef IW_CUSTOM_MAX +#define IW_CUSTOM_MAX 256 /* size of extra buffer used for translation of events */ +#endif /* IW_CUSTOM_MAX */ + +#define CMD_CHANNEL "CHANNEL" +#define CMD_CHANNELS "CHANNELS" +#define CMD_ROAM_TRIGGER "ROAM_TRIGGER" +#define CMD_KEEP_ALIVE "KEEP_ALIVE" +#define CMD_PM "PM" +#define CMD_MONITOR "MONITOR" +#define CMD_SET_SUSPEND_BCN_LI_DTIM "SET_SUSPEND_BCN_LI_DTIM" + +#ifdef WL_EXT_IAPSTA +#include +#define CMD_IAPSTA_INIT "IAPSTA_INIT" +#define CMD_IAPSTA_CONFIG "IAPSTA_CONFIG" +#define CMD_IAPSTA_ENABLE "IAPSTA_ENABLE" +#define CMD_IAPSTA_DISABLE "IAPSTA_DISABLE" +#define CMD_ISAM_INIT "ISAM_INIT" +#define CMD_ISAM_CONFIG "ISAM_CONFIG" +#define CMD_ISAM_ENABLE "ISAM_ENABLE" +#define CMD_ISAM_DISABLE "ISAM_DISABLE" +#define CMD_ISAM_DUMP "ISAM_DUMP" +#ifdef PROP_TXSTATUS +#ifdef PROP_TXSTATUS_VSDB +#include extern int disable_proptx; -#endif /* PROP_TXSTATUS_VSDB */ -#endif -#endif -#ifdef IDHCP -#define CMD_DHCPC_ENABLE "DHCPC_ENABLE" -#define CMD_DHCPC_DUMP "DHCPC_DUMP" -#endif -#define CMD_WL "WL" - -#define IEEE80211_BAND_2GHZ 0 -#define IEEE80211_BAND_5GHZ 1 - -int wl_ext_ioctl(struct net_device *dev, u32 cmd, void *arg, u32 len, u32 set) -{ - int ret; - - ret = wldev_ioctl(dev, cmd, arg, len, set); - if (ret) - ANDROID_ERROR(("%s: cmd=%d ret=%d\n", __FUNCTION__, cmd, ret)); - return ret; -} - -int wl_ext_iovar_getint(struct net_device *dev, s8 *iovar, s32 *val) -{ - int ret; - - ret = wldev_iovar_getint(dev, iovar, val); - if (ret) - ANDROID_ERROR(("%s: iovar=%s, ret=%d\n", __FUNCTION__, iovar, ret)); - - return ret; -} - -int wl_ext_iovar_setint(struct net_device *dev, s8 *iovar, s32 val) -{ - int ret; - - ret = wldev_iovar_setint(dev, iovar, val); - if (ret) - ANDROID_ERROR(("%s: iovar=%s, ret=%d\n", __FUNCTION__, iovar, ret)); - - return ret; -} - -int wl_ext_iovar_getbuf(struct net_device *dev, s8 *iovar_name, - void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync) -{ - int ret; - - ret = wldev_iovar_getbuf(dev, iovar_name, param, paramlen, buf, buflen, buf_sync); - if (ret != 0) - ANDROID_ERROR(("%s: iovar=%s, ret=%d\n", __FUNCTION__, iovar_name, ret)); - - return ret; -} - -int wl_ext_iovar_setbuf(struct net_device *dev, s8 *iovar_name, - void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync) -{ - int ret; - - ret = wldev_iovar_setbuf(dev, iovar_name, param, paramlen, buf, buflen, buf_sync); - if (ret != 0) - ANDROID_ERROR(("%s: iovar=%s, ret=%d\n", __FUNCTION__, iovar_name, ret)); - - return ret; -} - -#ifdef WL_EXT_IAPSTA -int wl_ext_iovar_setbuf_bsscfg(struct net_device *dev, s8 *iovar_name, - void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync) -{ - int ret; - - ret = wldev_iovar_setbuf_bsscfg(dev, iovar_name, param, paramlen, - buf, buflen, bsscfg_idx, buf_sync); - if (ret < 0) - ANDROID_ERROR(("%s: iovar_name=%s ret=%d\n", __FUNCTION__, iovar_name, ret)); - - return ret; -} - -int wl_ext_iovar_getbuf_bsscfg(struct net_device *dev, s8 *iovar_name, - void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync) -{ - int ret; - - ret = wldev_iovar_getbuf_bsscfg(dev, iovar_name, param, paramlen, - buf, buflen, bsscfg_idx, buf_sync); - if (ret < 0) - ANDROID_ERROR(("%s: iovar_name=%s ret=%d\n", __FUNCTION__, iovar_name, ret)); - - return ret; -} -#endif +#endif /* PROP_TXSTATUS_VSDB */ +#endif +#endif +#ifdef IDHCP +#define CMD_DHCPC_ENABLE "DHCPC_ENABLE" +#define CMD_DHCPC_DUMP "DHCPC_DUMP" +#endif +#define CMD_AUTOCHANNEL "AUTOCHANNEL" +#define CMD_WL "WL" + +int wl_ext_ioctl(struct net_device *dev, u32 cmd, void *arg, u32 len, u32 set) +{ + int ret; + + ret = wldev_ioctl(dev, cmd, arg, len, set); + if (ret) + ANDROID_ERROR(("%s: cmd=%d ret=%d\n", __FUNCTION__, cmd, ret)); + return ret; +} + +int wl_ext_iovar_getint(struct net_device *dev, s8 *iovar, s32 *val) +{ + int ret; + + ret = wldev_iovar_getint(dev, iovar, val); + if (ret) + ANDROID_ERROR(("%s: iovar=%s, ret=%d\n", __FUNCTION__, iovar, ret)); + + return ret; +} + +int wl_ext_iovar_setint(struct net_device *dev, s8 *iovar, s32 val) +{ + int ret; + + ret = wldev_iovar_setint(dev, iovar, val); + if (ret) + ANDROID_ERROR(("%s: iovar=%s, ret=%d\n", __FUNCTION__, iovar, ret)); + + return ret; +} + +int wl_ext_iovar_getbuf(struct net_device *dev, s8 *iovar_name, + void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync) +{ + int ret; + + ret = wldev_iovar_getbuf(dev, iovar_name, param, paramlen, buf, buflen, buf_sync); + if (ret != 0) + ANDROID_ERROR(("%s: iovar=%s, ret=%d\n", __FUNCTION__, iovar_name, ret)); + + return ret; +} + +int wl_ext_iovar_setbuf(struct net_device *dev, s8 *iovar_name, + void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync) +{ + int ret; + + ret = wldev_iovar_setbuf(dev, iovar_name, param, paramlen, buf, buflen, buf_sync); + if (ret != 0) + ANDROID_ERROR(("%s: iovar=%s, ret=%d\n", __FUNCTION__, iovar_name, ret)); + + return ret; +} + +#ifdef WL_EXT_IAPSTA +typedef enum IF_STATE { + IF_STATE_INIT = 1, + IF_STATE_DISALBE, + IF_STATE_ENABLE +} if_state_t; + +typedef enum APSTAMODE { + ISTAONLY_MODE = 1, + IAPONLY_MODE, + IAPSTA_MODE, + IDUALAP_MODE, + IMESHONLY_MODE, + IMESHSTA_MODE, + IMESHAP_MODE, + IMESHAPSTA_MODE, + IMESHAPAP_MODE, + IGOSTA_MODE +} apstamode_t; + +typedef enum IFMODE { + ISTA_MODE = 1, + IAP_MODE, + IMESH_MODE +} ifmode_t; + +typedef enum BGNMODE { + IEEE80211B = 1, + IEEE80211G, + IEEE80211BG, + IEEE80211BGN, + IEEE80211BGNAC +} bgnmode_t; + +typedef enum AUTHMODE { + AUTH_OPEN, + AUTH_SHARED, + AUTH_WPAPSK, + AUTH_WPA2PSK, + AUTH_WPAWPA2PSK, + AUTH_SAE +} authmode_t; + +typedef enum ENCMODE { + ENC_NONE, + ENC_WEP, + ENC_TKIP, + ENC_AES, + ENC_TKIPAES +} encmode_t; + +enum wl_if_list { + IF_PIF, + IF_VIF, + IF_VIF2, + MAX_IF_NUM +}; + +typedef enum WL_PRIO { + PRIO_AP, + PRIO_MESH, + PRIO_STA +}wl_prio_t; + +typedef struct wl_if_info { + struct net_device *dev; + if_state_t ifstate; + ifmode_t ifmode; + char prefix; + wl_prio_t prio; + uint bssidx; + char ifname[IFNAMSIZ+1]; + char ssid[DOT11_MAX_SSID_LEN]; + struct ether_addr bssid; + bgnmode_t bgnmode; + int hidden; + int maxassoc; + uint16 channel; + authmode_t amode; + encmode_t emode; + char key[100]; +} wl_if_info_t; + +#define CSA_FW_BIT (1<<0) +#define CSA_DRV_BIT (1<<1) + +typedef struct wl_apsta_params { + struct wl_if_info if_info[MAX_IF_NUM]; + int ioctl_ver; + bool init; + bool rsdb; + bool vsdb; + uint csa; + apstamode_t apstamode; + bool netif_change; + wait_queue_head_t netif_change_event; +} wl_apsta_params_t; + +static int wl_ext_iapsta_enable(struct net_device *dev, char *command, int total_len); +int wl_ext_iovar_setbuf_bsscfg(struct net_device *dev, s8 *iovar_name, + void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync) +{ + int ret; + + ret = wldev_iovar_setbuf_bsscfg(dev, iovar_name, param, paramlen, + buf, buflen, bsscfg_idx, buf_sync); + if (ret < 0) + ANDROID_ERROR(("%s: iovar_name=%s ret=%d\n", __FUNCTION__, iovar_name, ret)); + + return ret; +} + +int wl_ext_iovar_getbuf_bsscfg(struct net_device *dev, s8 *iovar_name, + void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync) +{ + int ret; + + ret = wldev_iovar_getbuf_bsscfg(dev, iovar_name, param, paramlen, + buf, buflen, bsscfg_idx, buf_sync); + if (ret < 0) + ANDROID_ERROR(("%s: iovar_name=%s ret=%d\n", __FUNCTION__, iovar_name, ret)); + + return ret; +} +#endif /* Return a legacy chanspec given a new chanspec * Returns INVCHANSPEC on error */ static chanspec_t -wl_ext_chspec_to_legacy(chanspec_t chspec) +wl_ext_chspec_to_legacy(chanspec_t chspec) { chanspec_t lchspec; if (wf_chspec_malformed(chspec)) { - ANDROID_ERROR(("wl_ext_chspec_to_legacy: input chanspec (0x%04X) malformed\n", + ANDROID_ERROR(("wl_ext_chspec_to_legacy: input chanspec (0x%04X) malformed\n", chspec)); return INVCHANSPEC; } @@ -174,25 +302,25 @@ wl_ext_chspec_to_legacy(chanspec_t chspec) } else { /* cannot express the bandwidth */ char chanbuf[CHANSPEC_STR_LEN]; - ANDROID_ERROR(( - "wl_ext_chspec_to_legacy: unable to convert chanspec %s (0x%04X) " + ANDROID_ERROR(( + "wl_ext_chspec_to_legacy: unable to convert chanspec %s (0x%04X) " "to pre-11ac format\n", wf_chspec_ntoa(chspec, chanbuf), chspec)); return INVCHANSPEC; } return lchspec; -} +} /* given a chanspec value, do the endian and chanspec version conversion to * a chanspec_t value * Returns INVCHANSPEC on error */ static chanspec_t -wl_ext_chspec_host_to_driver(int ioctl_ver, chanspec_t chanspec) +wl_ext_chspec_host_to_driver(int ioctl_ver, chanspec_t chanspec) { - if (ioctl_ver == 1) { - chanspec = wl_ext_chspec_to_legacy(chanspec); + if (ioctl_ver == 1) { + chanspec = wl_ext_chspec_to_legacy(chanspec); if (chanspec == INVCHANSPEC) { return chanspec; } @@ -200,99 +328,148 @@ wl_ext_chspec_host_to_driver(int ioctl_ver, chanspec_t chanspec) chanspec = htodchanspec(chanspec); return chanspec; -} - -static int -wl_ext_get_ioctl_ver(struct net_device *dev, int *ioctl_ver) -{ - int ret = 0; - s32 val = 0; - - val = 1; - ret = wl_ext_ioctl(dev, WLC_GET_VERSION, &val, sizeof(val), 0); - if (ret) { - ANDROID_ERROR(("WLC_GET_VERSION failed, err=%d\n", ret)); - return ret; - } - val = dtoh32(val); - if (val != WLC_IOCTL_VERSION && val != 1) { - ANDROID_ERROR(("Version mismatch, please upgrade. Got %d, expected %d or 1\n", - val, WLC_IOCTL_VERSION)); - return BCME_VERSION; - } - *ioctl_ver = val; - - return ret; -} - -static int -wl_ext_set_chanspec(struct net_device *dev, uint16 channel) -{ - s32 _chan = channel; +} + +#if defined(WL_EXT_IAPSTA) || defined(WL_CFG80211) || defined(WL_ESCAN) +static chanspec_t +wl_ext_chspec_from_legacy(chanspec_t legacy_chspec) +{ + chanspec_t chspec; + + /* get the channel number */ + chspec = LCHSPEC_CHANNEL(legacy_chspec); + + /* convert the band */ + if (LCHSPEC_IS2G(legacy_chspec)) { + chspec |= WL_CHANSPEC_BAND_2G; + } else { + chspec |= WL_CHANSPEC_BAND_5G; + } + + /* convert the bw and sideband */ + if (LCHSPEC_IS20(legacy_chspec)) { + chspec |= WL_CHANSPEC_BW_20; + } else { + chspec |= WL_CHANSPEC_BW_40; + if (LCHSPEC_CTL_SB(legacy_chspec) == WL_LCHANSPEC_CTL_SB_LOWER) { + chspec |= WL_CHANSPEC_CTL_SB_L; + } else { + chspec |= WL_CHANSPEC_CTL_SB_U; + } + } + + if (wf_chspec_malformed(chspec)) { + ANDROID_ERROR(("wl_ext_chspec_from_legacy: output chanspec (0x%04X) malformed\n", + chspec)); + return INVCHANSPEC; + } + + return chspec; +} + +static chanspec_t +wl_ext_chspec_driver_to_host(int ioctl_ver, chanspec_t chanspec) +{ + chanspec = dtohchanspec(chanspec); + if (ioctl_ver == 1) { + chanspec = wl_ext_chspec_from_legacy(chanspec); + } + + return chanspec; +} +#endif + +static int +wl_ext_get_ioctl_ver(struct net_device *dev, int *ioctl_ver) +{ + int ret = 0; + s32 val = 0; + + val = 1; + ret = wl_ext_ioctl(dev, WLC_GET_VERSION, &val, sizeof(val), 0); + if (ret) { + ANDROID_ERROR(("WLC_GET_VERSION failed, err=%d\n", ret)); + return ret; + } + val = dtoh32(val); + if (val != WLC_IOCTL_VERSION && val != 1) { + ANDROID_ERROR(("Version mismatch, please upgrade. Got %d, expected %d or 1\n", + val, WLC_IOCTL_VERSION)); + return BCME_VERSION; + } + *ioctl_ver = val; + + return ret; +} + +static int +wl_ext_set_chanspec(struct net_device *dev, uint16 channel, chanspec_t *ret_chspec) +{ + s32 _chan = channel; chanspec_t chspec = 0; - chanspec_t fw_chspec = 0; - u32 bw = WL_CHANSPEC_BW_20; + chanspec_t fw_chspec = 0; + u32 bw = WL_CHANSPEC_BW_20; s32 err = BCME_OK; - s32 bw_cap = 0; - s8 iovar_buf[WLC_IOCTL_SMLEN]; + s32 bw_cap = 0; + s8 iovar_buf[WLC_IOCTL_SMLEN]; struct { u32 band; u32 bw_cap; - } param = {0, 0}; - uint band; - int ioctl_ver = 0; - - if (_chan <= CH_MAX_2G_CHANNEL) - band = IEEE80211_BAND_2GHZ; - else - band = IEEE80211_BAND_5GHZ; - wl_ext_get_ioctl_ver(dev, &ioctl_ver); - - if (band == IEEE80211_BAND_5GHZ) { + } param = {0, 0}; + uint band; + int ioctl_ver = 0; + + if (_chan <= CH_MAX_2G_CHANNEL) + band = IEEE80211_BAND_2GHZ; + else + band = IEEE80211_BAND_5GHZ; + wl_ext_get_ioctl_ver(dev, &ioctl_ver); + + if (band == IEEE80211_BAND_5GHZ) { param.band = WLC_BAND_5G; err = wldev_iovar_getbuf(dev, "bw_cap", ¶m, sizeof(param), - iovar_buf, WLC_IOCTL_SMLEN, NULL); + iovar_buf, WLC_IOCTL_SMLEN, NULL); if (err) { if (err != BCME_UNSUPPORTED) { - ANDROID_ERROR(("bw_cap failed, %d\n", err)); + ANDROID_ERROR(("bw_cap failed, %d\n", err)); return err; } else { err = wldev_iovar_getint(dev, "mimo_bw_cap", &bw_cap); if (err) { - ANDROID_ERROR(("error get mimo_bw_cap (%d)\n", err)); + ANDROID_ERROR(("error get mimo_bw_cap (%d)\n", err)); } if (bw_cap != WLC_N_BW_20ALL) bw = WL_CHANSPEC_BW_40; } } else { - if (WL_BW_CAP_80MHZ(iovar_buf[0])) + if (WL_BW_CAP_80MHZ(iovar_buf[0])) bw = WL_CHANSPEC_BW_80; - else if (WL_BW_CAP_40MHZ(iovar_buf[0])) + else if (WL_BW_CAP_40MHZ(iovar_buf[0])) bw = WL_CHANSPEC_BW_40; else bw = WL_CHANSPEC_BW_20; - } - } - else if (band == IEEE80211_BAND_2GHZ) - bw = WL_CHANSPEC_BW_20; - + } + } + else if (band == IEEE80211_BAND_2GHZ) + bw = WL_CHANSPEC_BW_20; + set_channel: chspec = wf_channel2chspec(_chan, bw); if (wf_chspec_valid(chspec)) { - fw_chspec = wl_ext_chspec_host_to_driver(ioctl_ver, chspec); + fw_chspec = wl_ext_chspec_host_to_driver(ioctl_ver, chspec); if (fw_chspec != INVCHANSPEC) { - if ((err = wldev_iovar_setint(dev, "chanspec", fw_chspec)) == BCME_BADCHAN) { + if ((err = wldev_iovar_setint(dev, "chanspec", fw_chspec)) == BCME_BADCHAN) { if (bw == WL_CHANSPEC_BW_80) - goto change_bw; - wl_ext_ioctl(dev, WLC_SET_CHANNEL, &_chan, sizeof(_chan), 1); - printf("%s: channel %d\n", __FUNCTION__, _chan); + goto change_bw; + wl_ext_ioctl(dev, WLC_SET_CHANNEL, &_chan, sizeof(_chan), 1); + printf("%s: channel %d\n", __FUNCTION__, _chan); } else if (err) { - ANDROID_ERROR(("%s: failed to set chanspec error %d\n", __FUNCTION__, err)); - } else - printf("%s: channel %d, 0x%x\n", __FUNCTION__, channel, chspec); + ANDROID_ERROR(("%s: failed to set chanspec error %d\n", __FUNCTION__, err)); + } else + printf("%s: channel %d, 0x%x\n", __FUNCTION__, channel, chspec); } else { - ANDROID_ERROR(("%s: failed to convert host chanspec to fw chanspec\n", __FUNCTION__)); + ANDROID_ERROR(("%s: failed to convert host chanspec to fw chanspec\n", __FUNCTION__)); err = BCME_ERROR; } } else { @@ -305,1875 +482,3280 @@ wl_ext_set_chanspec(struct net_device *dev, uint16 channel) bw = 0; if (bw) goto set_channel; - ANDROID_ERROR(("%s: Invalid chanspec 0x%x\n", __FUNCTION__, chspec)); + ANDROID_ERROR(("%s: Invalid chanspec 0x%x\n", __FUNCTION__, chspec)); err = BCME_ERROR; - } - - return err; -} - -int -wl_ext_channel(struct net_device *dev, char* command, int total_len) -{ - int ret; - int channel=0; - channel_info_t ci; - int bytes_written = 0; - - ANDROID_TRACE(("%s: cmd %s\n", __FUNCTION__, command)); - - sscanf(command, "%*s %d", &channel); - - if (channel > 0) { - ret = wl_ext_set_chanspec(dev, channel); - } else { - if (!(ret = wldev_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(channel_info_t), FALSE))) { - ANDROID_TRACE(("hw_channel %d\n", ci.hw_channel)); - ANDROID_TRACE(("target_channel %d\n", ci.target_channel)); - ANDROID_TRACE(("scan_channel %d\n", ci.scan_channel)); - bytes_written = snprintf(command, sizeof(channel_info_t)+2, "channel %d", ci.hw_channel); - ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command)); - ret = bytes_written; - } - } - - return ret; -} - -int -wl_ext_channels(struct net_device *dev, char* command, int total_len) -{ - int ret, i; - int bytes_written = -1; - u8 valid_chan_list[sizeof(u32)*(WL_NUMCHANNELS + 1)]; - wl_uint32_list_t *list; - - ANDROID_TRACE(("%s: cmd %s\n", __FUNCTION__, command)); - - memset(valid_chan_list, 0, sizeof(valid_chan_list)); - list = (wl_uint32_list_t *)(void *) valid_chan_list; - list->count = htod32(WL_NUMCHANNELS); - ret = wldev_ioctl(dev, WLC_GET_VALID_CHANNELS, valid_chan_list, sizeof(valid_chan_list), 0); - if (ret<0) { - ANDROID_ERROR(("%s: get channels failed with %d\n", __FUNCTION__, ret)); - } else { - bytes_written = snprintf(command, total_len, "channels"); - for (i = 0; i < dtoh32(list->count); i++) { - bytes_written += snprintf(command+bytes_written, total_len, " %d", dtoh32(list->element[i])); - printf("%d ", dtoh32(list->element[i])); - } - printf("\n"); - ret = bytes_written; - } - - return ret; -} - -int -wl_ext_roam_trigger(struct net_device *dev, char* command, int total_len) -{ - int ret = 0; - int roam_trigger[2] = {0, 0}; - int trigger[2]= {0, 0}; - int bytes_written=-1; - - sscanf(command, "%*s %10d", &roam_trigger[0]); - - if (roam_trigger[0]) { - roam_trigger[1] = WLC_BAND_ALL; - ret = wldev_ioctl(dev, WLC_SET_ROAM_TRIGGER, roam_trigger, sizeof(roam_trigger), 1); - if (ret) - ANDROID_ERROR(("WLC_SET_ROAM_TRIGGER ERROR %d ret=%d\n", roam_trigger[0], ret)); - } else { - roam_trigger[1] = WLC_BAND_2G; - ret = wldev_ioctl(dev, WLC_GET_ROAM_TRIGGER, roam_trigger, sizeof(roam_trigger), 0); - if (!ret) - trigger[0] = roam_trigger[0]; - else - ANDROID_ERROR(("2G WLC_GET_ROAM_TRIGGER ERROR %d ret=%d\n", roam_trigger[0], ret)); - - roam_trigger[1] = WLC_BAND_5G; - ret = wldev_ioctl(dev, WLC_GET_ROAM_TRIGGER, roam_trigger, sizeof(roam_trigger), 0); - if (!ret) - trigger[1] = roam_trigger[0]; - else - ANDROID_ERROR(("5G WLC_GET_ROAM_TRIGGER ERROR %d ret=%d\n", roam_trigger[0], ret)); - - ANDROID_TRACE(("roam_trigger %d %d\n", trigger[0], trigger[1])); - bytes_written = snprintf(command, total_len, "%d %d", trigger[0], trigger[1]); - ret = bytes_written; - } - - return ret; -} - -static int -wl_ext_pattern_atoh(char *src, char *dst) -{ - int i; - if (strncmp(src, "0x", 2) != 0 && - strncmp(src, "0X", 2) != 0) { - ANDROID_ERROR(("Mask invalid format. Needs to start with 0x\n")); - return -1; - } - src = src + 2; /* Skip past 0x */ - if (strlen(src) % 2 != 0) { - DHD_ERROR(("Mask invalid format. Needs to be of even length\n")); - return -1; - } - for (i = 0; *src != '\0'; i++) { - char num[3]; - bcm_strncpy_s(num, sizeof(num), src, 2); - num[2] = '\0'; - dst[i] = (uint8)strtoul(num, NULL, 16); - src += 2; - } - return i; -} - -int -wl_ext_keep_alive(struct net_device *dev, char *command, int total_len) -{ - wl_mkeep_alive_pkt_t *mkeep_alive_pktp; - int ret = -1, i; - int id, period=-1, len_bytes=0, buf_len=0; - char data[200]="\0"; - char buf[WLC_IOCTL_SMLEN]="\0", iovar_buf[WLC_IOCTL_SMLEN]="\0"; - int bytes_written = -1; - - ANDROID_TRACE(("%s: command = %s\n", __FUNCTION__, command)); - sscanf(command, "%*s %d %d %s", &id, &period, data); - ANDROID_TRACE(("%s: id=%d, period=%d, data=%s\n", __FUNCTION__, id, period, data)); - - if (period >= 0) { - mkeep_alive_pktp = (wl_mkeep_alive_pkt_t *)buf; - mkeep_alive_pktp->version = htod16(WL_MKEEP_ALIVE_VERSION); - mkeep_alive_pktp->length = htod16(WL_MKEEP_ALIVE_FIXED_LEN); - mkeep_alive_pktp->keep_alive_id = id; - buf_len += WL_MKEEP_ALIVE_FIXED_LEN; - mkeep_alive_pktp->period_msec = period; - if (strlen(data)) { - len_bytes = wl_ext_pattern_atoh(data, (char *) mkeep_alive_pktp->data); - buf_len += len_bytes; - } - mkeep_alive_pktp->len_bytes = htod16(len_bytes); - - ret = wl_ext_iovar_setbuf(dev, "mkeep_alive", buf, buf_len, - iovar_buf, sizeof(iovar_buf), NULL); - } else { - if (id < 0) - id = 0; - ret = wl_ext_iovar_getbuf(dev, "mkeep_alive", &id, sizeof(id), buf, sizeof(buf), NULL); - if (ret) { - goto exit; - } else { - mkeep_alive_pktp = (wl_mkeep_alive_pkt_t *) buf; - printf("Id :%d\n" - "Period (msec) :%d\n" - "Length :%d\n" - "Packet :0x", - mkeep_alive_pktp->keep_alive_id, - dtoh32(mkeep_alive_pktp->period_msec), - dtoh16(mkeep_alive_pktp->len_bytes)); - for (i=0; ilen_bytes; i++) { - printf("%02x", mkeep_alive_pktp->data[i]); - } - printf("\n"); - } - bytes_written = snprintf(command, total_len, "mkeep_alive_period_msec %d ", dtoh32(mkeep_alive_pktp->period_msec)); - bytes_written += snprintf(command+bytes_written, total_len, "0x"); - for (i=0; ilen_bytes; i++) { - bytes_written += snprintf(command+bytes_written, total_len, "%x", mkeep_alive_pktp->data[i]); - } - ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command)); - ret = bytes_written; - } - -exit: - return ret; -} - -int -wl_ext_pm(struct net_device *dev, char *command, int total_len) -{ - int pm=-1, ret = -1; - char *pm_local; - int bytes_written=-1; - - ANDROID_TRACE(("%s: cmd %s\n", __FUNCTION__, command)); - - sscanf(command, "%*s %d", &pm); - - if (pm >= 0) { - ret = wldev_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm), FALSE); - if (ret) - ANDROID_ERROR(("WLC_SET_PM ERROR %d ret=%d\n", pm, ret)); - } else { - ret = wldev_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm), FALSE); - if (!ret) { - ANDROID_TRACE(("%s: PM = %d\n", __func__, pm)); - if (pm == PM_OFF) - pm_local = "PM_OFF"; - else if(pm == PM_MAX) - pm_local = "PM_MAX"; - else if(pm == PM_FAST) - pm_local = "PM_FAST"; - else { - pm = 0; - pm_local = "Invalid"; - } - bytes_written = snprintf(command, total_len, "PM %s", pm_local); - ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command)); - ret = bytes_written; - } - } - - return ret; -} - -static int -wl_ext_monitor(struct net_device *dev, char *command, int total_len) -{ - int val, ret = -1; - int bytes_written=-1; - - sscanf(command, "%*s %d", &val); - - if (val >=0) { - ret = wldev_ioctl(dev, WLC_SET_MONITOR, &val, sizeof(int), 1); - if (ret) - ANDROID_ERROR(("WLC_SET_MONITOR ERROR %d ret=%d\n", val, ret)); - } else { - ret = wldev_ioctl(dev, WLC_GET_MONITOR, &val, sizeof(val), FALSE); - if (!ret) { - ANDROID_TRACE(("%s: monitor = %d\n", __FUNCTION__, val)); - bytes_written = snprintf(command, total_len, "monitor %d", val); - ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command)); - ret = bytes_written; - } - } - - return ret; -} - -#ifdef WL_EXT_IAPSTA -struct wl_apsta_params g_apsta_params; -static int -wl_ext_parse_wep(char *key, struct wl_wsec_key *wsec_key) -{ - char hex[] = "XX"; - unsigned char *data = wsec_key->data; - char *keystr = key; - - switch (strlen(keystr)) { - case 5: - case 13: - case 16: - wsec_key->len = strlen(keystr); - memcpy(data, keystr, wsec_key->len + 1); - break; - case 12: - case 28: - case 34: - case 66: - /* strip leading 0x */ - if (!strnicmp(keystr, "0x", 2)) - keystr += 2; - else - return -1; - /* fall through */ - case 10: - case 26: - case 32: - case 64: - wsec_key->len = strlen(keystr) / 2; - while (*keystr) { - strncpy(hex, keystr, 2); - *data++ = (char) strtoul(hex, NULL, 16); - keystr += 2; - } - break; - default: - return -1; - } - - switch (wsec_key->len) { - case 5: - wsec_key->algo = CRYPTO_ALGO_WEP1; - break; - case 13: - wsec_key->algo = CRYPTO_ALGO_WEP128; - break; - case 16: - /* default to AES-CCM */ - wsec_key->algo = CRYPTO_ALGO_AES_CCM; - break; - case 32: - wsec_key->algo = CRYPTO_ALGO_TKIP; - break; - default: - return -1; - } - - /* Set as primary wsec_key by default */ - wsec_key->flags |= WL_PRIMARY_KEY; - - return 0; -} - -static int -wl_ext_set_bgnmode(struct wl_if_info *cur_if) -{ - struct net_device *dev = cur_if->dev; - bgnmode_t bgnmode = cur_if->bgnmode; - int val; - - if (bgnmode == 0) - return 0; - - wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); - if (bgnmode == IEEE80211B) { - wl_ext_iovar_setint(dev, "nmode", 0); - val = 0; - wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1); - ANDROID_TRACE(("%s: Network mode: B only\n", __FUNCTION__)); - } else if (bgnmode == IEEE80211G) { - wl_ext_iovar_setint(dev, "nmode", 0); - val = 2; - wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1); - ANDROID_TRACE(("%s: Network mode: G only\n", __FUNCTION__)); - } else if (bgnmode == IEEE80211BG) { - wl_ext_iovar_setint(dev, "nmode", 0); - val = 1; - wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1); - ANDROID_TRACE(("%s: Network mode: : B/G mixed\n", __FUNCTION__)); - } else if (bgnmode == IEEE80211BGN) { - wl_ext_iovar_setint(dev, "nmode", 0); - wl_ext_iovar_setint(dev, "nmode", 1); - wl_ext_iovar_setint(dev, "vhtmode", 0); - val = 1; - wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1); - ANDROID_TRACE(("%s: Network mode: : B/G/N mixed\n", __FUNCTION__)); - } else if (bgnmode == IEEE80211BGNAC) { - wl_ext_iovar_setint(dev, "nmode", 0); - wl_ext_iovar_setint(dev, "nmode", 1); - wl_ext_iovar_setint(dev, "vhtmode", 1); - val = 1; - wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1); - ANDROID_TRACE(("%s: Network mode: : B/G/N/AC mixed\n", __FUNCTION__)); - } - wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); - - return 0; -} - -static int -wl_ext_set_amode(struct wl_if_info *cur_if, struct wl_apsta_params *apsta_params) -{ - struct net_device *dev = cur_if->dev; - authmode_t amode = cur_if->amode; - int auth=0, wpa_auth=0; - - if (amode == AUTH_OPEN) { - auth = 0; - wpa_auth = 0; - ANDROID_TRACE(("%s: Authentication: Open System\n", __FUNCTION__)); - } else if (amode == AUTH_SHARED) { - auth = 1; - wpa_auth = 0; - ANDROID_TRACE(("%s: Authentication: Shared Key\n", __FUNCTION__)); - } else if (amode == AUTH_WPAPSK) { - auth = 0; - wpa_auth = 4; - ANDROID_TRACE(("%s: Authentication: WPA-PSK\n", __FUNCTION__)); - } else if (amode == AUTH_WPA2PSK) { - auth = 0; - wpa_auth = 128; - ANDROID_TRACE(("%s: Authentication: WPA2-PSK\n", __FUNCTION__)); - } else if (amode == AUTH_WPAWPA2PSK) { - auth = 0; - wpa_auth = 132; - ANDROID_TRACE(("%s: Authentication: WPA/WPA2-PSK\n", __FUNCTION__)); - } - wl_ext_iovar_setint(dev, "auth", auth); - - wl_ext_iovar_setint(dev, "wpa_auth", wpa_auth); - - return 0; -} - -static int -wl_ext_set_emode(struct wl_if_info *cur_if, struct wl_apsta_params *apsta_params) -{ - struct net_device *dev = cur_if->dev; - int wsec=0; - struct wl_wsec_key wsec_key; - wsec_pmk_t psk; - encmode_t emode = cur_if->emode; - char *key = cur_if->key; - - memset(&wsec_key, 0, sizeof(wsec_key)); - memset(&psk, 0, sizeof(psk)); - if (emode == ENC_NONE) { - wsec = 0; - ANDROID_TRACE(("%s: Encryption: No securiy\n", __FUNCTION__)); - } else if (emode == ENC_WEP) { - wsec = 1; - wl_ext_parse_wep(key, &wsec_key); - ANDROID_TRACE(("%s: Encryption: WEP\n", __FUNCTION__)); - ANDROID_TRACE(("%s: Key: %s\n", __FUNCTION__, wsec_key.data)); - } else if (emode == ENC_TKIP) { - wsec = 2; - psk.key_len = strlen(key); - psk.flags = WSEC_PASSPHRASE; - memcpy(psk.key, key, strlen(key)); - ANDROID_TRACE(("%s: Encryption: TKIP\n", __FUNCTION__)); - ANDROID_TRACE(("%s: Key: %s\n", __FUNCTION__, psk.key)); - } else if (emode == ENC_AES) { - wsec = 4; - psk.key_len = strlen(key); - psk.flags = WSEC_PASSPHRASE; - memcpy(psk.key, key, strlen(key)); - ANDROID_TRACE(("%s: Encryption: AES\n", __FUNCTION__)); - ANDROID_TRACE(("%s: Key: %s\n", __FUNCTION__, psk.key)); - } else if (emode == ENC_TKIPAES) { - wsec = 6; - psk.key_len = strlen(key); - psk.flags = WSEC_PASSPHRASE; - memcpy(psk.key, key, strlen(key)); - ANDROID_TRACE(("%s: Encryption: TKIP/AES\n", __FUNCTION__)); - ANDROID_TRACE(("%s: Key: %s\n", __FUNCTION__, psk.key)); - } - - wl_ext_iovar_setint(dev, "wsec", wsec); - - if (wsec == 1) { - wl_ext_ioctl(dev, WLC_SET_KEY, &wsec_key, sizeof(wsec_key), 1); - } else if (emode == ENC_TKIP || emode == ENC_AES || emode == ENC_TKIPAES) { - if (dev) { - if (cur_if->ifmode == ISTA_MODE) - wl_ext_iovar_setint(dev, "sup_wpa", 1); - wl_ext_ioctl(dev, WLC_SET_WSEC_PMK, &psk, sizeof(psk), 1); - } else { - ANDROID_ERROR(("%s: apdev is null\n", __FUNCTION__)); - } - } - - return 0; -} - -static int -wl_ext_iapsta_init(struct net_device *dev, char *command, int total_len) -{ - s32 val = 0; - char *pch, *pick_tmp, *param; - wlc_ssid_t ssid = { 0, {0} }; - s8 iovar_buf[WLC_IOCTL_SMLEN]; - struct wl_apsta_params *apsta_params = &g_apsta_params; - wl_interface_create_t iface; - struct dhd_pub *dhd; - wl_p2p_if_t ifreq; - - if (apsta_params->init) { - ANDROID_ERROR(("%s: don't init twice\n", __FUNCTION__)); - return -1; - } - - dhd = dhd_get_pub(dev); - memset(apsta_params, 0, sizeof(struct wl_apsta_params)); - - ANDROID_TRACE(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); - - pick_tmp = command; - param = bcmstrtok(&pick_tmp, " ", 0); // skip iapsta_init - param = bcmstrtok(&pick_tmp, " ", 0); - while (param != NULL) { - if (!strcmp(param, "mode")) { - pch = bcmstrtok(&pick_tmp, " ", 0); - if (pch) { - if (!strcmp(pch, "sta")) { - apsta_params->apstamode = ISTAONLY_MODE; - } else if (!strcmp(pch, "ap")) { - apsta_params->apstamode = IAPONLY_MODE; - } else if (!strcmp(pch, "apsta")) { - apsta_params->apstamode = IAPSTA_MODE; - } else if (!strcmp(pch, "dualap")) { - apsta_params->apstamode = IDUALAP_MODE; - } else if (!strcmp(pch, "gosta")) { - if (!FW_SUPPORTED(dhd, p2p)) { - return -1; - } - apsta_params->apstamode = IGOSTA_MODE; - } else if (!strcmp(pch, "gcsta")) { - if (!FW_SUPPORTED(dhd, p2p)) { - return -1; - } - apsta_params->apstamode = IGCSTA_MODE; - } else { - ANDROID_ERROR(("%s: mode [sta|ap|apsta|dualap]\n", __FUNCTION__)); - return -1; - } - } - } else if (!strcmp(param, "vifname")) { - pch = bcmstrtok(&pick_tmp, " ", 0); - if (pch) - strcpy(apsta_params->vif.ifname, pch); - else { - ANDROID_ERROR(("%s: vifname [wlan1]\n", __FUNCTION__)); - return -1; - } - } - param = bcmstrtok(&pick_tmp, " ", 0); - } - - if (apsta_params->apstamode == 0) { - ANDROID_ERROR(("%s: mode [sta|ap|apsta|dualap]\n", __FUNCTION__)); - return -1; - } - - apsta_params->pif.dev = dev; - apsta_params->pif.bssidx = 0; - strcpy(apsta_params->pif.ifname, dev->name); - strcpy(apsta_params->pif.ssid, "tttp"); - apsta_params->pif.maxassoc = -1; - apsta_params->pif.channel = 1; - - if (!strlen(apsta_params->vif.ifname)) - strcpy(apsta_params->vif.ifname, "wlan1"); - strcpy(apsta_params->vif.ssid, "tttv"); - apsta_params->vif.maxassoc = -1; - apsta_params->vif.channel = 1; - - if (apsta_params->apstamode == ISTAONLY_MODE) { - apsta_params->pif.ifmode = ISTA_MODE; - apsta_params->pif.ifstate = IF_STATE_INIT; - wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); - wl_ext_iovar_setint(dev, "apsta", 1); // keep 1 as we set in dhd_preinit_ioctls - // don't set WLC_SET_AP to 0, some parameters will be reset, such as bcn_timeout and roam_off - wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); - } else if (apsta_params->apstamode == IAPONLY_MODE) { - apsta_params->pif.ifmode = IAP_MODE; - apsta_params->pif.ifstate = IF_STATE_INIT; - wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); -#ifdef ARP_OFFLOAD_SUPPORT - /* IF SoftAP is enabled, disable arpoe */ - dhd_arp_offload_set(dhd, 0); - dhd_arp_offload_enable(dhd, FALSE); -#endif /* ARP_OFFLOAD_SUPPORT */ - wl_ext_iovar_setint(dev, "mpc", 0); - wl_ext_iovar_setint(dev, "apsta", 0); - val = 1; - wl_ext_ioctl(dev, WLC_SET_AP, &val, sizeof(val), 1); - } else if (apsta_params->apstamode == IAPSTA_MODE) { - apsta_params->pif.ifmode = ISTA_MODE; - apsta_params->pif.ifstate = IF_STATE_INIT; - apsta_params->vif.ifmode = IAP_MODE; - apsta_params->vif.ifstate = IF_STATE_INIT; - wl_ext_iovar_setint(dev, "mpc", 0); - wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); - wl_ext_iovar_setint(dev, "apsta", 1); - wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); - if (FW_SUPPORTED(dhd, rsdb)) { - bzero(&iface, sizeof(wl_interface_create_t)); - iface.ver = WL_INTERFACE_CREATE_VER; - iface.flags = WL_INTERFACE_CREATE_AP; - wl_ext_iovar_getbuf_bsscfg(dev, "interface_create", &iface, sizeof(iface), iovar_buf, - WLC_IOCTL_SMLEN, 1, NULL); - } else { - wl_ext_iovar_setbuf_bsscfg(dev, "ssid", &ssid, sizeof(ssid), iovar_buf, - WLC_IOCTL_SMLEN, 1, NULL); - } - } - else if (apsta_params->apstamode == IDUALAP_MODE) { - apsta_params->pif.ifmode = IAP_MODE; - apsta_params->pif.ifstate = IF_STATE_INIT; - apsta_params->vif.ifmode = IAP_MODE; - apsta_params->vif.ifstate = IF_STATE_INIT; - wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); - wl_ext_iovar_setint(dev, "apsta", 0); - wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); - val = 1; - wl_ext_ioctl(dev, WLC_SET_AP, &val, sizeof(val), 1); - /* IF SoftAP is enabled, disable arpoe or wlan1 will ping fail */ -#ifdef ARP_OFFLOAD_SUPPORT - /* IF SoftAP is enabled, disable arpoe */ - dhd_arp_offload_set(dhd, 0); - dhd_arp_offload_enable(dhd, FALSE); -#endif /* ARP_OFFLOAD_SUPPORT */ - bzero(&iface, sizeof(wl_interface_create_t)); - iface.ver = WL_INTERFACE_CREATE_VER; - iface.flags = WL_INTERFACE_CREATE_AP; - wl_ext_iovar_getbuf_bsscfg(dev, "interface_create", &iface, sizeof(iface), iovar_buf, - WLC_IOCTL_SMLEN, 1, NULL); - } - else if (apsta_params->apstamode == IGOSTA_MODE) { - apsta_params->pif.ifmode = ISTA_MODE; - apsta_params->pif.ifstate = IF_STATE_INIT; - apsta_params->vif.ifmode = IAP_MODE; - apsta_params->vif.ifstate = IF_STATE_INIT; - wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); - wl_ext_iovar_setint(dev, "apsta", 1); - wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); - bzero(&ifreq, sizeof(wl_p2p_if_t)); - ifreq.type = htod32(WL_P2P_IF_GO); - wl_ext_iovar_setbuf(dev, "p2p_ifadd", &ifreq, sizeof(ifreq), - iovar_buf, WLC_IOCTL_SMLEN, NULL); - } - else if (apsta_params->apstamode == IGCSTA_MODE) { - apsta_params->pif.ifmode = ISTA_MODE; - apsta_params->pif.ifstate = IF_STATE_INIT; - apsta_params->vif.ifmode = ISTA_MODE; - apsta_params->vif.ifstate = IF_STATE_INIT; - wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); - wl_ext_iovar_setint(dev, "apsta", 1); - wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); - bzero(&ifreq, sizeof(wl_p2p_if_t)); - ifreq.type = htod32(WL_P2P_IF_CLIENT); - wl_ext_iovar_setbuf(dev, "p2p_ifadd", &ifreq, sizeof(ifreq), - iovar_buf, WLC_IOCTL_SMLEN, NULL); - } - - wl_ext_get_ioctl_ver(dev, &apsta_params->ioctl_ver); - printf("%s: apstamode=%d\n", __FUNCTION__, apsta_params->apstamode); - - apsta_params->init = TRUE; - - return 0; -} - -static int -wl_ext_iapsta_config(struct net_device *dev, char *command, int total_len) -{ - int i; - char *pch, *pick_tmp, *param; - struct wl_apsta_params *apsta_params = &g_apsta_params; - char ifname[IFNAMSIZ+1]; - struct wl_if_info *cur_if = &apsta_params->pif; - - if (!apsta_params->init) { - ANDROID_ERROR(("%s: please init first\n", __FUNCTION__)); - return -1; - } - - ANDROID_TRACE(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); - - pick_tmp = command; - param = bcmstrtok(&pick_tmp, " ", 0); // skip iapsta_config - param = bcmstrtok(&pick_tmp, " ", 0); - - if (param != NULL) { - if (strcmp(param, "ifname")) { - ANDROID_ERROR(("%s: first arg must be ifname\n", __FUNCTION__)); - return -1; - } - } - - while (param != NULL) { - if (!strcmp(param, "ifname")) { - pch = bcmstrtok(&pick_tmp, " ", 0); - if (pch) - strcpy(ifname, pch); - else { - ANDROID_ERROR(("%s: ifname [wlanX]\n", __FUNCTION__)); - return -1; - } - if (!strcmp(apsta_params->pif.dev->name, ifname)) { - cur_if = &apsta_params->pif; - } else if (!strcmp(apsta_params->vif.ifname, ifname)) { - cur_if = &apsta_params->vif; - } else { - ANDROID_ERROR(("%s: wrong ifname=%s in apstamode=%d\n", __FUNCTION__, - ifname, apsta_params->apstamode)); - return -1; - } - } else if (!strcmp(param, "ssid")) { - pch = bcmstrtok(&pick_tmp, " ", 0); - if (pch) - strcpy(cur_if->ssid, pch); - } else if (!strcmp(param, "bssid")) { - pch = bcmstrtok(&pick_tmp, ": ", 0); - for (i=0; i<6 && pch; i++) { - ((u8 *)&cur_if->bssid)[i] = (int)simple_strtol(pch, NULL, 16); - pch = bcmstrtok(&pick_tmp, ": ", 0); - } - } else if (!strcmp(param, "bgnmode")) { - pch = bcmstrtok(&pick_tmp, " ", 0); - if (pch) { - if (!strcmp(pch, "b")) - cur_if->bgnmode = IEEE80211B; - else if (!strcmp(pch, "g")) - cur_if->bgnmode = IEEE80211G; - else if (!strcmp(pch, "bg")) - cur_if->bgnmode = IEEE80211BG; - else if (!strcmp(pch, "bgn")) - cur_if->bgnmode = IEEE80211BGN; - else if (!strcmp(pch, "bgnac")) - cur_if->bgnmode = IEEE80211BGNAC; - else { - ANDROID_ERROR(("%s: bgnmode [b|g|bg|bgn|bgnac]\n", __FUNCTION__)); - return -1; - } - } - } else if (!strcmp(param, "hidden")) { - pch = bcmstrtok(&pick_tmp, " ", 0); - if (pch) { - if (!strcmp(pch, "n")) - cur_if->hidden = 0; - else if (!strcmp(pch, "y")) - cur_if->hidden = 1; - else { - ANDROID_ERROR(("%s: hidden [y|n]\n", __FUNCTION__)); - return -1; - } - } - } else if (!strcmp(param, "maxassoc")) { - pch = bcmstrtok(&pick_tmp, " ", 0); - if (pch) - cur_if->maxassoc = (int)simple_strtol(pch, NULL, 10); - } else if (!strcmp(param, "chan")) { - pch = bcmstrtok(&pick_tmp, " ", 0); - if (pch) - cur_if->channel = (int)simple_strtol(pch, NULL, 10); - } else if (!strcmp(param, "amode")) { - pch = bcmstrtok(&pick_tmp, " ", 0); - if (pch) { - if (!strcmp(pch, "open")) - cur_if->amode = AUTH_OPEN; - else if (!strcmp(pch, "shared")) - cur_if->amode = AUTH_SHARED; - else if (!strcmp(pch, "wpapsk")) - cur_if->amode = AUTH_WPAPSK; - else if (!strcmp(pch, "wpa2psk")) - cur_if->amode = AUTH_WPA2PSK; - else if (!strcmp(pch, "wpawpa2psk")) - cur_if->amode = AUTH_WPAWPA2PSK; - else { - ANDROID_ERROR(("%s: amode [open|shared|wpapsk|wpa2psk|wpawpa2psk]\n", - __FUNCTION__)); - return -1; - } - } - } else if (!strcmp(param, "emode")) { - pch = bcmstrtok(&pick_tmp, " ", 0); - if (pch) { - if (!strcmp(pch, "none")) - cur_if->emode = ENC_NONE; - else if (!strcmp(pch, "wep")) - cur_if->emode = ENC_WEP; - else if (!strcmp(pch, "tkip")) - cur_if->emode = ENC_TKIP; - else if (!strcmp(pch, "aes")) - cur_if->emode = ENC_AES; - else if (!strcmp(pch, "tkipaes")) - cur_if->emode = ENC_TKIPAES; - else { - ANDROID_ERROR(("%s: emode [none|wep|tkip|aes|tkipaes]\n", - __FUNCTION__)); - return -1; - } - } - } else if (!strcmp(param, "key")) { - pch = bcmstrtok(&pick_tmp, " ", 0); - if (pch) { - strcpy(cur_if->key, pch); - } - } - param = bcmstrtok(&pick_tmp, " ", 0); - } - - return 0; -} - -static int -wl_ext_iapsta_disable(struct net_device *dev, char *command, int total_len) -{ - char *pch, *pick_tmp, *param; - s8 iovar_buf[WLC_IOCTL_SMLEN]; - wlc_ssid_t ssid = { 0, {0} }; - scb_val_t scbval; - struct { - s32 tmp; - s32 cfg; - s32 val; - } bss_setbuf; - struct wl_apsta_params *apsta_params = &g_apsta_params; - apstamode_t apstamode = apsta_params->apstamode; - char ifname[IFNAMSIZ+1]; - struct wl_if_info *cur_if; - struct dhd_pub *dhd; - - if (!apsta_params->init) { - ANDROID_ERROR(("%s: please init first\n", __FUNCTION__)); - return -1; - } - - ANDROID_TRACE(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); - dhd = dhd_get_pub(dev); - - pick_tmp = command; - param = bcmstrtok(&pick_tmp, " ", 0); // skip iapsta_disable - param = bcmstrtok(&pick_tmp, " ", 0); - while (param != NULL) { - if (!strcmp(param, "ifname")) { - pch = bcmstrtok(&pick_tmp, " ", 0); - if (pch) - strcpy(ifname, pch); - else { - ANDROID_ERROR(("%s: ifname [wlanX]\n", __FUNCTION__)); - return -1; - } - } - param = bcmstrtok(&pick_tmp, " ", 0); - } - if (!strcmp(apsta_params->pif.dev->name, ifname)) { - cur_if = &apsta_params->pif; - } else if (!strcmp(apsta_params->vif.ifname, ifname)) { - cur_if = &apsta_params->vif; - } else { - ANDROID_ERROR(("%s: wrong ifname=%s\n", __FUNCTION__, ifname)); - return -1; - } - if (!cur_if->dev) { - ANDROID_ERROR(("%s: %s is not ready\n", __FUNCTION__, ifname)); - return -1; - } - - if (cur_if->ifmode == ISTA_MODE) { - wl_ext_ioctl(cur_if->dev, WLC_DISASSOC, NULL, 0, 1); - } else if (cur_if->ifmode == IAP_MODE) { - // deauthenticate all STA first - memcpy(scbval.ea.octet, ðer_bcast, ETHER_ADDR_LEN); - wl_ext_ioctl(cur_if->dev, WLC_SCB_DEAUTHENTICATE, &scbval.ea, ETHER_ADDR_LEN, 1); - } - - if (apstamode == IAPONLY_MODE) { - wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); - wl_ext_ioctl(dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1); // reset ssid - wl_ext_iovar_setint(dev, "mpc", 1); - } else if ((apstamode==IAPSTA_MODE || apstamode==IGOSTA_MODE) && - cur_if->ifmode == IAP_MODE) { - // vif is AP mode - bss_setbuf.tmp = 0xffffffff; - bss_setbuf.cfg = 0; // must be 0, or wlan1 can not be down - bss_setbuf.val = htod32(0); - wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf, sizeof(bss_setbuf), - iovar_buf, WLC_IOCTL_SMLEN, NULL); - wl_ext_iovar_setint(dev, "mpc", 1); -#ifdef ARP_OFFLOAD_SUPPORT - /* IF SoftAP is disabled, enable arpoe back for STA mode. */ - dhd_arp_offload_set(dhd, dhd_arp_mode); - dhd_arp_offload_enable(dhd, TRUE); -#endif /* ARP_OFFLOAD_SUPPORT */ - } else if (apstamode == IDUALAP_MODE) { - bss_setbuf.tmp = 0xffffffff; - bss_setbuf.cfg = 0; // must be 0, or wlan1 can not be down - bss_setbuf.val = htod32(0); - wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf, sizeof(bss_setbuf), - iovar_buf, WLC_IOCTL_SMLEN, NULL); - } - -#ifdef PROP_TXSTATUS_VSDB -#if defined(BCMSDIO) - if (cur_if==&apsta_params->vif && dhd->conf->disable_proptx!=0) { - bool enabled; - dhd_wlfc_get_enable(dhd, &enabled); - if (enabled) { - dhd_wlfc_deinit(dhd); - } - } -#endif -#endif /* PROP_TXSTATUS_VSDB */ - - cur_if->ifstate = IF_STATE_DISALBE; - printf("%s: apstamode=%d, ifname=%s\n", __FUNCTION__, apstamode, ifname); - - return 0; -} - -static int -wl_ext_iapsta_enable(struct net_device *dev, char *command, int total_len) -{ - int ret = 0; - s32 val = 0; - char *pch, *pick_tmp, *param; - s8 iovar_buf[WLC_IOCTL_SMLEN]; - wlc_ssid_t ssid = { 0, {0} }; - struct { - s32 cfg; - s32 val; - } bss_setbuf; - struct wl_apsta_params *apsta_params = &g_apsta_params; - apstamode_t apstamode = apsta_params->apstamode; - char ifname[IFNAMSIZ+1]; - struct wl_if_info *cur_if; - char cmd[128] = "iapsta_stop ifname "; - struct dhd_pub *dhd; - - if (!apsta_params->init) { - ANDROID_ERROR(("%s: please init first\n", __FUNCTION__)); - return -1; - } - - ANDROID_TRACE(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); - dhd = dhd_get_pub(dev); - - pick_tmp = command; - param = bcmstrtok(&pick_tmp, " ", 0); // skip iapsta_enable - param = bcmstrtok(&pick_tmp, " ", 0); - while (param != NULL) { - if (!strcmp(param, "ifname")) { - pch = bcmstrtok(&pick_tmp, " ", 0); - if (pch) - strcpy(ifname, pch); - else { - ANDROID_ERROR(("%s: ifname [wlanX]\n", __FUNCTION__)); - return -1; - } - } - param = bcmstrtok(&pick_tmp, " ", 0); - } - if (!strcmp(apsta_params->pif.dev->name, ifname)) { - cur_if = &apsta_params->pif; - } else if (!strcmp(apsta_params->vif.ifname, ifname)) { - cur_if = &apsta_params->vif; - } else { - ANDROID_ERROR(("%s: wrong ifname=%s\n", __FUNCTION__, ifname)); - return -1; - } - if (!cur_if->dev) { - ANDROID_ERROR(("%s: %s is not ready\n", __FUNCTION__, ifname)); - return -1; - } - ssid.SSID_len = strlen(cur_if->ssid); - memcpy(ssid.SSID, cur_if->ssid, ssid.SSID_len); - ANDROID_TRACE(("%s: apstamode=%d, bssidx=%d\n", __FUNCTION__, apstamode, cur_if->bssidx)); - - snprintf(cmd, 128, "iapsta_stop ifname %s", cur_if->ifname); - ret = wl_ext_iapsta_disable(dev, cmd, strlen(cmd)); - if (ret) - goto exit; - - if (cur_if == &apsta_params->vif) { - wl_ext_iovar_setbuf(cur_if->dev, "cur_etheraddr", (u8 *)cur_if->dev->dev_addr, - ETHER_ADDR_LEN, iovar_buf, WLC_IOCTL_SMLEN, NULL); - } - - // set ssid for AP - if (cur_if->ifmode == IAP_MODE) { - wl_ext_iovar_setint(dev, "mpc", 0); - if (apstamode == IAPONLY_MODE) { - wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); - } else if (apstamode==IAPSTA_MODE || apstamode==IGOSTA_MODE) { - wl_ext_iovar_setbuf_bsscfg(cur_if->dev, "ssid", &ssid, sizeof(ssid), - iovar_buf, WLC_IOCTL_SMLEN, cur_if->bssidx, NULL); - } - } - - if (cur_if->ifmode == IAP_MODE) { - wl_ext_set_bgnmode(cur_if); - wl_ext_set_chanspec(cur_if->dev, cur_if->channel); - } - wl_ext_set_amode(cur_if, apsta_params); - wl_ext_set_emode(cur_if, apsta_params); - - if (apstamode == ISTAONLY_MODE || apstamode == IGCSTA_MODE) { - if (!ETHER_ISBCAST(&cur_if->bssid) && !ETHER_ISNULLADDR(&cur_if->bssid)) { - printf("%s: BSSID: %pM\n", __FUNCTION__, &cur_if->bssid); - wl_ext_ioctl(cur_if->dev, WLC_SET_BSSID, &cur_if->bssid, ETHER_ADDR_LEN, 1); - } - val = 1; - wl_ext_ioctl(dev, WLC_SET_INFRA, &val, sizeof(val), 1); - } - if (cur_if->ifmode == IAP_MODE) { - if (cur_if->maxassoc >= 0) - wl_ext_iovar_setint(dev, "maxassoc", cur_if->maxassoc); - printf("%s: Broadcast SSID: %s\n", __FUNCTION__, cur_if->hidden ? "OFF":"ON"); - // terence: fix me, hidden does not work in dualAP mode - wl_ext_ioctl(cur_if->dev, WLC_SET_CLOSED, &cur_if->hidden, sizeof(cur_if->hidden), 1); - } - - if (apstamode == ISTAONLY_MODE || apstamode == IGCSTA_MODE) { - wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1); - } else if (apstamode == IAPONLY_MODE) { - wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1); - wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); - } else if (apstamode == IAPSTA_MODE || apstamode == IGOSTA_MODE) { - if (cur_if->ifmode == ISTA_MODE) { - wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1); - } else { - if (FW_SUPPORTED(dhd, rsdb)) { - wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1); - } else { - bss_setbuf.cfg = htod32(cur_if->bssidx); - bss_setbuf.val = htod32(1); - wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf, sizeof(bss_setbuf), - iovar_buf, WLC_IOCTL_SMLEN, NULL); - } -#ifdef ARP_OFFLOAD_SUPPORT - /* IF SoftAP is enabled, disable arpoe */ - dhd_arp_offload_set(dhd, 0); - dhd_arp_offload_enable(dhd, FALSE); -#endif /* ARP_OFFLOAD_SUPPORT */ - } - } - else if (apstamode == IDUALAP_MODE) { - wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1); - } - -#ifdef PROP_TXSTATUS_VSDB -#if defined(BCMSDIO) - if (cur_if==&apsta_params->vif && !disable_proptx) { - bool enabled; - dhd_wlfc_get_enable(dhd, &enabled); - if (!enabled) { - dhd_wlfc_init(dhd); - wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); - } - } -#endif -#endif /* PROP_TXSTATUS_VSDB */ - - printf("%s: ifname=%s, SSID: %s\n", __FUNCTION__, ifname, cur_if->ssid); - - cur_if->ifstate = IF_STATE_ENABLE; - -exit: - return ret; -} - -void -wl_android_ext_iapsta_disconnect_sta(struct net_device *dev, u32 channel) -{ - struct wl_apsta_params *apsta_params = &g_apsta_params; - struct wl_if_info *cur_if = &apsta_params->vif; - scb_val_t scbval; - int ret; - channel_info_t ci; - struct dhd_pub *dhd; - - if (apsta_params->apstamode==IAPSTA_MODE && cur_if->ifstate==IF_STATE_ENABLE) { - dhd = dhd_get_pub(dev); - if (!FW_SUPPORTED(dhd, vsdb)) { - if (!(ret = wldev_ioctl(cur_if->dev, WLC_GET_CHANNEL, &ci, sizeof(channel_info_t), FALSE))) { - if (channel != ci.target_channel) { - printf("%s: deauthenticate all STA on vif\n", __FUNCTION__); - memcpy(scbval.ea.octet, ðer_bcast, ETHER_ADDR_LEN); - wl_ext_ioctl(cur_if->dev, WLC_SCB_DEAUTHENTICATE, &scbval.ea, ETHER_ADDR_LEN, 1); - } - } - } - } -} - -int wl_android_ext_attach_netdev(struct net_device *net, uint8 bssidx) -{ - g_apsta_params.vif.dev = net; - g_apsta_params.vif.bssidx = bssidx; - if (strlen(g_apsta_params.vif.ifname)) { - memset(net->name, 0, sizeof(IFNAMSIZ)); - strcpy(net->name, g_apsta_params.vif.ifname); - net->name[IFNAMSIZ - 1] = '\0'; - } - if (g_apsta_params.pif.dev) { - memcpy(net->dev_addr, g_apsta_params.pif.dev->dev_addr, ETHER_ADDR_LEN); - net->dev_addr[0] |= 0x02; - } - - return 0; -} - -int wl_android_ext_dettach_netdev(void) -{ - struct wl_apsta_params *apsta_params = &g_apsta_params; - - ANDROID_TRACE(("%s: Enter\n", __FUNCTION__)); - memset(apsta_params, 0, sizeof(struct wl_apsta_params)); - - return 0; -} -#endif - -#ifdef IDHCP -int wl_ext_ip_dump(int ip, char *buf) -{ - unsigned char bytes[4]; - int bytes_written=-1; - - bytes[0] = ip & 0xFF; - bytes[1] = (ip >> 8) & 0xFF; - bytes[2] = (ip >> 16) & 0xFF; - bytes[3] = (ip >> 24) & 0xFF; - bytes_written = sprintf(buf, "%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3]); - - return bytes_written; -} - -/* -terence 20170215: -dhd_priv dhcpc_dump ifname [wlan0|wlan1] -dhd_priv dhcpc_enable [0|1] -*/ -int -wl_ext_dhcpc_enable(struct net_device *dev, char *command, int total_len) -{ - int enable = -1, ret = -1; - int bytes_written = -1; - - ANDROID_TRACE(("%s: cmd %s\n", __FUNCTION__, command)); - - sscanf(command, "%*s %d", &enable); - - if (enable >= 0) - ret = wl_ext_iovar_setint(dev, "dhcpc_enable", enable); - else { - ret = wl_ext_iovar_getint(dev, "dhcpc_enable", &enable); - if (!ret) { - bytes_written = snprintf(command, total_len, "%d", enable); - ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command)); - ret = bytes_written; - } - } - - return ret; -} - -int -wl_ext_dhcpc_dump(struct net_device *dev, char *command, int total_len) -{ - - int ret = 0; - int bytes_written = 0; - uint32 ip_addr; - char buf[20]=""; - - ret = wl_ext_iovar_getint(dev, "dhcpc_ip_addr", &ip_addr); - if (!ret) { - wl_ext_ip_dump(ip_addr, buf); - bytes_written += snprintf(command+bytes_written, total_len, "ipaddr %s ", buf); - } - - ret = wl_ext_iovar_getint(dev, "dhcpc_ip_mask", &ip_addr); - if (!ret) { - wl_ext_ip_dump(ip_addr, buf); - bytes_written += snprintf(command+bytes_written, total_len, "mask %s ", buf); - } - - ret = wl_ext_iovar_getint(dev, "dhcpc_ip_gateway", &ip_addr); - if (!ret) { - wl_ext_ip_dump(ip_addr, buf); - bytes_written += snprintf(command+bytes_written, total_len, "gw %s ", buf); - } - - ret = wl_ext_iovar_getint(dev, "dhcpc_ip_dnsserv", &ip_addr); - if (!ret) { - wl_ext_ip_dump(ip_addr, buf); - bytes_written += snprintf(command+bytes_written, total_len, "dnsserv %s ", buf); - } - - if (!bytes_written) - bytes_written = -1; - - ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command)); - - return bytes_written; -} -#endif - -/* -dhd_priv dhd [string] ==> Not ready -1. Get dhd val: - Ex: dhd_priv dhd bussleep -2. Set dhd val: - Ex: dhd_priv dhd bussleep 1 - -dhd_priv wl [WLC_GET_PM] ==> Ready to get int val -dhd_priv wl [WLC_SET_PM] [int] ==> Ready to set int val -dhd_priv wl [string] ==> Ready to get int val -dhd_priv wl [string] [int] ==> Ready to set int val -Ex: get/set WLC_PM - dhd_priv wl 85 - dhd_priv wl 86 1 -Ex: get/set mpc - dhd_priv wl mpc - dhd_priv wl mpc 1 -*/ -int -wl_ext_iovar(struct net_device *dev, char *command, int total_len) -{ - int ret = 0; - char wl[3]="\0", arg[20]="\0", cmd_str[20]="\0", val_str[20]="\0"; - int cmd=-1, val=0; - int bytes_written=-1; - - ANDROID_TRACE(("%s: cmd %s\n", __FUNCTION__, command)); - - sscanf(command, "%s %d %s", wl, &cmd, arg); - if (cmd < 0) - sscanf(command, "%s %s %s", wl, cmd_str, val_str); - - if (!strcmp(wl, "wl")) { - if (cmd>=0 && cmd!=WLC_GET_VAR && cmd!=WLC_SET_VAR) { - ret = sscanf(arg, "%d", &val); - if (ret > 0) { // set - ret = wl_ext_ioctl(dev, cmd, &val, sizeof(val), TRUE); - } else { // get - ret = wl_ext_ioctl(dev, cmd, &val, sizeof(val), FALSE); - if (!ret) { - bytes_written = snprintf(command, total_len, "%d", val); - ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command)); - ret = bytes_written; - } - } - } else if (strlen(cmd_str)) { - ret = sscanf(val_str, "%d", &val); - if (ret > 0) { // set - ret = wl_ext_iovar_setint(dev, cmd_str, val); - } else { // get - ret = wl_ext_iovar_getint(dev, cmd_str, &val); - if (!ret) { - bytes_written = snprintf(command, total_len, "%d", val); - ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command)); - ret = bytes_written; - } - } - } - } - - return ret; -} - -int wl_android_ext_priv_cmd(struct net_device *net, char *command, int total_len, - int *bytes_written) -{ - int ret = 0; - - if (strnicmp(command, CMD_CHANNELS, strlen(CMD_CHANNELS)) == 0) { - *bytes_written = wl_ext_channels(net, command, total_len); - } - else if (strnicmp(command, CMD_CHANNEL, strlen(CMD_CHANNEL)) == 0) { - *bytes_written = wl_ext_channel(net, command, total_len); - } - else if (strnicmp(command, CMD_ROAM_TRIGGER, strlen(CMD_ROAM_TRIGGER)) == 0) { - *bytes_written = wl_ext_roam_trigger(net, command, total_len); - } - else if (strnicmp(command, CMD_KEEP_ALIVE, strlen(CMD_KEEP_ALIVE)) == 0) { - *bytes_written = wl_ext_keep_alive(net, command, total_len); - } - else if (strnicmp(command, CMD_PM, strlen(CMD_PM)) == 0) { - *bytes_written = wl_ext_pm(net, command, total_len); - } - else if (strnicmp(command, CMD_MONITOR, strlen(CMD_MONITOR)) == 0) { - *bytes_written = wl_ext_monitor(net, command, total_len); - } - else if (strnicmp(command, CMD_SET_SUSPEND_BCN_LI_DTIM, strlen(CMD_SET_SUSPEND_BCN_LI_DTIM)) == 0) { - int bcn_li_dtim; - bcn_li_dtim = (int)simple_strtol((command + strlen(CMD_SET_SUSPEND_BCN_LI_DTIM) + 1), NULL, 10); - *bytes_written = net_os_set_suspend_bcn_li_dtim(net, bcn_li_dtim); - } -#ifdef WL_EXT_IAPSTA - else if (strnicmp(command, CMD_IAPSTA_INIT, strlen(CMD_IAPSTA_INIT)) == 0) { - *bytes_written = wl_ext_iapsta_init(net, command, total_len); - } - else if (strnicmp(command, CMD_IAPSTA_CONFIG, strlen(CMD_IAPSTA_CONFIG)) == 0) { - *bytes_written = wl_ext_iapsta_config(net, command, total_len); - } - else if (strnicmp(command, CMD_IAPSTA_ENABLE, strlen(CMD_IAPSTA_ENABLE)) == 0) { - *bytes_written = wl_ext_iapsta_enable(net, command, total_len); - } - else if (strnicmp(command, CMD_IAPSTA_DISABLE, strlen(CMD_IAPSTA_DISABLE)) == 0) { - *bytes_written = wl_ext_iapsta_disable(net, command, total_len); - } -#endif -#ifdef IDHCP - else if (strnicmp(command, CMD_DHCPC_ENABLE, strlen(CMD_DHCPC_ENABLE)) == 0) { - *bytes_written = wl_ext_dhcpc_enable(net, command, total_len); - } - else if (strnicmp(command, CMD_DHCPC_DUMP, strlen(CMD_DHCPC_DUMP)) == 0) { - *bytes_written = wl_ext_dhcpc_dump(net, command, total_len); - } -#endif - else if (strnicmp(command, CMD_WL, strlen(CMD_WL)) == 0) { - *bytes_written = wl_ext_iovar(net, command, total_len); - } - else - ret = -1; - - return ret; -} - -#if defined(RSSIAVG) -void -wl_free_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl) -{ - wl_rssi_cache_t *node, *cur, **rssi_head; - int i=0; - - rssi_head = &rssi_cache_ctrl->m_cache_head; - node = *rssi_head; - - for (;node;) { - ANDROID_INFO(("%s: Free %d with BSSID %pM\n", - __FUNCTION__, i, &node->BSSID)); - cur = node; - node = cur->next; - kfree(cur); - i++; - } - *rssi_head = NULL; -} - -void -wl_delete_dirty_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl) -{ - wl_rssi_cache_t *node, *prev, **rssi_head; - int i = -1, tmp = 0; - struct timeval now; - - do_gettimeofday(&now); - - rssi_head = &rssi_cache_ctrl->m_cache_head; - node = *rssi_head; - prev = node; - for (;node;) { - i++; - if (now.tv_sec > node->tv.tv_sec) { - if (node == *rssi_head) { - tmp = 1; - *rssi_head = node->next; - } else { - tmp = 0; - prev->next = node->next; - } - ANDROID_INFO(("%s: Del %d with BSSID %pM\n", - __FUNCTION__, i, &node->BSSID)); - kfree(node); - if (tmp == 1) { - node = *rssi_head; - prev = node; - } else { - node = prev->next; - } - continue; - } - prev = node; - node = node->next; - } -} - -void -wl_delete_disconnected_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl, u8 *bssid) -{ - wl_rssi_cache_t *node, *prev, **rssi_head; - int i = -1, tmp = 0; - - rssi_head = &rssi_cache_ctrl->m_cache_head; - node = *rssi_head; - prev = node; - for (;node;) { - i++; - if (!memcmp(&node->BSSID, bssid, ETHER_ADDR_LEN)) { - if (node == *rssi_head) { - tmp = 1; - *rssi_head = node->next; - } else { - tmp = 0; - prev->next = node->next; - } - ANDROID_INFO(("%s: Del %d with BSSID %pM\n", - __FUNCTION__, i, &node->BSSID)); - kfree(node); - if (tmp == 1) { - node = *rssi_head; - prev = node; - } else { - node = prev->next; - } - continue; - } - prev = node; - node = node->next; - } -} - -void -wl_reset_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl) -{ - wl_rssi_cache_t *node, **rssi_head; - - rssi_head = &rssi_cache_ctrl->m_cache_head; - - /* reset dirty */ - node = *rssi_head; - for (;node;) { - node->dirty += 1; - node = node->next; - } -} - -int -wl_update_connected_rssi_cache(struct net_device *net, wl_rssi_cache_ctrl_t *rssi_cache_ctrl, int *rssi_avg) -{ - wl_rssi_cache_t *node, *prev, *leaf, **rssi_head; - int j, k=0; - int rssi, error=0; - struct ether_addr bssid; - struct timeval now, timeout; - - if (!g_wifi_on) - return 0; - - error = wldev_ioctl(net, WLC_GET_BSSID, &bssid, sizeof(bssid), false); - if (error == BCME_NOTASSOCIATED) { - ANDROID_INFO(("%s: Not Associated! res:%d\n", __FUNCTION__, error)); - return 0; - } - if (error) { - ANDROID_ERROR(("Could not get bssid (%d)\n", error)); - } - error = wldev_get_rssi(net, &rssi); - if (error) { - ANDROID_ERROR(("Could not get rssi (%d)\n", error)); - return error; - } - - do_gettimeofday(&now); - timeout.tv_sec = now.tv_sec + RSSICACHE_TIMEOUT; - if (timeout.tv_sec < now.tv_sec) { - /* - * Integer overflow - assume long enough timeout to be assumed - * to be infinite, i.e., the timeout would never happen. - */ - ANDROID_TRACE(("%s: Too long timeout (secs=%d) to ever happen - now=%lu, timeout=%lu", - __FUNCTION__, RSSICACHE_TIMEOUT, now.tv_sec, timeout.tv_sec)); - } - - /* update RSSI */ - rssi_head = &rssi_cache_ctrl->m_cache_head; - node = *rssi_head; - prev = NULL; - for (;node;) { - if (!memcmp(&node->BSSID, &bssid, ETHER_ADDR_LEN)) { - ANDROID_INFO(("%s: Update %d with BSSID %pM, RSSI=%d\n", - __FUNCTION__, k, &bssid, rssi)); - for (j=0; jRSSI[j] = node->RSSI[j+1]; - node->RSSI[j] = rssi; - node->dirty = 0; - node->tv = timeout; - goto exit; - } - prev = node; - node = node->next; - k++; - } - - leaf = kmalloc(sizeof(wl_rssi_cache_t), GFP_KERNEL); - if (!leaf) { - ANDROID_ERROR(("%s: Memory alloc failure %d\n", - __FUNCTION__, (int)sizeof(wl_rssi_cache_t))); - return 0; - } - ANDROID_INFO(("%s: Add %d with cached BSSID %pM, RSSI=%3d in the leaf\n", - __FUNCTION__, k, &bssid, rssi)); - - leaf->next = NULL; - leaf->dirty = 0; - leaf->tv = timeout; - memcpy(&leaf->BSSID, &bssid, ETHER_ADDR_LEN); - for (j=0; jRSSI[j] = rssi; - - if (!prev) - *rssi_head = leaf; - else - prev->next = leaf; - -exit: - *rssi_avg = (int)wl_get_avg_rssi(rssi_cache_ctrl, &bssid); - - return error; -} - -void -wl_update_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl, wl_scan_results_t *ss_list) -{ - wl_rssi_cache_t *node, *prev, *leaf, **rssi_head; - wl_bss_info_t *bi = NULL; - int i, j, k; - struct timeval now, timeout; - - if (!ss_list->count) - return; - - do_gettimeofday(&now); - timeout.tv_sec = now.tv_sec + RSSICACHE_TIMEOUT; - if (timeout.tv_sec < now.tv_sec) { - /* - * Integer overflow - assume long enough timeout to be assumed - * to be infinite, i.e., the timeout would never happen. - */ - ANDROID_TRACE(("%s: Too long timeout (secs=%d) to ever happen - now=%lu, timeout=%lu", - __FUNCTION__, RSSICACHE_TIMEOUT, now.tv_sec, timeout.tv_sec)); - } - - rssi_head = &rssi_cache_ctrl->m_cache_head; - - /* update RSSI */ - for (i = 0; i < ss_list->count; i++) { - node = *rssi_head; - prev = NULL; - k = 0; - bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : ss_list->bss_info; - for (;node;) { - if (!memcmp(&node->BSSID, &bi->BSSID, ETHER_ADDR_LEN)) { - ANDROID_INFO(("%s: Update %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n", - __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID)); - for (j=0; jRSSI[j] = node->RSSI[j+1]; - node->RSSI[j] = dtoh16(bi->RSSI); - node->dirty = 0; - node->tv = timeout; - break; - } - prev = node; - node = node->next; - k++; - } - - if (node) - continue; - - leaf = kmalloc(sizeof(wl_rssi_cache_t), GFP_KERNEL); - if (!leaf) { - ANDROID_ERROR(("%s: Memory alloc failure %d\n", - __FUNCTION__, (int)sizeof(wl_rssi_cache_t))); - return; - } - ANDROID_INFO(("%s: Add %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\" in the leaf\n", - __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID)); - - leaf->next = NULL; - leaf->dirty = 0; - leaf->tv = timeout; - memcpy(&leaf->BSSID, &bi->BSSID, ETHER_ADDR_LEN); - for (j=0; jRSSI[j] = dtoh16(bi->RSSI); - - if (!prev) - *rssi_head = leaf; - else - prev->next = leaf; - } -} - -int16 -wl_get_avg_rssi(wl_rssi_cache_ctrl_t *rssi_cache_ctrl, void *addr) -{ - wl_rssi_cache_t *node, **rssi_head; - int j, rssi_sum, rssi=RSSI_MINVAL; - - rssi_head = &rssi_cache_ctrl->m_cache_head; - - node = *rssi_head; - for (;node;) { - if (!memcmp(&node->BSSID, addr, ETHER_ADDR_LEN)) { - rssi_sum = 0; - rssi = 0; - for (j=0; jRSSI[RSSIAVG_LEN-j-1]; - rssi = rssi_sum / j; - break; - } - node = node->next; - } - rssi = MIN(rssi, RSSI_MAXVAL); - if (rssi == RSSI_MINVAL) { - ANDROID_ERROR(("%s: BSSID %pM does not in RSSI cache\n", - __FUNCTION__, addr)); - } - return (int16)rssi; -} -#endif - -#if defined(RSSIOFFSET) -int -wl_update_rssi_offset(struct net_device *net, int rssi) -{ -#if defined(RSSIOFFSET_NEW) - int j; -#endif - - if (!g_wifi_on) - return rssi; - -#if defined(RSSIOFFSET_NEW) - for (j=0; jm_cache_head; - node = *bss_head; - - for (;node;) { - ANDROID_TRACE(("%s: Free %d with BSSID %pM\n", - __FUNCTION__, i, &node->results.bss_info->BSSID)); - cur = node; - node = cur->next; - kfree(cur); - i++; - } - *bss_head = NULL; -} - -void -wl_delete_dirty_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl) -{ - wl_bss_cache_t *node, *prev, **bss_head; - int i = -1, tmp = 0; - struct timeval now; - - do_gettimeofday(&now); - - bss_head = &bss_cache_ctrl->m_cache_head; - node = *bss_head; - prev = node; - for (;node;) { - i++; - if (now.tv_sec > node->tv.tv_sec) { - if (node == *bss_head) { - tmp = 1; - *bss_head = node->next; - } else { - tmp = 0; - prev->next = node->next; - } - ANDROID_TRACE(("%s: Del %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n", - __FUNCTION__, i, &node->results.bss_info->BSSID, - dtoh16(node->results.bss_info->RSSI), node->results.bss_info->SSID)); - kfree(node); - if (tmp == 1) { - node = *bss_head; - prev = node; - } else { - node = prev->next; - } - continue; - } - prev = node; - node = node->next; - } -} - -void -wl_delete_disconnected_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl, u8 *bssid) -{ - wl_bss_cache_t *node, *prev, **bss_head; - int i = -1, tmp = 0; - - bss_head = &bss_cache_ctrl->m_cache_head; - node = *bss_head; - prev = node; - for (;node;) { - i++; - if (!memcmp(&node->results.bss_info->BSSID, bssid, ETHER_ADDR_LEN)) { - if (node == *bss_head) { - tmp = 1; - *bss_head = node->next; - } else { - tmp = 0; - prev->next = node->next; - } - ANDROID_TRACE(("%s: Del %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n", - __FUNCTION__, i, &node->results.bss_info->BSSID, - dtoh16(node->results.bss_info->RSSI), node->results.bss_info->SSID)); - kfree(node); - if (tmp == 1) { - node = *bss_head; - prev = node; - } else { - node = prev->next; - } - continue; - } - prev = node; - node = node->next; - } -} - -void -wl_reset_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl) -{ - wl_bss_cache_t *node, **bss_head; - - bss_head = &bss_cache_ctrl->m_cache_head; - - /* reset dirty */ - node = *bss_head; - for (;node;) { - node->dirty += 1; - node = node->next; - } -} - -void dump_bss_cache( -#if defined(RSSIAVG) - wl_rssi_cache_ctrl_t *rssi_cache_ctrl, -#endif - wl_bss_cache_t *node) -{ - int k = 0; - int16 rssi; - - for (;node;) { -#if defined(RSSIAVG) - rssi = wl_get_avg_rssi(rssi_cache_ctrl, &node->results.bss_info->BSSID); -#else - rssi = dtoh16(node->results.bss_info->RSSI); -#endif - ANDROID_TRACE(("%s: dump %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n", - __FUNCTION__, k, &node->results.bss_info->BSSID, rssi, node->results.bss_info->SSID)); - k++; - node = node->next; - } -} - -void -wl_update_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl, -#if defined(RSSIAVG) - wl_rssi_cache_ctrl_t *rssi_cache_ctrl, -#endif - wl_scan_results_t *ss_list) -{ - wl_bss_cache_t *node, *prev, *leaf, **bss_head; - wl_bss_info_t *bi = NULL; - int i, k=0; -#if defined(SORT_BSS_BY_RSSI) - int16 rssi, rssi_node; -#endif - struct timeval now, timeout; - - if (!ss_list->count) - return; - - do_gettimeofday(&now); - timeout.tv_sec = now.tv_sec + BSSCACHE_TIMEOUT; - if (timeout.tv_sec < now.tv_sec) { - /* - * Integer overflow - assume long enough timeout to be assumed - * to be infinite, i.e., the timeout would never happen. - */ - ANDROID_TRACE(("%s: Too long timeout (secs=%d) to ever happen - now=%lu, timeout=%lu", - __FUNCTION__, BSSCACHE_TIMEOUT, now.tv_sec, timeout.tv_sec)); - } - - bss_head = &bss_cache_ctrl->m_cache_head; - - for (i=0; i < ss_list->count; i++) { - node = *bss_head; - prev = NULL; - bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : ss_list->bss_info; - - for (;node;) { - if (!memcmp(&node->results.bss_info->BSSID, &bi->BSSID, ETHER_ADDR_LEN)) { - if (node == *bss_head) - *bss_head = node->next; - else { - prev->next = node->next; - } - break; - } - prev = node; - node = node->next; - } - - leaf = kmalloc(dtoh32(bi->length) + sizeof(wl_bss_cache_t), GFP_KERNEL); - if (!leaf) { - ANDROID_ERROR(("%s: Memory alloc failure %d\n", __FUNCTION__, - dtoh32(bi->length) + (int)sizeof(wl_bss_cache_t))); - return; - } - if (node) { - kfree(node); - node = NULL; - ANDROID_TRACE(("%s: Update %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n", - __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID)); - } else - ANDROID_TRACE(("%s: Add %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n", - __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID)); - - memcpy(leaf->results.bss_info, bi, dtoh32(bi->length)); - leaf->next = NULL; - leaf->dirty = 0; - leaf->tv = timeout; - leaf->results.count = 1; - leaf->results.version = ss_list->version; - k++; - - if (*bss_head == NULL) - *bss_head = leaf; - else { -#if defined(SORT_BSS_BY_RSSI) - node = *bss_head; -#if defined(RSSIAVG) - rssi = wl_get_avg_rssi(rssi_cache_ctrl, &leaf->results.bss_info->BSSID); -#else - rssi = dtoh16(leaf->results.bss_info->RSSI); -#endif - for (;node;) { -#if defined(RSSIAVG) - rssi_node = wl_get_avg_rssi(rssi_cache_ctrl, &node->results.bss_info->BSSID); -#else - rssi_node = dtoh16(node->results.bss_info->RSSI); -#endif - if (rssi > rssi_node) { - leaf->next = node; - if (node == *bss_head) - *bss_head = leaf; - else - prev->next = leaf; - break; - } - prev = node; - node = node->next; - } - if (node == NULL) - prev->next = leaf; -#else - leaf->next = *bss_head; - *bss_head = leaf; -#endif - } - } - dump_bss_cache( -#if defined(RSSIAVG) - rssi_cache_ctrl, -#endif - *bss_head); -} - -void -wl_release_bss_cache_ctrl(wl_bss_cache_ctrl_t *bss_cache_ctrl) -{ - ANDROID_TRACE(("%s:\n", __FUNCTION__)); - wl_free_bss_cache(bss_cache_ctrl); -} -#endif - - + } + *ret_chspec = fw_chspec; + + return err; +} + +int +wl_ext_channel(struct net_device *dev, char* command, int total_len) +{ + int ret; + int channel=0; + channel_info_t ci; + int bytes_written = 0; + chanspec_t fw_chspec; + + ANDROID_TRACE(("%s: cmd %s\n", __FUNCTION__, command)); + + sscanf(command, "%*s %d", &channel); + + if (channel > 0) { + ret = wl_ext_set_chanspec(dev, channel, &fw_chspec); + } else { + if (!(ret = wldev_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(channel_info_t), FALSE))) { + ANDROID_TRACE(("hw_channel %d\n", ci.hw_channel)); + ANDROID_TRACE(("target_channel %d\n", ci.target_channel)); + ANDROID_TRACE(("scan_channel %d\n", ci.scan_channel)); + bytes_written = snprintf(command, sizeof(channel_info_t)+2, "channel %d", ci.hw_channel); + ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command)); + ret = bytes_written; + } + } + + return ret; +} + +int +wl_ext_channels(struct net_device *dev, char* command, int total_len) +{ + int ret, i; + int bytes_written = -1; + u8 valid_chan_list[sizeof(u32)*(WL_NUMCHANNELS + 1)]; + wl_uint32_list_t *list; + + ANDROID_TRACE(("%s: cmd %s\n", __FUNCTION__, command)); + + memset(valid_chan_list, 0, sizeof(valid_chan_list)); + list = (wl_uint32_list_t *)(void *) valid_chan_list; + list->count = htod32(WL_NUMCHANNELS); + ret = wldev_ioctl(dev, WLC_GET_VALID_CHANNELS, valid_chan_list, sizeof(valid_chan_list), 0); + if (ret<0) { + ANDROID_ERROR(("%s: get channels failed with %d\n", __FUNCTION__, ret)); + } else { + bytes_written = snprintf(command, total_len, "channels"); + for (i = 0; i < dtoh32(list->count); i++) { + bytes_written += snprintf(command+bytes_written, total_len, " %d", dtoh32(list->element[i])); + printf("%d ", dtoh32(list->element[i])); + } + printf("\n"); + ret = bytes_written; + } + + return ret; +} + +int +wl_ext_roam_trigger(struct net_device *dev, char* command, int total_len) +{ + int ret = 0; + int roam_trigger[2] = {0, 0}; + int trigger[2]= {0, 0}; + int bytes_written=-1; + + sscanf(command, "%*s %10d", &roam_trigger[0]); + + if (roam_trigger[0]) { + roam_trigger[1] = WLC_BAND_ALL; + ret = wldev_ioctl(dev, WLC_SET_ROAM_TRIGGER, roam_trigger, sizeof(roam_trigger), 1); + if (ret) + ANDROID_ERROR(("WLC_SET_ROAM_TRIGGER ERROR %d ret=%d\n", roam_trigger[0], ret)); + } else { + roam_trigger[1] = WLC_BAND_2G; + ret = wldev_ioctl(dev, WLC_GET_ROAM_TRIGGER, roam_trigger, sizeof(roam_trigger), 0); + if (!ret) + trigger[0] = roam_trigger[0]; + else + ANDROID_ERROR(("2G WLC_GET_ROAM_TRIGGER ERROR %d ret=%d\n", roam_trigger[0], ret)); + + roam_trigger[1] = WLC_BAND_5G; + ret = wldev_ioctl(dev, WLC_GET_ROAM_TRIGGER, roam_trigger, sizeof(roam_trigger), 0); + if (!ret) + trigger[1] = roam_trigger[0]; + else + ANDROID_ERROR(("5G WLC_GET_ROAM_TRIGGER ERROR %d ret=%d\n", roam_trigger[0], ret)); + + ANDROID_TRACE(("roam_trigger %d %d\n", trigger[0], trigger[1])); + bytes_written = snprintf(command, total_len, "%d %d", trigger[0], trigger[1]); + ret = bytes_written; + } + + return ret; +} + +static int +wl_ext_pattern_atoh(char *src, char *dst) +{ + int i; + if (strncmp(src, "0x", 2) != 0 && + strncmp(src, "0X", 2) != 0) { + ANDROID_ERROR(("Mask invalid format. Needs to start with 0x\n")); + return -1; + } + src = src + 2; /* Skip past 0x */ + if (strlen(src) % 2 != 0) { + DHD_ERROR(("Mask invalid format. Needs to be of even length\n")); + return -1; + } + for (i = 0; *src != '\0'; i++) { + char num[3]; + bcm_strncpy_s(num, sizeof(num), src, 2); + num[2] = '\0'; + dst[i] = (uint8)strtoul(num, NULL, 16); + src += 2; + } + return i; +} + +int +wl_ext_keep_alive(struct net_device *dev, char *command, int total_len) +{ + wl_mkeep_alive_pkt_t *mkeep_alive_pktp; + int ret = -1, i; + int id, period=-1, len_bytes=0, buf_len=0; + char data[200]="\0"; + char buf[WLC_IOCTL_SMLEN]="\0", iovar_buf[WLC_IOCTL_SMLEN]="\0"; + int bytes_written = -1; + + ANDROID_TRACE(("%s: command = %s\n", __FUNCTION__, command)); + sscanf(command, "%*s %d %d %s", &id, &period, data); + ANDROID_TRACE(("%s: id=%d, period=%d, data=%s\n", __FUNCTION__, id, period, data)); + + if (period >= 0) { + mkeep_alive_pktp = (wl_mkeep_alive_pkt_t *)buf; + mkeep_alive_pktp->version = htod16(WL_MKEEP_ALIVE_VERSION); + mkeep_alive_pktp->length = htod16(WL_MKEEP_ALIVE_FIXED_LEN); + mkeep_alive_pktp->keep_alive_id = id; + buf_len += WL_MKEEP_ALIVE_FIXED_LEN; + mkeep_alive_pktp->period_msec = period; + if (strlen(data)) { + len_bytes = wl_ext_pattern_atoh(data, (char *) mkeep_alive_pktp->data); + buf_len += len_bytes; + } + mkeep_alive_pktp->len_bytes = htod16(len_bytes); + + ret = wl_ext_iovar_setbuf(dev, "mkeep_alive", buf, buf_len, + iovar_buf, sizeof(iovar_buf), NULL); + } else { + if (id < 0) + id = 0; + ret = wl_ext_iovar_getbuf(dev, "mkeep_alive", &id, sizeof(id), buf, sizeof(buf), NULL); + if (ret) { + goto exit; + } else { + mkeep_alive_pktp = (wl_mkeep_alive_pkt_t *) buf; + printf("Id :%d\n" + "Period (msec) :%d\n" + "Length :%d\n" + "Packet :0x", + mkeep_alive_pktp->keep_alive_id, + dtoh32(mkeep_alive_pktp->period_msec), + dtoh16(mkeep_alive_pktp->len_bytes)); + for (i=0; ilen_bytes; i++) { + printf("%02x", mkeep_alive_pktp->data[i]); + } + printf("\n"); + } + bytes_written = snprintf(command, total_len, "mkeep_alive_period_msec %d ", dtoh32(mkeep_alive_pktp->period_msec)); + bytes_written += snprintf(command+bytes_written, total_len, "0x"); + for (i=0; ilen_bytes; i++) { + bytes_written += snprintf(command+bytes_written, total_len, "%x", mkeep_alive_pktp->data[i]); + } + ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command)); + ret = bytes_written; + } + +exit: + return ret; +} + +int +wl_ext_pm(struct net_device *dev, char *command, int total_len) +{ + int pm=-1, ret = -1; + char *pm_local; + int bytes_written=-1; + + ANDROID_TRACE(("%s: cmd %s\n", __FUNCTION__, command)); + + sscanf(command, "%*s %d", &pm); + + if (pm >= 0) { + ret = wldev_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm), FALSE); + if (ret) + ANDROID_ERROR(("WLC_SET_PM ERROR %d ret=%d\n", pm, ret)); + } else { + ret = wldev_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm), FALSE); + if (!ret) { + ANDROID_TRACE(("%s: PM = %d\n", __func__, pm)); + if (pm == PM_OFF) + pm_local = "PM_OFF"; + else if(pm == PM_MAX) + pm_local = "PM_MAX"; + else if(pm == PM_FAST) + pm_local = "PM_FAST"; + else { + pm = 0; + pm_local = "Invalid"; + } + bytes_written = snprintf(command, total_len, "PM %s", pm_local); + ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command)); + ret = bytes_written; + } + } + + return ret; +} + +static int +wl_ext_monitor(struct net_device *dev, char *command, int total_len) +{ + int val, ret = -1; + int bytes_written=-1; + + sscanf(command, "%*s %d", &val); + + if (val >=0) { + ret = wldev_ioctl(dev, WLC_SET_MONITOR, &val, sizeof(int), 1); + if (ret) + ANDROID_ERROR(("WLC_SET_MONITOR ERROR %d ret=%d\n", val, ret)); + } else { + ret = wldev_ioctl(dev, WLC_GET_MONITOR, &val, sizeof(val), FALSE); + if (!ret) { + ANDROID_TRACE(("%s: monitor = %d\n", __FUNCTION__, val)); + bytes_written = snprintf(command, total_len, "monitor %d", val); + ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command)); + ret = bytes_written; + } + } + + return ret; +} + +#ifdef WL_EXT_IAPSTA +struct wl_apsta_params g_apsta_params; +static int +wl_ext_parse_wep(char *key, struct wl_wsec_key *wsec_key) +{ + char hex[] = "XX"; + unsigned char *data = wsec_key->data; + char *keystr = key; + + switch (strlen(keystr)) { + case 5: + case 13: + case 16: + wsec_key->len = strlen(keystr); + memcpy(data, keystr, wsec_key->len + 1); + break; + case 12: + case 28: + case 34: + case 66: + /* strip leading 0x */ + if (!strnicmp(keystr, "0x", 2)) + keystr += 2; + else + return -1; + /* fall through */ + case 10: + case 26: + case 32: + case 64: + wsec_key->len = strlen(keystr) / 2; + while (*keystr) { + strncpy(hex, keystr, 2); + *data++ = (char) strtoul(hex, NULL, 16); + keystr += 2; + } + break; + default: + return -1; + } + + switch (wsec_key->len) { + case 5: + wsec_key->algo = CRYPTO_ALGO_WEP1; + break; + case 13: + wsec_key->algo = CRYPTO_ALGO_WEP128; + break; + case 16: + /* default to AES-CCM */ + wsec_key->algo = CRYPTO_ALGO_AES_CCM; + break; + case 32: + wsec_key->algo = CRYPTO_ALGO_TKIP; + break; + default: + return -1; + } + + /* Set as primary wsec_key by default */ + wsec_key->flags |= WL_PRIMARY_KEY; + + return 0; +} + +static int +wl_ext_set_bgnmode(struct wl_if_info *cur_if) +{ + struct net_device *dev = cur_if->dev; + bgnmode_t bgnmode = cur_if->bgnmode; + int val; + + if (bgnmode == 0) + return 0; + + wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); + if (bgnmode == IEEE80211B) { + wl_ext_iovar_setint(dev, "nmode", 0); + val = 0; + wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1); + ANDROID_TRACE(("%s: Network mode: B only\n", __FUNCTION__)); + } else if (bgnmode == IEEE80211G) { + wl_ext_iovar_setint(dev, "nmode", 0); + val = 2; + wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1); + ANDROID_TRACE(("%s: Network mode: G only\n", __FUNCTION__)); + } else if (bgnmode == IEEE80211BG) { + wl_ext_iovar_setint(dev, "nmode", 0); + val = 1; + wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1); + ANDROID_TRACE(("%s: Network mode: B/G mixed\n", __FUNCTION__)); + } else if (bgnmode == IEEE80211BGN) { + wl_ext_iovar_setint(dev, "nmode", 0); + wl_ext_iovar_setint(dev, "nmode", 1); + wl_ext_iovar_setint(dev, "vhtmode", 0); + val = 1; + wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1); + ANDROID_TRACE(("%s: Network mode: B/G/N mixed\n", __FUNCTION__)); + } else if (bgnmode == IEEE80211BGNAC) { + wl_ext_iovar_setint(dev, "nmode", 0); + wl_ext_iovar_setint(dev, "nmode", 1); + wl_ext_iovar_setint(dev, "vhtmode", 1); + val = 1; + wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1); + ANDROID_TRACE(("%s: Network mode: B/G/N/AC mixed\n", __FUNCTION__)); + } + wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); + + return 0; +} + +static void +wl_ext_get_amode(struct wl_if_info *cur_if, char *amode) +{ + struct net_device *dev = cur_if->dev; + int auth=-1, wpa_auth=-1; + + wl_ext_iovar_getint(dev, "auth", &auth); + wl_ext_iovar_getint(dev, "wpa_auth", &wpa_auth); + + if (cur_if->ifmode == IMESH_MODE) { + if (auth == 0 && wpa_auth == 0) { + strcpy(amode, "open"); + } else if (auth == 0 && wpa_auth == 128) { + strcpy(amode, "sae"); + } + } else if (auth == 0 && wpa_auth == 0) { + strcpy(amode, "open"); + } else if (auth == 1 && wpa_auth == 0) { + strcpy(amode, "shared"); + } else if (auth == 0 && wpa_auth == 4) { + strcpy(amode, "wpapsk"); + } else if (auth == 0 && wpa_auth == 128) { + strcpy(amode, "wpa2psk"); + } else if (auth == 0 && wpa_auth == 132) { + strcpy(amode, "wpawpa2psk"); + } +} + +static void +wl_ext_get_emode(struct wl_if_info *cur_if, char *emode) +{ + struct net_device *dev = cur_if->dev; + int wsec=0; + + wl_ext_iovar_getint(dev, "wsec", &wsec); + + if (cur_if->ifmode == IMESH_MODE) { + if (wsec == 0) { + strcpy(emode, "none"); + } else { + strcpy(emode, "sae"); + } + } else if (wsec == 0) { + strcpy(emode, "none"); + } else if (wsec == 1) { + strcpy(emode, "wep"); + } else if (wsec == 2 || wsec == 10) { + strcpy(emode, "tkip"); + } else if (wsec == 4 || wsec == 12) { + strcpy(emode, "aes"); + } else if (wsec == 6 || wsec == 14) { + strcpy(emode, "tkipaes"); + } +} + +static int +wl_ext_set_amode(struct wl_if_info *cur_if) +{ + struct net_device *dev = cur_if->dev; + authmode_t amode = cur_if->amode; + int auth=0, wpa_auth=0; + + if (cur_if->ifmode == IMESH_MODE) { + if (amode == AUTH_SAE) { + auth = 0; + wpa_auth = 128; + ANDROID_INFO(("%s: Authentication: SAE\n", __FUNCTION__)); + } else { + auth = 0; + wpa_auth = 0; + ANDROID_INFO(("%s: Authentication: Open System\n", __FUNCTION__)); + } + } else if (amode == AUTH_OPEN) { + auth = 0; + wpa_auth = 0; + ANDROID_INFO(("%s: Authentication: Open System\n", __FUNCTION__)); + } else if (amode == AUTH_SHARED) { + auth = 1; + wpa_auth = 0; + ANDROID_INFO(("%s: Authentication: Shared Key\n", __FUNCTION__)); + } else if (amode == AUTH_WPAPSK) { + auth = 0; + wpa_auth = 4; + ANDROID_INFO(("%s: Authentication: WPA-PSK\n", __FUNCTION__)); + } else if (amode == AUTH_WPA2PSK) { + auth = 0; + wpa_auth = 128; + ANDROID_INFO(("%s: Authentication: WPA2-PSK\n", __FUNCTION__)); + } else if (amode == AUTH_WPAWPA2PSK) { + auth = 0; + wpa_auth = 132; + ANDROID_INFO(("%s: Authentication: WPA/WPA2-PSK\n", __FUNCTION__)); + } + if (cur_if->ifmode == IMESH_MODE) { + s32 val = WL_BSSTYPE_MESH; + wl_ext_ioctl(dev, WLC_SET_INFRA, &val, sizeof(val), 1); + } else if (cur_if->ifmode == ISTA_MODE) { + s32 val = WL_BSSTYPE_INFRA; + wl_ext_ioctl(dev, WLC_SET_INFRA, &val, sizeof(val), 1); + } + wl_ext_iovar_setint(dev, "auth", auth); + + wl_ext_iovar_setint(dev, "wpa_auth", wpa_auth); + + return 0; +} + +static int +wl_ext_set_emode(struct wl_if_info *cur_if, struct wl_apsta_params *apsta_params) +{ + struct net_device *dev = cur_if->dev; + int wsec=0; + struct wl_wsec_key wsec_key; + wsec_pmk_t psk; + authmode_t amode = cur_if->amode; + encmode_t emode = cur_if->emode; + char *key = cur_if->key; + s8 iovar_buf[WLC_IOCTL_SMLEN]; + struct dhd_pub *dhd = dhd_get_pub(dev); + + memset(&wsec_key, 0, sizeof(wsec_key)); + memset(&psk, 0, sizeof(psk)); + + if (cur_if->ifmode == IMESH_MODE) { + if (amode == AUTH_SAE) { + wsec = 4; + ANDROID_INFO(("%s: Encryption: AES\n", __FUNCTION__)); + } else { + wsec = 0; + ANDROID_INFO(("%s: Encryption: No securiy\n", __FUNCTION__)); + } + } else if (emode == ENC_NONE) { + wsec = 0; + ANDROID_INFO(("%s: Encryption: No securiy\n", __FUNCTION__)); + } else if (emode == ENC_WEP) { + wsec = 1; + wl_ext_parse_wep(key, &wsec_key); + ANDROID_INFO(("%s: Encryption: WEP\n", __FUNCTION__)); + ANDROID_INFO(("%s: Key: \"%s\"\n", __FUNCTION__, wsec_key.data)); + } else if (emode == ENC_TKIP) { + wsec = 2; + psk.key_len = strlen(key); + psk.flags = WSEC_PASSPHRASE; + memcpy(psk.key, key, strlen(key)); + ANDROID_INFO(("%s: Encryption: TKIP\n", __FUNCTION__)); + ANDROID_INFO(("%s: Key: \"%s\"\n", __FUNCTION__, psk.key)); + } else if (emode == ENC_AES || amode == AUTH_SAE) { + wsec = 4; + psk.key_len = strlen(key); + psk.flags = WSEC_PASSPHRASE; + memcpy(psk.key, key, strlen(key)); + ANDROID_INFO(("%s: Encryption: AES\n", __FUNCTION__)); + ANDROID_INFO(("%s: Key: \"%s\"\n", __FUNCTION__, psk.key)); + } else if (emode == ENC_TKIPAES) { + wsec = 6; + psk.key_len = strlen(key); + psk.flags = WSEC_PASSPHRASE; + memcpy(psk.key, key, strlen(key)); + ANDROID_INFO(("%s: Encryption: TKIP/AES\n", __FUNCTION__)); + ANDROID_INFO(("%s: Key: \"%s\"\n", __FUNCTION__, psk.key)); + } + if (dhd->conf->chip == BCM43430_CHIP_ID && cur_if->bssidx > 0 && wsec >= 2 && + apsta_params->apstamode == IAPSTA_MODE) { + wsec |= 0x8; // terence 20180628: fix me, this is a workaround + } + + wl_ext_iovar_setint(dev, "wsec", wsec); + + if (cur_if->ifmode == IMESH_MODE) { + if (amode == AUTH_SAE) { + ANDROID_INFO(("%s: Key: \"%s\"\n", __FUNCTION__, key)); + wl_ext_iovar_setint(dev, "mesh_auth_proto", 1); + wl_ext_iovar_setint(dev, "mfp", WL_MFP_REQUIRED); + wl_ext_iovar_setbuf(dev, "sae_password", key, strlen(key), + iovar_buf, WLC_IOCTL_SMLEN, NULL); + } else { + wl_ext_iovar_setint(dev, "mesh_auth_proto", 0); + wl_ext_iovar_setint(dev, "mfp", WL_MFP_NONE); + } + } else if (emode == ENC_WEP) { + wl_ext_ioctl(dev, WLC_SET_KEY, &wsec_key, sizeof(wsec_key), 1); + } else if (emode == ENC_TKIP || emode == ENC_AES || emode == ENC_TKIPAES) { + if (dev) { + if (cur_if->ifmode == ISTA_MODE) + wl_ext_iovar_setint(dev, "sup_wpa", 1); + wl_ext_ioctl(dev, WLC_SET_WSEC_PMK, &psk, sizeof(psk), 1); + } else { + ANDROID_ERROR(("%s: apdev is null\n", __FUNCTION__)); + } + } + + return 0; +} + +static chanspec_t +wl_ext_get_chanspec(struct net_device *dev, uint16 channel) +{ + s32 _chan = channel; + chanspec_t chspec = 0; + chanspec_t fw_chspec = 0; + u32 bw = WL_CHANSPEC_BW_20; + s32 err = BCME_OK; + s32 bw_cap = 0; + s8 iovar_buf[WLC_IOCTL_SMLEN]; + struct { + u32 band; + u32 bw_cap; + } param = {0, 0}; + uint band; + int ioctl_ver = 0; + + if (_chan <= CH_MAX_2G_CHANNEL) + band = IEEE80211_BAND_2GHZ; + else + band = IEEE80211_BAND_5GHZ; + wl_ext_get_ioctl_ver(dev, &ioctl_ver); + + if (band == IEEE80211_BAND_5GHZ) { + param.band = WLC_BAND_5G; + err = wldev_iovar_getbuf(dev, "bw_cap", ¶m, sizeof(param), + iovar_buf, WLC_IOCTL_SMLEN, NULL); + if (err) { + if (err != BCME_UNSUPPORTED) { + ANDROID_ERROR(("bw_cap failed, %d\n", err)); + return err; + } else { + err = wldev_iovar_getint(dev, "mimo_bw_cap", &bw_cap); + if (err) { + ANDROID_ERROR(("error get mimo_bw_cap (%d)\n", err)); + } + if (bw_cap != WLC_N_BW_20ALL) + bw = WL_CHANSPEC_BW_40; + } + } else { + if (WL_BW_CAP_80MHZ(iovar_buf[0])) + bw = WL_CHANSPEC_BW_80; + else if (WL_BW_CAP_40MHZ(iovar_buf[0])) + bw = WL_CHANSPEC_BW_40; + else + bw = WL_CHANSPEC_BW_20; + + } + } + else if (band == IEEE80211_BAND_2GHZ) + bw = WL_CHANSPEC_BW_20; + + chspec = wf_channel2chspec(_chan, bw); + if (wf_chspec_valid(chspec)) { + fw_chspec = wl_ext_chspec_host_to_driver(ioctl_ver, chspec); + if (fw_chspec == INVCHANSPEC) { + ANDROID_ERROR(("%s: failed to convert host chanspec to fw chanspec\n", + __FUNCTION__)); + fw_chspec = 0; + } + } + + return fw_chspec; +} + +static void +wl_ext_ch_to_chanspec(int ch, struct wl_join_params *join_params, + size_t *join_params_size) +{ + struct wl_apsta_params *apsta_params = &g_apsta_params; + chanspec_t chanspec = 0; + + if (ch != 0) { + join_params->params.chanspec_num = 1; + join_params->params.chanspec_list[0] = ch; + + if (join_params->params.chanspec_list[0] <= CH_MAX_2G_CHANNEL) + chanspec |= WL_CHANSPEC_BAND_2G; + else + chanspec |= WL_CHANSPEC_BAND_5G; + + chanspec |= WL_CHANSPEC_BW_20; + chanspec |= WL_CHANSPEC_CTL_SB_NONE; + + *join_params_size += WL_ASSOC_PARAMS_FIXED_SIZE + + join_params->params.chanspec_num * sizeof(chanspec_t); + + join_params->params.chanspec_list[0] &= WL_CHANSPEC_CHAN_MASK; + join_params->params.chanspec_list[0] |= chanspec; + join_params->params.chanspec_list[0] = + wl_ext_chspec_host_to_driver(apsta_params->ioctl_ver, + join_params->params.chanspec_list[0]); + + join_params->params.chanspec_num = + htod32(join_params->params.chanspec_num); + ANDROID_TRACE(("join_params->params.chanspec_list[0]= %X, %d channels\n", + join_params->params.chanspec_list[0], + join_params->params.chanspec_num)); + } +} + +static s32 +wl_ext_connect(struct wl_if_info *cur_if) +{ + struct wl_apsta_params *apsta_params = &g_apsta_params; + wl_extjoin_params_t *ext_join_params; + struct wl_join_params join_params; + size_t join_params_size; + s32 err = 0; + u32 chan_cnt = 0; + s8 iovar_buf[WLC_IOCTL_SMLEN]; + + if (cur_if->channel) { + chan_cnt = 1; + } + + /* + * Join with specific BSSID and cached SSID + * If SSID is zero join based on BSSID only + */ + join_params_size = WL_EXTJOIN_PARAMS_FIXED_SIZE + + chan_cnt * sizeof(chanspec_t); + ext_join_params = (wl_extjoin_params_t*)kzalloc(join_params_size, GFP_KERNEL); + if (ext_join_params == NULL) { + err = -ENOMEM; + goto exit; + } + ext_join_params->ssid.SSID_len = min(sizeof(ext_join_params->ssid.SSID), + strlen(cur_if->ssid)); + memcpy(&ext_join_params->ssid.SSID, cur_if->ssid, ext_join_params->ssid.SSID_len); + ext_join_params->ssid.SSID_len = htod32(ext_join_params->ssid.SSID_len); + /* increate dwell time to receive probe response or detect Beacon + * from target AP at a noisy air only during connect command + */ + ext_join_params->scan.active_time = chan_cnt ? WL_SCAN_JOIN_ACTIVE_DWELL_TIME_MS : -1; + ext_join_params->scan.passive_time = chan_cnt ? WL_SCAN_JOIN_PASSIVE_DWELL_TIME_MS : -1; + /* Set up join scan parameters */ + ext_join_params->scan.scan_type = -1; + ext_join_params->scan.nprobes = chan_cnt ? + (ext_join_params->scan.active_time/WL_SCAN_JOIN_PROBE_INTERVAL_MS) : -1; + ext_join_params->scan.home_time = -1; + + if (memcmp(ðer_null, &cur_if->bssid, ETHER_ADDR_LEN)) + memcpy(&ext_join_params->assoc.bssid, &cur_if->bssid, ETH_ALEN); + else + memcpy(&ext_join_params->assoc.bssid, ðer_bcast, ETH_ALEN); + ext_join_params->assoc.chanspec_num = chan_cnt; + if (chan_cnt) { + u16 channel, band, bw, ctl_sb; + chanspec_t chspec; + channel = cur_if->channel; + band = (channel <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G + : WL_CHANSPEC_BAND_5G; + bw = WL_CHANSPEC_BW_20; + ctl_sb = WL_CHANSPEC_CTL_SB_NONE; + chspec = (channel | band | bw | ctl_sb); + ext_join_params->assoc.chanspec_list[0] &= WL_CHANSPEC_CHAN_MASK; + ext_join_params->assoc.chanspec_list[0] |= chspec; + ext_join_params->assoc.chanspec_list[0] = + wl_ext_chspec_host_to_driver(apsta_params->ioctl_ver, + ext_join_params->assoc.chanspec_list[0]); + } + ext_join_params->assoc.chanspec_num = htod32(ext_join_params->assoc.chanspec_num); + if (ext_join_params->ssid.SSID_len < IEEE80211_MAX_SSID_LEN) { + ANDROID_INFO(("ssid \"%s\", len (%d)\n", ext_join_params->ssid.SSID, + ext_join_params->ssid.SSID_len)); + } + + err = wl_ext_iovar_setbuf_bsscfg(cur_if->dev, "join", ext_join_params, + join_params_size, iovar_buf, WLC_IOCTL_SMLEN, cur_if->bssidx, NULL); + + printf("Connecting with " MACDBG " channel (%d) ssid \"%s\", len (%d)\n\n", + MAC2STRDBG((u8*)(&ext_join_params->assoc.bssid)), cur_if->channel, + ext_join_params->ssid.SSID, ext_join_params->ssid.SSID_len); + + kfree(ext_join_params); + if (err) { + if (err == BCME_UNSUPPORTED) { + ANDROID_TRACE(("join iovar is not supported\n")); + goto set_ssid; + } else { + ANDROID_ERROR(("error (%d)\n", err)); + goto exit; + } + } else + goto exit; + +set_ssid: + memset(&join_params, 0, sizeof(join_params)); + join_params_size = sizeof(join_params.ssid); + + join_params.ssid.SSID_len = min(sizeof(join_params.ssid.SSID), strlen(cur_if->ssid)); + memcpy(&join_params.ssid.SSID, cur_if->ssid, join_params.ssid.SSID_len); + join_params.ssid.SSID_len = htod32(join_params.ssid.SSID_len); + if (memcmp(ðer_null, &cur_if->bssid, ETHER_ADDR_LEN)) + memcpy(&join_params.params.bssid, &cur_if->bssid, ETH_ALEN); + else + memcpy(&join_params.params.bssid, ðer_bcast, ETH_ALEN); + + wl_ext_ch_to_chanspec(cur_if->channel, &join_params, &join_params_size); + ANDROID_TRACE(("join_param_size %zu\n", join_params_size)); + + if (join_params.ssid.SSID_len < IEEE80211_MAX_SSID_LEN) { + ANDROID_INFO(("ssid \"%s\", len (%d)\n", join_params.ssid.SSID, + join_params.ssid.SSID_len)); + } + err = wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &join_params,join_params_size, 1); + if (err) { + ANDROID_ERROR(("error (%d)\n", err)); + } + +exit: + return err; + +} + +static void +wl_ext_wait_netif_change(struct wl_apsta_params *apsta_params, + bool need_rtnl_unlock) +{ + if (need_rtnl_unlock) + rtnl_unlock(); + wait_event_interruptible_timeout(apsta_params->netif_change_event, + apsta_params->netif_change, msecs_to_jiffies(1500)); + if (need_rtnl_unlock) + rtnl_lock(); +} + +static void +wl_ext_iapsta_preinit(struct net_device *dev, struct wl_apsta_params *apsta_params) +{ + struct dhd_pub *dhd; + apstamode_t apstamode = apsta_params->apstamode; + wl_interface_create_t iface; + struct wl_if_info *cur_if; + wlc_ssid_t ssid = { 0, {0} }; + s8 iovar_buf[WLC_IOCTL_SMLEN]; + wl_country_t cspec = {{0}, 0, {0}}; + wl_p2p_if_t ifreq; + s32 val = 0; + int i, dfs = 1; + + dhd = dhd_get_pub(dev); + + for (i=0; iif_info[i]; + if (i == 1 && !strlen(cur_if->ifname)) + strcpy(cur_if->ifname, "wlan1"); + if (i == 2 && !strlen(cur_if->ifname)) + strcpy(cur_if->ifname, "wlan2"); + if (cur_if->ifmode == ISTA_MODE) { + cur_if->channel = 0; + cur_if->maxassoc = -1; + cur_if->ifstate = IF_STATE_INIT; + cur_if->prio = PRIO_STA; + cur_if->prefix = 'S'; + snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_sta"); + } else if (cur_if->ifmode == IAP_MODE) { + cur_if->channel = 1; + cur_if->maxassoc = -1; + cur_if->ifstate = IF_STATE_INIT; + cur_if->prio = PRIO_AP; + cur_if->prefix = 'A'; + snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_ap"); + dfs = 0; + } else if (cur_if->ifmode == IMESH_MODE) { + cur_if->channel = 1; + cur_if->maxassoc = -1; + cur_if->ifstate = IF_STATE_INIT; + cur_if->prio = PRIO_MESH; + cur_if->prefix = 'M'; + snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_mesh"); + dfs = 0; + } + } + if (dfs == 0) { + dhd_conf_get_country(dhd, &cspec); + if (!dhd_conf_map_country_list(dhd, &cspec, 1)) { + dhd_conf_set_country(dhd, &cspec); + dhd_bus_country_set(dev, &cspec, TRUE); + } + wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); + wl_ext_iovar_setint(dev, "dfs_chan_disable", 1); + wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); + } + + if (apstamode == IMESHONLY_MODE || apstamode == IMESHSTA_MODE || + apstamode == IMESHAP_MODE || apstamode == IMESHAPSTA_MODE || + apstamode == IMESHAPAP_MODE) { + wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); + if (FW_SUPPORTED(dhd, rsdb)) { + wl_config_t rsdb_mode_cfg = {1, 0}; + if (apsta_params->rsdb == 0) { + rsdb_mode_cfg.config = 0; + } + // mesh-ap must set rsdb_mode=1 in same channel or AP mode not easy to be found + printf("%s: set rsdb_mode %d\n", __FUNCTION__, rsdb_mode_cfg.config); + wl_ext_iovar_setbuf(dev, "rsdb_mode", &rsdb_mode_cfg, + sizeof(rsdb_mode_cfg), iovar_buf, sizeof(iovar_buf), NULL); + } + wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); + } + + if (apstamode == ISTAONLY_MODE) { + wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); + wl_ext_iovar_setint(dev, "apsta", 1); // keep 1 as we set in dhd_preinit_ioctls + // don't set WLC_SET_AP to 0, some parameters will be reset, such as bcn_timeout and roam_off + wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); + } else if (apstamode == IAPONLY_MODE) { + wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); +#ifdef ARP_OFFLOAD_SUPPORT + /* IF SoftAP is enabled, disable arpoe */ + dhd_arp_offload_set(dhd, 0); + dhd_arp_offload_enable(dhd, FALSE); +#endif /* ARP_OFFLOAD_SUPPORT */ + wl_ext_iovar_setint(dev, "mpc", 0); + wl_ext_iovar_setint(dev, "apsta", 0); + val = 1; + wl_ext_ioctl(dev, WLC_SET_AP, &val, sizeof(val), 1); +#ifdef PROP_TXSTATUS_VSDB +#if defined(BCMSDIO) + if (!FW_SUPPORTED(dhd, rsdb) && !disable_proptx) { + bool enabled; + dhd_wlfc_get_enable(dhd, &enabled); + if (!enabled) { + dhd_wlfc_init(dhd); + wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); + } + } +#endif +#endif /* PROP_TXSTATUS_VSDB */ + } + else if (apstamode == IAPSTA_MODE) { + wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); + wl_ext_iovar_setint(dev, "mpc", 0); + wl_ext_iovar_setint(dev, "apsta", 1); + wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); + apsta_params->netif_change = FALSE; + if (FW_SUPPORTED(dhd, rsdb)) { + bzero(&iface, sizeof(wl_interface_create_t)); + iface.ver = WL_INTERFACE_CREATE_VER; + iface.flags = WL_INTERFACE_CREATE_AP; + wl_ext_iovar_getbuf_bsscfg(dev, "interface_create", &iface, + sizeof(iface), iovar_buf, WLC_IOCTL_SMLEN, 1, NULL); + } else { + wl_ext_iovar_setbuf_bsscfg(dev, "ssid", &ssid, sizeof(ssid), + iovar_buf, WLC_IOCTL_SMLEN, 1, NULL); + } + wl_ext_wait_netif_change(apsta_params, TRUE); + } + else if (apstamode == IDUALAP_MODE) { + wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); + /* IF SoftAP is enabled, disable arpoe or wlan1 will ping fail */ +#ifdef ARP_OFFLOAD_SUPPORT + /* IF SoftAP is enabled, disable arpoe */ + dhd_arp_offload_set(dhd, 0); + dhd_arp_offload_enable(dhd, FALSE); +#endif /* ARP_OFFLOAD_SUPPORT */ + wl_ext_iovar_setint(dev, "mpc", 0); + wl_ext_iovar_setint(dev, "mbcn", 1); + wl_ext_iovar_setint(dev, "apsta", 0); + wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); + val = 1; + wl_ext_ioctl(dev, WLC_SET_AP, &val, sizeof(val), 1); + bzero(&iface, sizeof(wl_interface_create_t)); + iface.ver = WL_INTERFACE_CREATE_VER; + iface.flags = WL_INTERFACE_CREATE_AP; + apsta_params->netif_change = FALSE; + wl_ext_iovar_getbuf_bsscfg(dev, "interface_create", &iface, sizeof(iface), + iovar_buf, WLC_IOCTL_SMLEN, 1, NULL); + wl_ext_wait_netif_change(apsta_params, TRUE); + } + else if (apstamode == IMESHONLY_MODE) { + wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); + wl_ext_iovar_setint(dev, "mpc", 0); + wl_ext_iovar_setint(dev, "apsta", 1); // keep 1 as we set in dhd_preinit_ioctls + wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); + // don't set WLC_SET_AP to 0, some parameters will be reset, such as bcn_timeout and roam_off + } + else if (apstamode == IMESHSTA_MODE) { + wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); + wl_ext_iovar_setint(dev, "mpc", 0); + wl_ext_iovar_setint(dev, "mbcn", 1); + wl_ext_iovar_setint(dev, "apsta", 1); + wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); + bzero(&iface, sizeof(wl_interface_create_t)); + iface.ver = WL_INTERFACE_CREATE_VER; + iface.flags = WL_INTERFACE_CREATE_STA; + apsta_params->netif_change = FALSE; + wl_ext_iovar_getbuf_bsscfg(dev, "interface_create", &iface, sizeof(iface), + iovar_buf, WLC_IOCTL_SMLEN, 0, NULL); + wl_ext_wait_netif_change(apsta_params, TRUE); + } + else if (apstamode == IMESHAP_MODE) { + wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); + wl_ext_iovar_setint(dev, "mpc", 0); + wl_ext_iovar_setint(dev, "mbcn", 1); + wl_ext_iovar_setint(dev, "apsta", 1); // keep 1 as we set in dhd_preinit_ioctls + wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); + // don't set WLC_SET_AP to 0, some parameters will be reset, such as bcn_timeout and roam_off + bzero(&iface, sizeof(wl_interface_create_t)); + iface.ver = WL_INTERFACE_CREATE_VER; + iface.flags = WL_INTERFACE_CREATE_AP; + apsta_params->netif_change = FALSE; + wl_ext_iovar_getbuf_bsscfg(dev, "interface_create", &iface, sizeof(iface), + iovar_buf, WLC_IOCTL_SMLEN, 0, NULL); + wl_ext_wait_netif_change(apsta_params, TRUE); + } + else if (apstamode == IMESHAPSTA_MODE) { + wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); + wl_ext_iovar_setint(dev, "mpc", 0); + wl_ext_iovar_setint(dev, "mbcn", 1); + wl_ext_iovar_setint(dev, "apsta", 1); // keep 1 as we set in dhd_preinit_ioctls + wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); + // don't set WLC_SET_AP to 0, some parameters will be reset, such as bcn_timeout and roam_off + bzero(&iface, sizeof(wl_interface_create_t)); + iface.ver = WL_INTERFACE_CREATE_VER; + iface.flags = WL_INTERFACE_CREATE_AP; + apsta_params->netif_change = FALSE; + wl_ext_iovar_getbuf_bsscfg(dev, "interface_create", &iface, sizeof(iface), + iovar_buf, WLC_IOCTL_SMLEN, 0, NULL); + wl_ext_wait_netif_change(apsta_params, TRUE); + bzero(&iface, sizeof(wl_interface_create_t)); + iface.ver = WL_INTERFACE_CREATE_VER; + iface.flags = WL_INTERFACE_CREATE_STA; + apsta_params->netif_change = FALSE; + wl_ext_iovar_getbuf_bsscfg(dev, "interface_create", &iface, sizeof(iface), + iovar_buf, WLC_IOCTL_SMLEN, 0, NULL); + wl_ext_wait_netif_change(apsta_params, TRUE); + } + else if (apstamode == IMESHAPAP_MODE) { + wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); + wl_ext_iovar_setint(dev, "mpc", 0); + wl_ext_iovar_setint(dev, "mbcn", 1); + wl_ext_iovar_setint(dev, "apsta", 1); // keep 1 as we set in dhd_preinit_ioctls + wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); + // don't set WLC_SET_AP to 0, some parameters will be reset, such as bcn_timeout and roam_off + bzero(&iface, sizeof(wl_interface_create_t)); + iface.ver = WL_INTERFACE_CREATE_VER; + iface.flags = WL_INTERFACE_CREATE_AP; + apsta_params->netif_change = FALSE; + wl_ext_iovar_getbuf_bsscfg(dev, "interface_create", &iface, sizeof(iface), + iovar_buf, WLC_IOCTL_SMLEN, 0, NULL); + wl_ext_wait_netif_change(apsta_params, TRUE); + bzero(&iface, sizeof(wl_interface_create_t)); + iface.ver = WL_INTERFACE_CREATE_VER; + iface.flags = WL_INTERFACE_CREATE_AP; + apsta_params->netif_change = FALSE; + wl_ext_iovar_getbuf_bsscfg(dev, "interface_create", &iface, sizeof(iface), + iovar_buf, WLC_IOCTL_SMLEN, 0, NULL); + wl_ext_wait_netif_change(apsta_params, TRUE); + } + else if (apstamode == IGOSTA_MODE) { + wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); + wl_ext_iovar_setint(dev, "apsta", 1); + wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); + bzero(&ifreq, sizeof(wl_p2p_if_t)); + ifreq.type = htod32(WL_P2P_IF_GO); + apsta_params->netif_change = FALSE; + wl_ext_iovar_setbuf(dev, "p2p_ifadd", &ifreq, sizeof(ifreq), + iovar_buf, WLC_IOCTL_SMLEN, NULL); + wl_ext_wait_netif_change(apsta_params, TRUE); + } + + wl_ext_get_ioctl_ver(dev, &apsta_params->ioctl_ver); + apsta_params->init = TRUE; + + printf("%s: apstamode=%d\n", __FUNCTION__, apstamode); +} + +static int +wl_ext_isam_init(struct net_device *dev, char *command, int total_len) +{ + char *pch, *pick_tmp, *pick_tmp2, *param; + struct wl_apsta_params *apsta_params = &g_apsta_params; + struct dhd_pub *dhd; + int i; + + if (apsta_params->init) { + ANDROID_ERROR(("%s: don't init twice\n", __FUNCTION__)); + return -1; + } + + dhd = dhd_get_pub(dev); + + ANDROID_TRACE(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); + + pick_tmp = command; + param = bcmstrtok(&pick_tmp, " ", 0); // skip iapsta_init + param = bcmstrtok(&pick_tmp, " ", 0); + while (param != NULL) { + if (!strcmp(param, "mode")) { + pch = NULL; + pick_tmp2 = bcmstrtok(&pick_tmp, " ", 0); + if (pick_tmp2) { + if (!strcmp(pick_tmp2, "sta")) { + apsta_params->apstamode = ISTAONLY_MODE; + } else if (!strcmp(pick_tmp2, "ap")) { + apsta_params->apstamode = IAPONLY_MODE; + } else if (!strcmp(pick_tmp2, "sta-ap")) { + apsta_params->apstamode = IAPSTA_MODE; + } else if (!strcmp(pick_tmp2, "ap-ap")) { + apsta_params->apstamode = IDUALAP_MODE; + } else if (!strcmp(pick_tmp2, "mesh")) { + apsta_params->apstamode = IMESHONLY_MODE; + } else if (!strcmp(pick_tmp2, "mesh-sta") || + !strcmp(pick_tmp2, "sta-mesh")) { + apsta_params->apstamode = IMESHSTA_MODE; + } else if (!strcmp(pick_tmp2, "mesh-ap") || + !strcmp(pick_tmp2, "ap-mesh")) { + apsta_params->apstamode = IMESHAP_MODE; + } else if (!strcmp(pick_tmp2, "mesh-ap-sta") || + !strcmp(pick_tmp2, "sta-ap-mesh") || + !strcmp(pick_tmp2, "sta-mesh-ap")) { + apsta_params->apstamode = IMESHAPSTA_MODE; + } else if (!strcmp(pick_tmp2, "mesh-ap-ap") || + !strcmp(pick_tmp2, "ap-ap-mesh")) { + apsta_params->apstamode = IMESHAPAP_MODE; + } else if (!strcmp(pick_tmp2, "apsta")) { + apsta_params->apstamode = IAPSTA_MODE; + apsta_params->if_info[IF_PIF].ifmode = ISTA_MODE; + apsta_params->if_info[IF_VIF].ifmode = IAP_MODE; + } else if (!strcmp(pick_tmp2, "dualap")) { + apsta_params->apstamode = IDUALAP_MODE; + apsta_params->if_info[IF_PIF].ifmode = IAP_MODE; + apsta_params->if_info[IF_VIF].ifmode = IAP_MODE; + } else if (!strcmp(pick_tmp2, "gosta")) { + if (!FW_SUPPORTED(dhd, p2p)) { + return -1; + } + apsta_params->apstamode = IGOSTA_MODE; + apsta_params->if_info[IF_PIF].ifmode = ISTA_MODE; + apsta_params->if_info[IF_VIF].ifmode = IAP_MODE; + } else { + ANDROID_ERROR(("%s: mode [sta|ap|sta-ap|ap-ap]\n", __FUNCTION__)); + return -1; + } + pch = bcmstrtok(&pick_tmp2, " -", 0); + for (i=0; iif_info[i].ifmode = ISTA_MODE; + else if (!strcmp(pch, "ap")) + apsta_params->if_info[i].ifmode = IAP_MODE; + else if (!strcmp(pch, "mesh")) { + if (dhd->conf->fw_type != FW_TYPE_MESH) { + ANDROID_ERROR(("%s: wrong fw type\n", __FUNCTION__)); + return -1; + } + apsta_params->if_info[i].ifmode = IMESH_MODE; + } + pch = bcmstrtok(&pick_tmp2, " -", 0); + } + } + } + else if (!strcmp(param, "rsdb")) { + pch = bcmstrtok(&pick_tmp, " ", 0); + if (pch) { + if (!strcmp(pch, "y")) { + apsta_params->rsdb = TRUE; + } else if (!strcmp(pch, "n")) { + apsta_params->rsdb = FALSE; + } else { + ANDROID_ERROR(("%s: rsdb [y|n]\n", __FUNCTION__)); + return -1; + } + } + } else if (!strcmp(param, "vsdb")) { + pch = bcmstrtok(&pick_tmp, " ", 0); + if (pch) { + if (!strcmp(pch, "y")) { + apsta_params->vsdb = TRUE; + } else if (!strcmp(pch, "n")) { + apsta_params->vsdb = FALSE; + } else { + ANDROID_ERROR(("%s: vsdb [y|n]\n", __FUNCTION__)); + return -1; + } + } + } else if (!strcmp(param, "csa")) { + pch = bcmstrtok(&pick_tmp, " ", 0); + if (pch) { + apsta_params->csa = (int)simple_strtol(pch, NULL, 0); + } + } else if (!strcmp(param, "ifname")) { + pch = NULL; + pick_tmp2 = bcmstrtok(&pick_tmp, " ", 0); + if (pick_tmp2) + pch = bcmstrtok(&pick_tmp2, " -", 0); + for (i=0; iif_info[i].ifname, pch); + pch = bcmstrtok(&pick_tmp2, " -", 0); + } + } else if (!strcmp(param, "vifname")) { + pch = bcmstrtok(&pick_tmp, " ", 0); + if (pch) + strcpy(apsta_params->if_info[IF_VIF].ifname, pch); + else { + ANDROID_ERROR(("%s: vifname [wlan1]\n", __FUNCTION__)); + return -1; + } + } + param = bcmstrtok(&pick_tmp, " ", 0); + } + + if (apsta_params->apstamode == 0) { + ANDROID_ERROR(("%s: mode [sta|ap|sta-ap|ap-ap]\n", __FUNCTION__)); + return -1; + } + + wl_ext_iapsta_preinit(dev, apsta_params); + + return 0; +} + +static int +wl_ext_parse_config(struct wl_if_info *cur_if, char *command, char **pick_next) +{ + char *pch, *pick_tmp; + char name[20], data[100]; + int i, j, len; + char *ifname_head = NULL; + + typedef struct config_map_t { + char name[20]; + char *head; + char *tail; + } config_map_t; + + config_map_t config_map [] = { + {" ifname ", NULL, NULL}, + {" ssid ", NULL, NULL}, + {" bssid ", NULL, NULL}, + {" bgnmode ", NULL, NULL}, + {" hidden ", NULL, NULL}, + {" maxassoc ", NULL, NULL}, + {" chan ", NULL, NULL}, + {" amode ", NULL, NULL}, + {" emode ", NULL, NULL}, + {" key ", NULL, NULL}, + }; + config_map_t *row, *row_prev; + + pick_tmp = command; + + // reset head and tail + for (i = 0; i < sizeof(config_map)/sizeof(config_map[0]); i++) { + row = &config_map[i]; + row->head = NULL; + row->tail = pick_tmp + strlen(pick_tmp); + } + + // pick head + for (i = 0; i < sizeof(config_map)/sizeof(config_map[0]); i++) { + row = &config_map[i]; + pch = strstr(pick_tmp, row->name); + if (pch) { + row->head = pch; + } + } + + // sort by head + for (i = 0; i < sizeof(config_map)/sizeof(config_map[0]) - 1; i++) { + row_prev = &config_map[i]; + for (j = i+1; j < sizeof(config_map)/sizeof(config_map[0]); j++) { + row = &config_map[j]; + if (row->head < row_prev->head) { + strcpy(name, row_prev->name); + strcpy(row_prev->name, row->name); + strcpy(row->name, name); + pch = row_prev->head; + row_prev->head = row->head; + row->head = pch; + } + } + } + + // pick tail + for (i = 0; i < sizeof(config_map)/sizeof(config_map[0]) - 1; i++) { + row_prev = &config_map[i]; + row = &config_map[i+1]; + if (row_prev->head) { + row_prev->tail = row->head; + } + } + + // remove name from head + for (i = 0; i < sizeof(config_map)/sizeof(config_map[0]); i++) { + row = &config_map[i]; + if (row->head) { + if (!strcmp(row->name, " ifname ")) { + ifname_head = row->head + 1; + break; + } + row->head += strlen(row->name); + } + } + + for (i = 0; i < sizeof(config_map)/sizeof(config_map[0]); i++) { + row = &config_map[i]; + if (row->head) { + memset(data, 0, sizeof(data)); + if (row->tail && row->tail > row->head) { + strncpy(data, row->head, row->tail-row->head); + } else { + strcpy(data, row->head); + } + pick_tmp = data; + + if (!strcmp(row->name, " ifname ")) { + break; + } else if (!strcmp(row->name, " ssid ")) { + len = strlen(pick_tmp); + memset(cur_if->ssid, 0, sizeof(cur_if->ssid)); + if (pick_tmp[0] == '"' && pick_tmp[len-1] == '"') + strncpy(cur_if->ssid, &pick_tmp[1], len-2); + else + strcpy(cur_if->ssid, pick_tmp); + } else if (!strcmp(row->name, " bssid ")) { + pch = bcmstrtok(&pick_tmp, ": ", 0); + for (j=0; j<6 && pch; j++) { + ((u8 *)&cur_if->bssid)[j] = (int)simple_strtol(pch, NULL, 16); + pch = bcmstrtok(&pick_tmp, ": ", 0); + } + } else if (!strcmp(row->name, " bgnmode ")) { + if (!strcmp(pick_tmp, "b")) + cur_if->bgnmode = IEEE80211B; + else if (!strcmp(pick_tmp, "g")) + cur_if->bgnmode = IEEE80211G; + else if (!strcmp(pick_tmp, "bg")) + cur_if->bgnmode = IEEE80211BG; + else if (!strcmp(pick_tmp, "bgn")) + cur_if->bgnmode = IEEE80211BGN; + else if (!strcmp(pick_tmp, "bgnac")) + cur_if->bgnmode = IEEE80211BGNAC; + else { + ANDROID_ERROR(("%s: bgnmode [b|g|bg|bgn|bgnac]\n", __FUNCTION__)); + return -1; + } + } else if (!strcmp(row->name, " hidden ")) { + if (!strcmp(pick_tmp, "n")) + cur_if->hidden = 0; + else if (!strcmp(pick_tmp, "y")) + cur_if->hidden = 1; + else { + ANDROID_ERROR(("%s: hidden [y|n]\n", __FUNCTION__)); + return -1; + } + } else if (!strcmp(row->name, " maxassoc ")) { + cur_if->maxassoc = (int)simple_strtol(pick_tmp, NULL, 10); + } else if (!strcmp(row->name, " chan ")) { + cur_if->channel = (int)simple_strtol(pick_tmp, NULL, 10); + } else if (!strcmp(row->name, " amode ")) { + if (!strcmp(pick_tmp, "open")) + cur_if->amode = AUTH_OPEN; + else if (!strcmp(pick_tmp, "shared")) + cur_if->amode = AUTH_SHARED; + else if (!strcmp(pick_tmp, "wpapsk")) + cur_if->amode = AUTH_WPAPSK; + else if (!strcmp(pick_tmp, "wpa2psk")) + cur_if->amode = AUTH_WPA2PSK; + else if (!strcmp(pick_tmp, "wpawpa2psk")) + cur_if->amode = AUTH_WPAWPA2PSK; + else if (!strcmp(pick_tmp, "sae")) + cur_if->amode = AUTH_SAE; + else { + ANDROID_ERROR(("%s: amode [open|shared|wpapsk|wpa2psk|wpawpa2psk]\n", + __FUNCTION__)); + return -1; + } + } else if (!strcmp(row->name, " emode ")) { + if (!strcmp(pick_tmp, "none")) + cur_if->emode = ENC_NONE; + else if (!strcmp(pick_tmp, "wep")) + cur_if->emode = ENC_WEP; + else if (!strcmp(pick_tmp, "tkip")) + cur_if->emode = ENC_TKIP; + else if (!strcmp(pick_tmp, "aes")) + cur_if->emode = ENC_AES; + else if (!strcmp(pick_tmp, "tkipaes")) + cur_if->emode = ENC_TKIPAES; + else { + ANDROID_ERROR(("%s: emode [none|wep|tkip|aes|tkipaes]\n", + __FUNCTION__)); + return -1; + } + } else if (!strcmp(row->name, " key ")) { + len = strlen(pick_tmp); + memset(cur_if->key, 0, sizeof(cur_if->key)); + if (pick_tmp[0] == '"' && pick_tmp[len-1] == '"') + strncpy(cur_if->key, &pick_tmp[1], len-2); + else + strcpy(cur_if->key, pick_tmp); + } + } + } + + *pick_next = ifname_head; + return 0; +} + +static int +wl_ext_iapsta_config(struct net_device *dev, char *command, int total_len) +{ + int ret=0, i; + char *pch, *pch2, *pick_tmp, *pick_next=NULL, *param; + struct wl_apsta_params *apsta_params = &g_apsta_params; + char ifname[IFNAMSIZ+1]; + struct wl_if_info *cur_if = NULL; + + if (!apsta_params->init) { + ANDROID_ERROR(("%s: please init first\n", __FUNCTION__)); + return -1; + } + + ANDROID_TRACE(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); + + pick_tmp = command; + param = bcmstrtok(&pick_tmp, " ", 0); // skip iapsta_config + + while (pick_tmp != NULL) { + memset(ifname, 0, IFNAMSIZ+1); + if (!strncmp(pick_tmp, "ifname ", strlen("ifname "))) { + pch = pick_tmp + strlen("ifname "); + pch2 = strchr(pch, ' '); + if (pch && pch2) { + strncpy(ifname, pch, pch2-pch); + } else { + ANDROID_ERROR(("%s: ifname [wlanX]\n", __FUNCTION__)); + return -1; + } + for (i=0; iif_info[i].dev && + !strcmp(apsta_params->if_info[i].dev->name, ifname)) { + cur_if = &apsta_params->if_info[i]; + break; + } + } + if (!cur_if) { + ANDROID_ERROR(("%s: wrong ifname=%s in apstamode=%d\n", __FUNCTION__, + ifname, apsta_params->apstamode)); + return -1; + } + ret = wl_ext_parse_config(cur_if, pick_tmp, &pick_next); + if (ret) + return -1; + pick_tmp = pick_next; + } else { + ANDROID_ERROR(("%s: first arg must be ifname\n", __FUNCTION__)); + return -1; + } + + } + + return 0; +} + +static int +wl_ext_iapsta_disable(struct net_device *dev, char *command, int total_len) +{ + char *pch, *pick_tmp, *param; + s8 iovar_buf[WLC_IOCTL_SMLEN]; + wlc_ssid_t ssid = { 0, {0} }; + scb_val_t scbval; + struct { + s32 tmp; + s32 cfg; + s32 val; + } bss_setbuf; + struct wl_apsta_params *apsta_params = &g_apsta_params; + apstamode_t apstamode = apsta_params->apstamode; + char ifname[IFNAMSIZ+1]; + struct wl_if_info *cur_if = NULL; + struct dhd_pub *dhd; + int i; + + if (!apsta_params->init) { + ANDROID_ERROR(("%s: please init first\n", __FUNCTION__)); + return -1; + } + + ANDROID_TRACE(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); + dhd = dhd_get_pub(dev); + + pick_tmp = command; + param = bcmstrtok(&pick_tmp, " ", 0); // skip iapsta_disable + param = bcmstrtok(&pick_tmp, " ", 0); + while (param != NULL) { + if (!strcmp(param, "ifname")) { + pch = bcmstrtok(&pick_tmp, " ", 0); + if (pch) + strcpy(ifname, pch); + else { + ANDROID_ERROR(("%s: ifname [wlanX]\n", __FUNCTION__)); + return -1; + } + } + param = bcmstrtok(&pick_tmp, " ", 0); + } + + for (i=0; iif_info[i].dev && + !strcmp(apsta_params->if_info[i].dev->name, ifname)) { + cur_if = &apsta_params->if_info[i]; + break; + } + } + if (!cur_if) { + ANDROID_ERROR(("%s: wrong ifname=%s or dev not ready\n", __FUNCTION__, ifname)); + return -1; + } + + printf("%s: Disabling %s\n", __FUNCTION__, ifname); + + if (cur_if->ifmode == ISTA_MODE) { + wl_ext_ioctl(cur_if->dev, WLC_DISASSOC, NULL, 0, 1); + } else if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) { + // deauthenticate all STA first + memcpy(scbval.ea.octet, ðer_bcast, ETHER_ADDR_LEN); + wl_ext_ioctl(cur_if->dev, WLC_SCB_DEAUTHENTICATE, &scbval.ea, ETHER_ADDR_LEN, 1); + } + + if (apstamode == IAPONLY_MODE || apstamode == IMESHONLY_MODE) { + wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1); + wl_ext_ioctl(dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1); // reset ssid + wl_ext_iovar_setint(dev, "mpc", 1); + } else if ((apstamode==IAPSTA_MODE || apstamode==IGOSTA_MODE) && + cur_if->ifmode == IAP_MODE) { + bss_setbuf.tmp = 0xffffffff; + bss_setbuf.cfg = 0; // must be 0, or wlan1 can not be down + bss_setbuf.val = htod32(0); + wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf, sizeof(bss_setbuf), + iovar_buf, WLC_IOCTL_SMLEN, NULL); + wl_ext_iovar_setint(dev, "mpc", 1); +#ifdef ARP_OFFLOAD_SUPPORT + /* IF SoftAP is disabled, enable arpoe back for STA mode. */ + dhd_arp_offload_set(dhd, dhd_arp_mode); + dhd_arp_offload_enable(dhd, TRUE); +#endif /* ARP_OFFLOAD_SUPPORT */ +#ifdef PROP_TXSTATUS_VSDB +#if defined(BCMSDIO) + if (dhd->conf->disable_proptx!=0) { + bool enabled; + dhd_wlfc_get_enable(dhd, &enabled); + if (enabled) { + dhd_wlfc_deinit(dhd); + } + } +#endif +#endif /* PROP_TXSTATUS_VSDB */ + } else if (apstamode == IDUALAP_MODE) { + bss_setbuf.tmp = 0xffffffff; + bss_setbuf.cfg = 0; // must be 0, or wlan1 can not be down + bss_setbuf.val = htod32(0); + wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf, sizeof(bss_setbuf), + iovar_buf, WLC_IOCTL_SMLEN, NULL); + } else if (apstamode == IMESHSTA_MODE || apstamode == IMESHAP_MODE || + apstamode == IMESHAPSTA_MODE || apstamode == IMESHAPAP_MODE) { + bss_setbuf.tmp = 0xffffffff; + bss_setbuf.cfg = 0; // must be 0, or wlan1 can not be down + bss_setbuf.val = htod32(0); + wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf, sizeof(bss_setbuf), + iovar_buf, WLC_IOCTL_SMLEN, NULL); + } + + cur_if->ifstate = IF_STATE_DISALBE; + + printf("%s: disabled %s SSID: \"%s\"\n", __FUNCTION__, ifname, cur_if->ssid); + + return 0; +} + +static uint16 +wl_ext_get_chan(struct net_device *dev) +{ + struct wl_apsta_params *apsta_params = &g_apsta_params; + int ret = 0; + uint16 chan = 0, ctl_chan; + struct ether_addr bssid; + u32 chanspec = 0; + + ret = wldev_ioctl(dev, WLC_GET_BSSID, &bssid, sizeof(bssid), 0); + if (ret != BCME_NOTASSOCIATED && memcmp(ðer_null, &bssid, ETHER_ADDR_LEN)) { + if (wldev_iovar_getint(dev, "chanspec", (s32 *)&chanspec) == BCME_OK) { + chanspec = wl_ext_chspec_driver_to_host(apsta_params->ioctl_ver, chanspec); + ctl_chan = wf_chspec_ctlchan(chanspec); + chan = (u16)(ctl_chan & 0x00FF); + ANDROID_INFO(("%s: cur_chan=%d(0x%x)\n", __FUNCTION__, + chan, chanspec)); + return chan; + } + } + + return 0; +} + +static uint16 +wl_ext_get_vsdb_chan(struct net_device *dev, + struct wl_if_info *cur_if, struct wl_if_info *another_if) +{ + struct wl_apsta_params *apsta_params = &g_apsta_params; + uint16 another_chan = 0, cur_chan = cur_if->channel; + struct dhd_pub *dhd; + + dhd = dhd_get_pub(dev); + + another_chan = wl_ext_get_chan(another_if->dev); + if (another_chan) { + ANDROID_INFO(("%s: cur_chan=%d, another_chan=%d\n", + __FUNCTION__, cur_chan, another_chan)); + if ((cur_chan <= CH_MAX_2G_CHANNEL && another_chan > CH_MAX_2G_CHANNEL) || + (cur_chan > CH_MAX_2G_CHANNEL && another_chan <= CH_MAX_2G_CHANNEL)) { + // different band + if (!FW_SUPPORTED(dhd, rsdb) || !apsta_params->rsdb) + return another_chan; + } else { + // same band + if (another_chan != cur_chan) + return another_chan; + } + } + + return 0; +} + +static int +wl_ext_triger_csa(struct wl_if_info *cur_if) +{ + struct wl_apsta_params *apsta_params = &g_apsta_params; + s8 iovar_buf[WLC_IOCTL_SMLEN]; + + if (apsta_params->csa & CSA_DRV_BIT && + (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE)) { + wl_chan_switch_t csa_arg; + memset(&csa_arg, 0, sizeof(csa_arg)); + csa_arg.mode = 1; + csa_arg.count = 3; + csa_arg.chspec = wl_ext_get_chanspec(cur_if->dev, cur_if->channel); + printf("%s: Trigger CSA to channel %d\n", __FUNCTION__, cur_if->channel); + wl_ext_iovar_setbuf(cur_if->dev, "csa", &csa_arg, sizeof(csa_arg), + iovar_buf, sizeof(iovar_buf), NULL); + OSL_SLEEP(500); + } + + return 0; +} + +static void +wl_ext_move_channel(struct wl_if_info *cur_if, uint16 chan) +{ + struct wl_apsta_params *apsta_params = &g_apsta_params; + + if (chan) { + char cmd[32] = ""; + cur_if->channel = chan; + if (apsta_params->csa == 0) { + printf("%s: %s deauthenticate all STA and move to channel %d\n", + __FUNCTION__, cur_if->ifname, chan); + snprintf(cmd, 32, "%s %s", "isam_disable ifname", cur_if->ifname); + wl_ext_iapsta_disable(cur_if->dev, cmd, strlen(cmd)); + + snprintf(cmd, 32, "%s %s", "isam_enable ifname", cur_if->ifname); + wl_ext_iapsta_enable(cur_if->dev, cmd, strlen(cmd)); + } else { + wl_ext_triger_csa(cur_if); + } + } +} + +static uint16 +wl_ext_move_cur_channel(struct net_device *dev, + struct wl_if_info *cur_if) +{ + struct wl_apsta_params *apsta_params = &g_apsta_params; + struct wl_if_info *another_if, *final_if = NULL; + uint16 new_chan = 0; + wl_prio_t cur_prio; + int i; + + if (apsta_params->vsdb) { + return cur_if->channel; + } + + // find the max prio + cur_prio = cur_if->prio; + for (i=0; iif_info[i]; + if (another_if->ifstate >= IF_STATE_INIT && cur_if != another_if && + another_if->prio > cur_prio) { + new_chan = wl_ext_get_vsdb_chan(dev, cur_if, another_if); + if (new_chan) { + final_if = another_if; + cur_prio = another_if->prio; + } + } + } + + if (new_chan) { + printf("%s: %s channel=%d => %s channel=%d\n", __FUNCTION__, + cur_if->ifname, cur_if->channel, final_if->ifname, new_chan); + cur_if->channel = new_chan; + } + + return cur_if->channel; +} + +static void +wl_ext_move_other_channel(struct net_device *dev, + struct wl_if_info *cur_if) +{ + struct wl_apsta_params *apsta_params = &g_apsta_params; + struct wl_if_info *another_if, *final_if=NULL; + uint16 new_chan = 0; + wl_prio_t prio = 0, cur_prio; + int i; + + if (apsta_params->vsdb) { + return; + } + + // find the max prio, but lower than cur_if + cur_prio = cur_if->prio; + for (i=0; iif_info[i]; + if (another_if->ifstate >= IF_STATE_INIT && cur_if != another_if && + another_if->prio >= prio && another_if->prio < cur_prio) { + new_chan = wl_ext_get_vsdb_chan(dev, cur_if, another_if); + if (new_chan) { + final_if = another_if; + prio = another_if->prio; + } + } + } + + if (new_chan) { + printf("%s: %s channel=%d => %s channel=%d\n", __FUNCTION__, + final_if->ifname, final_if->channel, cur_if->ifname, cur_if->channel); + wl_ext_move_channel(final_if, cur_if->channel); + } + +} + +static int +wl_ext_isam_dump_status(struct net_device *dev) +{ + struct wl_apsta_params *apsta_params = &g_apsta_params; + int i; + bool now_if; + struct wl_if_info *tmp_if; + uint16 chan = 0; + wlc_ssid_t ssid = { 0, {0} }; + char amode[16], emode[16]; + + if (apsta_params->init == FALSE) { + return 0; + } + + printf("****************************\n"); + printf("%s: apstamode=%d\n", __FUNCTION__, apsta_params->apstamode); + for (i=0; iif_info[i]; + if (dev == tmp_if->dev) + now_if = TRUE; + if (tmp_if->dev) { + chan = wl_ext_get_chan(tmp_if->dev); + if (chan) { + wl_ext_ioctl(tmp_if->dev, WLC_GET_SSID, &ssid, sizeof(ssid), 0); + wl_ext_get_amode(tmp_if, amode); + wl_ext_get_emode(tmp_if, emode); + } + if (chan) { + printf("%s[%c-%c%s]: chan %3d, amode %s, emode %s, SSID \"%s\"\n", + tmp_if->ifname, tmp_if->prefix, chan?'E':'D', + now_if?"*":" ", chan, amode, emode, ssid.SSID); + } else { + printf("%s[%c-%c%s]:\n", + tmp_if->ifname, tmp_if->prefix, chan?'E':'D', + now_if?"*":" "); + } + } + } + printf("****************************\n"); + + return 0; +} + +static int +wl_ext_enable_iface(struct net_device *dev, char *ifname) +{ + int i; + s8 iovar_buf[WLC_IOCTL_SMLEN]; + wlc_ssid_t ssid = { 0, {0} }; + chanspec_t fw_chspec; + struct wl_join_params join_params; + size_t join_params_size; + struct { + s32 cfg; + s32 val; + } bss_setbuf; + struct wl_apsta_params *apsta_params = &g_apsta_params; + apstamode_t apstamode = apsta_params->apstamode; + struct wl_if_info *cur_if = NULL; + struct dhd_pub *dhd; + uint16 cur_chan; + + dhd = dhd_get_pub(dev); + + for (i=0; iif_info[i].dev && + !strcmp(apsta_params->if_info[i].dev->name, ifname)) { + cur_if = &apsta_params->if_info[i]; + break; + } + } + if (!cur_if) { + ANDROID_ERROR(("%s: wrong ifname=%s or dev not ready\n", __FUNCTION__, ifname)); + return -1; + } + + printf("%s: Enabling %s\n", __FUNCTION__, ifname); + + wl_ext_isam_dump_status(cur_if->dev); + + wl_ext_move_cur_channel(dev, cur_if); + + cur_chan = wl_ext_get_chan(cur_if->dev); + if (cur_chan) { + ANDROID_INFO(("%s: Associated!\n", __FUNCTION__)); + if (cur_chan != cur_if->channel) + wl_ext_triger_csa(cur_if); + return 0; + } + + wl_ext_move_other_channel(dev, cur_if); + + if (cur_if->bssidx > 0) { + wl_ext_iovar_setbuf(cur_if->dev, "cur_etheraddr", (u8 *)cur_if->dev->dev_addr, + ETHER_ADDR_LEN, iovar_buf, WLC_IOCTL_SMLEN, NULL); + } + + // set ssid for AP + ssid.SSID_len = strlen(cur_if->ssid); + memcpy(ssid.SSID, cur_if->ssid, ssid.SSID_len); + if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) { + wl_ext_iovar_setint(dev, "mpc", 0); + if (apstamode == IAPONLY_MODE) { + wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); + } else if (apstamode==IAPSTA_MODE || apstamode==IGOSTA_MODE) { + wl_ext_iovar_setbuf_bsscfg(cur_if->dev, "ssid", &ssid, sizeof(ssid), + iovar_buf, WLC_IOCTL_SMLEN, cur_if->bssidx, NULL); + } + } + + if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) { + wl_ext_set_bgnmode(cur_if); + if (!cur_if->channel) { +#ifdef WL_CFG80211 + char *pick_tmp, *param; + char cmd[128]; + uint16 cur_chan; + cur_chan = 1; + snprintf(cmd, 128, "get_best_channels"); + wl_cfg80211_get_best_channels(dev, cmd, strlen(cmd)); + pick_tmp = cmd; + param = bcmstrtok(&pick_tmp, " ", 0); + while (param != NULL) { + if (!strnicmp(param, "2g=", strlen("2g="))) { + cur_chan = (int)simple_strtol(param+strlen("2g="), NULL, 10); + } else if (!strnicmp(param, "5g=", strlen("5g="))) { + cur_chan = (int)simple_strtol(param+strlen("5g="), NULL, 10); + } + param = bcmstrtok(&pick_tmp, " ", 0); + } + cur_if->channel = cur_chan; +#else + cur_if->channel = 1; +#endif + } + wl_ext_set_chanspec(cur_if->dev, cur_if->channel, &fw_chspec); + } + + wl_ext_set_amode(cur_if); + wl_ext_set_emode(cur_if, apsta_params); + + if (cur_if->ifmode == IAP_MODE) { + if (cur_if->maxassoc >= 0) + wl_ext_iovar_setint(dev, "maxassoc", cur_if->maxassoc); + // terence: fix me, hidden does not work in dualAP mode + if (cur_if->hidden > 0) { + wl_ext_ioctl(cur_if->dev, WLC_SET_CLOSED, &cur_if->hidden, + sizeof(cur_if->hidden), 1); + printf("%s: Broadcast SSID: %s\n", __FUNCTION__, + cur_if->hidden ? "OFF":"ON"); + } + } + + if (apstamode == ISTAONLY_MODE) { + wl_ext_connect(cur_if); + } else if (apstamode == IAPONLY_MODE) { + wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1); + wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); + } else if (apstamode == IAPSTA_MODE || apstamode == IGOSTA_MODE) { + if (cur_if->ifmode == ISTA_MODE) { + wl_ext_connect(cur_if); + } else { + if (FW_SUPPORTED(dhd, rsdb)) { + wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1); + } else { + bss_setbuf.cfg = htod32(cur_if->bssidx); + bss_setbuf.val = htod32(1); + wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf, + sizeof(bss_setbuf), iovar_buf, WLC_IOCTL_SMLEN, NULL); + } +#ifdef ARP_OFFLOAD_SUPPORT + /* IF SoftAP is enabled, disable arpoe */ + dhd_arp_offload_set(dhd, 0); + dhd_arp_offload_enable(dhd, FALSE); +#endif /* ARP_OFFLOAD_SUPPORT */ +#ifdef PROP_TXSTATUS_VSDB +#if defined(BCMSDIO) + if (!FW_SUPPORTED(dhd, rsdb) && !disable_proptx) { + bool enabled; + dhd_wlfc_get_enable(dhd, &enabled); + if (!enabled) { + dhd_wlfc_init(dhd); + wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); + } + } +#endif +#endif /* PROP_TXSTATUS_VSDB */ + } + } + else if (apstamode == IDUALAP_MODE) { + wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1); + } else if (apstamode == IMESHONLY_MODE || + apstamode == IMESHSTA_MODE || apstamode == IMESHAP_MODE || + apstamode == IMESHAPSTA_MODE || apstamode == IMESHAPAP_MODE) { + if (cur_if->ifmode == ISTA_MODE) { + wl_ext_connect(cur_if); + } else if (cur_if->ifmode == IAP_MODE) { + wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1); + } else if (cur_if->ifmode == IMESH_MODE) { + // need to up before setting ssid + wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1); + memset(&join_params, 0, sizeof(join_params)); + join_params.ssid.SSID_len = strlen(cur_if->ssid); + memcpy((void *)join_params.ssid.SSID, cur_if->ssid, ssid.SSID_len); + join_params.params.chanspec_list[0] = fw_chspec; + join_params.params.chanspec_num = 1; + join_params_size = sizeof(join_params); + wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &join_params, join_params_size, 1); + } else { + printf("%s: wrong ifmode %d\n", __FUNCTION__, cur_if->ifmode); + } + } + + OSL_SLEEP(1000); + printf("%s: enabled %s SSID: \"%s\"\n", __FUNCTION__, ifname, cur_if->ssid); + + cur_if->ifstate = IF_STATE_ENABLE; + + return 0; +} + +static int +wl_ext_iapsta_enable(struct net_device *dev, char *command, int total_len) +{ + int ret = 0; + char *pch, *pick_tmp, *param; + struct wl_apsta_params *apsta_params = &g_apsta_params; + char ifname[IFNAMSIZ+1]; + + if (!apsta_params->init) { + ANDROID_ERROR(("%s: please init first\n", __FUNCTION__)); + return -1; + } + + ANDROID_TRACE(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); + + pick_tmp = command; + param = bcmstrtok(&pick_tmp, " ", 0); // skip iapsta_enable + param = bcmstrtok(&pick_tmp, " ", 0); + while (param != NULL) { + if (!strcmp(param, "ifname")) { + pch = bcmstrtok(&pick_tmp, " ", 0); + if (pch) { + strcpy(ifname, pch); + ret = wl_ext_enable_iface(dev, ifname); + if (ret) + return ret; + } else { + ANDROID_ERROR(("%s: ifname [wlanX]\n", __FUNCTION__)); + return -1; + } + } + param = bcmstrtok(&pick_tmp, " ", 0); + } + + return ret; +} + +int +wl_ext_iapsta_alive_preinit(struct net_device *dev) +{ + struct wl_apsta_params *apsta_params = &g_apsta_params; + struct wl_if_info *cur_if; + int i; + + if (apsta_params->init == TRUE) { + ANDROID_ERROR(("%s: don't init twice\n", __FUNCTION__)); + return -1; + } + + ANDROID_TRACE(("%s: Enter\n", __FUNCTION__)); + + for (i=0; iif_info[i]; + if (i == 1 && !strlen(cur_if->ifname)) + strcpy(cur_if->ifname, "wlan1"); + if (i == 2 && !strlen(cur_if->ifname)) + strcpy(cur_if->ifname, "wlan2"); + if (cur_if->ifmode == ISTA_MODE) { + cur_if->channel = 0; + cur_if->maxassoc = -1; + cur_if->ifstate = IF_STATE_INIT; + cur_if->prio = PRIO_STA; + cur_if->prefix = 'S'; + snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_sta"); + } else if (cur_if->ifmode == IAP_MODE) { + cur_if->channel = 1; + cur_if->maxassoc = -1; + cur_if->ifstate = IF_STATE_INIT; + cur_if->prio = PRIO_AP; + cur_if->prefix = 'A'; + snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_ap"); + } else if (cur_if->ifmode == IMESH_MODE) { + cur_if->channel = 1; + cur_if->maxassoc = -1; + cur_if->ifstate = IF_STATE_INIT; + cur_if->prio = PRIO_MESH; + cur_if->prefix = 'M'; + snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_mesh"); + } + } + + apsta_params->init = TRUE; + + return 0; +} + +int +wl_ext_iapsta_alive_postinit(struct net_device *dev) +{ + s32 apsta = 0; + struct wl_apsta_params *apsta_params = &g_apsta_params; + + wl_ext_iovar_getint(dev, "apsta", &apsta); + if (apsta == 1) { + apsta_params->apstamode = ISTAONLY_MODE; + apsta_params->if_info[IF_PIF].ifmode = ISTA_MODE; + op_mode = DHD_FLAG_STA_MODE; + } else { + apsta_params->apstamode = IAPONLY_MODE; + apsta_params->if_info[IF_PIF].ifmode = IAP_MODE; + op_mode = DHD_FLAG_HOSTAP_MODE; + } + // fix me: how to check it's IAPSTA_MODE or IDUALAP_MODE? + + wl_ext_get_ioctl_ver(dev, &apsta_params->ioctl_ver); + printf("%s: apstamode=%d\n", __FUNCTION__, apsta_params->apstamode); + + return op_mode; +} + +#if defined(WL_WIRELESS_EXT) +static bool +wl_ext_conn_status_str(uint32 event_type, + uint32 status, uint32 reason, char* stringBuf, uint buflen) +{ + int i; + + typedef struct conn_fail_event_map_t { + uint32 inEvent; /* input: event type to match */ + uint32 inStatus; /* input: event status code to match */ + uint32 inReason; /* input: event reason code to match */ + } conn_fail_event_map_t; + + /* Map of WLC_E events to connection failure strings */ +# define WL_IW_DONT_CARE 9999 + const conn_fail_event_map_t event_map [] = { + /* inEvent inStatus inReason */ + {WLC_E_LINK, WL_IW_DONT_CARE, WL_IW_DONT_CARE}, + {WLC_E_DEAUTH, WL_IW_DONT_CARE, WL_IW_DONT_CARE}, + {WLC_E_DEAUTH_IND, WL_IW_DONT_CARE, WL_IW_DONT_CARE}, + {WLC_E_DISASSOC, WL_IW_DONT_CARE, WL_IW_DONT_CARE}, + {WLC_E_DISASSOC_IND, WL_IW_DONT_CARE, WL_IW_DONT_CARE}, + {WLC_E_OVERLAY_REQ, WL_IW_DONT_CARE, WL_IW_DONT_CARE}, + {WLC_E_ASSOC_IND, WL_IW_DONT_CARE, DOT11_SC_SUCCESS}, + {WLC_E_REASSOC_IND, WL_IW_DONT_CARE, DOT11_SC_SUCCESS}, + }; + + /* Search the event map table for a matching event */ + for (i = 0; i < sizeof(event_map)/sizeof(event_map[0]); i++) { + const conn_fail_event_map_t* row = &event_map[i]; + if (row->inEvent == event_type && + (row->inStatus == status || row->inStatus == WL_IW_DONT_CARE) && + (row->inReason == reason || row->inReason == WL_IW_DONT_CARE)) { + memset(stringBuf, 0, buflen); + snprintf(stringBuf, buflen, "isam_event event=%d reason=%d", + event_type, reason); + return TRUE; + } + } + + return FALSE; +} +#endif /* WL_WIRELESS_EXT */ + +int +wl_ext_iapsta_event(struct net_device *dev, wl_event_msg_t *e, void* data) +{ + struct wl_apsta_params *apsta_params = &g_apsta_params; + struct wl_if_info *cur_if = NULL; + int i; +#if defined(WL_WIRELESS_EXT) + char extra[IW_CUSTOM_MAX + 1]; + union iwreq_data wrqu; +#endif + uint32 event_type = ntoh32(e->event_type); + uint32 status = ntoh32(e->status); + uint32 reason = ntoh32(e->reason); + uint16 flags = ntoh16(e->flags); + + if (!apsta_params->init) { + ANDROID_TRACE(("%s: please init first\n", __FUNCTION__)); + return -1; + } + + for (i=0; iif_info[i].bssidx == e->ifidx) { + cur_if = &apsta_params->if_info[i]; + break; + } + } + if (!cur_if || !cur_if->dev) { + ANDROID_ERROR(("%s: %s ifidx %d is not ready\n", __FUNCTION__, + dev->name, e->ifidx)); + return -1; + } + + if (cur_if->ifmode == ISTA_MODE) { + if (event_type == WLC_E_LINK) { + if (!(flags & WLC_EVENT_MSG_LINK)) { + printf("%s: %s[%c] Link Down with "MACSTR"\n", __FUNCTION__, + cur_if->ifname, cur_if->prefix, MAC2STR((u8 *)&e->addr)); + } else { + printf("%s: %s[%c] Link UP with "MACSTR"\n", __FUNCTION__, + cur_if->ifname, cur_if->prefix, MAC2STR((u8 *)&e->addr)); + } + } + } + else if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) { + if ((event_type == WLC_E_SET_SSID && status == WLC_E_STATUS_SUCCESS) || + (event_type == WLC_E_LINK && status == WLC_E_STATUS_SUCCESS && + reason == WLC_E_REASON_INITIAL_ASSOC)) { + printf("%s: %s[%c] Link up\n", __FUNCTION__, + cur_if->ifname, cur_if->prefix); + } else if ((event_type == WLC_E_LINK && reason == WLC_E_LINK_BSSCFG_DIS) || + (event_type == WLC_E_LINK && status == WLC_E_STATUS_SUCCESS && + reason == WLC_E_REASON_DEAUTH)) { + printf("%s: %s[%c] Link down\n", __FUNCTION__, + cur_if->ifname, cur_if->prefix); + } + else if ((event_type == WLC_E_ASSOC_IND || event_type == WLC_E_REASSOC_IND) && + reason == DOT11_SC_SUCCESS) { + printf("%s: %s[%c] connected device "MACDBG"\n", __FUNCTION__, + cur_if->ifname, cur_if->prefix, MAC2STRDBG(e->addr.octet)); + } else if (event_type == WLC_E_DISASSOC_IND) { + printf("%s: %s[%c] disassociated device "MACDBG"\n", __FUNCTION__, + cur_if->ifname, cur_if->prefix, MAC2STRDBG(e->addr.octet)); + } else if (event_type == WLC_E_DEAUTH_IND || + (event_type == WLC_E_DEAUTH && reason != DOT11_RC_RESERVED)) { + printf("%s: %s[%c] deauthenticated device "MACDBG"\n", __FUNCTION__, + cur_if->ifname, cur_if->prefix, MAC2STRDBG(e->addr.octet)); + } + } + +#if defined(WL_WIRELESS_EXT) + memset(extra, 0, sizeof(extra)); + memset(&wrqu, 0, sizeof(wrqu)); + memcpy(wrqu.addr.sa_data, &e->addr, ETHER_ADDR_LEN); + wrqu.addr.sa_family = ARPHRD_ETHER; + if (wl_ext_conn_status_str(event_type, status, reason, extra, sizeof(extra))) { + wrqu.data.length = strlen(extra); + wireless_send_event(cur_if->dev, IWEVCUSTOM, &wrqu, extra); + ANDROID_INFO(("%s: %s[%c] event=%d, status=%d, reason=%d, flags=%d sent up\n", + __FUNCTION__, cur_if->ifname, cur_if->prefix, event_type, status, + reason, flags)); + } else +#endif /* WL_WIRELESS_EXT */ + { + ANDROID_INFO(("%s: %s[%c] event=%d, status=%d, reason=%d, flags=%d\n", + __FUNCTION__, cur_if->ifname, cur_if->prefix, event_type, status, + reason, flags)); + } + + return 0; +} + +u32 +wl_ext_iapsta_disconnect_sta(struct net_device *dev, u32 channel) +{ + struct wl_apsta_params *apsta_params = &g_apsta_params; + struct wl_if_info *cur_if = NULL; + int i; + + wl_ext_isam_dump_status(dev); + for (i=0; iif_info[i]; + if (cur_if->dev == dev) { + cur_if->channel = channel; + channel = wl_ext_move_cur_channel(apsta_params->if_info[IF_PIF].dev, cur_if); + wl_ext_move_other_channel(apsta_params->if_info[IF_PIF].dev, cur_if); + break; + } + } + return channel; +} + +int +wl_ext_iapsta_attach_name(struct net_device *net, uint8 bssidx) +{ + struct wl_apsta_params *apsta_params = &g_apsta_params; + struct dhd_pub *dhd; + struct wl_if_info *cur_if = NULL; + + dhd = dhd_get_pub(net); + + ANDROID_TRACE(("%s: bssidx=%d, %s\n", __FUNCTION__, bssidx, net->name)); + if (bssidx < MAX_IF_NUM) { + cur_if = &apsta_params->if_info[bssidx]; + } + if (bssidx == 0) { + if (dhd->conf->fw_type == FW_TYPE_MESH) { + apsta_params->rsdb = TRUE; + apsta_params->csa = CSA_FW_BIT | CSA_DRV_BIT; + } + strcpy(cur_if->ifname, net->name); + } else if (cur_if && cur_if->ifstate == IF_STATE_INIT) { + strcpy(cur_if->ifname, net->name); + apsta_params->netif_change = TRUE; + wake_up_interruptible(&apsta_params->netif_change_event); + } + + return 0; +} + +int +wl_ext_iapsta_attach_netdev(struct net_device *net, uint8 bssidx) +{ + struct wl_apsta_params *apsta_params = &g_apsta_params; + struct dhd_pub *dhd; + struct wl_if_info *cur_if = NULL, *primary_if; + + dhd = dhd_get_pub(net); + + printf("%s: bssidx=%d\n", __FUNCTION__, bssidx); + if (bssidx < MAX_IF_NUM) { + cur_if = &apsta_params->if_info[bssidx]; + } + if (bssidx == 0) { + memset(apsta_params, 0, sizeof(struct wl_apsta_params)); + apsta_params->vsdb = FALSE; + cur_if->dev = net; + cur_if->bssidx = bssidx; + strcpy(cur_if->ifname, net->name); + init_waitqueue_head(&apsta_params->netif_change_event); + } else if (cur_if && cur_if->ifstate == IF_STATE_INIT) { + primary_if = &apsta_params->if_info[IF_PIF]; + cur_if->dev = net; + cur_if->bssidx = bssidx; + if (strlen(cur_if->ifname)) { + memset(net->name, 0, sizeof(IFNAMSIZ)); + strcpy(net->name, cur_if->ifname); + net->name[IFNAMSIZ-1] = '\0'; + } + memcpy(net->dev_addr, primary_if->dev->dev_addr, ETHER_ADDR_LEN); + net->dev_addr[0] |= 0x02; + if (bssidx >= 2) { + net->dev_addr[4] ^= 0x80; + net->dev_addr[4] += bssidx; + net->dev_addr[5] += bssidx; + } + if (cur_if->ifmode == ISTA_MODE) { + wl_ext_iovar_setint(net, "roam_off", dhd->conf->roam_off); + wl_ext_iovar_setint(net, "bcn_timeout", dhd->conf->bcn_timeout); + } + } + + return 0; +} + +int +wl_ext_iapsta_dettach_netdev(void) +{ + struct wl_apsta_params *apsta_params = &g_apsta_params; + + printf("%s: Enter\n", __FUNCTION__); + memset(apsta_params, 0, sizeof(struct wl_apsta_params)); + + return 0; +} +#endif + +#ifdef IDHCP +int +wl_ext_ip_dump(int ip, char *buf) +{ + unsigned char bytes[4]; + int bytes_written=-1; + + bytes[0] = ip & 0xFF; + bytes[1] = (ip >> 8) & 0xFF; + bytes[2] = (ip >> 16) & 0xFF; + bytes[3] = (ip >> 24) & 0xFF; + bytes_written = sprintf(buf, "%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3]); + + return bytes_written; +} + +/* +terence 20170215: +dhd_priv dhcpc_dump ifname [wlan0|wlan1] +dhd_priv dhcpc_enable [0|1] +*/ +int +wl_ext_dhcpc_enable(struct net_device *dev, char *command, int total_len) +{ + int enable = -1, ret = -1; + int bytes_written = -1; + + ANDROID_TRACE(("%s: cmd %s\n", __FUNCTION__, command)); + + sscanf(command, "%*s %d", &enable); + + if (enable >= 0) + ret = wl_ext_iovar_setint(dev, "dhcpc_enable", enable); + else { + ret = wl_ext_iovar_getint(dev, "dhcpc_enable", &enable); + if (!ret) { + bytes_written = snprintf(command, total_len, "%d", enable); + ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command)); + ret = bytes_written; + } + } + + return ret; +} + +int +wl_ext_dhcpc_dump(struct net_device *dev, char *command, int total_len) +{ + int ret = 0; + int bytes_written = 0; + uint32 ip_addr; + char buf[20]=""; + + ret = wl_ext_iovar_getint(dev, "dhcpc_ip_addr", &ip_addr); + if (!ret) { + wl_ext_ip_dump(ip_addr, buf); + bytes_written += snprintf(command+bytes_written, total_len, "ipaddr %s ", buf); + } + + ret = wl_ext_iovar_getint(dev, "dhcpc_ip_mask", &ip_addr); + if (!ret) { + wl_ext_ip_dump(ip_addr, buf); + bytes_written += snprintf(command+bytes_written, total_len, "mask %s ", buf); + } + + ret = wl_ext_iovar_getint(dev, "dhcpc_ip_gateway", &ip_addr); + if (!ret) { + wl_ext_ip_dump(ip_addr, buf); + bytes_written += snprintf(command+bytes_written, total_len, "gw %s ", buf); + } + + ret = wl_ext_iovar_getint(dev, "dhcpc_ip_dnsserv", &ip_addr); + if (!ret) { + wl_ext_ip_dump(ip_addr, buf); + bytes_written += snprintf(command+bytes_written, total_len, "dnsserv %s ", buf); + } + + if (!bytes_written) + bytes_written = -1; + + ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command)); + + return bytes_written; +} +#endif + +/* +dhd_priv dhd [string] ==> Not ready +1. Get dhd val: + Ex: dhd_priv dhd bussleep +2. Set dhd val: + Ex: dhd_priv dhd bussleep 1 + +dhd_priv wl [WLC_GET_PM] ==> Ready to get int val +dhd_priv wl [WLC_SET_PM] [int] ==> Ready to set int val +dhd_priv wl [string] ==> Ready to get int val +dhd_priv wl [string] [int] ==> Ready to set int val +Ex: get/set WLC_PM + dhd_priv wl 85 + dhd_priv wl 86 1 +Ex: get/set mpc + dhd_priv wl mpc + dhd_priv wl mpc 1 +*/ +int +wl_ext_iovar(struct net_device *dev, char *command, int total_len) +{ + int ret = 0; + char wl[3]="\0", arg[20]="\0", cmd_str[20]="\0", val_str[20]="\0"; + int cmd=-1, val=0; + int bytes_written=-1; + + ANDROID_TRACE(("%s: cmd %s\n", __FUNCTION__, command)); + + sscanf(command, "%s %d %s", wl, &cmd, arg); + if (cmd < 0) + sscanf(command, "%s %s %s", wl, cmd_str, val_str); + + if (!strcmp(wl, "wl")) { + if (cmd>=0 && cmd!=WLC_GET_VAR && cmd!=WLC_SET_VAR) { + ret = sscanf(arg, "%d", &val); + if (ret > 0) { // set + ret = wl_ext_ioctl(dev, cmd, &val, sizeof(val), TRUE); + } else { // get + ret = wl_ext_ioctl(dev, cmd, &val, sizeof(val), FALSE); + if (!ret) { + bytes_written = snprintf(command, total_len, "%d", val); + ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command)); + ret = bytes_written; + } + } + } else if (strlen(cmd_str)) { + ret = sscanf(val_str, "%d", &val); + if (ret > 0) { // set + ret = wl_ext_iovar_setint(dev, cmd_str, val); + } else { // get + ret = wl_ext_iovar_getint(dev, cmd_str, &val); + if (!ret) { + bytes_written = snprintf(command, total_len, "%d", val); + ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command)); + ret = bytes_written; + } + } + } + } + + return ret; +} + +int wl_android_ext_priv_cmd(struct net_device *net, char *command, int total_len, + int *bytes_written) +{ + int ret = 0; + + if (strnicmp(command, CMD_CHANNELS, strlen(CMD_CHANNELS)) == 0) { + *bytes_written = wl_ext_channels(net, command, total_len); + } + else if (strnicmp(command, CMD_CHANNEL, strlen(CMD_CHANNEL)) == 0) { + *bytes_written = wl_ext_channel(net, command, total_len); + } + else if (strnicmp(command, CMD_ROAM_TRIGGER, strlen(CMD_ROAM_TRIGGER)) == 0) { + *bytes_written = wl_ext_roam_trigger(net, command, total_len); + } + else if (strnicmp(command, CMD_KEEP_ALIVE, strlen(CMD_KEEP_ALIVE)) == 0) { + *bytes_written = wl_ext_keep_alive(net, command, total_len); + } + else if (strnicmp(command, CMD_PM, strlen(CMD_PM)) == 0) { + *bytes_written = wl_ext_pm(net, command, total_len); + } + else if (strnicmp(command, CMD_MONITOR, strlen(CMD_MONITOR)) == 0) { + *bytes_written = wl_ext_monitor(net, command, total_len); + } + else if (strnicmp(command, CMD_SET_SUSPEND_BCN_LI_DTIM, strlen(CMD_SET_SUSPEND_BCN_LI_DTIM)) == 0) { + int bcn_li_dtim; + bcn_li_dtim = (int)simple_strtol((command + strlen(CMD_SET_SUSPEND_BCN_LI_DTIM) + 1), NULL, 10); + *bytes_written = net_os_set_suspend_bcn_li_dtim(net, bcn_li_dtim); + } +#ifdef WL_EXT_IAPSTA + else if (strnicmp(command, CMD_IAPSTA_INIT, strlen(CMD_IAPSTA_INIT)) == 0) { + *bytes_written = wl_ext_isam_init(net, command, total_len); + } + else if (strnicmp(command, CMD_ISAM_INIT, strlen(CMD_ISAM_INIT)) == 0) { + *bytes_written = wl_ext_isam_init(net, command, total_len); + } + else if (strnicmp(command, CMD_IAPSTA_CONFIG, strlen(CMD_IAPSTA_CONFIG)) == 0) { + *bytes_written = wl_ext_iapsta_config(net, command, total_len); + } + else if (strnicmp(command, CMD_ISAM_CONFIG, strlen(CMD_ISAM_CONFIG)) == 0) { + *bytes_written = wl_ext_iapsta_config(net, command, total_len); + } + else if (strnicmp(command, CMD_IAPSTA_ENABLE, strlen(CMD_IAPSTA_ENABLE)) == 0) { + *bytes_written = wl_ext_iapsta_enable(net, command, total_len); + } + else if (strnicmp(command, CMD_ISAM_ENABLE, strlen(CMD_ISAM_ENABLE)) == 0) { + *bytes_written = wl_ext_iapsta_enable(net, command, total_len); + } + else if (strnicmp(command, CMD_IAPSTA_DISABLE, strlen(CMD_IAPSTA_DISABLE)) == 0) { + *bytes_written = wl_ext_iapsta_disable(net, command, total_len); + } + else if (strnicmp(command, CMD_ISAM_DISABLE, strlen(CMD_ISAM_DISABLE)) == 0) { + *bytes_written = wl_ext_iapsta_disable(net, command, total_len); + } + else if (strnicmp(command, CMD_ISAM_DUMP, strlen(CMD_ISAM_DUMP)) == 0) { + *bytes_written = wl_ext_isam_dump_status(net); + } +#endif +#ifdef IDHCP + else if (strnicmp(command, CMD_DHCPC_ENABLE, strlen(CMD_DHCPC_ENABLE)) == 0) { + *bytes_written = wl_ext_dhcpc_enable(net, command, total_len); + } + else if (strnicmp(command, CMD_DHCPC_DUMP, strlen(CMD_DHCPC_DUMP)) == 0) { + *bytes_written = wl_ext_dhcpc_dump(net, command, total_len); + } +#endif +#ifdef WL_CFG80211 + else if (strnicmp(command, CMD_AUTOCHANNEL, strlen(CMD_AUTOCHANNEL)) == 0) { + *bytes_written = wl_cfg80211_autochannel(net, command, total_len); + } +#endif +#ifdef WL_ESCAN + else if (strnicmp(command, CMD_AUTOCHANNEL, strlen(CMD_AUTOCHANNEL)) == 0) { + *bytes_written = wl_escan_autochannel(net, command, total_len); + } +#endif + else if (strnicmp(command, CMD_WL, strlen(CMD_WL)) == 0) { + *bytes_written = wl_ext_iovar(net, command, total_len); + } + else + ret = -1; + + return ret; +} + +#if defined(WL_CFG80211) || defined(WL_ESCAN) +int +wl_ext_get_distance(struct net_device *net, u32 band) +{ + u32 bw = WL_CHANSPEC_BW_20; + s32 bw_cap = 0, distance = 0; + struct { + u32 band; + u32 bw_cap; + } param = {0, 0}; + char buf[WLC_IOCTL_SMLEN]="\0"; + s32 err = BCME_OK; + + param.band = band; + err = wldev_iovar_getbuf(net, "bw_cap", ¶m, sizeof(param), buf, sizeof(buf), NULL); + if (err) { + if (err != BCME_UNSUPPORTED) { + ANDROID_ERROR(("bw_cap failed, %d\n", err)); + return err; + } else { + err = wl_ext_iovar_getint(net, "mimo_bw_cap", &bw_cap); + if (err) { + ANDROID_ERROR(("error get mimo_bw_cap (%d)\n", err)); + } + if (bw_cap != WLC_N_BW_20ALL) + bw = WL_CHANSPEC_BW_40; + } + } else { + if (WL_BW_CAP_80MHZ(buf[0])) + bw = WL_CHANSPEC_BW_80; + else if (WL_BW_CAP_40MHZ(buf[0])) + bw = WL_CHANSPEC_BW_40; + else + bw = WL_CHANSPEC_BW_20; + } + + if (bw == WL_CHANSPEC_BW_20) + distance = 2; + else if (bw == WL_CHANSPEC_BW_40) + distance = 4; + else if (bw == WL_CHANSPEC_BW_80) + distance = 8; + else + distance = 16; + ANDROID_INFO(("%s: bw=0x%x, distance=%d\n", __FUNCTION__, bw, distance)); + + return distance; +} + +int +wl_ext_get_best_channel(struct net_device *net, +#if defined(BSSCACHE) + wl_bss_cache_ctrl_t *bss_cache_ctrl, +#else + struct wl_scan_results *bss_list, +#endif + int *best_2g_ch, int *best_5g_ch +) +{ + struct wl_bss_info *bi = NULL; /* must be initialized */ + s32 i, j; +#if defined(BSSCACHE) + wl_bss_cache_t *node; +#endif + int b_band[CH_MAX_2G_CHANNEL]={0}, a_band1[4]={0}, a_band4[5]={0}; + s32 cen_ch, distance, distance_2g, distance_5g, ch, min_ap=999; + u8 valid_chan_list[sizeof(u32)*(WL_NUMCHANNELS + 1)]; + wl_uint32_list_t *list; + int ret; + int ioctl_ver = 0; + chanspec_t chanspec; + + memset(b_band, -1, sizeof(b_band)); + memset(a_band1, -1, sizeof(a_band1)); + memset(a_band4, -1, sizeof(a_band4)); + + memset(valid_chan_list, 0, sizeof(valid_chan_list)); + list = (wl_uint32_list_t *)(void *) valid_chan_list; + list->count = htod32(WL_NUMCHANNELS); + ret = wldev_ioctl(net, WLC_GET_VALID_CHANNELS, valid_chan_list, sizeof(valid_chan_list), 0); + if (ret<0) { + ANDROID_ERROR(("%s: get channels failed with %d\n", __FUNCTION__, ret)); + return 0; + } else { + for (i = 0; i < dtoh32(list->count); i++) { + ch = dtoh32(list->element[i]); + if (ch < CH_MAX_2G_CHANNEL) + b_band[ch-1] = 0; + else if (ch <= 48) + a_band1[(ch-36)/4] = 0; + else if (ch >= 149 && ch <= 161) + a_band4[(ch-149)/4] = 0; + } + } + wl_ext_get_ioctl_ver(net, &ioctl_ver); + + distance_2g = wl_ext_get_distance(net, WLC_BAND_2G); + distance_5g = wl_ext_get_distance(net, WLC_BAND_5G); + +#if defined(BSSCACHE) + node = bss_cache_ctrl->m_cache_head; + for (i=0; node && i<256; i++) +#else + for (i=0; i < bss_list->count; i++) +#endif + { +#if defined(BSSCACHE) + bi = node->results.bss_info; +#else + bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : bss_list->bss_info; +#endif + chanspec = wl_ext_chspec_driver_to_host(ioctl_ver, bi->chanspec); + cen_ch = CHSPEC_CHANNEL(bi->chanspec); + distance = 0; + if (CHSPEC_IS20(chanspec)) + distance += 2; + else if (CHSPEC_IS40(chanspec)) + distance += 4; + else if (CHSPEC_IS80(chanspec)) + distance += 8; + else + distance += 16; + + if (CHSPEC_IS2G(chanspec)) { + distance += distance_2g; + for (j=0; j= 0 && abs(cen_ch-(1+j)) <= distance) + b_band[j] += 1; + } + } else { + distance += distance_5g; + if (cen_ch <= 48) { + for (j=0; j= 0 && abs(cen_ch-(36+j*4)) <= distance) + a_band1[j] += 1; + } + } else if (cen_ch >= 149) { + for (j=0; j= 0 && abs(cen_ch-(149+j*4)) <= distance) + a_band4[j] += 1; + } + } + } +#if defined(BSSCACHE) + node = node->next; +#endif + } + + *best_2g_ch = 0; + min_ap = 999; + for (i=0; i= 0) { + min_ap = b_band[i]; + *best_2g_ch = i+1; + } + } + *best_5g_ch = 0; + min_ap = 999; + for (i=0; i= 0) { + min_ap = a_band1[i]; + *best_5g_ch = i*4 + 36; + } + } + for (i=0; i= 0) { + min_ap = a_band4[i]; + *best_5g_ch = i*4 + 149; + } + } + + if (android_msg_level&ANDROID_INFO_LEVEL) { + printf("%s: b_band: ", __FUNCTION__); + for (j=0; jm_cache_head; + node = *rssi_head; + + for (;node;) { + ANDROID_INFO(("%s: Free %d with BSSID %pM\n", + __FUNCTION__, i, &node->BSSID)); + cur = node; + node = cur->next; + kfree(cur); + i++; + } + *rssi_head = NULL; +} + +void +wl_delete_dirty_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl) +{ + wl_rssi_cache_t *node, *prev, **rssi_head; + int i = -1, tmp = 0; + struct timeval now; + + do_gettimeofday(&now); + + rssi_head = &rssi_cache_ctrl->m_cache_head; + node = *rssi_head; + prev = node; + for (;node;) { + i++; + if (now.tv_sec > node->tv.tv_sec) { + if (node == *rssi_head) { + tmp = 1; + *rssi_head = node->next; + } else { + tmp = 0; + prev->next = node->next; + } + ANDROID_INFO(("%s: Del %d with BSSID %pM\n", + __FUNCTION__, i, &node->BSSID)); + kfree(node); + if (tmp == 1) { + node = *rssi_head; + prev = node; + } else { + node = prev->next; + } + continue; + } + prev = node; + node = node->next; + } +} + +void +wl_delete_disconnected_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl, u8 *bssid) +{ + wl_rssi_cache_t *node, *prev, **rssi_head; + int i = -1, tmp = 0; + + rssi_head = &rssi_cache_ctrl->m_cache_head; + node = *rssi_head; + prev = node; + for (;node;) { + i++; + if (!memcmp(&node->BSSID, bssid, ETHER_ADDR_LEN)) { + if (node == *rssi_head) { + tmp = 1; + *rssi_head = node->next; + } else { + tmp = 0; + prev->next = node->next; + } + ANDROID_INFO(("%s: Del %d with BSSID %pM\n", + __FUNCTION__, i, &node->BSSID)); + kfree(node); + if (tmp == 1) { + node = *rssi_head; + prev = node; + } else { + node = prev->next; + } + continue; + } + prev = node; + node = node->next; + } +} + +void +wl_reset_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl) +{ + wl_rssi_cache_t *node, **rssi_head; + + rssi_head = &rssi_cache_ctrl->m_cache_head; + + /* reset dirty */ + node = *rssi_head; + for (;node;) { + node->dirty += 1; + node = node->next; + } +} + +int +wl_update_connected_rssi_cache(struct net_device *net, wl_rssi_cache_ctrl_t *rssi_cache_ctrl, int *rssi_avg) +{ + wl_rssi_cache_t *node, *prev, *leaf, **rssi_head; + int j, k=0; + int rssi, error=0; + struct ether_addr bssid; + struct timeval now, timeout; + scb_val_t scbval; + + if (!g_wifi_on) + return 0; + + error = wldev_ioctl(net, WLC_GET_BSSID, &bssid, sizeof(bssid), false); + if (error == BCME_NOTASSOCIATED) { + ANDROID_INFO(("%s: Not Associated! res:%d\n", __FUNCTION__, error)); + return 0; + } + if (error) { + ANDROID_ERROR(("Could not get bssid (%d)\n", error)); + } + error = wldev_get_rssi(net, &scbval); + if (error) { + ANDROID_ERROR(("Could not get rssi (%d)\n", error)); + return error; + } + rssi = scbval.val; + + do_gettimeofday(&now); + timeout.tv_sec = now.tv_sec + RSSICACHE_TIMEOUT; + if (timeout.tv_sec < now.tv_sec) { + /* + * Integer overflow - assume long enough timeout to be assumed + * to be infinite, i.e., the timeout would never happen. + */ + ANDROID_TRACE(("%s: Too long timeout (secs=%d) to ever happen - now=%lu, timeout=%lu", + __FUNCTION__, RSSICACHE_TIMEOUT, now.tv_sec, timeout.tv_sec)); + } + + /* update RSSI */ + rssi_head = &rssi_cache_ctrl->m_cache_head; + node = *rssi_head; + prev = NULL; + for (;node;) { + if (!memcmp(&node->BSSID, &bssid, ETHER_ADDR_LEN)) { + ANDROID_INFO(("%s: Update %d with BSSID %pM, RSSI=%d\n", + __FUNCTION__, k, &bssid, rssi)); + for (j=0; jRSSI[j] = node->RSSI[j+1]; + node->RSSI[j] = rssi; + node->dirty = 0; + node->tv = timeout; + goto exit; + } + prev = node; + node = node->next; + k++; + } + + leaf = kmalloc(sizeof(wl_rssi_cache_t), GFP_KERNEL); + if (!leaf) { + ANDROID_ERROR(("%s: Memory alloc failure %d\n", + __FUNCTION__, (int)sizeof(wl_rssi_cache_t))); + return 0; + } + ANDROID_INFO(("%s: Add %d with cached BSSID %pM, RSSI=%3d in the leaf\n", + __FUNCTION__, k, &bssid, rssi)); + + leaf->next = NULL; + leaf->dirty = 0; + leaf->tv = timeout; + memcpy(&leaf->BSSID, &bssid, ETHER_ADDR_LEN); + for (j=0; jRSSI[j] = rssi; + + if (!prev) + *rssi_head = leaf; + else + prev->next = leaf; + +exit: + *rssi_avg = (int)wl_get_avg_rssi(rssi_cache_ctrl, &bssid); + + return error; +} + +void +wl_update_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl, wl_scan_results_t *ss_list) +{ + wl_rssi_cache_t *node, *prev, *leaf, **rssi_head; + wl_bss_info_t *bi = NULL; + int i, j, k; + struct timeval now, timeout; + + if (!ss_list->count) + return; + + do_gettimeofday(&now); + timeout.tv_sec = now.tv_sec + RSSICACHE_TIMEOUT; + if (timeout.tv_sec < now.tv_sec) { + /* + * Integer overflow - assume long enough timeout to be assumed + * to be infinite, i.e., the timeout would never happen. + */ + ANDROID_TRACE(("%s: Too long timeout (secs=%d) to ever happen - now=%lu, timeout=%lu", + __FUNCTION__, RSSICACHE_TIMEOUT, now.tv_sec, timeout.tv_sec)); + } + + rssi_head = &rssi_cache_ctrl->m_cache_head; + + /* update RSSI */ + for (i = 0; i < ss_list->count; i++) { + node = *rssi_head; + prev = NULL; + k = 0; + bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : ss_list->bss_info; + for (;node;) { + if (!memcmp(&node->BSSID, &bi->BSSID, ETHER_ADDR_LEN)) { + ANDROID_INFO(("%s: Update %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n", + __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID)); + for (j=0; jRSSI[j] = node->RSSI[j+1]; + node->RSSI[j] = dtoh16(bi->RSSI); + node->dirty = 0; + node->tv = timeout; + break; + } + prev = node; + node = node->next; + k++; + } + + if (node) + continue; + + leaf = kmalloc(sizeof(wl_rssi_cache_t), GFP_KERNEL); + if (!leaf) { + ANDROID_ERROR(("%s: Memory alloc failure %d\n", + __FUNCTION__, (int)sizeof(wl_rssi_cache_t))); + return; + } + ANDROID_INFO(("%s: Add %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\" in the leaf\n", + __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID)); + + leaf->next = NULL; + leaf->dirty = 0; + leaf->tv = timeout; + memcpy(&leaf->BSSID, &bi->BSSID, ETHER_ADDR_LEN); + for (j=0; jRSSI[j] = dtoh16(bi->RSSI); + + if (!prev) + *rssi_head = leaf; + else + prev->next = leaf; + } +} + +int16 +wl_get_avg_rssi(wl_rssi_cache_ctrl_t *rssi_cache_ctrl, void *addr) +{ + wl_rssi_cache_t *node, **rssi_head; + int j, rssi_sum, rssi=RSSI_MINVAL; + + rssi_head = &rssi_cache_ctrl->m_cache_head; + + node = *rssi_head; + for (;node;) { + if (!memcmp(&node->BSSID, addr, ETHER_ADDR_LEN)) { + rssi_sum = 0; + rssi = 0; + for (j=0; jRSSI[RSSIAVG_LEN-j-1]; + rssi = rssi_sum / j; + break; + } + node = node->next; + } + rssi = MIN(rssi, RSSI_MAXVAL); + if (rssi == RSSI_MINVAL) { + ANDROID_ERROR(("%s: BSSID %pM does not in RSSI cache\n", + __FUNCTION__, addr)); + } + return (int16)rssi; +} +#endif + +#if defined(RSSIOFFSET) +int +wl_update_rssi_offset(struct net_device *net, int rssi) +{ +#if defined(RSSIOFFSET_NEW) + int j; +#endif + + if (!g_wifi_on) + return rssi; + +#if defined(RSSIOFFSET_NEW) + for (j=0; jm_cache_head; + node = *bss_head; + + for (;node;) { + ANDROID_TRACE(("%s: Free %d with BSSID %pM\n", + __FUNCTION__, i, &node->results.bss_info->BSSID)); + cur = node; + node = cur->next; + kfree(cur); + i++; + } + *bss_head = NULL; +} + +void +wl_delete_dirty_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl) +{ + wl_bss_cache_t *node, *prev, **bss_head; + int i = -1, tmp = 0; + struct timeval now; + + do_gettimeofday(&now); + + bss_head = &bss_cache_ctrl->m_cache_head; + node = *bss_head; + prev = node; + for (;node;) { + i++; + if (now.tv_sec > node->tv.tv_sec) { + if (node == *bss_head) { + tmp = 1; + *bss_head = node->next; + } else { + tmp = 0; + prev->next = node->next; + } + ANDROID_TRACE(("%s: Del %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n", + __FUNCTION__, i, &node->results.bss_info->BSSID, + dtoh16(node->results.bss_info->RSSI), node->results.bss_info->SSID)); + kfree(node); + if (tmp == 1) { + node = *bss_head; + prev = node; + } else { + node = prev->next; + } + continue; + } + prev = node; + node = node->next; + } +} + +void +wl_delete_disconnected_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl, u8 *bssid) +{ + wl_bss_cache_t *node, *prev, **bss_head; + int i = -1, tmp = 0; + + bss_head = &bss_cache_ctrl->m_cache_head; + node = *bss_head; + prev = node; + for (;node;) { + i++; + if (!memcmp(&node->results.bss_info->BSSID, bssid, ETHER_ADDR_LEN)) { + if (node == *bss_head) { + tmp = 1; + *bss_head = node->next; + } else { + tmp = 0; + prev->next = node->next; + } + ANDROID_TRACE(("%s: Del %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n", + __FUNCTION__, i, &node->results.bss_info->BSSID, + dtoh16(node->results.bss_info->RSSI), node->results.bss_info->SSID)); + kfree(node); + if (tmp == 1) { + node = *bss_head; + prev = node; + } else { + node = prev->next; + } + continue; + } + prev = node; + node = node->next; + } +} + +void +wl_reset_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl) +{ + wl_bss_cache_t *node, **bss_head; + + bss_head = &bss_cache_ctrl->m_cache_head; + + /* reset dirty */ + node = *bss_head; + for (;node;) { + node->dirty += 1; + node = node->next; + } +} + +void dump_bss_cache( +#if defined(RSSIAVG) + wl_rssi_cache_ctrl_t *rssi_cache_ctrl, +#endif + wl_bss_cache_t *node) +{ + int k = 0; + int16 rssi; + + for (;node;) { +#if defined(RSSIAVG) + rssi = wl_get_avg_rssi(rssi_cache_ctrl, &node->results.bss_info->BSSID); +#else + rssi = dtoh16(node->results.bss_info->RSSI); +#endif + ANDROID_TRACE(("%s: dump %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n", + __FUNCTION__, k, &node->results.bss_info->BSSID, rssi, node->results.bss_info->SSID)); + k++; + node = node->next; + } +} + +void +wl_update_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl, +#if defined(RSSIAVG) + wl_rssi_cache_ctrl_t *rssi_cache_ctrl, +#endif + wl_scan_results_t *ss_list) +{ + wl_bss_cache_t *node, *prev, *leaf, **bss_head; + wl_bss_info_t *bi = NULL; + int i, k=0; +#if defined(SORT_BSS_BY_RSSI) + int16 rssi, rssi_node; +#endif + struct timeval now, timeout; + + if (!ss_list->count) + return; + + do_gettimeofday(&now); + timeout.tv_sec = now.tv_sec + BSSCACHE_TIMEOUT; + if (timeout.tv_sec < now.tv_sec) { + /* + * Integer overflow - assume long enough timeout to be assumed + * to be infinite, i.e., the timeout would never happen. + */ + ANDROID_TRACE(("%s: Too long timeout (secs=%d) to ever happen - now=%lu, timeout=%lu", + __FUNCTION__, BSSCACHE_TIMEOUT, now.tv_sec, timeout.tv_sec)); + } + + bss_head = &bss_cache_ctrl->m_cache_head; + + for (i=0; i < ss_list->count; i++) { + node = *bss_head; + prev = NULL; + bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : ss_list->bss_info; + + for (;node;) { + if (!memcmp(&node->results.bss_info->BSSID, &bi->BSSID, ETHER_ADDR_LEN)) { + if (node == *bss_head) + *bss_head = node->next; + else { + prev->next = node->next; + } + break; + } + prev = node; + node = node->next; + } + + leaf = kmalloc(dtoh32(bi->length) + sizeof(wl_bss_cache_t), GFP_KERNEL); + if (!leaf) { + ANDROID_ERROR(("%s: Memory alloc failure %d\n", __FUNCTION__, + dtoh32(bi->length) + (int)sizeof(wl_bss_cache_t))); + return; + } + if (node) { + kfree(node); + node = NULL; + ANDROID_TRACE(("%s: Update %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n", + __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID)); + } else + ANDROID_TRACE(("%s: Add %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n", + __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID)); + + memcpy(leaf->results.bss_info, bi, dtoh32(bi->length)); + leaf->next = NULL; + leaf->dirty = 0; + leaf->tv = timeout; + leaf->results.count = 1; + leaf->results.version = ss_list->version; + k++; + + if (*bss_head == NULL) + *bss_head = leaf; + else { +#if defined(SORT_BSS_BY_RSSI) + node = *bss_head; +#if defined(RSSIAVG) + rssi = wl_get_avg_rssi(rssi_cache_ctrl, &leaf->results.bss_info->BSSID); +#else + rssi = dtoh16(leaf->results.bss_info->RSSI); +#endif + for (;node;) { +#if defined(RSSIAVG) + rssi_node = wl_get_avg_rssi(rssi_cache_ctrl, &node->results.bss_info->BSSID); +#else + rssi_node = dtoh16(node->results.bss_info->RSSI); +#endif + if (rssi > rssi_node) { + leaf->next = node; + if (node == *bss_head) + *bss_head = leaf; + else + prev->next = leaf; + break; + } + prev = node; + node = node->next; + } + if (node == NULL) + prev->next = leaf; +#else + leaf->next = *bss_head; + *bss_head = leaf; +#endif + } + } + dump_bss_cache( +#if defined(RSSIAVG) + rssi_cache_ctrl, +#endif + *bss_head); +} + +void +wl_release_bss_cache_ctrl(wl_bss_cache_ctrl_t *bss_cache_ctrl) +{ + ANDROID_TRACE(("%s:\n", __FUNCTION__)); + wl_free_bss_cache(bss_cache_ctrl); +} +#endif + + diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfg80211.c index aad697d7..1123b86f 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfg80211.c @@ -513,6 +513,14 @@ static s32 wl_cfg80211_del_station(struct wiphy *wiphy, static s32 wl_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev, u8* mac_addr); #endif +#ifdef WLMESH +static s32 wl_cfg80211_join_mesh( + struct wiphy *wiphy, struct net_device *dev, + const struct mesh_config *conf, + const struct mesh_setup *setup); +static s32 wl_cfg80211_leave_mesh(struct wiphy *wiphy, + struct net_device *dev); +#endif /* WLMESH */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) static s32 wl_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev, const u8 *mac, struct station_parameters *params); @@ -565,7 +573,11 @@ static s32 wl_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, #endif #endif #ifdef WL_SCHED_SCAN -static int wl_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev); +static int wl_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) + , u64 reqid +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ +); #endif static s32 wl_cfg80211_set_ap_role(struct bcm_cfg80211 *cfg, struct net_device *dev); #if defined(WL_VIRTUAL_APSTA) || defined(DUAL_STA_STATIC_IF) @@ -1312,6 +1324,13 @@ wl_cfg80211_ether_atoe(const char *a, struct ether_addr *n) /* There isn't a lot of sense in it, but you can transmit anything you like */ static const struct ieee80211_txrx_stypes wl_cfg80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = { +#ifdef WLMESH + [NL80211_IFTYPE_MESH_POINT] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_AUTH >> 4) + }, +#endif /* WLMESH */ [NL80211_IFTYPE_ADHOC] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ACTION >> 4) @@ -1611,7 +1630,10 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) unsigned char name_assign_type, #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) */ - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)) + u32 *flags, +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)) */ struct vif_params *params) { s32 err = -ENODEV; @@ -1628,10 +1650,10 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, struct ether_addr primary_mac; bcm_struct_cfgdev *new_cfgdev; #ifdef PROP_TXSTATUS_VSDB -#if defined(BCMSDIO) +#if defined(BCMSDIO) || defined(BCMDBUS) s32 up = 1; bool enabled; -#endif +#endif /* BCMSDIO || BCMDBUS */ #endif /* PROP_TXSTATUS_VSDB */ dhd_pub_t *dhd; bool hang_required = false; @@ -1771,7 +1793,7 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, wl_cfg80211_scan_abort(cfg); #ifdef PROP_TXSTATUS_VSDB -#if defined(BCMSDIO) +#if defined(BCMSDIO) || defined(BCMDBUS) if (!cfg->wlfc_on && !disable_proptx) { dhd_wlfc_get_enable(dhd, &enabled); if (!enabled && dhd->op_mode != DHD_FLAG_HOSTAP_MODE && @@ -1783,7 +1805,7 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, } cfg->wlfc_on = true; } -#endif +#endif /* BCMSDIO || BCMDBUS */ #endif /* PROP_TXSTATUS_VSDB */ /* Dual p2p doesn't support multiple P2PGO interfaces, @@ -1971,14 +1993,14 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, memset(cfg->p2p->vir_ifname, '\0', IFNAMSIZ); wl_to_p2p_bss_bssidx(cfg, cfg_type) = -1; #ifdef PROP_TXSTATUS_VSDB -#if defined(BCMSDIO) +#if defined(BCMSDIO) || defined(BCMDBUS) dhd_wlfc_get_enable(dhd, &enabled); if (enabled && cfg->wlfc_on && dhd->op_mode != DHD_FLAG_HOSTAP_MODE && dhd->op_mode != DHD_FLAG_IBSS_MODE && dhd->conf->disable_proptx!=0) { dhd_wlfc_deinit(dhd); cfg->wlfc_on = false; } -#endif +#endif /* BCMSDIO || BCMDBUS */ #endif /* PROP_TXSTATUS_VSDB */ } } @@ -2185,7 +2207,10 @@ wl_cfg80211_del_virtual_iface(struct wiphy *wiphy, bcm_struct_cfgdev *cfgdev) static s32 wl_cfg80211_change_virtual_iface(struct wiphy *wiphy, struct net_device *ndev, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)) + u32 *flags, +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)) */ struct vif_params *params) { s32 ap = 0; @@ -2204,11 +2229,18 @@ wl_cfg80211_change_virtual_iface(struct wiphy *wiphy, struct net_device *ndev, switch (type) { case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_WDS: +#ifndef WLMESH case NL80211_IFTYPE_MESH_POINT: +#endif /* WLMESH */ ap = 1; WL_ERR(("type (%d) : currently we do not support this type\n", type)); break; +#ifdef WLMESH + case NL80211_IFTYPE_MESH_POINT: + infra_ibss = WL_BSSTYPE_MESH; + break; +#endif /* WLMESH */ case NL80211_IFTYPE_ADHOC: mode = WL_MODE_IBSS; infra_ibss = 0; @@ -2433,10 +2465,10 @@ static s32 wl_cfg80211_handle_ifdel(struct bcm_cfg80211 *cfg, wl_if_event_info * s32 type = -1; s32 bssidx = -1; #ifdef PROP_TXSTATUS_VSDB -#if defined(BCMSDIO) +#if defined(BCMSDIO) || defined(BCMDBUS) dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub); bool enabled; -#endif +#endif /* BCMSDIO || BCMDBUS */ #endif /* PROP_TXSTATUS_VSDB */ bssidx = if_event_info->bssidx; @@ -2466,14 +2498,14 @@ static s32 wl_cfg80211_handle_ifdel(struct bcm_cfg80211 *cfg, wl_if_event_info * } #ifdef PROP_TXSTATUS_VSDB -#if defined(BCMSDIO) +#if defined(BCMSDIO) || defined(BCMDBUS) dhd_wlfc_get_enable(dhd, &enabled); if (enabled && cfg->wlfc_on && dhd->op_mode != DHD_FLAG_HOSTAP_MODE && dhd->op_mode != DHD_FLAG_IBSS_MODE && dhd->conf->disable_proptx!=0) { dhd_wlfc_deinit(dhd); cfg->wlfc_on = false; } -#endif +#endif /* BCMSDIO || BCMDBUS */ #endif /* PROP_TXSTATUS_VSDB */ } @@ -2789,9 +2821,7 @@ wl_run_escan(struct bcm_cfg80211 *cfg, struct net_device *ndev, err = wldev_iovar_setbuf(ndev, "escan", params, params_size, cfg->escan_ioctl_buf, WLC_IOCTL_MEDLEN, NULL); - WL_DBG(("LEGACY_SCAN sync ID: %d, bssidx: %d\n", - params->sync_id, bssidx)); - + printf("%s: LEGACY_SCAN sync ID: %d, bssidx: %d\n", __FUNCTION__, params->sync_id, bssidx); if (unlikely(err)) { if (err == BCME_EPERM) /* Scan Not permitted at this point of time */ @@ -3664,6 +3694,51 @@ bcm_cfg80211_del_ibss_if(struct wiphy *wiphy, bcm_struct_cfgdev *cfgdev) } #endif /* WLAIBSS_MCHAN */ +#ifdef WLMESH +s32 +wl_cfg80211_interface_ops(struct bcm_cfg80211 *cfg, + struct net_device *ndev, s32 bsscfg_idx, + enum nl80211_iftype iface_type, s32 del, u8 *addr) +{ + wl_interface_create_t iface; + s32 ret; + wl_interface_info_t *info; + + bzero(&iface, sizeof(wl_interface_create_t)); + + iface.ver = WL_INTERFACE_CREATE_VER; + + if (iface_type == NL80211_IFTYPE_AP) + iface.flags = WL_INTERFACE_CREATE_AP; + else + iface.flags = WL_INTERFACE_CREATE_STA; + + if (del) { + ret = wldev_iovar_setbuf(ndev, "interface_remove", + NULL, 0, cfg->ioctl_buf, WLC_IOCTL_MEDLEN, NULL); + } else { + if (addr) { + memcpy(&iface.mac_addr.octet, addr, ETH_ALEN); + iface.flags |= WL_INTERFACE_MAC_USE; + } + ret = wldev_iovar_getbuf(ndev, "interface_create", + &iface, sizeof(wl_interface_create_t), + cfg->ioctl_buf, WLC_IOCTL_MAXLEN, &cfg->ioctl_buf_sync); + if (ret == 0) { + /* success */ + info = (wl_interface_info_t *)cfg->ioctl_buf; + WL_DBG(("wl interface create success!! bssidx:%d \n", + info->bsscfgidx)); + } + } + + if (ret < 0) + WL_ERR(("Interface %s failed!! ret %d\n", + del ? "remove" : "create", ret)); + + return ret; +} +#else s32 wl_cfg80211_interface_ops(struct bcm_cfg80211 *cfg, struct net_device *ndev, s32 bsscfg_idx, @@ -3758,6 +3833,7 @@ wl_cfg80211_interface_ops(struct bcm_cfg80211 *cfg, WL_DBG(("wl interface create success!! bssidx:%d \n", ret)); return ret; } +#endif bool wl_customer6_legacy_chip_check(struct bcm_cfg80211 *cfg, @@ -3792,7 +3868,27 @@ void wl_bss_iovar_war(struct bcm_cfg80211 *cfg, struct net_device *ndev, s32 *val) { - if (wl_customer6_legacy_chip_check(cfg, ndev)) { + u32 chipnum; + wlc_rev_info_t revinfo; + int ret; + bool need_war = false; + + /* Get the device rev info */ + memset(&revinfo, 0, sizeof(revinfo)); + ret = wldev_ioctl_get(ndev, WLC_GET_REVINFO, &revinfo, sizeof(revinfo)); + if (ret < 0) { + WL_ERR(("%s: GET revinfo FAILED. ret:%d\n", __FUNCTION__, ret)); + } else { + WL_DBG(("%s: GET_REVINFO device 0x%x, vendor 0x%x, chipnum 0x%x\n", __FUNCTION__, + dtoh32(revinfo.deviceid), dtoh32(revinfo.vendorid), dtoh32(revinfo.chipnum))); + chipnum = revinfo.chipnum; + if ((chipnum == BCM4359_CHIP_ID) || (chipnum == BCM43596_CHIP_ID)) { + /* WAR required */ + need_war = true; + } + } + + if (wl_customer6_legacy_chip_check(cfg, ndev) || need_war) { /* Few firmware branches have issues in bss iovar handling and * that can't be changed since they are in production. */ @@ -4182,6 +4278,9 @@ wl_cfg80211_create_iface(struct wiphy *wiphy, wl_if_event_info *event = NULL; u8 addr[ETH_ALEN]; struct net_info *iter, *next; +#ifdef WLMESH + u16 role = 0, mode = 0; +#endif WL_DBG(("Enter\n")); if (!name) { @@ -4283,6 +4382,11 @@ wl_cfg80211_create_iface(struct wiphy *wiphy, } event = &cfg->if_event_info; +#ifdef WLMESH + cfg80211_to_wl_iftype(iface_type, &role, &mode); + event->role = role; +#endif + /* * Since FW operation is successful,we can go ahead with the * the host interface creation. @@ -4359,6 +4463,157 @@ wl_cfg80211_del_iface(struct wiphy *wiphy, bcm_struct_cfgdev *cfgdev) } #endif /* defined(WL_VIRTUAL_APSTA) || defined(DUAL_STA_STATIC_IF) */ +#ifdef WLMESH +s32 wl_cfg80211_set_sae_password(struct net_device *dev, char* buf, int len) +{ + struct bcm_cfg80211 *cfg = wl_get_cfg(dev); + + sscanf(buf, "%s %d", cfg->sae_password, &cfg->sae_password_len); + return 0; +} + +static s32 wl_cfg80211_join_mesh( + struct wiphy *wiphy, struct net_device *dev, + const struct mesh_config *conf, + const struct mesh_setup *setup) +{ + struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)) + struct ieee80211_channel *chan = setup->chandef.chan; +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION (3, 6, 0)) + struct ieee80211_channel *chan = setup->channel; +#endif + u32 param[2] = {0, 0}; + s32 err = 0; + u32 bw_cap = 0; + u32 beacon_interval = setup->beacon_interval; + u32 dtim_period = setup->dtim_period; + size_t join_params_size; + struct wl_join_params join_params; + chanspec_t chanspec = 0; + + cfg->channel = ieee80211_frequency_to_channel(chan->center_freq); + + if (wl_get_drv_status(cfg, CONNECTED, dev)) { + struct wlc_ssid *lssid = (struct wlc_ssid *)wl_read_prof(cfg, dev, WL_PROF_SSID); + u8 *bssid = (u8 *)wl_read_prof(cfg, dev, WL_PROF_BSSID); + u32 *channel = (u32 *)wl_read_prof(cfg, dev, WL_PROF_CHAN); + if ((memcmp(setup->mesh_id, lssid->SSID, lssid->SSID_len) == 0) && + (*channel == cfg->channel)) { + WL_ERR(("MESH connection already existed to " MACDBG "\n", + MAC2STRDBG((u8 *)wl_read_prof(cfg, dev, WL_PROF_BSSID)))); + return -EISCONN; + } + WL_ERR(("Previous connecton existed, please disconnect mesh %s (" MACDBG ") first\n", + lssid->SSID, MAC2STRDBG(bssid))); + return -EISCONN; + } + + if (chan) { + if (chan->band == IEEE80211_BAND_5GHZ) + param[0] = WLC_BAND_5G; + else if (chan->band == IEEE80211_BAND_2GHZ) + param[0] = WLC_BAND_2G; + err = wldev_iovar_getint(dev, "bw_cap", param); + if (unlikely(err)) { + WL_ERR(("Get bw_cap Failed (%d)\n", err)); + return err; + } + bw_cap = param[0]; + chanspec = channel_to_chanspec(wiphy, dev, cfg->channel, bw_cap); + } + + memset(&join_params, 0, sizeof(join_params)); + memcpy((void *)join_params.ssid.SSID, (void *)setup->mesh_id, + setup->mesh_id_len); + + join_params.ssid.SSID_len = htod32(setup->mesh_id_len); + join_params.params.chanspec_list[0] = chanspec; + join_params.params.chanspec_num = 1; + wldev_iovar_setint(dev, "chanspec", chanspec); + join_params_size = sizeof(join_params); + + wldev_iovar_setint(dev, "wpa_auth", WPA_AUTH_DISABLED); + wldev_iovar_setint(dev, "wsec", 0); + + if (cfg->sae_password_len > 0) { + wldev_iovar_setint(dev, "mesh_auth_proto", 1); + wldev_iovar_setint(dev, "wpa_auth", WPA2_AUTH_PSK); + wldev_iovar_setint(dev, "wsec", AES_ENABLED); + wldev_iovar_setint(dev, "mfp", WL_MFP_REQUIRED); + printf("%s: password=%s, len=%d\n", __FUNCTION__, + cfg->sae_password, cfg->sae_password_len); + wldev_iovar_setbuf(dev, "sae_password", cfg->sae_password, cfg->sae_password_len, + cfg->ioctl_buf, WLC_IOCTL_MAXLEN, NULL); + } else { + wldev_iovar_setint(dev, "mesh_auth_proto", 0); + wldev_iovar_setint(dev, "mfp", WL_MFP_NONE); + } + + if (beacon_interval) { + if ((err = wldev_ioctl_set(dev, WLC_SET_BCNPRD, + &beacon_interval, sizeof(s32))) < 0) { + WL_ERR(("Beacon Interval Set Error, %d\n", err)); + return err; + } + } + + if (dtim_period) { + if ((err = wldev_ioctl_set(dev, WLC_SET_DTIMPRD, + &dtim_period, sizeof(s32))) < 0) { + WL_ERR(("DTIM Interval Set Error, %d\n", err)); + return err; + } + } + wldev_iovar_setint(dev, "mpc", 0); + + WL_ERR(("JOIN %s on channel %d with chanspec 0x%4x\n", + join_params.ssid.SSID, cfg->channel, chanspec)); + + err = wldev_ioctl_set(dev, WLC_SET_SSID, &join_params, + join_params_size); + + if (unlikely(err)) { + WL_ERR(("Error (%d)\n", err)); + return err; + } + + wl_update_prof(cfg, dev, NULL, &join_params.ssid, WL_PROF_SSID); + wl_update_prof(cfg, dev, NULL, &cfg->channel, WL_PROF_CHAN); + return err; +} + + +static s32 wl_cfg80211_leave_mesh( + struct wiphy *wiphy, struct net_device *dev) +{ + struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); + s32 err = 0; + scb_val_t scbval; + u8 *curbssid; + + RETURN_EIO_IF_NOT_UP(cfg); + wl_link_down(cfg); + + WL_ERR(("Leave MESH\n")); + curbssid = wl_read_prof(cfg, dev, WL_PROF_BSSID); + wl_set_drv_status(cfg, DISCONNECTING, dev); + scbval.val = 0; + memcpy(&scbval.ea, curbssid, ETHER_ADDR_LEN); + err = wldev_ioctl_set(dev, WLC_DISASSOC, &scbval, + sizeof(scb_val_t)); + if (unlikely(err)) { + wl_clr_drv_status(cfg, DISCONNECTING, dev); + WL_ERR(("error(%d)\n", err)); + return err; + } + memset(cfg->sae_password, 0, SAE_MAX_PASSWD_LEN); + cfg->sae_password_len = 0; + + return err; +} +#endif /* WLMESH */ + static s32 wl_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ibss_params *params) @@ -4824,7 +5079,7 @@ wl_cfg80211_set_mfp(struct bcm_cfg80211 *cfg, /* if mfp > 0, mfp capability set in wpa ie, but * FW indicated error for mfp. Propagate the error up. */ - WL_ERR(("mfp capability found in wpaie. But fw doesn't" + WL_ERR(("mfp capability found in wpaie. But fw doesn't " "seem to support MFP\n")); return -EINVAL; } else { @@ -5123,6 +5378,9 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, WL_DBG(("In\n")); BCM_REFERENCE(dhdp); +#ifdef WLMESH + wl_config_ifmode(cfg, dev, dev->ieee80211_ptr->iftype); +#endif #if defined(SUPPORT_RANDOM_MAC_SCAN) wl_cfg80211_set_random_mac(dev, FALSE); #endif /* SUPPORT_RANDOM_MAC_SCAN */ @@ -5181,7 +5439,11 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, * A start scan occuring during connect is unlikely */ if (cfg->sched_scan_req) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) + wl_cfg80211_sched_scan_stop(wiphy, bcmcfg_to_prmry_ndev(cfg), 0); +#else wl_cfg80211_sched_scan_stop(wiphy, bcmcfg_to_prmry_ndev(cfg)); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ } #endif #if defined(ESCAN_RESULT_PATCH) @@ -5391,7 +5653,7 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, DHD_DISABLE_RUNTIME_PM((dhd_pub_t *)cfg->pub); #endif /* BCMDONGLEHOST && CUSTOMER_HW2 */ #ifdef WL_EXT_IAPSTA - wl_android_ext_iapsta_disconnect_sta(dev, cfg->channel); + wl_ext_iapsta_disconnect_sta(dev, cfg->channel); #endif err = wldev_iovar_setbuf_bsscfg(dev, "join", ext_join_params, join_params_size, cfg->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &cfg->ioctl_buf_sync); @@ -5783,9 +6045,18 @@ wl_cfg80211_interface_create(struct net_device *dev, char *name) { struct bcm_cfg80211 *cfg = wl_get_cfg(dev); bcm_struct_cfgdev *new_cfgdev; + char ifname[IFNAMSIZ]; + char iftype[IFNAMSIZ]; + enum nl80211_iftype iface_type = NL80211_IFTYPE_STATION; + + sscanf(name, "%s %s", ifname, iftype); + + if (strnicmp(iftype, "AP", strlen("AP")) == 0) { + iface_type = NL80211_IFTYPE_AP; + } new_cfgdev = wl_cfg80211_create_iface(cfg->wdev->wiphy, - NL80211_IFTYPE_STATION, NULL, name); + iface_type, NULL, ifname); if (!new_cfgdev) { return BCME_ERROR; } @@ -6116,14 +6387,6 @@ wl_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, #endif /* MFP */ } -#if defined(RSSIAVG) -static wl_rssi_cache_ctrl_t g_rssi_cache_ctrl; -static wl_rssi_cache_ctrl_t g_connected_rssi_cache_ctrl; -#endif -#if defined(BSSCACHE) -static wl_bss_cache_ctrl_t g_bss_cache_ctrl; -#endif - static s32 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) wl_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, @@ -6281,13 +6544,13 @@ wl_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, } rssi = dtoh32(scb_val.val); #if defined(RSSIAVG) - err = wl_update_connected_rssi_cache(dev, &g_connected_rssi_cache_ctrl, &rssi); + err = wl_update_connected_rssi_cache(dev, &cfg->g_connected_rssi_cache_ctrl, &rssi); if (err) { WL_ERR(("Could not get rssi (%d)\n", err)); goto get_station_err; } - wl_delete_dirty_rssi_cache(&g_connected_rssi_cache_ctrl); - wl_reset_rssi_cache(&g_connected_rssi_cache_ctrl); + wl_delete_dirty_rssi_cache(&cfg->g_connected_rssi_cache_ctrl); + wl_reset_rssi_cache(&cfg->g_connected_rssi_cache_ctrl); #endif #if defined(RSSIOFFSET) rssi = wl_update_rssi_offset(dev, rssi); @@ -7788,6 +8051,9 @@ wl_cfg80211_set_channel(struct wiphy *wiphy, struct net_device *dev, dev = ndev_to_wlc_ndev(dev, cfg); _chan = ieee80211_frequency_to_channel(chan->center_freq); +#ifdef WL_EXT_IAPSTA + _chan = wl_ext_iapsta_disconnect_sta(dev, _chan); +#endif printf("%s: netdev_ifidx(%d), chan_type(%d) target channel(%d) \n", __FUNCTION__, dev->ifindex, channel_type, _chan); @@ -8824,6 +9090,9 @@ wl_cfg80211_bcn_bringup_ap( s32 join_params_size = 0; s32 ap = 1; s32 wsec; +#ifdef WLMESH + bool retried = false; +#endif #ifdef SOFTAP_UAPSD_OFF uint32 wme_apsd = 0; #endif /* SOFTAP_UAPSD_OFF */ @@ -8960,6 +9229,9 @@ wl_cfg80211_bcn_bringup_ap( } #endif /* MFP */ +#ifdef WLMESH +ssid_retry: +#endif memset(&join_params, 0, sizeof(join_params)); /* join parameters starts with ssid */ join_params_size = sizeof(join_params.ssid); @@ -8992,6 +9264,13 @@ wl_cfg80211_bcn_bringup_ap( timeout = wait_event_interruptible_timeout(cfg->netif_change_event, wl_get_drv_status(cfg, AP_CREATED, dev), msecs_to_jiffies(MAX_AP_LINK_WAIT_TIME)); if (timeout <= 0 || !wl_get_drv_status(cfg, AP_CREATED, dev)) { +#ifdef WLMESH + if (!retried) { + retried = true; + WL_ERR(("Link up didn't come for AP interface. Try to set ssid again to recover it! \n")); + goto ssid_retry; + } +#endif WL_ERR(("Link up didn't come for AP interface. AP/GO creation failed! \n")); if (timeout == -ERESTARTSYS) { WL_ERR(("waitqueue was interrupted by a signal, returns -ERESTARTSYS\n")); @@ -9275,7 +9554,9 @@ wl_cfg80211_del_station( } else { #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)) */ #endif /* CUSTOM_BLOCK_DEAUTH_AT_EAP_FAILURE */ +#ifndef BCMDBUS dhd_wait_pend8021x(dev); +#endif /* !BCMDBUS */ scb_val.val = DOT11_RC_DEAUTH_LEAVING; err = wldev_ioctl_set(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, &scb_val, sizeof(scb_val_t)); @@ -9403,6 +9684,10 @@ wl_cfg80211_start_ap( s32 bssidx = 0; u32 dev_role = 0; dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub); +#ifdef WLMESH + struct wl_join_params join_params; + s32 join_params_size = 0; +#endif WL_DBG(("Enter \n")); @@ -9503,6 +9788,35 @@ wl_cfg80211_start_ap( // goto fail; } +#ifdef WLMESH + OSL_SLEEP(1000); + if ((dev_role == NL80211_IFTYPE_P2P_GO) || (dev_role == NL80211_IFTYPE_AP)) { + memset(&join_params, 0, sizeof(join_params)); + /* join parameters starts with ssid */ + join_params_size = sizeof(join_params.ssid); + if (dev_role == NL80211_IFTYPE_P2P_GO) { + join_params.ssid.SSID_len = min(cfg->p2p->ssid.SSID_len, + (uint32)DOT11_MAX_SSID_LEN); + memcpy(join_params.ssid.SSID, cfg->p2p->ssid.SSID, + join_params.ssid.SSID_len); + } else if (dev_role == NL80211_IFTYPE_AP) { + join_params.ssid.SSID_len = min(cfg->hostapd_ssid.SSID_len, + (uint32)DOT11_MAX_SSID_LEN); + memcpy(join_params.ssid.SSID, cfg->hostapd_ssid.SSID, + join_params.ssid.SSID_len); + } + join_params.ssid.SSID_len = htod32(join_params.ssid.SSID_len); + /* create softap */ + if ((err = wldev_ioctl_set(dev, WLC_SET_SSID, &join_params, + join_params_size)) != 0) { + WL_ERR(("SoftAP/GO set ssid failed! \n")); + goto fail; + } else { + WL_DBG((" SoftAP SSID \"%s\" \n", join_params.ssid.SSID)); + } + } +#endif + WL_DBG(("** AP/GO Created **\n")); #ifdef WL_CFG80211_ACL @@ -10126,7 +10440,11 @@ wl_cfg80211_sched_scan_start(struct wiphy *wiphy, } static int -wl_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev) +wl_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) + , u64 reqid +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ +) { struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); dhd_pub_t *dhdp = (dhd_pub_t *)(cfg->pub); @@ -10412,6 +10730,10 @@ static struct cfg80211_ops wl_cfg80211_ops = { .change_station = wl_cfg80211_change_station, .mgmt_tx_cancel_wait = wl_cfg80211_mgmt_tx_cancel_wait, #endif /* WL_SUPPORT_BACKPORTED_KPATCHES || KERNEL_VERSION >= (3,2,0) */ +#ifdef WLMESH + .join_mesh = wl_cfg80211_join_mesh, + .leave_mesh = wl_cfg80211_leave_mesh, +#endif /* WLMESH */ #if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 2, 0)) .tdls_mgmt = wl_cfg80211_tdls_mgmt, .tdls_oper = wl_cfg80211_tdls_oper, @@ -10440,6 +10762,10 @@ s32 wl_mode_to_nl80211_iftype(s32 mode) return NL80211_IFTYPE_ADHOC; case WL_MODE_AP: return NL80211_IFTYPE_AP; +#ifdef WLMESH + case WL_MODE_MESH: + return NL80211_IFTYPE_MESH_POINT; +#endif default: return NL80211_IFTYPE_UNSPECIFIED; } @@ -10552,8 +10878,15 @@ static s32 wl_setup_wiphy(struct wireless_dev *wdev, struct device *sdiofunc_dev wdev->wiphy->max_sched_scan_ssids = MAX_PFN_LIST_COUNT; wdev->wiphy->max_match_sets = MAX_PFN_LIST_COUNT; wdev->wiphy->max_sched_scan_ie_len = WL_SCAN_IE_LEN_MAX; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) + wdev->wiphy->max_sched_scan_plan_interval = PNO_SCAN_MAX_FW_SEC; +#else wdev->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ #endif /* WL_SCHED_SCAN */ +#ifdef WLMESH + wdev->wiphy->flags |= WIPHY_FLAG_MESH_AUTH; +#endif wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) @@ -10567,13 +10900,18 @@ static s32 wl_setup_wiphy(struct wireless_dev *wdev, struct device *sdiofunc_dev #if defined(WL_CFG80211_P2P_DEV_IF) | BIT(NL80211_IFTYPE_P2P_DEVICE) #endif /* WL_CFG80211_P2P_DEV_IF */ +#ifdef WLMESH + | BIT(NL80211_IFTYPE_MESH_POINT) +#endif /* WLMESH */ | BIT(NL80211_IFTYPE_AP); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)) && \ (defined(WL_IFACE_COMB_NUM_CHANNELS) || defined(WL_CFG80211_P2P_DEV_IF)) WL_DBG(("Setting interface combinations for common mode\n")); +#ifndef BCMDBUS if (dhd->conf->num_different_channels >= 0) common_iface_combinations[0].num_different_channels = dhd->conf->num_different_channels; +#endif /* !BCMDBUS */ wdev->wiphy->iface_combinations = common_iface_combinations; wdev->wiphy->n_iface_combinations = ARRAY_SIZE(common_iface_combinations); @@ -10761,8 +11099,8 @@ static s32 wl_inform_bss(struct bcm_cfg80211 *cfg) struct wl_bss_info *bi = NULL; /* must be initialized */ s32 err = 0; s32 i; -#if defined(RSSIAVG) struct net_device *ndev = bcmcfg_to_prmry_ndev(cfg); +#if defined(RSSIAVG) int rssi; #endif #if defined(BSSCACHE) @@ -10774,18 +11112,18 @@ static s32 wl_inform_bss(struct bcm_cfg80211 *cfg) /* Free cache in p2p scanning*/ if (p2p_is_on(cfg) && p2p_scan(cfg)) { #if defined(RSSIAVG) - wl_free_rssi_cache(&g_rssi_cache_ctrl); + wl_free_rssi_cache(&cfg->g_rssi_cache_ctrl); #endif #if defined(BSSCACHE) - wl_free_bss_cache(&g_bss_cache_ctrl); + wl_free_bss_cache(&cfg->g_bss_cache_ctrl); #endif } /* Delete disconnected cache */ #if defined(BSSCACHE) - wl_delete_disconnected_bss_cache(&g_bss_cache_ctrl, (u8*)&cfg->disconnected_bssid); + wl_delete_disconnected_bss_cache(&cfg->g_bss_cache_ctrl, (u8*)&cfg->disconnected_bssid); #if defined(RSSIAVG) - wl_delete_disconnected_rssi_cache(&g_rssi_cache_ctrl, (u8*)&cfg->disconnected_bssid); + wl_delete_disconnected_rssi_cache(&cfg->g_rssi_cache_ctrl, (u8*)&cfg->disconnected_bssid); #endif if (cfg->p2p_disconnected == 0) memset(&cfg->disconnected_bssid, 0, ETHER_ADDR_LEN); @@ -10793,43 +11131,45 @@ static s32 wl_inform_bss(struct bcm_cfg80211 *cfg) /* Update cache */ #if defined(RSSIAVG) - wl_update_rssi_cache(&g_rssi_cache_ctrl, bss_list); + wl_update_rssi_cache(&cfg->g_rssi_cache_ctrl, bss_list); if (!in_atomic()) - wl_update_connected_rssi_cache(ndev, &g_rssi_cache_ctrl, &rssi); + wl_update_connected_rssi_cache(ndev, &cfg->g_rssi_cache_ctrl, &rssi); #endif #if defined(BSSCACHE) - wl_update_bss_cache(&g_bss_cache_ctrl, + wl_update_bss_cache(&cfg->g_bss_cache_ctrl, #if defined(RSSIAVG) - &g_rssi_cache_ctrl, + &cfg->g_rssi_cache_ctrl, #endif bss_list); #endif /* delete dirty cache */ #if defined(RSSIAVG) - wl_delete_dirty_rssi_cache(&g_rssi_cache_ctrl); - wl_reset_rssi_cache(&g_rssi_cache_ctrl); + wl_delete_dirty_rssi_cache(&cfg->g_rssi_cache_ctrl); + wl_reset_rssi_cache(&cfg->g_rssi_cache_ctrl); #endif #if defined(BSSCACHE) - wl_delete_dirty_bss_cache(&g_bss_cache_ctrl); - wl_reset_bss_cache(&g_bss_cache_ctrl); + wl_delete_dirty_bss_cache(&cfg->g_bss_cache_ctrl); + wl_reset_bss_cache(&cfg->g_bss_cache_ctrl); #endif #if defined(BSSCACHE) if (cfg->p2p_disconnected > 0) { // terence 20130703: Fix for wrong group_capab (timing issue) - wl_delete_disconnected_bss_cache(&g_bss_cache_ctrl, (u8*)&cfg->disconnected_bssid); + wl_delete_disconnected_bss_cache(&cfg->g_bss_cache_ctrl, (u8*)&cfg->disconnected_bssid); #if defined(RSSIAVG) - wl_delete_disconnected_rssi_cache(&g_rssi_cache_ctrl, (u8*)&cfg->disconnected_bssid); + wl_delete_disconnected_rssi_cache(&cfg->g_rssi_cache_ctrl, (u8*)&cfg->disconnected_bssid); #endif } WL_SCAN(("scanned AP count (%d)\n", bss_list->count)); - node = g_bss_cache_ctrl.m_cache_head; + node = cfg->g_bss_cache_ctrl.m_cache_head; for (i=0; node && iresults.bss_info; err = wl_inform_single_bss(cfg, bi, false); node = node->next; } + if (cfg->autochannel) + wl_ext_get_best_channel(ndev, &cfg->g_bss_cache_ctrl, &cfg->best_2g_ch, &cfg->best_5g_ch); #else WL_SCAN(("scanned AP count (%d)\n", bss_list->count)); preempt_disable(); @@ -10840,6 +11180,8 @@ static s32 wl_inform_bss(struct bcm_cfg80211 *cfg) err = wl_inform_single_bss(cfg, bi, false); } preempt_enable(); + if (cfg->autochannel) + wl_ext_get_best_channel(ndev, bss_list, &cfg->best_2g_ch, &cfg->best_5g_ch); #endif if (cfg->p2p_disconnected > 0) { @@ -10874,6 +11216,7 @@ static s32 wl_inform_single_bss(struct bcm_cfg80211 *cfg, struct wl_bss_info *bi u32 freq; s32 err = 0; gfp_t aflags; + chanspec_t chanspec; if (unlikely(dtoh32(bi->length) > WL_BSS_INFO_MAX)) { WL_DBG(("Beacon is larger than buffer. Discarding\n")); @@ -10887,8 +11230,8 @@ static s32 wl_inform_single_bss(struct bcm_cfg80211 *cfg, struct wl_bss_info *bi return -ENOMEM; } mgmt = (struct ieee80211_mgmt *)notif_bss_info->frame_buf; - notif_bss_info->channel = - wf_chspec_ctlchan(wl_chspec_driver_to_host(bi->chanspec)); + chanspec = wl_chspec_driver_to_host(bi->chanspec); + notif_bss_info->channel = wf_chspec_ctlchan(chanspec); if (notif_bss_info->channel <= CH_MAX_2G_CHANNEL) band = wiphy->bands[IEEE80211_BAND_2GHZ]; @@ -10901,7 +11244,7 @@ static s32 wl_inform_single_bss(struct bcm_cfg80211 *cfg, struct wl_bss_info *bi } notif_bss_info->rssi = dtoh16(bi->RSSI); #if defined(RSSIAVG) - notif_bss_info->rssi = wl_get_avg_rssi(&g_rssi_cache_ctrl, &bi->BSSID); + notif_bss_info->rssi = wl_get_avg_rssi(&cfg->g_rssi_cache_ctrl, &bi->BSSID); if (notif_bss_info->rssi == RSSI_MINVAL) notif_bss_info->rssi = MIN(dtoh16(bi->RSSI), RSSI_MAXVAL); #endif @@ -10943,8 +11286,12 @@ static s32 wl_inform_single_bss(struct bcm_cfg80211 *cfg, struct wl_bss_info *bi return -EINVAL; } channel = ieee80211_get_channel(wiphy, freq); - WL_SCAN(("BSSID %pM, channel %2d, rssi %3d, capa 0x04%x, mgmt_type %d, " - "frame_len %d, SSID \"%s\"\n", &bi->BSSID, notif_bss_info->channel, + WL_SCAN(("BSSID %pM, channel %2d(%2d %sMHz), rssi %3d, capa 0x04%x, mgmt_type %d, " + "frame_len %d, SSID \"%s\"\n", + &bi->BSSID, notif_bss_info->channel, CHSPEC_CHANNEL(chanspec), + CHSPEC_IS20(chanspec)?"20": + CHSPEC_IS40(chanspec)?"40": + CHSPEC_IS80(chanspec)?"80":"160", notif_bss_info->rssi, mgmt->u.beacon.capab_info, mgmt_type, notif_bss_info->frame_len, bi->SSID)); if (unlikely(!channel)) { @@ -11209,6 +11556,9 @@ wl_notify_connect_status_ap(struct bcm_cfg80211 *cfg, struct net_device *ndev, printf("%s: ** AP/GO Link up event **\n", __FUNCTION__); wl_set_drv_status(cfg, AP_CREATED, ndev); wake_up_interruptible(&cfg->netif_change_event); + if (!memcmp(ndev->name, WL_P2P_INTERFACE_PREFIX, strlen(WL_P2P_INTERFACE_PREFIX))) { + dhd_conf_set_mchan_bw(cfg->pub, WL_P2P_IF_GO, -1); + } return 0; } } @@ -11863,6 +12213,9 @@ wl_notify_connect_status(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev, wl_update_prof(cfg, ndev, e, &act, WL_PROF_ACT); wl_update_prof(cfg, ndev, NULL, (const void *)&e->addr, WL_PROF_BSSID); dhd_conf_set_wme(cfg->pub, 0); + if (!memcmp(ndev->name, WL_P2P_INTERFACE_PREFIX, strlen(WL_P2P_INTERFACE_PREFIX))) { + dhd_conf_set_mchan_bw(cfg->pub, WL_P2P_IF_CLIENT, -1); + } } else if (WL_IS_LINKDOWN(cfg, e, data) || ((event == WLC_E_SET_SSID) && (ntoh32(e->status) != WLC_E_STATUS_SUCCESS) && @@ -11907,6 +12260,27 @@ wl_notify_connect_status(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev, wl_get_bss_info(cfg, ndev, (u8*)(&e->addr)); } #endif /* DHD_ENABLE_BIGDATA_LOGGING */ + if (wl_get_drv_status(cfg, CONNECTED, ndev)) { + u8 *curbssid = wl_read_prof(cfg, ndev, WL_PROF_BSSID); + if (memcmp(curbssid, &e->addr, ETHER_ADDR_LEN) != 0) { + bool fw_assoc_state = TRUE; + dhd_pub_t *dhd = (dhd_pub_t *)cfg->pub; + fw_assoc_state = dhd_is_associated(dhd, e->ifidx, &err); + if (!fw_assoc_state) { + WL_ERR(("Event sends up even different BSSID" + " cur: " MACDBG " event: " MACDBG"\n", + MAC2STRDBG(curbssid), + MAC2STRDBG((const u8*)(&e->addr)))); + } else { + WL_ERR(("BSSID of event is not the connected BSSID" + "(ignore it) cur: " MACDBG + " event: " MACDBG"\n", + MAC2STRDBG(curbssid), + MAC2STRDBG((const u8*)(&e->addr)))); + return 0; + } + } + } /* Explicitly calling unlink to remove BSS in CFG */ wiphy = bcmcfg_to_wiphy(cfg); ssid = (struct wlc_ssid *)wl_read_prof(cfg, ndev, WL_PROF_SSID); @@ -12096,8 +12470,10 @@ wl_notify_connect_status(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev, } DHD_ENABLE_RUNTIME_PM((dhd_pub_t *)cfg->pub); } - else { - WL_ERR(("Invalid ndev status %d\n", wl_get_mode_by_netdev(cfg, ndev))); + else { + printf("wl_notify_connect_status : Invalid %s mode %d event %d status %d\n", + ndev->name, wl_get_mode_by_netdev(cfg, ndev), ntoh32(e->event_type), + ntoh32(e->status)); } return err; } @@ -12694,6 +13070,9 @@ wl_bss_roaming_done(struct bcm_cfg80211 *cfg, struct net_device *ndev, #if defined(WLADPS_SEAK_AP_WAR) || defined(WBTEXT) dhd_pub_t *dhdp = (dhd_pub_t *)(cfg->pub); #endif /* WLADPS_SEAK_AP_WAR || WBTEXT */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) + struct cfg80211_roam_info roam_info = {}; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ #ifdef WLADPS_SEAK_AP_WAR BCM_REFERENCE(dhdp); @@ -12758,6 +13137,18 @@ wl_bss_roaming_done(struct bcm_cfg80211 *cfg, struct net_device *ndev, MAC2STRDBG((const u8*)(&e->addr)), *channel); dhd_conf_set_wme(cfg->pub, 0); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) + roam_info.channel = notify_channel; + roam_info.bssid = curbssid; + roam_info.req_ie = conn_info->req_ie; + roam_info.req_ie_len = conn_info->req_ie_len; + roam_info.resp_ie = conn_info->resp_ie; + roam_info.resp_ie_len = conn_info->resp_ie_len; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) + cfg80211_roamed(ndev, &roam_info, GFP_KERNEL); +#else cfg80211_roamed(ndev, #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) notify_channel, @@ -12765,6 +13156,7 @@ wl_bss_roaming_done(struct bcm_cfg80211 *cfg, struct net_device *ndev, curbssid, conn_info->req_ie, conn_info->req_ie_len, conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ WL_DBG(("Report roaming result\n")); memcpy(&cfg->last_roamed_addr, &e->addr, ETHER_ADDR_LEN); @@ -14077,10 +14469,20 @@ void wl_terminate_event_handler(struct net_device *dev) } } -static void wl_scan_timeout(unsigned long data) +static void wl_scan_timeout( +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + struct timer_list *t +#else + unsigned long data +#endif +) { wl_event_msg_t msg; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + struct bcm_cfg80211 *cfg = from_timer(cfg, t, scan_timeout); +#else struct bcm_cfg80211 *cfg = (struct bcm_cfg80211 *)data; +#endif struct wireless_dev *wdev = NULL; struct net_device *ndev = NULL; struct wl_scan_results *bss_list; @@ -14161,9 +14563,19 @@ static void wl_del_roam_timeout(struct bcm_cfg80211 *cfg) } -static void wl_roam_timeout(unsigned long data) +static void wl_roam_timeout( +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + struct timer_list *t +#else + unsigned long data +#endif +) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + struct bcm_cfg80211 *cfg = from_timer(cfg, t, roam_timeout); +#else struct bcm_cfg80211 *cfg = (struct bcm_cfg80211 *)data; +#endif dhd_pub_t *dhdp = (dhd_pub_t *)(cfg->pub); WL_ERR(("roam timer expired\n")); @@ -14371,8 +14783,13 @@ static s32 wl_notify_escan_complete(struct bcm_cfg80211 *cfg, #ifdef WL_SCHED_SCAN if (cfg->sched_scan_req && !cfg->scan_request) { WL_PNO((">>> REPORTING SCHED SCAN RESULTS \n")); - if (!aborted) + if (!aborted) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) + cfg80211_sched_scan_results(cfg->sched_scan_req->wiphy, 0); +#else cfg80211_sched_scan_results(cfg->sched_scan_req->wiphy); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ + } DBG_EVENT_LOG(dhdp, WIFI_EVENT_DRIVER_PNO_SCAN_COMPLETE); cfg->sched_scan_running = FALSE; @@ -15197,9 +15614,13 @@ static s32 wl_init_scan(struct bcm_cfg80211 *cfg) wl_escan_init_sync_id(cfg); /* Init scan_timeout timer */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + timer_setup(&cfg->scan_timeout, wl_scan_timeout, 0); +#else init_timer(&cfg->scan_timeout); cfg->scan_timeout.data = (unsigned long) cfg; cfg->scan_timeout.function = wl_scan_timeout; +#endif return err; } @@ -15210,9 +15631,13 @@ static s32 wl_init_roam_timeout(struct bcm_cfg80211 *cfg) int err = 0; /* Init roam timer */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + timer_setup(&cfg->roam_timeout, wl_roam_timeout, 0); +#else init_timer(&cfg->roam_timeout); cfg->roam_timeout.data = (unsigned long) cfg; cfg->roam_timeout.function = wl_roam_timeout; +#endif return err; } @@ -15234,9 +15659,9 @@ static s32 wl_init_priv(struct bcm_cfg80211 *cfg) cfg->active_scan = true; cfg->rf_blocked = false; cfg->vsdb_mode = false; -#if defined(BCMSDIO) +#if defined(BCMSDIO) || defined(BCMDBUS) cfg->wlfc_on = false; -#endif +#endif /* BCMSDIO || BCMDBUS */ cfg->roam_flags |= WL_ROAM_OFF_ON_CONCURRENT; cfg->disable_roam_event = false; /* register interested state */ @@ -15433,7 +15858,11 @@ s32 wl_cfg80211_attach(struct net_device *ndev, void *context) kfree(wdev); return -ENOMEM; } +#ifdef WLMESH + wdev->iftype = wl_mode_to_nl80211_iftype(WL_MODE_MESH); +#else wdev->iftype = wl_mode_to_nl80211_iftype(WL_MODE_BSS); +#endif cfg = wiphy_priv(wdev->wiphy); cfg->wdev = wdev; cfg->pub = context; @@ -15560,14 +15989,14 @@ void wl_cfg80211_detach(struct bcm_cfg80211 *cfg) wl_cfg80211_clear_mgmt_vndr_ies(cfg); wl_deinit_priv(cfg); wl_cfg80211_clear_parent_dev(); - wl_free_wdev(cfg); #if defined(RSSIAVG) - wl_free_rssi_cache(&g_rssi_cache_ctrl); - wl_free_rssi_cache(&g_connected_rssi_cache_ctrl); + wl_free_rssi_cache(&cfg->g_rssi_cache_ctrl); + wl_free_rssi_cache(&cfg->g_connected_rssi_cache_ctrl); #endif #if defined(BSSCACHE) - wl_release_bss_cache_ctrl(&g_bss_cache_ctrl); + wl_release_bss_cache_ctrl(&cfg->g_bss_cache_ctrl); #endif + wl_free_wdev(cfg); /* PLEASE do NOT call any function after wl_free_wdev, the driver's private * structure "cfg", which is the private part of wiphy, has been freed in * wl_free_wdev !!!!!!!!!!! @@ -15761,6 +16190,12 @@ static s32 wl_config_ifmode(struct bcm_cfg80211 *cfg, struct net_device *ndev, s mode = WL_MODE_BSS; infra = 1; break; +#ifdef WLMESH + case NL80211_IFTYPE_MESH_POINT: + mode = WL_MODE_MESH; + infra = WL_BSSTYPE_MESH; + break; +#endif /* WLMESH */ case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: mode = WL_MODE_AP; @@ -16375,9 +16810,9 @@ static s32 __wl_cfg80211_down(struct bcm_cfg80211 *cfg) struct net_device *p2p_net = cfg->p2p_net; #endif #ifdef PROP_TXSTATUS_VSDB -#if defined(BCMSDIO) +#if defined(BCMSDIO) || defined(BCMDBUS) dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub); -#endif +#endif /* BCMSDIO || BCMDBUS */ #endif /* PROP_TXSTATUS_VSDB */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)) struct cfg80211_scan_info info; @@ -16405,7 +16840,7 @@ static s32 __wl_cfg80211_down(struct bcm_cfg80211 *cfg) if (cfg->p2p_supported) { wl_clr_p2p_status(cfg, GO_NEG_PHASE); #ifdef PROP_TXSTATUS_VSDB -#if defined(BCMSDIO) +#if defined(BCMSDIO) || defined(BCMDBUS) if (wl_cfgp2p_vif_created(cfg)) { bool enabled = false; dhd_wlfc_get_enable(dhd, &enabled); @@ -16415,7 +16850,7 @@ static s32 __wl_cfg80211_down(struct bcm_cfg80211 *cfg) cfg->wlfc_on = false; } } -#endif +#endif /* BCMSDIO || BCMDBUS */ #endif /* PROP_TXSTATUS_VSDB */ } @@ -16583,6 +17018,10 @@ s32 wl_cfg80211_up(struct net_device *net) return err; } } +#ifdef WLMESH + cfg->wdev->wiphy->features |= NL80211_FEATURE_USERSPACE_MPM; +#endif /* WLMESH */ + err = __wl_cfg80211_up(cfg); if (unlikely(err)) WL_ERR(("__wl_cfg80211_up failed\n")); @@ -16658,10 +17097,10 @@ int wl_cfg80211_hang(struct net_device *dev, u16 reason) CFG80211_DISCONNECTED(dev, reason, NULL, 0, false, GFP_KERNEL); } #if defined(RSSIAVG) - wl_free_rssi_cache(&g_rssi_cache_ctrl); + wl_free_rssi_cache(&cfg->g_rssi_cache_ctrl); #endif #if defined(BSSCACHE) - wl_free_bss_cache(&g_bss_cache_ctrl); + wl_free_bss_cache(&cfg->g_bss_cache_ctrl); #endif if (cfg != NULL) { wl_link_down(cfg); @@ -16679,10 +17118,10 @@ s32 wl_cfg80211_down(struct net_device *dev) return err; mutex_lock(&cfg->usr_sync); #if defined(RSSIAVG) - wl_free_rssi_cache(&g_rssi_cache_ctrl); + wl_free_rssi_cache(&cfg->g_rssi_cache_ctrl); #endif #if defined(BSSCACHE) - wl_free_bss_cache(&g_bss_cache_ctrl); + wl_free_bss_cache(&cfg->g_bss_cache_ctrl); #endif err = __wl_cfg80211_down(cfg); mutex_unlock(&cfg->usr_sync); @@ -17489,13 +17928,14 @@ wl_cfg80211_get_best_channel(struct net_device *ndev, void *buf, int buflen, ret = wldev_ioctl_get(ndev, WLC_GET_CHANNEL_SEL, &chosen, sizeof(chosen)); if ((ret == 0) && (dtoh32(chosen) != 0)) { chip = dhd_conf_get_chip(dhd_get_pub(ndev)); - if (chip != BCM43362_CHIP_ID && chip != BCM4330_CHIP_ID) { + if (chip != BCM43362_CHIP_ID && chip != BCM4330_CHIP_ID && + chip != BCM43143_CHIP_ID) { u32 chanspec = 0; int ctl_chan; chanspec = wl_chspec_driver_to_host(chosen); - printf("selected chanspec = 0x%x\n", chanspec); + WL_INFORM(("selected chanspec = 0x%x\n", chanspec)); ctl_chan = wf_chspec_ctlchan(chanspec); - printf("selected ctl_chan = %d\n", ctl_chan); + WL_INFORM(("selected ctl_chan = %d\n", ctl_chan)); *channel = (u16)(ctl_chan & 0x00FF); } else *channel = (u16)(chosen & 0x00FF); @@ -17597,8 +18037,10 @@ wl_cfg80211_get_best_channels(struct net_device *dev, char* cmd, int total_len) // terence 20140120: fix for some chipsets only return 2.4GHz channel (4330b2/43341b0/4339a0) band = band_cur==WLC_BAND_2G ? band_cur : WLC_BAND_5G; ret = wldev_ioctl(dev, WLC_SET_BAND, &band, sizeof(band), true); - if (ret < 0) + if (ret < 0) { WL_ERR(("WLC_SET_BAND error %d\n", ret)); + goto done; + } /* Best channel selection in 5GHz band. */ ret = wl_cfg80211_get_chanspecs_5g(ndev, (void *)buf, CHANSPEC_BUF_SIZE); @@ -21233,3 +21675,24 @@ wl_set_rssi_logging(struct net_device *dev, void *param) return err; } #endif /* SUPPORT_RSSI_LOGGING */ + +s32 wl_cfg80211_autochannel(struct net_device *dev, char* command, int total_len) +{ + struct bcm_cfg80211 *cfg = wl_get_cfg(dev); + int ret = 0; + int bytes_written = -1; + + sscanf(command, "%*s %d", &cfg->autochannel); + + if (cfg->autochannel == 0) { + cfg->best_2g_ch = 0; + cfg->best_5g_ch = 0; + } else if (cfg->autochannel == 2) { + bytes_written = snprintf(command, total_len, "2g=%d 5g=%d", + cfg->best_2g_ch, cfg->best_5g_ch); + ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command)); + ret = bytes_written; + } + + return ret; +} diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfg80211.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfg80211.h old mode 100755 new mode 100644 index 9d06534d..395dfd5a --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfg80211.h +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfg80211.h @@ -45,6 +45,7 @@ #include #include #include +#include struct wl_conf; struct wl_iface; struct bcm_cfg80211; @@ -205,6 +206,11 @@ do { \ #define IEEE80211_BAND_5GHZ NL80211_BAND_5GHZ #define IEEE80211_NUM_BANDS NUM_NL80211_BANDS #endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)) +#ifdef WLMESH +#undef WLMESH +#endif +#endif #define WL_SCAN_RETRY_MAX 3 #define WL_NUM_PMKIDS_MAX MAXPMKID @@ -339,7 +345,10 @@ enum wl_status { enum wl_mode { WL_MODE_BSS, WL_MODE_IBSS, - WL_MODE_AP + WL_MODE_AP, +#ifdef WLMESH + WL_MODE_MESH +#endif }; /* driver profile list */ @@ -735,7 +744,7 @@ struct bcm_cfg80211 { bool pwr_save; bool roam_on; /* on/off switch for self-roaming */ bool scan_tried; /* indicates if first scan attempted */ -#if defined(BCMSDIO) || defined(BCMPCIE) +#if defined(BCMSDIO) || defined(BCMDBUS) bool wlfc_on; #endif bool vsdb_mode; @@ -845,9 +854,23 @@ struct bcm_cfg80211 { #ifdef STAT_REPORT void *stat_report_info; +#endif +#ifdef WLMESH + char sae_password[SAE_MAX_PASSWD_LEN]; + uint sae_password_len; +#endif /* WLMESH */ +#if defined(RSSIAVG) + wl_rssi_cache_ctrl_t g_rssi_cache_ctrl; + wl_rssi_cache_ctrl_t g_connected_rssi_cache_ctrl; +#endif +#if defined(BSSCACHE) + wl_bss_cache_ctrl_t g_bss_cache_ctrl; #endif int p2p_disconnected; // terence 20130703: Fix for wrong group_capab (timing issue) struct ether_addr disconnected_bssid; + int autochannel; + int best_2g_ch; + int best_5g_ch; }; #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == \ @@ -1462,6 +1485,9 @@ extern s32 wl_cfg80211_set_wps_p2p_ie(struct net_device *net, char *buf, int len extern s32 wl_cfg80211_set_p2p_ps(struct net_device *net, char* buf, int len); extern s32 wl_cfg80211_set_p2p_ecsa(struct net_device *net, char* buf, int len); extern s32 wl_cfg80211_increase_p2p_bw(struct net_device *net, char* buf, int len); +#ifdef WLMESH +extern s32 wl_cfg80211_set_sae_password(struct net_device *net, char* buf, int len); +#endif #ifdef WL11ULB extern s32 wl_cfg80211_set_ulb_mode(struct net_device *dev, int mode); extern s32 wl_cfg80211_set_ulb_bw(struct net_device *dev, @@ -1652,4 +1678,5 @@ int wl_cfg80211_iface_count(struct net_device *dev); struct net_device* wl_get_ap_netdev(struct bcm_cfg80211 *cfg, char *ifname); struct net_device* wl_get_netdev_by_name(struct bcm_cfg80211 *cfg, char *ifname); int wl_cfg80211_get_vndr_ouilist(struct bcm_cfg80211 *cfg, uint8 *buf, int max_cnt); +s32 wl_cfg80211_autochannel(struct net_device *dev, char* command, int total_len); #endif /* _wl_cfg80211_h_ */ diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfg_btcoex.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfg_btcoex.c old mode 100755 new mode 100644 index 4fd40fd7..6d62b3a4 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfg_btcoex.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfg_btcoex.c @@ -294,9 +294,19 @@ wl_cfg80211_bt_setflag(struct net_device *dev, bool set) #endif } -static void wl_cfg80211_bt_timerfunc(ulong data) +static void wl_cfg80211_bt_timerfunc( +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + struct timer_list *t +#else + unsigned long data +#endif +) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + struct btcoex_info *bt_local = from_timer(bt_local, t, timer); +#else struct btcoex_info *bt_local = (struct btcoex_info *)data; +#endif WL_TRACE(("Enter\n")); bt_local->timer_on = 0; schedule_work(&bt_local->work); @@ -393,9 +403,13 @@ void* wl_cfg80211_btcoex_init(struct net_device *ndev) btco_inf->ts_dhcp_ok = 0; /* Set up timer for BT */ btco_inf->timer_ms = 10; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + timer_setup(&btco_inf->timer, wl_cfg80211_bt_timerfunc, 0); +#else init_timer(&btco_inf->timer); btco_inf->timer.data = (ulong)btco_inf; btco_inf->timer.function = wl_cfg80211_bt_timerfunc; +#endif btco_inf->dev = ndev; diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfgp2p.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfgp2p.c old mode 100755 new mode 100644 index f0cf56e3..69f7a050 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfgp2p.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfgp2p.c @@ -56,6 +56,7 @@ #include #include #include +#include #if defined(BCMPCIE) && defined(DHD_FW_COREDUMP) extern int dhd_bus_mem_dump(dhd_pub_t *dhd); @@ -333,6 +334,9 @@ wl_cfgp2p_init_priv(struct bcm_cfg80211 *cfg) return -ENOMEM; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + cfg->p2p->cfg = cfg; +#endif wl_to_p2p_bss_ndev(cfg, P2PAPI_BSSCFG_PRIMARY) = bcmcfg_to_prmry_ndev(cfg); wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_PRIMARY) = 0; wl_to_p2p_bss_ndev(cfg, P2PAPI_BSSCFG_DEVICE) = NULL; @@ -1385,10 +1389,21 @@ wl_cfgp2p_listen_complete(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev, * so lets do it from thread context. */ void -wl_cfgp2p_listen_expired(unsigned long data) +wl_cfgp2p_listen_expired( +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + struct timer_list *t +#else + ulong data +#endif +) { wl_event_msg_t msg; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + struct p2p_info *p2p = from_timer(p2p, t, listen_timer); + struct bcm_cfg80211 *cfg = p2p->cfg; +#else struct bcm_cfg80211 *cfg = (struct bcm_cfg80211 *) data; +#endif struct net_device *ndev; CFGP2P_DBG((" Enter\n")); @@ -1742,6 +1757,8 @@ wl_cfgp2p_supported(struct bcm_cfg80211 *cfg, struct net_device *ndev) return ret; } } + if (cfg->pub->conf->fw_type == FW_TYPE_MESH) + p2p_supported = 0; if (p2p_supported == 1) { CFGP2P_INFO(("p2p is supported\n")); } else { @@ -1750,6 +1767,7 @@ wl_cfgp2p_supported(struct bcm_cfg80211 *cfg, struct net_device *ndev) } return p2p_supported; } + /* Cleanup P2P resources */ s32 wl_cfgp2p_down(struct bcm_cfg80211 *cfg) diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfgp2p.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfgp2p.h old mode 100755 new mode 100644 index dba6b478..ca930acc --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfgp2p.h +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfgp2p.h @@ -71,6 +71,9 @@ struct p2p_bss { }; struct p2p_info { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + struct bcm_cfg80211 *cfg; +#endif bool on; /**< p2p on/off switch */ bool scan; int16 search_state; @@ -183,6 +186,14 @@ enum wl_cfgp2p_status { printk args; \ } \ } while (0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) +#define INIT_TIMER(timer, func, duration, extra_delay) \ + do { \ + timer_setup(timer, func, 0); \ + timer->expires = jiffies + msecs_to_jiffies(duration + extra_delay); \ + add_timer(timer); \ + } while (0); +#else #define INIT_TIMER(timer, func, duration, extra_delay) \ do { \ init_timer(timer); \ @@ -191,6 +202,7 @@ enum wl_cfgp2p_status { timer->data = (unsigned long) cfg; \ add_timer(timer); \ } while (0); +#endif #if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 0, 8)) #ifdef WL_SUPPORT_BACKPORTED_KPATCHES @@ -245,7 +257,13 @@ enum wl_cfgp2p_status { #define P2P_ECSA_CNT 50 extern void -wl_cfgp2p_listen_expired(unsigned long data); +wl_cfgp2p_listen_expired( +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + struct timer_list *t +#else + ulong data +#endif +); extern bool wl_cfgp2p_is_pub_action(void *frame, u32 frame_len); extern bool diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfgvendor.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfgvendor.c index bd918e65..c5b4b2b0 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfgvendor.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_cfgvendor.c @@ -2322,10 +2322,15 @@ static int wl_cfgvendor_lstats_get_info(struct wiphy *wiphy, ((wl_cnt_info_t *)iovar_buf)->datalen, WL_CNT_XTLV_CNTV_LE10_UCODE, NULL, BCM_XTLV_OPTION_ALIGN32)) == NULL) { - macstat_cnt = bcm_get_data_from_xtlv_buf(((wl_cnt_info_t *)iovar_buf)->data, + if ((macstat_cnt = bcm_get_data_from_xtlv_buf(((wl_cnt_info_t *)iovar_buf)->data, ((wl_cnt_info_t *)iovar_buf)->datalen, WL_CNT_XTLV_GE40_UCODE_V1, NULL, + BCM_XTLV_OPTION_ALIGN32)) == NULL) { + macstat_cnt = bcm_get_data_from_xtlv_buf(((wl_cnt_info_t *)iovar_buf)->data, + ((wl_cnt_info_t *)iovar_buf)->datalen, + WL_CNT_XTLV_LT40_UCODE_V1, NULL, BCM_XTLV_OPTION_ALIGN32); + } } if (macstat_cnt == NULL) { diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_escan.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_escan.c index a5aa76ba..3778d497 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_escan.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_escan.c @@ -1,5 +1,4 @@ /* SPDX-License-Identifier: GPL-2.0 */ - #if defined(WL_ESCAN) #include @@ -29,21 +28,21 @@ #define ESCAN_ERROR(x) \ do { \ if (iw_msg_level & ESCAN_ERROR_LEVEL) { \ - printf(KERN_ERR "ESCAN-ERROR) "); \ + printf(KERN_ERR "ESCAN-ERROR) %s : ", __func__); \ printf x; \ } \ } while (0) #define ESCAN_SCAN(x) \ do { \ if (iw_msg_level & ESCAN_SCAN_LEVEL) { \ - printf(KERN_ERR "ESCAN-SCAN) "); \ + printf(KERN_ERR "ESCAN-SCAN) %s : ", __func__); \ printf x; \ } \ } while (0) #define ESCAN_TRACE(x) \ do { \ if (iw_msg_level & ESCAN_TRACE_LEVEL) { \ - printf(KERN_ERR "ESCAN-TRACE) "); \ + printf(KERN_ERR "ESCAN-TRACE) %s : ", __func__); \ printf x; \ } \ } while (0) @@ -73,15 +72,6 @@ typedef struct { #endif /* ESCAN_BUF_OVERFLOW_MGMT */ struct wl_escan_info *g_escan = NULL; - -#if defined(RSSIAVG) -static wl_rssi_cache_ctrl_t g_rssi_cache_ctrl; -static wl_rssi_cache_ctrl_t g_connected_rssi_cache_ctrl; -#endif -#if defined(BSSCACHE) -static wl_bss_cache_ctrl_t g_bss_cache_ctrl; -#endif - /* Return a new chanspec given a legacy chanspec * Returns INVCHANSPEC on error */ @@ -416,7 +406,7 @@ static s32 wl_escan_event_handler(void *data) } void -wl_escan_event(struct net_device *ndev, const wl_event_msg_t * e, void *data) +wl_escan_event(struct net_device *dev, const wl_event_msg_t * e, void *data) { u32 event_type = ntoh32(e->event_type); struct wl_escan_info *escan = g_escan; @@ -443,7 +433,7 @@ wl_escan_event(struct net_device *ndev, const wl_event_msg_t * e, void *data) } DHD_EVENT_WAKE_LOCK(escan->pub); - if (likely(!wl_enq_event(escan, ndev, event_type, e, data))) { + if (likely(!wl_enq_event(escan, dev, event_type, e, data))) { wl_wakeup_event(escan); } else { DHD_EVENT_WAKE_UNLOCK(escan->pub); @@ -462,34 +452,39 @@ static s32 wl_escan_inform_bss(struct wl_escan_info *escan) /* Delete disconnected cache */ #if defined(BSSCACHE) - wl_delete_disconnected_bss_cache(&g_bss_cache_ctrl, (u8*)&escan->disconnected_bssid); + wl_delete_disconnected_bss_cache(&escan->g_bss_cache_ctrl, (u8*)&escan->disconnected_bssid); #if defined(RSSIAVG) - wl_delete_disconnected_rssi_cache(&g_rssi_cache_ctrl, (u8*)&escan->disconnected_bssid); + wl_delete_disconnected_rssi_cache(&escan->g_rssi_cache_ctrl, (u8*)&escan->disconnected_bssid); #endif #endif /* Update cache */ #if defined(RSSIAVG) - wl_update_rssi_cache(&g_rssi_cache_ctrl, bss_list); + wl_update_rssi_cache(&escan->g_rssi_cache_ctrl, bss_list); if (!in_atomic()) - wl_update_connected_rssi_cache(escan->dev, &g_rssi_cache_ctrl, &rssi); + wl_update_connected_rssi_cache(escan->dev, &escan->g_rssi_cache_ctrl, &rssi); #endif #if defined(BSSCACHE) - wl_update_bss_cache(&g_bss_cache_ctrl, + wl_update_bss_cache(&escan->g_bss_cache_ctrl, #if defined(RSSIAVG) - &g_rssi_cache_ctrl, + &escan->g_rssi_cache_ctrl, #endif bss_list); #endif /* delete dirty cache */ #if defined(RSSIAVG) - wl_delete_dirty_rssi_cache(&g_rssi_cache_ctrl); - wl_reset_rssi_cache(&g_rssi_cache_ctrl); + wl_delete_dirty_rssi_cache(&escan->g_rssi_cache_ctrl); + wl_reset_rssi_cache(&escan->g_rssi_cache_ctrl); #endif #if defined(BSSCACHE) - wl_delete_dirty_bss_cache(&g_bss_cache_ctrl); - wl_reset_bss_cache(&g_bss_cache_ctrl); + wl_delete_dirty_bss_cache(&escan->g_bss_cache_ctrl); + wl_reset_bss_cache(&escan->g_bss_cache_ctrl); + if (escan->autochannel) + wl_ext_get_best_channel(escan->dev, &escan->g_bss_cache_ctrl, &escan->best_2g_ch, &escan->best_5g_ch); +#else + if (escan->autochannel) + wl_ext_get_best_channel(escan->dev, bss_list, &escan->best_2g_ch, &escan->best_5g_ch); #endif ESCAN_TRACE(("scanned AP count (%d)\n", bss_list->count)); @@ -849,20 +844,21 @@ static s32 wl_escan_handler(struct wl_escan_info *escan, } else if (status == WLC_E_STATUS_SUCCESS) { escan->escan_state = ESCAN_STATE_IDLE; - - ESCAN_TRACE(("ESCAN COMPLETED\n")); - escan->bss_list = wl_escan_get_buf(escan); - ESCAN_TRACE(("SCAN COMPLETED: scanned AP count=%d\n", - escan->bss_list->count)); - wl_escan_inform_bss(escan); - wl_notify_escan_complete(escan, false); - + ESCAN_TRACE(("ESCAN COMPLETED\n")); + escan->bss_list = wl_escan_get_buf(escan); + ESCAN_TRACE(("SCAN COMPLETED: scanned AP count=%d\n", + escan->bss_list->count)); + wl_escan_inform_bss(escan); + wl_notify_escan_complete(escan, false); } else if ((status == WLC_E_STATUS_ABORT) || (status == WLC_E_STATUS_NEWSCAN) || (status == WLC_E_STATUS_11HQUIET) || (status == WLC_E_STATUS_CS_ABORT) || (status == WLC_E_STATUS_NEWASSOC)) { /* Handle all cases of scan abort */ escan->escan_state = ESCAN_STATE_IDLE; ESCAN_TRACE(("ESCAN ABORT reason: %d\n", status)); + escan->bss_list = wl_escan_get_buf(escan); + ESCAN_TRACE(("SCAN ABORT: scanned AP count=%d\n", + escan->bss_list->count)); wl_escan_inform_bss(escan); wl_notify_escan_complete(escan, false); } else if (status == WLC_E_STATUS_TIMEOUT) { @@ -871,6 +867,7 @@ static s32 wl_escan_handler(struct wl_escan_info *escan, if (e->reason == 0xFFFFFFFF) { wl_notify_escan_complete(escan, true); } + escan->escan_state = ESCAN_STATE_IDLE; } else { ESCAN_ERROR(("unexpected Escan Event %d : abort\n", status)); escan->escan_state = ESCAN_STATE_IDLE; @@ -983,9 +980,8 @@ wl_escan_prep(struct wl_escan_info *escan, wl_uint32_list_t *list, return err; } -static int wl_escan_reset(void) { - struct wl_escan_info *escan = g_escan; - +static int wl_escan_reset(struct wl_escan_info *escan) +{ if (timer_pending(&escan->scan_timeout)) del_timer_sync(&escan->scan_timeout); escan->escan_state = ESCAN_STATE_IDLE; @@ -993,10 +989,20 @@ static int wl_escan_reset(void) { return 0; } -static void wl_escan_timeout(unsigned long data) +static void wl_escan_timeout( +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + struct timer_list *t +#else + unsigned long data +#endif +) { wl_event_msg_t msg; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + struct wl_escan_info *escan = from_timer(escan, t, scan_timeout); +#else struct wl_escan_info *escan = (struct wl_escan_info *)data; +#endif struct wl_scan_results *bss_list; struct wl_bss_info *bi = NULL; s32 i; @@ -1047,7 +1053,7 @@ wl_escan_set_scan( wl_escan_params_t *params = NULL; scb_val_t scbval; static int cnt = 0; - struct wl_escan_info *escan = NULL; + struct wl_escan_info *escan = g_escan; wlc_ssid_t ssid; u32 n_channels = 0; wl_uint32_list_t *list; @@ -1056,9 +1062,8 @@ wl_escan_set_scan( ESCAN_TRACE(("Enter \n")); - escan = g_escan; if (!escan) { - ESCAN_ERROR(("device is not ready\n")); \ + ESCAN_ERROR(("device is not ready\n")); return -EIO; } mutex_lock(&escan->usr_sync); @@ -1145,7 +1150,7 @@ wl_escan_set_scan( ESCAN_TRACE(("Escan not permitted at this time (%d)\n", err)); else ESCAN_ERROR(("Escan set error (%d)\n", err)); - wl_escan_reset(); + wl_escan_reset(escan); } kfree(params); @@ -1206,10 +1211,15 @@ wl_escan_get_scan( err = -EAGAIN; goto exit; } + if (!escan->bss_list) { + ESCAN_ERROR(("%s: scan not ready\n", dev->name)); + err = -EAGAIN; + goto exit; + } #if defined(BSSCACHE) - bss_list = &g_bss_cache_ctrl.m_cache_head->results; - node = g_bss_cache_ctrl.m_cache_head; + bss_list = &escan->g_bss_cache_ctrl.m_cache_head->results; + node = escan->g_bss_cache_ctrl.m_cache_head; for (i=0; node && ibss_list; @@ -1228,7 +1238,7 @@ wl_escan_get_scan( } #if defined(RSSIAVG) - rssi = wl_get_avg_rssi(&g_rssi_cache_ctrl, &bi->BSSID); + rssi = wl_get_avg_rssi(&escan->g_rssi_cache_ctrl, &bi->BSSID); if (rssi == RSSI_MINVAL) rssi = MIN(dtoh16(bi->RSSI), RSSI_MAXVAL); #else @@ -1236,8 +1246,8 @@ wl_escan_get_scan( rssi = MIN(dtoh16(bi->RSSI), RSSI_MAXVAL); #endif channel = wf_chspec_ctlchan(wl_chspec_driver_to_host(escan->ioctl_ver, bi->chanspec)); - ESCAN_SCAN(("%s: BSSID="MACSTR", channel=%d, RSSI=%d, SSID=\"%s\"\n", - __FUNCTION__, MAC2STR(bi->BSSID.octet), channel, rssi, bi->SSID)); + ESCAN_SCAN(("BSSID="MACSTR", channel=%d, RSSI=%d, SSID=\"%s\"\n", + MAC2STR(bi->BSSID.octet), channel, rssi, bi->SSID)); /* First entry must be the BSSID */ iwe.cmd = SIOCGIWAP; @@ -1323,6 +1333,27 @@ wl_escan_get_scan( return err; } +s32 wl_escan_autochannel(struct net_device *dev, char* command, int total_len) +{ + struct wl_escan_info *escan = g_escan; + int ret = 0; + int bytes_written = -1; + + sscanf(command, "%*s %d", &escan->autochannel); + + if (escan->autochannel == 0) { + escan->best_2g_ch = 0; + escan->best_5g_ch = 0; + } else if (escan->autochannel == 2) { + bytes_written = snprintf(command, total_len, "2g=%d 5g=%d", + escan->best_2g_ch, escan->best_5g_ch); + ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command)); + ret = bytes_written; + } + + return ret; +} + static s32 wl_create_event_handler(struct wl_escan_info *escan) { int ret = 0; @@ -1343,42 +1374,44 @@ static void wl_destroy_event_handler(struct wl_escan_info *escan) PROC_STOP(&escan->event_tsk); } -static void wl_escan_deinit(void) +static void wl_escan_deinit(struct wl_escan_info *escan) { - struct wl_escan_info *escan = g_escan; - printf("%s: Enter\n", __FUNCTION__); if (!escan) { - ESCAN_ERROR(("device is not ready\n")); \ + ESCAN_ERROR(("device is not ready\n")); return; } wl_destroy_event_handler(escan); wl_flush_eq(escan); del_timer_sync(&escan->scan_timeout); + escan->escan_state = ESCAN_STATE_IDLE; #if defined(RSSIAVG) - wl_free_rssi_cache(&g_rssi_cache_ctrl); + wl_free_rssi_cache(&escan->g_rssi_cache_ctrl); #endif #if defined(BSSCACHE) - wl_free_bss_cache(&g_bss_cache_ctrl); + wl_free_bss_cache(&escan->g_bss_cache_ctrl); #endif } -static s32 wl_escan_init(void) +static s32 wl_escan_init(struct wl_escan_info *escan) { - struct wl_escan_info *escan = g_escan; int err = 0; printf("%s: Enter\n", __FUNCTION__); if (!escan) { - ESCAN_ERROR(("device is not ready\n")); \ + ESCAN_ERROR(("device is not ready\n")); return -EIO; } /* Init scan_timeout timer */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + timer_setup(&escan->scan_timeout, wl_escan_timeout, 0); +#else init_timer(&escan->scan_timeout); escan->scan_timeout.data = (unsigned long) escan; escan->scan_timeout.function = wl_escan_timeout; +#endif if (wl_create_event_handler(escan)) { err = -ENOMEM; @@ -1393,7 +1426,7 @@ static s32 wl_escan_init(void) return 0; err: - wl_escan_deinit(); + wl_escan_deinit(escan); return err; } @@ -1404,11 +1437,11 @@ void wl_escan_detach(dhd_pub_t *dhdp) printf("%s: Enter\n", __FUNCTION__); if (!escan) { - ESCAN_ERROR(("device is not ready\n")); \ + ESCAN_ERROR(("device is not ready\n")); return; } - wl_escan_deinit(); + wl_escan_deinit(escan); if (escan->escan_ioctl_buf) { kfree(escan->escan_ioctl_buf); @@ -1430,10 +1463,10 @@ wl_escan_attach(struct net_device *dev, dhd_pub_t *dhdp) escan = (wl_escan_info_t *)DHD_OS_PREALLOC(dhdp, DHD_PREALLOC_WL_ESCAN_INFO, sizeof(struct wl_escan_info)); if (!escan) return -ENOMEM; + g_escan = escan; memset(escan, 0, sizeof(struct wl_escan_info)); /* we only care about main interface so save a global here */ - g_escan = escan; escan->dev = dev; escan->pub = dhdp; escan->escan_state = ESCAN_STATE_IDLE; @@ -1444,9 +1477,7 @@ wl_escan_attach(struct net_device *dev, dhd_pub_t *dhdp) goto err ; } wl_init_eq(escan); -#ifdef WL_ESCAN - wl_escan_init(); -#endif + wl_escan_init(escan); return 0; err: diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_escan.h b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_escan.h index 62bc0d28..6d84ce56 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_escan.h +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_escan.h @@ -1,5 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef _wl_escan_ #define _wl_escan_ @@ -57,9 +56,19 @@ typedef struct wl_escan_info { tsk_ctl_t event_tsk; /* task of main event handler thread */ ESCAN_EVENT_HANDLER evt_handler[WLC_E_LAST]; struct mutex usr_sync; /* maily for up/down synchronization */ + int autochannel; + int best_2g_ch; + int best_5g_ch; +#if defined(RSSIAVG) + wl_rssi_cache_ctrl_t g_rssi_cache_ctrl; + wl_rssi_cache_ctrl_t g_connected_rssi_cache_ctrl; +#endif +#if defined(BSSCACHE) + wl_bss_cache_ctrl_t g_bss_cache_ctrl; +#endif } wl_escan_info_t; -void wl_escan_event(struct net_device *ndev, const wl_event_msg_t * e, void *data); +void wl_escan_event(struct net_device *dev, const wl_event_msg_t * e, void *data); int wl_escan_set_scan( struct net_device *dev, @@ -69,6 +78,7 @@ int wl_escan_set_scan( ); int wl_escan_get_scan(struct net_device *dev, struct iw_request_info *info, struct iw_point *dwrq, char *extra); +s32 wl_escan_autochannel(struct net_device *dev, char* command, int total_len); int wl_escan_attach(struct net_device *dev, dhd_pub_t *dhdp); void wl_escan_detach(dhd_pub_t *dhdp); diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_iw.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_iw.c index c2883471..4713882c 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_iw.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wl_iw.c @@ -627,16 +627,18 @@ wl_iw_get_freq( char *extra ) { - channel_info_t ci; int error; + u32 chanspec = 0; + int ctl_chan; WL_TRACE(("%s: SIOCGIWFREQ\n", dev->name)); - if ((error = dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(ci)))) + if ((error = dev_wlc_intvar_get(dev, "chanspec", &chanspec))) return error; + ctl_chan = wf_chspec_ctlchan(chanspec); /* Return radio channel in channel form */ - fwrq->m = dtoh32(ci.hw_channel); + fwrq->m = ctl_chan; fwrq->e = dtoh32(0); return 0; } @@ -1779,15 +1781,14 @@ wl_iw_get_essid( /* Max SSID length check */ if (ssid.SSID_len > IW_ESSID_MAX_SIZE) { ssid.SSID_len = IW_ESSID_MAX_SIZE; - /* Get the current SSID */ - memcpy(extra, ssid.SSID, ssid.SSID_len); - /* NULL terminating as length of extra buffer is IW_ESSID_MAX_SIZE ie 32 */ - extra[IW_ESSID_MAX_SIZE - 1] = '\0'; - } else { - /* Get the current SSID */ - memcpy(extra, ssid.SSID, ssid.SSID_len); } + /* Get the current SSID */ + memcpy(extra, ssid.SSID, ssid.SSID_len); + + /* NULL terminating as length of extra buffer is IW_ESSID_MAX_SIZE ie 32 */ + extra[IW_ESSID_MAX_SIZE] = '\0'; + dwrq->length = ssid.SSID_len; dwrq->flags = 1; /* active */ @@ -3304,6 +3305,7 @@ wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data) uint16 flags = ntoh16(e->flags); uint32 datalen = ntoh32(e->datalen); uint32 status = ntoh32(e->status); + uint32 reason = ntoh32(e->reason); memset(&wrqu, 0, sizeof(wrqu)); memset(extra, 0, sizeof(extra)); @@ -3333,12 +3335,12 @@ wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data) cmd = SIOCGIWAP; wrqu.data.length = strlen(extra); if (!(flags & WLC_EVENT_MSG_LINK)) { - printf("%s: Link Down with BSSID="MACSTR"\n", __FUNCTION__, - MAC2STR((u8 *)wrqu.addr.sa_data)); + printf("%s: Link Down with "MACSTR", reason=%d\n", __FUNCTION__, + MAC2STR((u8 *)wrqu.addr.sa_data), reason); bzero(wrqu.addr.sa_data, ETHER_ADDR_LEN); bzero(&extra, ETHER_ADDR_LEN); } else { - printf("%s: Link UP with BSSID="MACSTR"\n", __FUNCTION__, + printf("%s: Link UP with "MACSTR"\n", __FUNCTION__, MAC2STR((u8 *)wrqu.addr.sa_data)); } break; @@ -3545,15 +3547,19 @@ int wl_iw_get_wireless_stats(struct net_device *dev, struct iw_statistics *wstat #endif /* WIRELESS_EXT > 11 */ phy_noise = 0; - if ((res = dev_wlc_ioctl(dev, WLC_GET_PHY_NOISE, &phy_noise, sizeof(phy_noise)))) + if ((res = dev_wlc_ioctl(dev, WLC_GET_PHY_NOISE, &phy_noise, sizeof(phy_noise)))) { + WL_ERROR(("%s: WLC_GET_PHY_NOISE error=%d\n", __FUNCTION__, res)); goto done; + } phy_noise = dtoh32(phy_noise); WL_TRACE(("wl_iw_get_wireless_stats phy noise=%d\n *****", phy_noise)); - scb_val.val = 0; - if ((res = dev_wlc_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t)))) + memset(&scb_val, 0, sizeof(scb_val)); + if ((res = dev_wlc_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t)))) { + WL_ERROR(("%s: WLC_GET_RSSI error=%d\n", __FUNCTION__, res)); goto done; + } rssi = dtoh32(scb_val.val); rssi = MIN(rssi, RSSI_MAXVAL); @@ -3647,9 +3653,19 @@ int wl_iw_get_wireless_stats(struct net_device *dev, struct iw_statistics *wstat #ifndef WL_ESCAN static void -wl_iw_timerfunc(ulong data) +wl_iw_timerfunc( +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + struct timer_list *t +#else + unsigned long data +#endif +) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + iscan_info_t *iscan = from_timer(iscan, t, timer); +#else iscan_info_t *iscan = (iscan_info_t *)data; +#endif iscan->timer_on = 0; if (iscan->iscan_state != ISCAN_STATE_IDLE) { WL_TRACE(("timer trigger\n")); @@ -3887,9 +3903,13 @@ wl_iw_attach(struct net_device *dev, void * dhdp) /* Set up the timer */ iscan->timer_ms = 2000; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + timer_setup(&iscan->timer, wl_iw_timerfunc, 0); +#else init_timer(&iscan->timer); iscan->timer.data = (ulong)iscan; iscan->timer.function = wl_iw_timerfunc; +#endif sema_init(&iscan->sysioc_sem, 0); init_completion(&iscan->sysioc_exited); diff --git a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wldev_common.c b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wldev_common.c index 1b985b09..8be455fc 100644 --- a/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wldev_common.c +++ b/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/wldev_common.c @@ -34,7 +34,9 @@ #include #include +#ifdef WL_CFG80211 #include +#endif #include #define htod32(i) (i) @@ -478,9 +480,11 @@ int wldev_set_country( wl_country_t cur_cspec = {{0}, 0, {0}}; /* current ccode */ scb_val_t scbval; char smbuf[WLC_IOCTL_SMLEN]; +#ifdef WL_CFG80211 struct wireless_dev *wdev = ndev_to_wdev(dev); struct wiphy *wiphy = wdev->wiphy; struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); +#endif if (!country_code) return error; @@ -495,7 +499,7 @@ int wldev_set_country( cspec.rev = revinfo; memcpy(cspec.country_abbrev, country_code, WLC_CNTRY_BUF_SZ); memcpy(cspec.ccode, country_code, WLC_CNTRY_BUF_SZ); - error = dhd_conf_get_country_from_config(dhd_get_pub(dev), &cspec); + error = dhd_conf_map_country_list(dhd_get_pub(dev), &cspec, 0); if (error) dhd_get_customized_country_code(dev, (char *)&cspec.country_abbrev, &cspec); @@ -506,7 +510,11 @@ int wldev_set_country( dhd_force_country_change(dev) || (strncmp(cspec.ccode, cur_cspec.ccode, WLC_CNTRY_BUF_SZ) != 0)) { - if ((user_enforced) && (wl_get_drv_status(cfg, CONNECTED, dev))) { + if ((user_enforced) +#ifdef WL_CFG80211 + && (wl_get_drv_status(cfg, CONNECTED, dev)) +#endif + ) { bzero(&scbval, sizeof(scb_val_t)); error = wldev_ioctl_set(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t)); diff --git a/drivers/net/wireless/rockchip_wlan/rtl8723bs/Makefile b/drivers/net/wireless/rockchip_wlan/rtl8723bs/Makefile index fe9d5638..e8653b07 100644 --- a/drivers/net/wireless/rockchip_wlan/rtl8723bs/Makefile +++ b/drivers/net/wireless/rockchip_wlan/rtl8723bs/Makefile @@ -1347,7 +1347,7 @@ EXTRA_CFLAGS += -DCONFIG_LITTLE_ENDIAN -DCONFIG_PLATFORM_ANDROID -DCONFIG_PLATFO EXTRA_CFLAGS += -DCONFIG_IOCTL_CFG80211 -DRTW_USE_CFG80211_STA_EVENT EXTRA_CFLAGS += -DCONFIG_CONCURRENT_MODE # default setting for Power control -EXTRA_CFLAGS += -DRTW_ENABLE_WIFI_CONTROL_FUNC +#EXTRA_CFLAGS += -DRTW_ENABLE_WIFI_CONTROL_FUNC EXTRA_CFLAGS += -DRTW_SUPPORT_PLATFORM_SHUTDOWN EXTRA_CFLAGS += -DCONFIG_RESUME_IN_WORKQUEUE # default setting for Special function diff --git a/drivers/net/wireless/rockchip_wlan/rtl8723bs/os_dep/linux/sdio_intf.c b/drivers/net/wireless/rockchip_wlan/rtl8723bs/os_dep/linux/sdio_intf.c index b4654d22..e49e5cb8 100644 --- a/drivers/net/wireless/rockchip_wlan/rtl8723bs/os_dep/linux/sdio_intf.c +++ b/drivers/net/wireless/rockchip_wlan/rtl8723bs/os_dep/linux/sdio_intf.c @@ -45,6 +45,14 @@ static struct mmc_host *mmc_host = NULL; static const struct sdio_device_id sdio_ids[] = { #ifdef CONFIG_RTL8723B + { SDIO_DEVICE(0x024c, 0x0240), .driver_data = RTL8723B}, + { SDIO_DEVICE(0x024c, 0x0241), .driver_data = RTL8723B}, + { SDIO_DEVICE(0x024c, 0x0523), .driver_data = RTL8723B}, + { SDIO_DEVICE(0x024c, 0x0524), .driver_data = RTL8723B}, + { SDIO_DEVICE(0x024c, 0x0623), .driver_data = RTL8723B}, + { SDIO_DEVICE(0x024c, 0x0624), .driver_data = RTL8723B}, + { SDIO_DEVICE(0x024c, 0x0626), .driver_data = RTL8723B}, + { SDIO_DEVICE(0x024c, 0x8753), .driver_data = RTL8723B}, { SDIO_DEVICE(0x024c, 0xB723), .driver_data = RTL8723B}, #endif #ifdef CONFIG_RTL8188E diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index e2a48415..c112c9f2 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -112,4 +112,12 @@ config OF_OVERLAY While this option is selected automatically when needed, you can enable it manually to improve device tree unit test coverage. +config OF_CONFIGFS + bool "Device Tree Overlay ConfigFS interface" + select CONFIGFS_FS + select OF_FLATTREE + depends on OF_OVERLAY + help + Enable a simple user-space driven DT overlay interface. + endif # OF diff --git a/drivers/of/Makefile b/drivers/of/Makefile index 478d4edc..732fa66b 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -1,4 +1,5 @@ obj-y = base.o device.o platform.o property.o +obj-$(CONFIG_OF_CONFIGFS) += configfs.o obj-$(CONFIG_OF_DYNAMIC) += dynamic.o obj-$(CONFIG_OF_FLATTREE) += fdt.o obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o diff --git a/drivers/of/configfs.c b/drivers/of/configfs.c new file mode 100644 index 00000000..908ce496 --- /dev/null +++ b/drivers/of/configfs.c @@ -0,0 +1,314 @@ +/* + * Configfs entries for device-tree + * + * Copyright (C) 2013 - Pantelis Antoniou + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "of_private.h" + +struct cfs_overlay_item { + struct config_item item; + + char path[PATH_MAX]; + + const struct firmware *fw; + struct device_node *overlay; + int ov_id; + + void *dtbo; + int dtbo_size; +}; + +static int create_overlay(struct cfs_overlay_item *overlay, void *blob) +{ + int err; + + /* unflatten the tree */ + of_fdt_unflatten_tree(blob, &overlay->overlay); + if (overlay->overlay == NULL) { + pr_err("%s: failed to unflatten tree\n", __func__); + err = -EINVAL; + goto out_err; + } + pr_debug("%s: unflattened OK\n", __func__); + + /* mark it as detached */ + of_node_set_flag(overlay->overlay, OF_DETACHED); + + /* perform resolution */ + err = of_resolve_phandles(overlay->overlay); + if (err != 0) { + pr_err("%s: Failed to resolve tree\n", __func__); + goto out_err; + } + pr_debug("%s: resolved OK\n", __func__); + + err = of_overlay_create(overlay->overlay); + if (err < 0) { + pr_err("%s: Failed to create overlay (err=%d)\n", + __func__, err); + goto out_err; + } + overlay->ov_id = err; + +out_err: + return err; +} + +static inline struct cfs_overlay_item *to_cfs_overlay_item( + struct config_item *item) +{ + return item ? container_of(item, struct cfs_overlay_item, item) : NULL; +} + +static ssize_t cfs_overlay_item_path_show(struct config_item *item, + char *page) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + return sprintf(page, "%s\n", overlay->path); +} + +static ssize_t cfs_overlay_item_path_store(struct config_item *item, + const char *page, size_t count) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + const char *p = page; + char *s; + int err; + + /* if it's set do not allow changes */ + if (overlay->path[0] != '\0' || overlay->dtbo_size > 0) + return -EPERM; + + /* copy to path buffer (and make sure it's always zero terminated */ + count = snprintf(overlay->path, sizeof(overlay->path) - 1, "%s", p); + overlay->path[sizeof(overlay->path) - 1] = '\0'; + + /* strip trailing newlines */ + s = overlay->path + strlen(overlay->path); + while (s > overlay->path && *--s == '\n') + *s = '\0'; + + pr_debug("%s: path is '%s'\n", __func__, overlay->path); + + err = request_firmware(&overlay->fw, overlay->path, NULL); + if (err != 0) + goto out_err; + + err = create_overlay(overlay, (void *)overlay->fw->data); + if (err < 0) + goto out_err; + + return count; + +out_err: + + release_firmware(overlay->fw); + overlay->fw = NULL; + + overlay->path[0] = '\0'; + return err; +} + +static ssize_t cfs_overlay_item_status_show(struct config_item *item, + char *page) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + + return sprintf(page, "%s\n", + overlay->ov_id >= 0 ? "applied" : "unapplied"); +} + +CONFIGFS_ATTR(cfs_overlay_item_, path); +CONFIGFS_ATTR_RO(cfs_overlay_item_, status); + +static struct configfs_attribute *cfs_overlay_attrs[] = { + &cfs_overlay_item_attr_path, + &cfs_overlay_item_attr_status, + NULL, +}; + +ssize_t cfs_overlay_item_dtbo_read(struct config_item *item, + void *buf, size_t max_count) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + + pr_debug("%s: buf=%p max_count=%zu\n", __func__, + buf, max_count); + + if (overlay->dtbo == NULL) + return 0; + + /* copy if buffer provided */ + if (buf != NULL) { + /* the buffer must be large enough */ + if (overlay->dtbo_size > max_count) + return -ENOSPC; + + memcpy(buf, overlay->dtbo, overlay->dtbo_size); + } + + return overlay->dtbo_size; +} + +ssize_t cfs_overlay_item_dtbo_write(struct config_item *item, + const void *buf, size_t count) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + int err; + + /* if it's set do not allow changes */ + if (overlay->path[0] != '\0' || overlay->dtbo_size > 0) + return -EPERM; + + /* copy the contents */ + overlay->dtbo = kmemdup(buf, count, GFP_KERNEL); + if (overlay->dtbo == NULL) + return -ENOMEM; + + overlay->dtbo_size = count; + + err = create_overlay(overlay, overlay->dtbo); + if (err < 0) + goto out_err; + + return count; + +out_err: + kfree(overlay->dtbo); + overlay->dtbo = NULL; + overlay->dtbo_size = 0; + + return err; +} + +CONFIGFS_BIN_ATTR(cfs_overlay_item_, dtbo, NULL, SZ_1M); + +static struct configfs_bin_attribute *cfs_overlay_bin_attrs[] = { + &cfs_overlay_item_attr_dtbo, + NULL, +}; + +static void cfs_overlay_release(struct config_item *item) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + + if (overlay->ov_id >= 0) + of_overlay_destroy(overlay->ov_id); + if (overlay->fw) + release_firmware(overlay->fw); + /* kfree with NULL is safe */ + kfree(overlay->dtbo); + kfree(overlay); +} + +static struct configfs_item_operations cfs_overlay_item_ops = { + .release = cfs_overlay_release, +}; + +static struct config_item_type cfs_overlay_type = { + .ct_item_ops = &cfs_overlay_item_ops, + .ct_attrs = cfs_overlay_attrs, + .ct_bin_attrs = cfs_overlay_bin_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_item *cfs_overlay_group_make_item( + struct config_group *group, const char *name) +{ + struct cfs_overlay_item *overlay; + + overlay = kzalloc(sizeof(*overlay), GFP_KERNEL); + if (!overlay) + return ERR_PTR(-ENOMEM); + overlay->ov_id = -1; + + config_item_init_type_name(&overlay->item, name, &cfs_overlay_type); + return &overlay->item; +} + +static void cfs_overlay_group_drop_item(struct config_group *group, + struct config_item *item) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + + config_item_put(&overlay->item); +} + +static struct configfs_group_operations overlays_ops = { + .make_item = cfs_overlay_group_make_item, + .drop_item = cfs_overlay_group_drop_item, +}; + +static struct config_item_type overlays_type = { + .ct_group_ops = &overlays_ops, + .ct_owner = THIS_MODULE, +}; + +static struct configfs_group_operations of_cfs_ops = { + /* empty - we don't allow anything to be created */ +}; + +static struct config_item_type of_cfs_type = { + .ct_group_ops = &of_cfs_ops, + .ct_owner = THIS_MODULE, +}; + +struct config_group of_cfs_overlay_group; + +struct config_group *of_cfs_def_groups[] = { + &of_cfs_overlay_group, + NULL +}; + +static struct configfs_subsystem of_cfs_subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = "device-tree", + .ci_type = &of_cfs_type, + }, + .default_groups = of_cfs_def_groups, + }, + .su_mutex = __MUTEX_INITIALIZER(of_cfs_subsys.su_mutex), +}; + +static int __init of_cfs_init(void) +{ + int ret; + + pr_info("%s\n", __func__); + + config_group_init(&of_cfs_subsys.su_group); + config_group_init_type_name(&of_cfs_overlay_group, "overlays", + &overlays_type); + + ret = configfs_register_subsystem(&of_cfs_subsys); + if (ret != 0) { + pr_err("%s: failed to register subsys\n", __func__); + goto out; + } + pr_info("%s: OK\n", __func__); +out: + return ret; +} +late_initcall(of_cfs_init); diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi-phy.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi-phy.c index 0161f80a..6cf39140 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi-phy.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi-phy.c @@ -278,6 +278,70 @@ static const struct pre_pll_config pre_pll_cfg_table[] = { {594000000, 371250000, 4, 495, 1, 2, 0, 1, 3, 1, 1, 1, 0}, {593407000, 593407000, 1, 98, 0, 2, 0, 1, 0, 1, 1, 0, 0xE6AE6B}, {594000000, 594000000, 1, 99, 0, 2, 0, 1, 0, 1, 1, 0, 0}, + { 25175000, 25175000, 30, 1007, 1, 2, 2, 1, 2, 3, 4, 0, 0}, + { 31500000, 31500000, 1, 21, 1, 1, 1, 1, 2, 2, 2, 0, 0}, + { 33750000, 33750000, 1, 45, 1, 2, 2, 1, 2, 3, 4, 0, 0}, + { 35500000, 35500000, 3, 71, 1, 1, 1, 1, 2, 2, 2, 0, 0}, + { 36000000, 36000000, 1, 12, 0, 1, 1, 1, 0, 2, 2, 0, 0}, + { 49500000, 49500000, 1, 33, 1, 1, 1, 1, 2, 2, 2, 0, 0}, + { 50000000, 50000000, 3, 50, 0, 1, 1, 1, 0, 2, 2, 0, 0}, + { 56250000, 56250000, 1, 75, 1, 2, 2, 1, 2, 3, 4, 0, 0}, + { 68250000, 68250000, 1, 91, 1, 2, 2, 1, 2, 3, 4, 0, 0}, + { 72000000, 72000000, 1, 24, 0, 1, 1, 1, 0, 2, 2, 0, 0}, + { 73250000, 73250000, 3, 293, 1, 2, 2, 1, 2, 3, 4, 0, 0}, + { 75000000, 75000000, 1, 25, 0, 1, 1, 1, 0, 2, 2, 0, 0}, + { 78750000, 78750000, 1, 105, 1, 2, 2, 1, 2, 3, 4, 0, 0}, + { 79500000, 79500000, 1, 53, 1, 1, 1, 1, 2, 2, 2, 0, 0}, + { 85500000, 85500000, 1, 57, 1, 1, 1, 1, 2, 2, 2, 0, 0}, + { 94500000, 94500000, 1, 63, 1, 1, 1, 1, 2, 2, 2, 0, 0}, + {101000000, 101000000, 3, 101, 0, 1, 1, 1, 0, 2, 2, 0, 0}, + {102250000, 102250000, 3, 409, 1, 2, 2, 1, 2, 3, 4, 0, 0}, + {106500000, 106500000, 1, 71, 1, 1, 1, 1, 2, 2, 2, 0, 0}, + {115500000, 115500000, 1, 77, 1, 1, 1, 1, 2, 2, 2, 0, 0}, + {117500000, 117500000, 3, 235, 1, 1, 1, 1, 2, 2, 2, 0, 0}, + {121750000, 121750000, 3, 487, 1, 2, 2, 1, 2, 3, 4, 0, 0}, + {122500000, 122500000, 3, 245, 1, 1, 1, 1, 2, 2, 2, 0, 0}, + {135000000, 135000000, 1, 45, 0, 1, 1, 1, 0, 2, 2, 0, 0}, + {136750000, 136750000, 3, 547, 1, 2, 2, 1, 2, 3, 4, 0, 0}, + {140250000, 140250000, 1, 187, 1, 2, 2, 1, 2, 3, 4, 0, 0}, + {146250000, 146250000, 1, 195, 1, 2, 2, 1, 2, 3, 4, 0, 0}, + {148250000, 148250000, 3, 593, 1, 2, 2, 1, 2, 3, 4, 0, 0}, + {154000000, 154000000, 3, 154, 0, 1, 1, 1, 0, 2, 2, 0, 0}, + {156000000, 156000000, 1, 52, 0, 1, 1, 1, 0, 2, 2, 0, 0}, + {156750000, 156750000, 1, 209, 1, 2, 2, 1, 2, 3, 4, 0, 0}, + {157000000, 157000000, 3, 157, 0, 1, 1, 1, 0, 2, 2, 0, 0}, + {157500000, 157500000, 1, 105, 1, 1, 1, 1, 2, 2, 2, 0, 0}, + {175500000, 175500000, 1, 117, 1, 1, 1, 1, 2, 2, 2, 0, 0}, + {179500000, 179500000, 3, 359, 1, 1, 1, 1, 2, 2, 2, 0, 0}, + {182750000, 182750000, 3, 731, 1, 2, 2, 1, 2, 3, 4, 0, 0}, + {187000000, 187000000, 3, 187, 0, 1, 1, 1, 0, 2, 2, 0, 0}, + {187250000, 187250000, 3, 749, 1, 2, 2, 1, 2, 3, 4, 0, 0}, + {189000000, 189000000, 1, 63, 0, 1, 1, 1, 0, 2, 2, 0, 0}, + {193250000, 193250000, 3, 773, 1, 2, 2, 1, 2, 3, 4, 0, 0}, + {202500000, 202500000, 1, 135, 1, 1, 1, 1, 2, 2, 2, 0, 0}, + {204750000, 204750000, 1, 273, 1, 2, 2, 1, 2, 3, 4, 0, 0}, + {208000000, 208000000, 3, 208, 0, 1, 1, 1, 0, 2, 2, 0, 0}, + {214750000, 214750000, 3, 859, 1, 2, 2, 1, 2, 3, 4, 0, 0}, + {218250000, 218250000, 1, 291, 1, 2, 2, 1, 2, 3, 4, 0, 0}, + {229500000, 229500000, 1, 153, 1, 1, 1, 1, 2, 2, 2, 0, 0}, + {234000000, 234000000, 1, 78, 0, 1, 1, 1, 0, 2, 2, 0, 0}, + {241500000, 241500000, 1, 161, 1, 1, 1, 1, 2, 2, 2, 0, 0}, + {245250000, 245250000, 1, 327, 1, 2, 2, 1, 2, 3, 4, 0, 0}, + {245500000, 245500000, 3, 491, 1, 1, 1, 1, 2, 2, 2, 0, 0}, + {261000000, 261000000, 1, 87, 0, 1, 1, 1, 0, 2, 2, 0, 0}, + {268250000, 268250000, 3, 1073, 1, 2, 2, 1, 2, 3, 4, 0, 0}, + {268500000, 268500000, 1, 179, 1, 1, 1, 1, 2, 2, 2, 0, 0}, + {281250000, 281250000, 1, 375, 1, 2, 2, 1, 2, 3, 4, 0, 0}, + {288000000, 288000000, 1, 96, 0, 1, 1, 1, 0, 2, 2, 0, 0}, + {312250000, 312250000, 3, 1249, 1, 2, 2, 1, 2, 3, 4, 0, 0}, + {317000000, 317000000, 3, 317, 0, 1, 1, 1, 0, 2, 2, 0, 0}, + {333250000, 333250000, 3, 1333, 1, 2, 2, 1, 2, 3, 4, 0, 0}, + {348500000, 348500000, 3, 697, 1, 1, 1, 1, 2, 2, 2, 0, 0}, + {356500000, 356500000, 3, 713, 1, 1, 1, 1, 2, 2, 2, 0, 0}, + {380500000, 380500000, 3, 761, 1, 1, 1, 1, 2, 2, 2, 0, 0}, + {443250000, 443250000, 1, 591, 1, 2, 2, 1, 2, 3, 4, 0, 0}, + {505250000, 505250000, 3, 2021, 1, 2, 2, 1, 2, 3, 4, 0, 0}, + {552750000, 552750000, 1, 737, 1, 2, 2, 1, 2, 3, 4, 0, 0}, { ~0UL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c index 74e5ae2b..6b0854a3 100644 --- a/drivers/regulator/fan53555.c +++ b/drivers/regulator/fan53555.c @@ -78,6 +78,7 @@ enum { enum { SILERGY_SYR82X = 8, + SILERGY_SYR83X = 9, }; struct fan53555_device_info { @@ -323,6 +324,7 @@ static int fan53555_voltages_setup_silergy(struct fan53555_device_info *di) /* Init voltage range and step */ switch (di->chip_id) { case SILERGY_SYR82X: + case SILERGY_SYR83X: di->vsel_min = 712500; di->vsel_step = 12500; break; diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index cbac385b..f0b3a845 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -160,37 +160,34 @@ int serial8250_tx_dma(struct uart_8250_port *p) int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir) { - unsigned int rfl, i = 0, fcr = 0; + unsigned int rfl, i = 0, fcr = 0, cur_index = 0; unsigned char buf[MAX_FIFO_SIZE]; struct uart_port *port = &p->port; struct tty_port *tty_port = &p->port.state->port; + struct dma_tx_state state; + struct uart_8250_dma *dma = p->dma; + if ((iir & 0xf) != UART_IIR_RX_TIMEOUT) return 0; - rfl = serial_port_in(port, UART_RFL_16550A); + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_T_TRIG_10 | UART_FCR_R_TRIG_11; + serial_port_out(port, UART_FCR, fcr); - if (rfl > (p->port.fifosize / 2 - 4)) { - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_T_TRIG_10 | UART_FCR_R_TRIG_01; - serial_port_out(port, UART_FCR, fcr); - } else { - while (i < rfl) - buf[i++] = serial_port_in(port, UART_RX); - } + do { + dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); + cur_index = dma->rx_size - state.residue; + } while (cur_index % dma->rxconf.src_maxburst); + + rfl = serial_port_in(port, UART_RFL_16550A); + while (i < rfl) + buf[i++] = serial_port_in(port, UART_RX); __dma_rx_complete(p); - if (i == 0) { - rfl = serial_port_in(port, UART_RFL_16550A); - if (rfl == 0) { - __dma_rx_complete(p); - tty_flip_buffer_push(tty_port); - } - } else { - tty_insert_flip_string(tty_port, buf, i); - p->port.icount.rx += i; - tty_flip_buffer_push(tty_port); - } + tty_insert_flip_string(tty_port, buf, i); + p->port.icount.rx += i; + tty_flip_buffer_push(tty_port); if (fcr) serial_port_out(port, UART_FCR, p->fcr); diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 14665c09..5650b155 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1551,7 +1551,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) unsigned char status; unsigned long flags; struct uart_8250_port *up = up_to_u8250p(port); - int dma_err = 0; + int dma_err = 0, idx; if (iir & UART_IIR_NO_INT) return 0; @@ -1573,7 +1573,24 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) if ((!up->dma || (up->dma && up->dma->tx_err)) && (status & UART_LSR_THRE)) serial8250_tx_chars(up); +#ifdef CONFIG_ARCH_ROCKCHIP + if (status & UART_LSR_BRK_ERROR_BITS) { + + idx = serial_index(port); + + if (status & UART_LSR_OE) + pr_err("ttyS%d: Overrun error!\n", idx); + if (status & UART_LSR_PE) + pr_err("ttyS%d: Parity error!\n", idx); + if (status & UART_LSR_FE) + pr_err("ttyS%d: Frame error!\n", idx); + if (status & UART_LSR_BI) + pr_err("ttyS%d: Break interrupt!\n", idx); + pr_err("ttyS%d: maybe rx pin is low or baudrate is not correct!\n", + idx); + } +#endif spin_unlock_irqrestore(&port->lock, flags); return 1; } diff --git a/drivers/usb/dwc3/dwc3-rockchip.c b/drivers/usb/dwc3/dwc3-rockchip.c index db482d37..3d3b5774 100644 --- a/drivers/usb/dwc3/dwc3-rockchip.c +++ b/drivers/usb/dwc3/dwc3-rockchip.c @@ -836,6 +836,15 @@ static int dwc3_rockchip_probe(struct platform_device *pdev) (extcon_get_cable_state_(rockchip->edev, EXTCON_USB_HOST) > 0)) schedule_work(&rockchip->otg_work); + } else { + /* + * DWC3 work as Host only mode or Peripheral + * only mode, set connected flag to true, it + * can avoid to reset the DWC3 controller when + * resume from PM suspend which may cause the + * usb device to be reenumerated. + */ + rockchip->connected = true; } dwc3_rockchip_debugfs_init(rockchip); diff --git a/drivers/video/rockchip/iep/Makefile b/drivers/video/rockchip/iep/Makefile index 37760fbf..c6f7ae87 100644 --- a/drivers/video/rockchip/iep/Makefile +++ b/drivers/video/rockchip/iep/Makefile @@ -1,2 +1,2 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_IEP) += hw_iep_reg.o iep_drv.o iep_iommu_drm.o iep_iommu_ion.o iep_iommu_ops.o +obj-$(CONFIG_IEP) += hw_iep_reg.o iep_drv.o iep_iommu_drm.o iep_iommu_ops.o diff --git a/drivers/video/rockchip/vcodec/vcodec_service.c b/drivers/video/rockchip/vcodec/vcodec_service.c index c4ee73be..9d5ee5c8 100644 --- a/drivers/video/rockchip/vcodec/vcodec_service.c +++ b/drivers/video/rockchip/vcodec/vcodec_service.c @@ -1630,9 +1630,6 @@ static void try_set_reg(struct vpu_subdev_data *data) reg_from_wait_to_run(pservice, reg); reg_copy_to_hw(reg->data, reg); } - } else { - if (pservice->hw_ops->reduce_freq) - pservice->hw_ops->reduce_freq(pservice); } mutex_unlock(&pservice->shutdown_lock); @@ -2385,6 +2382,7 @@ static void vcodec_set_freq_rk3328(struct vpu_service_info *pservice, if (curr == reg->freq) return; + atomic_set(&pservice->freq_status, reg->freq); if (pservice->dev_id == VCODEC_DEVICE_ID_RKVDEC) { if (reg->reg[1] & 0x00800000) { if (rkv_dec_get_fmt(reg->reg) == FMT_H264D) diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index a52ca5cb..24c8d7bc 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -57,6 +57,7 @@ #include #include #include +#include #include #include @@ -223,6 +224,140 @@ static int do_video_set_spu_palette(unsigned int fd, unsigned int cmd, return err; } +struct compat_dtv_property { + __u32 cmd; + __u32 reserved[3]; + union { + __u32 data; + struct { + __u8 data[32]; + __u32 len; + __u32 reserved1[3]; + compat_uptr_t reserved2; + } buffer; + } u; + int result; +}; + +struct compat_dtv_properties { + __u32 num; + compat_uptr_t props; +}; + +#define FE_SET_PROPERTY32 _IOW('o', 82, struct compat_dtv_properties) +#define FE_GET_PROPERTY32 _IOR('o', 83, struct compat_dtv_properties) + +static int do_fe_set_property(unsigned int fd, unsigned int cmd, + struct compat_dtv_properties __user *dtv32) +{ + struct dtv_properties __user *dtv; + struct dtv_property __user *properties; + struct compat_dtv_property __user *properties32; + compat_uptr_t data; + + int err; + int i; + __u32 num; + + err = get_user(num, &dtv32->num); + err |= get_user(data, &dtv32->props); + + if(err) + return -EFAULT; + + dtv = compat_alloc_user_space(sizeof(struct dtv_properties) + + sizeof(struct dtv_property) * num); + properties = (struct dtv_property*)((char*)dtv + + sizeof(struct dtv_properties)); + + err = put_user(properties, &dtv->props); + err |= put_user(num, &dtv->num); + + properties32 = compat_ptr(data); + + if(err) + return -EFAULT; + + for(i = 0; i < num; i++) { + compat_uptr_t reserved2; + + err |= copy_in_user(&properties[i], &properties32[i], + (8 * sizeof(__u32)) + (32 * sizeof(__u8))); + err |= get_user(reserved2, &properties32[i].u.buffer.reserved2); + err |= put_user(compat_ptr(reserved2), + &properties[i].u.buffer.reserved2); + } + + if(err) + return -EFAULT; + + err = sys_ioctl(fd, FE_SET_PROPERTY, (unsigned long) dtv); + + for(i = 0; i < num; i++) { + if(copy_in_user(&properties32[i].result, &properties[i].result, + sizeof(int))) + return -EFAULT; + } + + return err; +} + +static int do_fe_get_property(unsigned int fd, unsigned int cmd, + struct compat_dtv_properties __user *dtv32) +{ + struct dtv_properties __user *dtv; + struct dtv_property __user *properties; + struct compat_dtv_property __user *properties32; + compat_uptr_t data; + + int err; + int i; + __u32 num; + + err = get_user(num, &dtv32->num); + err |= get_user(data, &dtv32->props); + + if(err) + return -EFAULT; + + dtv = compat_alloc_user_space(sizeof(struct dtv_properties) + + sizeof(struct dtv_property) * num); + properties = (struct dtv_property*)((char*)dtv + + sizeof(struct dtv_properties)); + + err = put_user(properties, &dtv->props); + err |= put_user(num, &dtv->num); + + properties32 = compat_ptr(data); + + if(err) + return -EFAULT; + + for(i = 0; i < num; i++) { + compat_uptr_t reserved2; + + err |= copy_in_user(&properties[i], &properties32[i], + (8 * sizeof(__u32)) + (32 * sizeof(__u8))); + err |= get_user(reserved2, &properties32[i].u.buffer.reserved2); + err |= put_user(compat_ptr(reserved2), + &properties[i].u.buffer.reserved2); + } + + if(err) + return -EFAULT; + + err = sys_ioctl(fd, FE_GET_PROPERTY, (unsigned long) dtv); + + for(i = 0; i < num; i++) { + + if(copy_in_user(&properties32[i], &properties[i], + sizeof(properties32[i]))) + return -EFAULT; + } + + return err; +} + #ifdef CONFIG_BLOCK typedef struct sg_io_hdr32 { compat_int_t interface_id; /* [i] 'S' for SCSI generic (required) */ @@ -1381,6 +1516,17 @@ COMPATIBLE_IOCTL(VIDEO_GET_NAVI) COMPATIBLE_IOCTL(VIDEO_SET_ATTRIBUTES) COMPATIBLE_IOCTL(VIDEO_GET_SIZE) COMPATIBLE_IOCTL(VIDEO_GET_FRAME_RATE) +/* cec */ +COMPATIBLE_IOCTL(CEC_ADAP_G_CAPS) +COMPATIBLE_IOCTL(CEC_ADAP_G_LOG_ADDRS) +COMPATIBLE_IOCTL(CEC_ADAP_S_LOG_ADDRS) +COMPATIBLE_IOCTL(CEC_ADAP_G_PHYS_ADDR) +COMPATIBLE_IOCTL(CEC_ADAP_S_PHYS_ADDR) +COMPATIBLE_IOCTL(CEC_G_MODE) +COMPATIBLE_IOCTL(CEC_S_MODE) +COMPATIBLE_IOCTL(CEC_TRANSMIT) +COMPATIBLE_IOCTL(CEC_RECEIVE) +COMPATIBLE_IOCTL(CEC_DQEVENT) /* joystick */ COMPATIBLE_IOCTL(JSIOCGVERSION) @@ -1483,6 +1629,10 @@ static long do_ioctl_trans(int fd, unsigned int cmd, return do_video_stillpicture(fd, cmd, argp); case VIDEO_SET_SPU_PALETTE: return do_video_set_spu_palette(fd, cmd, argp); + case FE_SET_PROPERTY32: + return do_fe_set_property(fd, cmd, argp); + case FE_GET_PROPERTY32: + return do_fe_get_property(fd, cmd, argp); } /* diff --git a/fs/configfs/configfs_internal.h b/fs/configfs/configfs_internal.h index b65d1ef5..ccc31fa6 100644 --- a/fs/configfs/configfs_internal.h +++ b/fs/configfs/configfs_internal.h @@ -53,13 +53,14 @@ struct configfs_dirent { #define CONFIGFS_ROOT 0x0001 #define CONFIGFS_DIR 0x0002 #define CONFIGFS_ITEM_ATTR 0x0004 +#define CONFIGFS_ITEM_BIN_ATTR 0x0008 #define CONFIGFS_ITEM_LINK 0x0020 #define CONFIGFS_USET_DIR 0x0040 #define CONFIGFS_USET_DEFAULT 0x0080 #define CONFIGFS_USET_DROPPING 0x0100 #define CONFIGFS_USET_IN_MKDIR 0x0200 #define CONFIGFS_USET_CREATING 0x0400 -#define CONFIGFS_NOT_PINNED (CONFIGFS_ITEM_ATTR) +#define CONFIGFS_NOT_PINNED (CONFIGFS_ITEM_ATTR | CONFIGFS_ITEM_BIN_ATTR) extern struct mutex configfs_symlink_mutex; extern spinlock_t configfs_dirent_lock; @@ -72,6 +73,8 @@ extern struct inode * configfs_new_inode(umode_t mode, struct configfs_dirent *, extern int configfs_create(struct dentry *, umode_t mode, void (*init)(struct inode *)); extern int configfs_create_file(struct config_item *, const struct configfs_attribute *); +extern int configfs_create_bin_file(struct config_item *, + const struct configfs_bin_attribute *); extern int configfs_make_dirent(struct configfs_dirent *, struct dentry *, void *, umode_t, int); extern int configfs_dirent_is_ready(struct configfs_dirent *); @@ -88,7 +91,7 @@ extern void configfs_release_fs(void); extern struct rw_semaphore configfs_rename_sem; extern const struct file_operations configfs_dir_operations; extern const struct file_operations configfs_file_operations; -extern const struct file_operations bin_fops; +extern const struct file_operations configfs_bin_file_operations; extern const struct inode_operations configfs_dir_inode_operations; extern const struct inode_operations configfs_root_inode_operations; extern const struct inode_operations configfs_symlink_inode_operations; @@ -119,6 +122,13 @@ static inline struct configfs_attribute * to_attr(struct dentry * dentry) return ((struct configfs_attribute *) sd->s_element); } +static inline struct configfs_bin_attribute *to_bin_attr(struct dentry *dentry) +{ + struct configfs_attribute *attr = to_attr(dentry); + + return container_of(attr, struct configfs_bin_attribute, cb_attr); +} + static inline struct config_item *configfs_get_config_item(struct dentry *dentry) { struct config_item * item = NULL; diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index a7a1b218..7ae97e83 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -255,6 +255,12 @@ static void configfs_init_file(struct inode * inode) inode->i_fop = &configfs_file_operations; } +static void configfs_init_bin_file(struct inode *inode) +{ + inode->i_size = 0; + inode->i_fop = &configfs_bin_file_operations; +} + static void init_symlink(struct inode * inode) { inode->i_op = &configfs_symlink_inode_operations; @@ -423,7 +429,9 @@ static int configfs_attach_attr(struct configfs_dirent * sd, struct dentry * den spin_unlock(&configfs_dirent_lock); error = configfs_create(dentry, (attr->ca_mode & S_IALLUGO) | S_IFREG, - configfs_init_file); + (sd->s_type & CONFIGFS_ITEM_BIN_ATTR) ? + configfs_init_bin_file : + configfs_init_file); if (error) { configfs_put(sd); return error; @@ -583,6 +591,7 @@ static int populate_attrs(struct config_item *item) { struct config_item_type *t = item->ci_type; struct configfs_attribute *attr; + struct configfs_bin_attribute *bin_attr; int error = 0; int i; @@ -594,6 +603,13 @@ static int populate_attrs(struct config_item *item) break; } } + if (t->ct_bin_attrs) { + for (i = 0; (bin_attr = t->ct_bin_attrs[i]) != NULL; i++) { + error = configfs_create_bin_file(item, bin_attr); + if (error) + break; + } + } if (error) detach_attrs(item); diff --git a/fs/configfs/file.c b/fs/configfs/file.c index d39099ea..3687187c 100644 --- a/fs/configfs/file.c +++ b/fs/configfs/file.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -48,6 +49,10 @@ struct configfs_buffer { struct configfs_item_operations * ops; struct mutex mutex; int needs_read_fill; + bool read_in_progress; + bool write_in_progress; + char *bin_buffer; + int bin_buffer_size; }; @@ -123,6 +128,87 @@ configfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *pp return retval; } +/** + * configfs_read_bin_file - read a binary attribute. + * @file: file pointer. + * @buf: buffer to fill. + * @count: number of bytes to read. + * @ppos: starting offset in file. + * + * Userspace wants to read a binary attribute file. The attribute + * descriptor is in the file's ->d_fsdata. The target item is in the + * directory's ->d_fsdata. + * + * We check whether we need to refill the buffer. If so we will + * call the attributes' attr->read() twice. The first time we + * will pass a NULL as a buffer pointer, which the attributes' method + * will use to return the size of the buffer required. If no error + * occurs we will allocate the buffer using vmalloc and call + * attr->read() again passing that buffer as an argument. + * Then we just copy to user-space using simple_read_from_buffer. + */ + +static ssize_t +configfs_read_bin_file(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct configfs_buffer *buffer = file->private_data; + struct dentry *dentry = file->f_path.dentry; + struct config_item *item = to_item(dentry->d_parent); + struct configfs_bin_attribute *bin_attr = to_bin_attr(dentry); + ssize_t retval = 0; + ssize_t len = min_t(size_t, count, PAGE_SIZE); + + mutex_lock(&buffer->mutex); + + /* we don't support switching read/write modes */ + if (buffer->write_in_progress) { + retval = -ETXTBSY; + goto out; + } + buffer->read_in_progress = 1; + + if (buffer->needs_read_fill) { + /* perform first read with buf == NULL to get extent */ + len = bin_attr->read(item, NULL, 0); + if (len <= 0) { + retval = len; + goto out; + } + + /* do not exceed the maximum value */ + if (bin_attr->cb_max_size && len > bin_attr->cb_max_size) { + retval = -EFBIG; + goto out; + } + + buffer->bin_buffer = vmalloc(len); + if (buffer->bin_buffer == NULL) { + retval = -ENOMEM; + goto out; + } + buffer->bin_buffer_size = len; + + /* perform second read to fill buffer */ + len = bin_attr->read(item, buffer->bin_buffer, len); + if (len < 0) { + retval = len; + vfree(buffer->bin_buffer); + buffer->bin_buffer_size = 0; + buffer->bin_buffer = NULL; + goto out; + } + + buffer->needs_read_fill = 0; + } + + retval = simple_read_from_buffer(buf, count, ppos, buffer->bin_buffer, + buffer->bin_buffer_size); +out: + mutex_unlock(&buffer->mutex); + return retval; +} + /** * fill_write_buffer - copy buffer from userspace. @@ -209,10 +295,80 @@ configfs_write_file(struct file *file, const char __user *buf, size_t count, lof return len; } -static int check_perm(struct inode * inode, struct file * file) +/** + * configfs_write_bin_file - write a binary attribute. + * @file: file pointer + * @buf: data to write + * @count: number of bytes + * @ppos: starting offset + * + * Writing to a binary attribute file is similar to a normal read. + * We buffer the consecutive writes (binary attribute files do not + * support lseek) in a continuously growing buffer, but we don't + * commit until the close of the file. + */ + +static ssize_t +configfs_write_bin_file(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct configfs_buffer *buffer = file->private_data; + struct dentry *dentry = file->f_path.dentry; + struct configfs_bin_attribute *bin_attr = to_bin_attr(dentry); + void *tbuf = NULL; + ssize_t len; + + mutex_lock(&buffer->mutex); + + /* we don't support switching read/write modes */ + if (buffer->read_in_progress) { + len = -ETXTBSY; + goto out; + } + buffer->write_in_progress = 1; + + /* buffer grows? */ + if (*ppos + count > buffer->bin_buffer_size) { + + if (bin_attr->cb_max_size && + *ppos + count > bin_attr->cb_max_size) { + len = -EFBIG; + } + + tbuf = vmalloc(*ppos + count); + if (tbuf == NULL) { + len = -ENOMEM; + goto out; + } + + /* copy old contents */ + if (buffer->bin_buffer) { + memcpy(tbuf, buffer->bin_buffer, + buffer->bin_buffer_size); + vfree(buffer->bin_buffer); + } + + /* clear the new area */ + memset(tbuf + buffer->bin_buffer_size, 0, + *ppos + count - buffer->bin_buffer_size); + buffer->bin_buffer = tbuf; + buffer->bin_buffer_size = *ppos + count; + } + + len = simple_write_to_buffer(buffer->bin_buffer, + buffer->bin_buffer_size, ppos, buf, count); + if (len > 0) + *ppos += len; +out: + mutex_unlock(&buffer->mutex); + return len; +} + +static int check_perm(struct inode * inode, struct file * file, int type) { struct config_item *item = configfs_get_config_item(file->f_path.dentry->d_parent); struct configfs_attribute * attr = to_attr(file->f_path.dentry); + struct configfs_bin_attribute *bin_attr = NULL; struct configfs_buffer * buffer; struct configfs_item_operations * ops = NULL; int error = 0; @@ -220,6 +376,9 @@ static int check_perm(struct inode * inode, struct file * file) if (!item || !attr) goto Einval; + if (type & CONFIGFS_ITEM_BIN_ATTR) + bin_attr = to_bin_attr(file->f_path.dentry); + /* Grab the module reference for this attribute if we have one */ if (!try_module_get(attr->ca_owner)) { error = -ENODEV; @@ -236,9 +395,14 @@ static int check_perm(struct inode * inode, struct file * file) * and we must have a store method. */ if (file->f_mode & FMODE_WRITE) { - if (!(inode->i_mode & S_IWUGO) || !attr->store) + if (!(inode->i_mode & S_IWUGO)) + goto Eaccess; + + if ((type & CONFIGFS_ITEM_ATTR) && !attr->store) goto Eaccess; + if ((type & CONFIGFS_ITEM_BIN_ATTR) && !bin_attr->write) + goto Eaccess; } /* File needs read support. @@ -246,7 +410,13 @@ static int check_perm(struct inode * inode, struct file * file) * must be a show method for it. */ if (file->f_mode & FMODE_READ) { - if (!(inode->i_mode & S_IRUGO) || !attr->show) + if (!(inode->i_mode & S_IRUGO)) + goto Eaccess; + + if ((type & CONFIGFS_ITEM_ATTR) && !attr->show) + goto Eaccess; + + if ((type & CONFIGFS_ITEM_BIN_ATTR) && !bin_attr->read) goto Eaccess; } @@ -260,6 +430,8 @@ static int check_perm(struct inode * inode, struct file * file) } mutex_init(&buffer->mutex); buffer->needs_read_fill = 1; + buffer->read_in_progress = 0; + buffer->write_in_progress = 0; buffer->ops = ops; file->private_data = buffer; goto Done; @@ -277,12 +449,7 @@ static int check_perm(struct inode * inode, struct file * file) return error; } -static int configfs_open_file(struct inode * inode, struct file * filp) -{ - return check_perm(inode,filp); -} - -static int configfs_release(struct inode * inode, struct file * filp) +static int configfs_release(struct inode *inode, struct file *filp) { struct config_item * item = to_item(filp->f_path.dentry->d_parent); struct configfs_attribute * attr = to_attr(filp->f_path.dentry); @@ -303,6 +470,47 @@ static int configfs_release(struct inode * inode, struct file * filp) return 0; } +static int configfs_open_file(struct inode *inode, struct file *filp) +{ + return check_perm(inode, filp, CONFIGFS_ITEM_ATTR); +} + +static int configfs_open_bin_file(struct inode *inode, struct file *filp) +{ + return check_perm(inode, filp, CONFIGFS_ITEM_BIN_ATTR); +} + +static int configfs_release_bin_file(struct inode *inode, struct file *filp) +{ + struct configfs_buffer *buffer = filp->private_data; + struct dentry *dentry = filp->f_path.dentry; + struct config_item *item = to_item(dentry->d_parent); + struct configfs_bin_attribute *bin_attr = to_bin_attr(dentry); + ssize_t len = 0; + int ret; + + buffer->read_in_progress = 0; + + if (buffer->write_in_progress) { + buffer->write_in_progress = 0; + + len = bin_attr->write(item, buffer->bin_buffer, + buffer->bin_buffer_size); + + /* vfree on NULL is safe */ + vfree(buffer->bin_buffer); + buffer->bin_buffer = NULL; + buffer->bin_buffer_size = 0; + buffer->needs_read_fill = 1; + } + + ret = configfs_release(inode, filp); + if (len < 0) + return len; + return ret; +} + + const struct file_operations configfs_file_operations = { .read = configfs_read_file, .write = configfs_write_file, @@ -311,6 +519,14 @@ const struct file_operations configfs_file_operations = { .release = configfs_release, }; +const struct file_operations configfs_bin_file_operations = { + .read = configfs_read_bin_file, + .write = configfs_write_bin_file, + .llseek = NULL, /* bin file is not seekable */ + .open = configfs_open_bin_file, + .release = configfs_release_bin_file, +}; + /** * configfs_create_file - create an attribute file for an item. * @item: item we're creating for. @@ -332,3 +548,24 @@ int configfs_create_file(struct config_item * item, const struct configfs_attrib return error; } +/** + * configfs_create_bin_file - create a binary attribute file for an item. + * @item: item we're creating for. + * @attr: atrribute descriptor. + */ + +int configfs_create_bin_file(struct config_item *item, + const struct configfs_bin_attribute *bin_attr) +{ + struct dentry *dir = item->ci_dentry; + struct configfs_dirent *parent_sd = dir->d_fsdata; + umode_t mode = (bin_attr->cb_attr.ca_mode & S_IALLUGO) | S_IFREG; + int error = 0; + + mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_NORMAL); + error = configfs_make_dirent(parent_sd, NULL, (void *) bin_attr, mode, + CONFIGFS_ITEM_BIN_ATTR); + mutex_unlock(&dir->d_inode->i_mutex); + + return error; +} diff --git a/fs/configfs/inode.c b/fs/configfs/inode.c index eae87575..0cc810e9 100644 --- a/fs/configfs/inode.c +++ b/fs/configfs/inode.c @@ -218,7 +218,7 @@ const unsigned char * configfs_get_name(struct configfs_dirent *sd) if (sd->s_type & (CONFIGFS_DIR | CONFIGFS_ITEM_LINK)) return sd->s_dentry->d_name.name; - if (sd->s_type & CONFIGFS_ITEM_ATTR) { + if (sd->s_type & (CONFIGFS_ITEM_ATTR | CONFIGFS_ITEM_BIN_ATTR)) { attr = sd->s_element; return attr->ca_name; } diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 04edcd32..4aba6478 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -365,10 +366,10 @@ struct drm_pending_event { void (*destroy)(struct drm_pending_event *event); }; -/* initial implementaton using a linked list - todo hashtab */ struct drm_prime_file_private { - struct list_head head; struct mutex lock; + struct rb_root dmabufs; + struct rb_root handles; }; /** File private data */ @@ -1123,6 +1124,8 @@ static inline int drm_debugfs_remove_files(const struct drm_info_list *files, } #endif +struct dma_buf_export_info; + extern struct dma_buf *drm_gem_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags); @@ -1133,6 +1136,8 @@ extern struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev, struct dma_buf *dma_buf); extern int drm_gem_prime_fd_to_handle(struct drm_device *dev, struct drm_file *file_priv, int prime_fd, uint32_t *handle); +struct dma_buf *drm_gem_dmabuf_export(struct drm_device *dev, + struct dma_buf_export_info *exp_info); extern void drm_gem_dmabuf_release(struct dma_buf *dma_buf); extern int drm_prime_sg_to_page_addr_arrays(struct sg_table *sgt, struct page **pages, diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index 85861b63..55201e7e 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -254,6 +254,7 @@ struct detailed_timing { # define DRM_ELD_AUD_SYNCH_DELAY_MAX 0xfa /* 500 ms */ #define DRM_ELD_SPEAKER 7 +# define DRM_ELD_SPEAKER_MASK 0x7f # define DRM_ELD_SPEAKER_RLRC (1 << 6) # define DRM_ELD_SPEAKER_FLRC (1 << 5) # define DRM_ELD_SPEAKER_RC (1 << 4) @@ -417,6 +418,18 @@ static inline int drm_eld_size(const uint8_t *eld) return DRM_ELD_HEADER_BLOCK_SIZE + eld[DRM_ELD_BASELINE_ELD_LEN] * 4; } +/** + * drm_eld_get_spk_alloc - Get speaker allocation + * @eld: pointer to an ELD memory structure + * + * The returned value is the speakers mask. User has to use %DRM_ELD_SPEAKER + * field definitions to identify speakers. + */ +static inline u8 drm_eld_get_spk_alloc(const uint8_t *eld) +{ + return eld[DRM_ELD_SPEAKER] & DRM_ELD_SPEAKER_MASK; +} + struct edid *drm_do_get_edid(struct drm_connector *connector, int (*get_edid_block)(void *data, u8 *buf, unsigned int block, size_t len), diff --git a/include/linux/amba/pl330.h b/include/linux/amba/pl330.h deleted file mode 100644 index fe93758e..00000000 --- a/include/linux/amba/pl330.h +++ /dev/null @@ -1,35 +0,0 @@ -/* linux/include/linux/amba/pl330.h - * - * Copyright (C) 2010 Samsung Electronics Co. Ltd. - * Jaswinder Singh - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#ifndef __AMBA_PL330_H_ -#define __AMBA_PL330_H_ - -#include - -struct dma_pl330_platdata { - /* - * Number of valid peripherals connected to DMAC. - * This may be different from the value read from - * CR0, as the PL330 implementation might have 'holes' - * in the peri list or the peri could also be reached - * from another DMAC which the platform prefers. - */ - u8 nr_valid_peri; - /* Array of valid peripherals */ - u8 *peri_id; - /* Operational capabilities */ - dma_cap_mask_t cap_mask; - /* Bytes to allocate for MC buffer */ - unsigned mcbuf_sz; -}; - -extern bool pl330_filter(struct dma_chan *chan, void *param); -#endif /* __AMBA_PL330_H_ */ diff --git a/include/linux/bitfield.h b/include/linux/bitfield.h new file mode 100644 index 00000000..8b9d6fff --- /dev/null +++ b/include/linux/bitfield.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2014 Felix Fietkau + * Copyright (C) 2004 - 2009 Ivo van Doorn + * + * 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 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _LINUX_BITFIELD_H +#define _LINUX_BITFIELD_H + +#include + +/* + * Bitfield access macros + * + * FIELD_{GET,PREP} macros take as first parameter shifted mask + * from which they extract the base mask and shift amount. + * Mask must be a compilation time constant. + * + * Example: + * + * #define REG_FIELD_A GENMASK(6, 0) + * #define REG_FIELD_B BIT(7) + * #define REG_FIELD_C GENMASK(15, 8) + * #define REG_FIELD_D GENMASK(31, 16) + * + * Get: + * a = FIELD_GET(REG_FIELD_A, reg); + * b = FIELD_GET(REG_FIELD_B, reg); + * + * Set: + * reg = FIELD_PREP(REG_FIELD_A, 1) | + * FIELD_PREP(REG_FIELD_B, 0) | + * FIELD_PREP(REG_FIELD_C, c) | + * FIELD_PREP(REG_FIELD_D, 0x40); + * + * Modify: + * reg &= ~REG_FIELD_C; + * reg |= FIELD_PREP(REG_FIELD_C, c); + */ + +#define __bf_shf(x) (__builtin_ffsll(x) - 1) + +#define __BF_FIELD_CHECK(_mask, _reg, _val, _pfx) \ + ({ \ + BUILD_BUG_ON_MSG(!__builtin_constant_p(_mask), \ + _pfx "mask is not constant"); \ + BUILD_BUG_ON_MSG(!(_mask), _pfx "mask is zero"); \ + BUILD_BUG_ON_MSG(__builtin_constant_p(_val) ? \ + ~((_mask) >> __bf_shf(_mask)) & (_val) : 0, \ + _pfx "value too large for the field"); \ + BUILD_BUG_ON_MSG((_mask) > (typeof(_reg))~0ull, \ + _pfx "type of reg too small for mask"); \ + __BUILD_BUG_ON_NOT_POWER_OF_2((_mask) + \ + (1ULL << __bf_shf(_mask))); \ + }) + +/** + * FIELD_FIT() - check if value fits in the field + * @_mask: shifted mask defining the field's length and position + * @_val: value to test against the field + * + * Return: true if @_val can fit inside @_mask, false if @_val is too big. + */ +#define FIELD_FIT(_mask, _val) \ + ({ \ + __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_FIT: "); \ + !((((typeof(_mask))_val) << __bf_shf(_mask)) & ~(_mask)); \ + }) + +/** + * FIELD_PREP() - prepare a bitfield element + * @_mask: shifted mask defining the field's length and position + * @_val: value to put in the field + * + * FIELD_PREP() masks and shifts up the value. The result should + * be combined with other fields of the bitfield using logical OR. + */ +#define FIELD_PREP(_mask, _val) \ + ({ \ + __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: "); \ + ((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask); \ + }) + +/** + * FIELD_GET() - extract a bitfield element + * @_mask: shifted mask defining the field's length and position + * @_reg: 32bit value of entire bitfield + * + * FIELD_GET() extracts the field specified by @_mask from the + * bitfield passed in as @_reg by masking and shifting it down. + */ +#define FIELD_GET(_mask, _reg) \ + ({ \ + __BF_FIELD_CHECK(_mask, _reg, 0U, "FIELD_GET: "); \ + (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \ + }) + +#endif diff --git a/include/linux/bug.h b/include/linux/bug.h index 7f481867..edd3d8d3 100644 --- a/include/linux/bug.h +++ b/include/linux/bug.h @@ -13,6 +13,7 @@ enum bug_trap_type { struct pt_regs; #ifdef __CHECKER__ +#define __BUILD_BUG_ON_NOT_POWER_OF_2(n) (0) #define BUILD_BUG_ON_NOT_POWER_OF_2(n) (0) #define BUILD_BUG_ON_ZERO(e) (0) #define BUILD_BUG_ON_NULL(e) ((void*)0) @@ -23,6 +24,8 @@ struct pt_regs; #else /* __CHECKER__ */ /* Force a compilation error if a constant expression is not a power of 2 */ +#define __BUILD_BUG_ON_NOT_POWER_OF_2(n) \ + BUILD_BUG_ON(((n) & ((n) - 1)) != 0) #define BUILD_BUG_ON_NOT_POWER_OF_2(n) \ BUILD_BUG_ON((n) == 0 || (((n) & ((n) - 1)) != 0)) diff --git a/include/linux/configfs.h b/include/linux/configfs.h index 758a0290..658066d6 100644 --- a/include/linux/configfs.h +++ b/include/linux/configfs.h @@ -51,6 +51,7 @@ struct module; struct configfs_item_operations; struct configfs_group_operations; struct configfs_attribute; +struct configfs_bin_attribute; struct configfs_subsystem; struct config_item { @@ -84,6 +85,7 @@ struct config_item_type { struct configfs_item_operations *ct_item_ops; struct configfs_group_operations *ct_group_ops; struct configfs_attribute **ct_attrs; + struct configfs_bin_attribute **ct_bin_attrs; }; /** @@ -154,6 +156,54 @@ static struct configfs_attribute _pfx##attr_##_name = { \ .store = _pfx##_name##_store, \ } +struct file; +struct vm_area_struct; + +struct configfs_bin_attribute { + struct configfs_attribute cb_attr; /* std. attribute */ + void *cb_private; /* for user */ + size_t cb_max_size; /* max core size */ + ssize_t (*read)(struct config_item *, void *, size_t); + ssize_t (*write)(struct config_item *, const void *, size_t); +}; + +#define CONFIGFS_BIN_ATTR(_pfx, _name, _priv, _maxsz) \ +static struct configfs_bin_attribute _pfx##attr_##_name = { \ + .cb_attr = { \ + .ca_name = __stringify(_name), \ + .ca_mode = S_IRUGO | S_IWUSR, \ + .ca_owner = THIS_MODULE, \ + }, \ + .cb_private = _priv, \ + .cb_max_size = _maxsz, \ + .read = _pfx##_name##_read, \ + .write = _pfx##_name##_write, \ +} + +#define CONFIGFS_BIN_ATTR_RO(_pfx, _name, _priv, _maxsz) \ +static struct configfs_bin_attribute _pfx##attr_##_name = { \ + .cb_attr = { \ + .ca_name = __stringify(_name), \ + .ca_mode = S_IRUGO, \ + .ca_owner = THIS_MODULE, \ + }, \ + .cb_private = _priv, \ + .cb_max_size = _maxsz, \ + .read = _pfx##_name##_read, \ +} + +#define CONFIGFS_BIN_ATTR_WO(_pfx, _name, _priv, _maxsz) \ +static struct configfs_bin_attribute _pfx##attr_##_name = { \ + .cb_attr = { \ + .ca_name = __stringify(_name), \ + .ca_mode = S_IWUSR, \ + .ca_owner = THIS_MODULE, \ + }, \ + .cb_private = _priv, \ + .cb_max_size = _maxsz, \ + .write = _pfx##_name##_write, \ +} + /* * If allow_link() exists, the item can symlink(2) out to other * items. If the item is a group, it may support mkdir(2). diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 3050f88d..948c17e4 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -365,8 +365,6 @@ struct dma_slave_config { u32 dst_maxburst; bool device_fc; unsigned int slave_id; - unsigned int src_interlace_size; - unsigned int dst_interlace_size; }; /** diff --git a/include/linux/rockchip-iovmm.h b/include/linux/rockchip-iovmm.h index 73e2ff15..d87b8d2c 100644 --- a/include/linux/rockchip-iovmm.h +++ b/include/linux/rockchip-iovmm.h @@ -10,6 +10,7 @@ #include #include #include +#include #define IEP_IOMMU_COMPATIBLE_NAME "rockchip,iep_mmu" #define VIP_IOMMU_COMPATIBLE_NAME "rockchip,vip_mmu" diff --git a/include/media/cec-notifier.h b/include/media/cec-notifier.h index ca19a930..8bb169ac 100644 --- a/include/media/cec-notifier.h +++ b/include/media/cec-notifier.h @@ -91,6 +91,14 @@ void cec_notifier_register(struct cec_notifier *n, */ void cec_notifier_unregister(struct cec_notifier *n); +/** + * cec_register_cec_notifier - register the notifier with the cec adapter. + * @adap: the CEC adapter + * @notifier: the CEC notifier + */ +void cec_register_cec_notifier(struct cec_adapter *adap, + struct cec_notifier *notifier); + #else static inline struct cec_notifier *cec_notifier_get(struct device *dev) { @@ -111,6 +119,20 @@ static inline void cec_notifier_set_phys_addr_from_edid(struct cec_notifier *n, { } +static inline void cec_notifier_register(struct cec_notifier *n, + struct cec_adapter *adap, + void (*callback)(struct cec_adapter *adap, u16 pa)) +{ +} + +static inline void cec_notifier_unregister(struct cec_notifier *n) +{ +} + +static inline void cec_register_cec_notifier(struct cec_adapter *adap, + struct cec_notifier *notifier) +{ +} #endif /** diff --git a/include/media/cec.h b/include/media/cec.h index df3c94f0..f64807a7 100644 --- a/include/media/cec.h +++ b/include/media/cec.h @@ -31,6 +31,9 @@ #include #include +#define CEC_CAP_DEFAULTS (CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT | \ + CEC_CAP_PASSTHROUGH | CEC_CAP_RC) + /** * struct cec_devnode - cec device node * @dev: cec device @@ -188,6 +191,11 @@ struct cec_adapter { u32 tx_timeouts; +#ifdef CONFIG_MEDIA_CEC_RC + bool rc_repeating; + int rc_last_scancode; + u64 rc_last_keypress; +#endif #ifdef CONFIG_CEC_NOTIFIER struct cec_notifier *notifier; #endif @@ -226,7 +234,7 @@ static inline bool cec_is_sink(const struct cec_adapter *adap) struct edid; -#if IS_ENABLED(CONFIG_CEC_CORE) +#if IS_REACHABLE(CONFIG_CEC_CORE) struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, void *priv, const char *name, u32 caps, u8 available_las); int cec_register_adapter(struct cec_adapter *adap, struct device *parent); @@ -373,11 +381,6 @@ u16 cec_phys_addr_for_input(u16 phys_addr, u8 input); */ int cec_phys_addr_validate(u16 phys_addr, u16 *parent, u16 *port); -#ifdef CONFIG_CEC_NOTIFIER -void cec_register_cec_notifier(struct cec_adapter *adap, - struct cec_notifier *notifier); -#endif - #else static inline int cec_register_adapter(struct cec_adapter *adap, @@ -424,9 +427,26 @@ static inline u16 cec_phys_addr_for_input(u16 phys_addr, u8 input) static inline int cec_phys_addr_validate(u16 phys_addr, u16 *parent, u16 *port) { + if (parent) + *parent = phys_addr; + if (port) + *port = 0; return 0; } #endif +/** + * cec_phys_addr_invalidate() - set the physical address to INVALID + * + * @adap: the CEC adapter + * + * This is a simple helper function to invalidate the physical + * address. + */ +static inline void cec_phys_addr_invalidate(struct cec_adapter *adap) +{ + cec_s_phys_addr(adap, CEC_PHYS_ADDR_INVALID, false); +} + #endif /* _MEDIA_CEC_H */ diff --git a/include/media/rc-map.h b/include/media/rc-map.h index 7c4bbc4d..d7d9c805 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -31,6 +31,7 @@ enum rc_type { RC_TYPE_RC6_MCE = 16, /* MCE (Philips RC6-6A-32 subtype) protocol */ RC_TYPE_SHARP = 17, /* Sharp protocol */ RC_TYPE_XMP = 18, /* XMP protocol */ + RC_TYPE_CEC = 19, /* CEC protocol */ }; #define RC_BIT_NONE 0 @@ -53,6 +54,7 @@ enum rc_type { #define RC_BIT_RC6_MCE (1 << RC_TYPE_RC6_MCE) #define RC_BIT_SHARP (1 << RC_TYPE_SHARP) #define RC_BIT_XMP (1 << RC_TYPE_XMP) +#define RC_BIT_CEC (1 << RC_TYPE_CEC) #define RC_BIT_ALL (RC_BIT_UNKNOWN | RC_BIT_OTHER | \ RC_BIT_RC5 | RC_BIT_RC5X | RC_BIT_RC5_SZ | \ @@ -61,7 +63,7 @@ enum rc_type { RC_BIT_NEC | RC_BIT_SANYO | RC_BIT_MCE_KBD | \ RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 | \ RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE | RC_BIT_SHARP | \ - RC_BIT_XMP) + RC_BIT_XMP | RC_BIT_CEC) #define RC_SCANCODE_UNKNOWN(x) (x) @@ -123,6 +125,7 @@ void rc_map_init(void); #define RC_MAP_BEHOLD_COLUMBUS "rc-behold-columbus" #define RC_MAP_BEHOLD "rc-behold" #define RC_MAP_BUDGET_CI_OLD "rc-budget-ci-old" +#define RC_MAP_CEC "rc-cec" #define RC_MAP_CINERGY_1400 "rc-cinergy-1400" #define RC_MAP_CINERGY "rc-cinergy" #define RC_MAP_DELOCK_61959 "rc-delock-61959" @@ -154,6 +157,7 @@ void rc_map_init(void); #define RC_MAP_IT913X_V1 "rc-it913x-v1" #define RC_MAP_IT913X_V2 "rc-it913x-v2" #define RC_MAP_KAIOMY "rc-kaiomy" +#define RC_MAP_KHADAS "rc-khadas" #define RC_MAP_KWORLD_315U "rc-kworld-315u" #define RC_MAP_KWORLD_PC150U "rc-kworld-pc150u" #define RC_MAP_KWORLD_PLUS_TV_ANALOG "rc-kworld-plus-tv-analog" @@ -172,7 +176,9 @@ void rc_map_init(void); #define RC_MAP_NEC_TERRATEC_CINERGY_XS "rc-nec-terratec-cinergy-xs" #define RC_MAP_NORWOOD "rc-norwood" #define RC_MAP_NPGTECH "rc-npgtech" +#define RC_MAP_ODROID "rc-odroid" #define RC_MAP_PCTV_SEDNA "rc-pctv-sedna" +#define RC_MAP_PINE64 "rc-pine64" #define RC_MAP_PINNACLE_COLOR "rc-pinnacle-color" #define RC_MAP_PINNACLE_GREY "rc-pinnacle-grey" #define RC_MAP_PINNACLE_PCTV_HD "rc-pinnacle-pctv-hd" @@ -189,6 +195,7 @@ void rc_map_init(void); #define RC_MAP_RC6_MCE "rc-rc6-mce" #define RC_MAP_REAL_AUDIO_220_32_KEYS "rc-real-audio-220-32-keys" #define RC_MAP_REDDO "rc-reddo" +#define RC_MAP_ROC_CC "rc-roc-cc" #define RC_MAP_SNAPSTREAM_FIREFLY "rc-snapstream-firefly" #define RC_MAP_STREAMZAP "rc-streamzap" #define RC_MAP_TBS_NEC "rc-tbs-nec" @@ -207,6 +214,9 @@ void rc_map_init(void); #define RC_MAP_TT_1500 "rc-tt-1500" #define RC_MAP_TWINHAN_DTV_CAB_CI "rc-twinhan-dtv-cab-ci" #define RC_MAP_TWINHAN_VP1027_DVBS "rc-twinhan1027" +#define RC_MAP_TRN9 "rc-trn9" +#define RC_MAP_WETEK_HUB "rc-wetek-hub" +#define RC_MAP_WETEK_PLAY_2 "rc-wetek-play-2" #define RC_MAP_VIDEOMATE_K100 "rc-videomate-k100" #define RC_MAP_VIDEOMATE_S350 "rc-videomate-s350" #define RC_MAP_VIDEOMATE_TV_PVR "rc-videomate-tv-pvr" diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 212eaaf1..345e4f8e 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -230,6 +230,9 @@ struct snd_soc_dai_driver { int (*resume)(struct snd_soc_dai *dai); /* compress dai */ int (*compress_new)(struct snd_soc_pcm_runtime *rtd, int num); + /* Optional Callback used at pcm creation*/ + int (*pcm_new)(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_dai *dai); /* DAI is also used for the control bus */ bool bus_control; diff --git a/include/uapi/drm/Kbuild b/include/uapi/drm/Kbuild index 38d43709..b7ae9969 100644 --- a/include/uapi/drm/Kbuild +++ b/include/uapi/drm/Kbuild @@ -11,6 +11,7 @@ header-y += nouveau_drm.h header-y += qxl_drm.h header-y += r128_drm.h header-y += radeon_drm.h +header-y += rockchip_drm.h header-y += savage_drm.h header-y += sis_drm.h header-y += tegra_drm.h diff --git a/include/uapi/linux/cec-funcs.h b/include/uapi/linux/cec-funcs.h index c451eec4..270b251a 100644 --- a/include/uapi/linux/cec-funcs.h +++ b/include/uapi/linux/cec-funcs.h @@ -895,6 +895,7 @@ static inline void cec_ops_report_features(const struct cec_msg *msg, *cec_version = msg->msg[2]; *all_device_types = msg->msg[3]; *rc_profile = p; + *dev_features = NULL; while (p < &msg->msg[14] && (*p & CEC_OP_FEAT_EXT)) p++; if (!(*p & CEC_OP_FEAT_EXT)) { diff --git a/include/uapi/linux/cec.h b/include/uapi/linux/cec.h index af6682f5..b9f8df3a 100644 --- a/include/uapi/linux/cec.h +++ b/include/uapi/linux/cec.h @@ -223,7 +223,7 @@ static inline int cec_msg_status_is_ok(const struct cec_msg *msg) #define CEC_LOG_ADDR_BACKUP_2 13 #define CEC_LOG_ADDR_SPECIFIC 14 #define CEC_LOG_ADDR_UNREGISTERED 15 /* as initiator address */ -#define CEC_LOG_ADDR_BROADCAST 15 /* ad destination address */ +#define CEC_LOG_ADDR_BROADCAST 15 /* as destination address */ /* The logical address types that the CEC device wants to claim */ #define CEC_LOG_ADDR_TYPE_TV 0 diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h index 87cf351b..b0c5c488 100644 --- a/include/uapi/linux/input-event-codes.h +++ b/include/uapi/linux/input-event-codes.h @@ -611,6 +611,37 @@ #define KEY_KBDINPUTASSIST_ACCEPT 0x264 #define KEY_KBDINPUTASSIST_CANCEL 0x265 +/* Diagonal movement keys */ +#define KEY_RIGHT_UP 0x266 +#define KEY_RIGHT_DOWN 0x267 +#define KEY_LEFT_UP 0x268 +#define KEY_LEFT_DOWN 0x269 + +#define KEY_ROOT_MENU 0x26a /* Show Device's Root Menu */ +/* Show Top Menu of the Media (e.g. DVD) */ +#define KEY_MEDIA_TOP_MENU 0x26b +#define KEY_NUMERIC_11 0x26c +#define KEY_NUMERIC_12 0x26d +/* + * Toggle Audio Description: refers to an audio service that helps blind and + * visually impaired consumers understand the action in a program. Note: in + * some countries this is referred to as "Video Description". + */ +#define KEY_AUDIO_DESC 0x26e +#define KEY_3D_MODE 0x26f +#define KEY_NEXT_FAVORITE 0x270 +#define KEY_STOP_RECORD 0x271 +#define KEY_PAUSE_RECORD 0x272 +#define KEY_VOD 0x273 /* Video on Demand */ +#define KEY_UNMUTE 0x274 +#define KEY_FASTREVERSE 0x275 +#define KEY_SLOWREVERSE 0x276 +/* + * Control a data application associated with the currently viewed channel, + * e.g. teletext or data broadcast application (MHEG, MHP, HbbTV, etc.) + */ +#define KEY_DATA 0x277 + #define BTN_TRIGGER_HAPPY 0x2c0 #define BTN_TRIGGER_HAPPY1 0x2c0 #define BTN_TRIGGER_HAPPY2 0x2c1 diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h index 27586873..41e8dff5 100644 --- a/include/uapi/linux/input.h +++ b/include/uapi/linux/input.h @@ -246,6 +246,7 @@ struct input_mask { #define BUS_GSC 0x1A #define BUS_ATARI 0x1B #define BUS_SPI 0x1C +#define BUS_CEC 0x1E /* * MT_TOOL types diff --git a/include/uapi/linux/serio.h b/include/uapi/linux/serio.h index becdd782..89b72003 100644 --- a/include/uapi/linux/serio.h +++ b/include/uapi/linux/serio.h @@ -77,5 +77,7 @@ #define SERIO_PS2MULT 0x3c #define SERIO_TSC40 0x3d #define SERIO_WACOM_IV 0x3e +#define SERIO_PULSE8_CEC 0x40 +#define SERIO_RAINSHADOW_CEC 0x41 #endif /* _UAPI_SERIO_H */ diff --git a/net/rfkill/rfkill-bt.c b/net/rfkill/rfkill-bt.c index b19e0fb9..29d88313 100644 --- a/net/rfkill/rfkill-bt.c +++ b/net/rfkill/rfkill-bt.c @@ -296,20 +296,23 @@ static int rfkill_rk_set_power(void *data, bool blocked) msleep(20); } - if (gpio_is_valid(poweron->io) && gpio_is_valid(wake_host->io)) - { - gpio_direction_output(poweron->io, !poweron->enable); - msleep(20); - gpio_direction_output(poweron->io, poweron->enable); - msleep(20); - gpio_direction_input(wake_host->io); - LOG("%s: set bt wake_host pin input!\n", __func__); + if (gpio_is_valid(poweron->io) && gpio_is_valid(wake_host->io)) { + if (gpio_get_value(poweron->io) == !poweron->enable) { + gpio_direction_output(poweron->io, !poweron->enable); + msleep(20); + gpio_direction_output(poweron->io, poweron->enable); + msleep(20); + gpio_direction_input(wake_host->io); + LOG("%s: set bt wake_host pin input!\n", __func__); + } } - if (gpio_is_valid(reset->io)) - { + + if (gpio_is_valid(reset->io)) { + if (gpio_get_value(reset->io) == !reset->enable) { gpio_direction_output(reset->io, !reset->enable); - msleep(20); + msleep(20); gpio_direction_output(reset->io, reset->enable); + } } if (pinctrl != NULL && gpio_is_valid(rts->io)) @@ -326,20 +329,23 @@ static int rfkill_rk_set_power(void *data, bool blocked) bt_power_state = 1; LOG("bt turn on power\n"); } else { - if (gpio_is_valid(poweron->io)) - { - gpio_direction_output(poweron->io, !poweron->enable); - msleep(20); - } - - bt_power_state = 0; - LOG("bt shut off power\n"); - if (gpio_is_valid(reset->io)) - { - gpio_direction_output(reset->io, !reset->enable);/* bt reset active*/ - msleep(20); - } + if (gpio_is_valid(poweron->io)) { + if (gpio_get_value(poweron->io) == poweron->enable) { + gpio_direction_output(poweron->io, + !poweron->enable); + msleep(20); + } + } + bt_power_state = 0; + LOG("bt shut off power\n"); + if (gpio_is_valid(reset->io)) { + if (gpio_get_value(reset->io) == reset->enable) { + gpio_direction_output(reset->io, + !reset->enable); + msleep(20); + } + } } return 0; diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index 028d60c1..b74659bc 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -21,12 +21,255 @@ #include #include #include +#include #include #include #include #include /* This is only to get MAX_ELD_BYTES */ +#define HDMI_CODEC_CHMAP_IDX_UNKNOWN -1 + +struct hdmi_codec_channel_map_table { + unsigned char map; /* ALSA API channel map position */ + unsigned long spk_mask; /* speaker position bit mask */ +}; + +/* + * CEA speaker placement for HDMI 1.4: + * + * FL FLC FC FRC FR FRW + * + * LFE + * + * RL RLC RC RRC RR + * + * Speaker placement has to be extended to support HDMI 2.0 + */ +enum hdmi_codec_cea_spk_placement { + FL = BIT(0), /* Front Left */ + FC = BIT(1), /* Front Center */ + FR = BIT(2), /* Front Right */ + FLC = BIT(3), /* Front Left Center */ + FRC = BIT(4), /* Front Right Center */ + RL = BIT(5), /* Rear Left */ + RC = BIT(6), /* Rear Center */ + RR = BIT(7), /* Rear Right */ + RLC = BIT(8), /* Rear Left Center */ + RRC = BIT(9), /* Rear Right Center */ + LFE = BIT(10), /* Low Frequency Effect */ +}; + +/* + * cea Speaker allocation structure + */ +struct hdmi_codec_cea_spk_alloc { + const int ca_id; + unsigned int n_ch; + unsigned long mask; +}; + +/* Channel maps stereo HDMI */ +const struct snd_pcm_chmap_elem hdmi_codec_stereo_chmaps[] = { + { .channels = 2, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, + { } +}; + +/* Channel maps for multi-channel playbacks, up to 8 n_ch */ +const struct snd_pcm_chmap_elem hdmi_codec_8ch_chmaps[] = { + { .channels = 2, /* CA_ID 0x00 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, + { .channels = 4, /* CA_ID 0x01 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_NA } }, + { .channels = 4, /* CA_ID 0x02 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FC } }, + { .channels = 4, /* CA_ID 0x03 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_FC } }, + { .channels = 6, /* CA_ID 0x04 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_NA, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, + { .channels = 6, /* CA_ID 0x05 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_NA, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, + { .channels = 6, /* CA_ID 0x06 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FC, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, + { .channels = 6, /* CA_ID 0x07 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_FC, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, + { .channels = 6, /* CA_ID 0x08 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, + { .channels = 6, /* CA_ID 0x09 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, + { .channels = 6, /* CA_ID 0x0A */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, + { .channels = 6, /* CA_ID 0x0B */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, + { .channels = 8, /* CA_ID 0x0C */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, + SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, + { .channels = 8, /* CA_ID 0x0D */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, + SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, + { .channels = 8, /* CA_ID 0x0E */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, + SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, + { .channels = 8, /* CA_ID 0x0F */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, + SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, + { .channels = 8, /* CA_ID 0x10 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, + SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } }, + { .channels = 8, /* CA_ID 0x11 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, + SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } }, + { .channels = 8, /* CA_ID 0x12 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, + SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } }, + { .channels = 8, /* CA_ID 0x13 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, + SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } }, + { .channels = 8, /* CA_ID 0x14 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x15 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x16 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x17 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x18 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x19 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x1A */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x1B */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x1C */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x1D */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x1E */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x1F */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { } +}; + +/* + * hdmi_codec_channel_alloc: speaker configuration available for CEA + * + * This is an ordered list that must match with hdmi_codec_8ch_chmaps struct + * The preceding ones have better chances to be selected by + * hdmi_codec_get_ch_alloc_table_idx(). + */ +static const struct hdmi_codec_cea_spk_alloc hdmi_codec_channel_alloc[] = { + { .ca_id = 0x00, .n_ch = 2, + .mask = FL | FR }, + { .ca_id = 0x03, .n_ch = 4, + .mask = FL | FR | LFE | FC }, + { .ca_id = 0x02, .n_ch = 4, + .mask = FL | FR | FC }, + { .ca_id = 0x01, .n_ch = 4, + .mask = FL | FR | LFE }, + { .ca_id = 0x0b, .n_ch = 6, + .mask = FL | FR | LFE | FC | RL | RR }, + { .ca_id = 0x0a, .n_ch = 6, + .mask = FL | FR | FC | RL | RR }, + { .ca_id = 0x09, .n_ch = 6, + .mask = FL | FR | LFE | RL | RR }, + { .ca_id = 0x08, .n_ch = 6, + .mask = FL | FR | RL | RR }, + { .ca_id = 0x07, .n_ch = 6, + .mask = FL | FR | LFE | FC | RC }, + { .ca_id = 0x06, .n_ch = 6, + .mask = FL | FR | FC | RC }, + { .ca_id = 0x05, .n_ch = 6, + .mask = FL | FR | LFE | RC }, + { .ca_id = 0x04, .n_ch = 6, + .mask = FL | FR | RC }, + { .ca_id = 0x13, .n_ch = 8, + .mask = FL | FR | LFE | FC | RL | RR | RLC | RRC }, + { .ca_id = 0x1f, .n_ch = 8, + .mask = FL | FR | LFE | FC | RL | RR | FLC | FRC }, + { .ca_id = 0x12, .n_ch = 8, + .mask = FL | FR | FC | RL | RR | RLC | RRC }, + { .ca_id = 0x1e, .n_ch = 8, + .mask = FL | FR | FC | RL | RR | FLC | FRC }, + { .ca_id = 0x11, .n_ch = 8, + .mask = FL | FR | LFE | RL | RR | RLC | RRC }, + { .ca_id = 0x1d, .n_ch = 8, + .mask = FL | FR | LFE | RL | RR | FLC | FRC }, + { .ca_id = 0x10, .n_ch = 8, + .mask = FL | FR | RL | RR | RLC | RRC }, + { .ca_id = 0x1c, .n_ch = 8, + .mask = FL | FR | RL | RR | FLC | FRC }, + { .ca_id = 0x0f, .n_ch = 8, + .mask = FL | FR | LFE | FC | RL | RR | RC }, + { .ca_id = 0x1b, .n_ch = 8, + .mask = FL | FR | LFE | RC | FC | FLC | FRC }, + { .ca_id = 0x0e, .n_ch = 8, + .mask = FL | FR | FC | RL | RR | RC }, + { .ca_id = 0x1a, .n_ch = 8, + .mask = FL | FR | RC | FC | FLC | FRC }, + { .ca_id = 0x0d, .n_ch = 8, + .mask = FL | FR | LFE | RL | RR | RC }, + { .ca_id = 0x19, .n_ch = 8, + .mask = FL | FR | LFE | RC | FLC | FRC }, + { .ca_id = 0x0c, .n_ch = 8, + .mask = FL | FR | RC | RL | RR }, + { .ca_id = 0x18, .n_ch = 8, + .mask = FL | FR | RC | FLC | FRC }, + { .ca_id = 0x17, .n_ch = 8, + .mask = FL | FR | LFE | FC | FLC | FRC }, + { .ca_id = 0x16, .n_ch = 8, + .mask = FL | FR | FC | FLC | FRC }, + { .ca_id = 0x15, .n_ch = 8, + .mask = FL | FR | LFE | FLC | FRC }, + { .ca_id = 0x14, .n_ch = 8, + .mask = FL | FR | FLC | FRC }, +}; + struct hdmi_codec_priv { struct hdmi_codec_pdata hcd; struct snd_soc_dai_driver *daidrv; @@ -41,6 +284,8 @@ struct hdmi_codec_priv { struct notifier_block nb; unsigned int jack_status; unsigned int mode; + struct snd_pcm_chmap *chmap_info; + unsigned int chmap_idx; }; static const struct snd_soc_dapm_widget hdmi_widgets[] = { @@ -109,6 +354,83 @@ static int hdmi_audio_mode_put(struct snd_kcontrol *kcontrol, return 0; } +static unsigned long hdmi_codec_spk_mask_from_alloc(int spk_alloc) +{ + int i; + const unsigned long hdmi_codec_eld_spk_alloc_bits[] = { + [0] = FL | FR, [1] = LFE, [2] = FC, [3] = RL | RR, + [4] = RC, [5] = FLC | FRC, [6] = RLC | RRC, + }; + unsigned long spk_mask = 0; + + for (i = 0; i < ARRAY_SIZE(hdmi_codec_eld_spk_alloc_bits); i++) { + if (spk_alloc & (1 << i)) + spk_mask |= hdmi_codec_eld_spk_alloc_bits[i]; + } + + return spk_mask; +} + +void hdmi_codec_eld_chmap(struct hdmi_codec_priv *hcp) +{ + u8 spk_alloc; + unsigned long spk_mask; + + spk_alloc = drm_eld_get_spk_alloc(hcp->eld); + spk_mask = hdmi_codec_spk_mask_from_alloc(spk_alloc); + + /* Detect if only stereo supported, else return 8 channels mappings */ + if ((spk_mask & ~(FL | FR)) && hcp->chmap_info->max_channels > 2) + hcp->chmap_info->chmap = hdmi_codec_8ch_chmaps; + else + hcp->chmap_info->chmap = hdmi_codec_stereo_chmaps; +} + +static int hdmi_codec_get_ch_alloc_table_idx(struct hdmi_codec_priv *hcp, + unsigned char channels) +{ + int i; + u8 spk_alloc; + unsigned long spk_mask; + const struct hdmi_codec_cea_spk_alloc *cap = hdmi_codec_channel_alloc; + + spk_alloc = drm_eld_get_spk_alloc(hcp->eld); + spk_mask = hdmi_codec_spk_mask_from_alloc(spk_alloc); + + for (i = 0; i < ARRAY_SIZE(hdmi_codec_channel_alloc); i++, cap++) { + /* If spk_alloc == 0, HDMI is unplugged return stereo config*/ + if (!spk_alloc && cap->ca_id == 0) + return i; + if (cap->n_ch != channels) + continue; + if (!(cap->mask == (spk_mask & cap->mask))) + continue; + return i; + } + + return -EINVAL; +} +static int hdmi_codec_chmap_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned const char *map; + unsigned int i; + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct hdmi_codec_priv *hcp = info->private_data; + + map = info->chmap[hcp->chmap_idx].map; + + for (i = 0; i < info->max_channels; i++) { + if (hcp->chmap_idx == HDMI_CODEC_CHMAP_IDX_UNKNOWN) + ucontrol->value.integer.value[i] = 0; + else + ucontrol->value.integer.value[i] = map[i]; + } + + return 0; +} + + static const struct snd_kcontrol_new hdmi_controls[] = { { .access = SNDRV_CTL_ELEM_ACCESS_READ | @@ -184,6 +506,9 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream, ret = snd_pcm_hw_constraint_eld(substream->runtime, hcp->eld); mutex_unlock(&hcp->eld_lock); + + /* Select chmap supported */ + hdmi_codec_eld_chmap(hcp); } return ret; } @@ -201,6 +526,7 @@ static void hdmi_codec_shutdown(struct snd_pcm_substream *substream, WARN_ON(hcp->current_stream != substream); + hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; hcp->hcd.ops->audio_shutdown(dai->dev->parent, hcp->hcd.data); mutex_lock(&hcp->current_stream_lock); @@ -221,7 +547,7 @@ static int hdmi_codec_hw_params(struct snd_pcm_substream *substream, .dig_subframe = { 0 }, } }; - int ret; + int ret, idx; dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__, params_width(params), params_rate(params), @@ -248,6 +574,17 @@ static int hdmi_codec_hw_params(struct snd_pcm_substream *substream, hp.cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM; hp.cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM; + /* Select a channel allocation that matches with ELD and pcm channels */ + idx = hdmi_codec_get_ch_alloc_table_idx(hcp, hp.cea.channels); + if (idx < 0) { + dev_err(dai->dev, "Not able to map channels to speakers (%d)\n", + idx); + hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; + return idx; + } + hp.cea.channel_allocation = hdmi_codec_channel_alloc[idx].ca_id; + hcp->chmap_idx = hdmi_codec_channel_alloc[idx].ca_id; + hp.sample_width = params_width(params); hp.sample_rate = params_rate(params); hp.channels = params_channels(params); @@ -377,6 +714,32 @@ static const struct snd_soc_dai_ops hdmi_dai_ops = { SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE) +static int hdmi_codec_pcm_new(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_dai *dai) +{ + struct snd_soc_dai_driver *drv = dai->driver; + struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); + int ret; + + dev_dbg(dai->dev, "%s()\n", __func__); + + ret = snd_pcm_add_chmap_ctls(rtd->pcm, SNDRV_PCM_STREAM_PLAYBACK, + NULL, drv->playback.channels_max, 0, + &hcp->chmap_info); + if (ret < 0) + return ret; + + /* override handlers */ + hcp->chmap_info->private_data = hcp; + hcp->chmap_info->kctl->get = hdmi_codec_chmap_ctl_get; + + /* default chmap supported is stereo */ + hcp->chmap_info->chmap = hdmi_codec_stereo_chmaps; + hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; + + return 0; +} + static struct snd_soc_dai_driver hdmi_i2s_dai = { .name = "i2s-hifi", .id = DAI_ID_I2S, @@ -389,6 +752,7 @@ static struct snd_soc_dai_driver hdmi_i2s_dai = { .sig_bits = 24, }, .ops = &hdmi_dai_ops, + .pcm_new = hdmi_codec_pcm_new, }; static const struct snd_soc_dai_driver hdmi_spdif_dai = { @@ -402,6 +766,7 @@ static const struct snd_soc_dai_driver hdmi_spdif_dai = { .formats = SPDIF_FORMATS, }, .ops = &hdmi_dai_ops, + .pcm_new = hdmi_codec_pcm_new, }; static struct snd_soc_codec_driver hdmi_codec = { @@ -534,6 +899,7 @@ static int hdmi_codec_remove(struct platform_device *pdev) { struct hdmi_codec_priv *hcp = platform_get_drvdata(pdev); + kfree(hcp->chmap_info); hdmi_unregister_notifier(&hcp->nb); snd_soc_unregister_codec(&pdev->dev); return 0; diff --git a/sound/soc/codecs/rk3328_codec.c b/sound/soc/codecs/rk3328_codec.c index af1b7429..d0b4578f 100644 --- a/sound/soc/codecs/rk3328_codec.c +++ b/sound/soc/codecs/rk3328_codec.c @@ -354,7 +354,12 @@ static struct snd_soc_dai_driver rk3328_dai[] = { .stream_name = "HIFI Playback", .channels_min = 1, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_96000, + .rates = (SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_64000 | + SNDRV_PCM_RATE_96000), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE | diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index b359639c..5297373f 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -514,6 +514,7 @@ static bool rockchip_i2s_rd_reg(struct device *dev, unsigned int reg) case I2S_INTCR: case I2S_XFER: case I2S_CLR: + case I2S_TXDR: case I2S_RXDR: case I2S_FIFOLR: case I2S_INTSR: @@ -528,6 +529,9 @@ static bool rockchip_i2s_volatile_reg(struct device *dev, unsigned int reg) switch (reg) { case I2S_INTSR: case I2S_CLR: + case I2S_FIFOLR: + case I2S_TXDR: + case I2S_RXDR: return true; default: return false; @@ -537,6 +541,8 @@ static bool rockchip_i2s_volatile_reg(struct device *dev, unsigned int reg) static bool rockchip_i2s_precious_reg(struct device *dev, unsigned int reg) { switch (reg) { + case I2S_RXDR: + return true; default: return false; } @@ -594,10 +600,8 @@ static int rockchip_i2s_probe(struct platform_device *pdev) int val; i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); - if (!i2s) { - dev_err(&pdev->dev, "Can't allocate rk_i2s_dev\n"); + if (!i2s) return -ENOMEM; - } i2s->dev = &pdev->dev; @@ -658,12 +662,13 @@ static int rockchip_i2s_probe(struct platform_device *pdev) goto err_pm_disable; } - soc_dai = devm_kzalloc(&pdev->dev, + soc_dai = devm_kmemdup(&pdev->dev, &rockchip_i2s_dai, sizeof(*soc_dai), GFP_KERNEL); - if (!soc_dai) - return -ENOMEM; + if (!soc_dai) { + ret = -ENOMEM; + goto err_pm_disable; + } - memcpy(soc_dai, &rockchip_i2s_dai, sizeof(*soc_dai)); if (!of_property_read_u32(node, "rockchip,playback-channels", &val)) { if (val >= 2 && val <= 8) soc_dai->playback.channels_max = val; @@ -728,7 +733,6 @@ static int rockchip_i2s_remove(struct platform_device *pdev) if (!pm_runtime_status_suspended(&pdev->dev)) i2s_runtime_suspend(&pdev->dev); - clk_disable_unprepare(i2s->mclk); clk_disable_unprepare(i2s->hclk); return 0; diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index c211750b..831e4caf 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -69,6 +69,7 @@ static int __maybe_unused rk_spdif_runtime_suspend(struct device *dev) { struct rk_spdif_dev *spdif = dev_get_drvdata(dev); + regcache_cache_only(spdif->regmap, true); clk_disable_unprepare(spdif->mclk); clk_disable_unprepare(spdif->hclk); @@ -92,7 +93,16 @@ static int __maybe_unused rk_spdif_runtime_resume(struct device *dev) return ret; } - return 0; + regcache_cache_only(spdif->regmap, false); + regcache_mark_dirty(spdif->regmap); + + ret = regcache_sync(spdif->regmap); + if (ret) { + clk_disable_unprepare(spdif->mclk); + clk_disable_unprepare(spdif->hclk); + } + + return ret; } static int rk_spdif_hw_params(struct snd_pcm_substream *substream, @@ -105,21 +115,7 @@ static int rk_spdif_hw_params(struct snd_pcm_substream *substream, int ret; srate = params_rate(params); - switch (srate) { - case 32000: - case 48000: - case 96000: - mclk = 96000 * 128; /* 12288000 hz */ - break; - case 44100: - mclk = 44100 * 256; /* 11289600 hz */ - break; - case 192000: - mclk = 192000 * 128; /* 24576000 hz */ - break; - default: - return -EINVAL; - } + mclk = srate * 128; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: @@ -143,7 +139,6 @@ static int rk_spdif_hw_params(struct snd_pcm_substream *substream, return ret; } - val |= SPDIF_CFGR_CLK_DIV(mclk/(srate * 256)); ret = regmap_update_bits(spdif->regmap, SPDIF_CFGR, SPDIF_CFGR_CLK_DIV_MASK | SPDIF_CFGR_HALFWORD_ENABLE | SDPIF_CFGR_VDW_MASK, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 3c6713da..e46e80c0 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1289,6 +1289,27 @@ static int soc_probe_dai(struct snd_soc_dai *dai, int order) return 0; } +static int soc_link_dai_pcm_new(struct snd_soc_dai **dais, int num_dais, + struct snd_soc_pcm_runtime *rtd) +{ + int i, ret = 0; + + for (i = 0; i < num_dais; ++i) { + struct snd_soc_dai_driver *drv = dais[i]->driver; + + if (!rtd->dai_link->no_pcm && drv->pcm_new) + ret = drv->pcm_new(rtd, dais[i]); + if (ret < 0) { + dev_err(dais[i]->dev, + "ASoC: Failed to bind %s with pcm device\n", + dais[i]->name); + return ret; + } + } + + return 0; +} + static int soc_link_dai_widgets(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link, struct snd_soc_pcm_runtime *rtd) @@ -1400,6 +1421,13 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) dai_link->stream_name, ret); return ret; } + ret = soc_link_dai_pcm_new(&cpu_dai, 1, rtd); + if (ret < 0) + return ret; + ret = soc_link_dai_pcm_new(rtd->codec_dais, + rtd->num_codecs, rtd); + if (ret < 0) + return ret; } else { INIT_DELAYED_WORK(&rtd->delayed_work, codec2codec_close_delayed_work); diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index 53dd085d..bf7ce340 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -19,6 +19,7 @@ #include #include #include +#include int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots) { @@ -160,9 +161,18 @@ static int snd_soc_dummy_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id soc_dummy_ids[] = { + { .compatible = "linux,snd-soc-dummy", }, + { } +}; +MODULE_DEVICE_TABLE(of, soc_dummy_ids); +#endif + static struct platform_driver soc_dummy_driver = { .driver = { .name = "snd-soc-dummy", + .of_match_table = of_match_ptr(soc_dummy_ids), }, .probe = snd_soc_dummy_probe, .remove = snd_soc_dummy_remove, diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 69bf5cf1..00672a81 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -3324,4 +3324,13 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), } }, +{ + USB_DEVICE(0x0bda, 0x481a), + .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { + .vendor_name = "Realtek", + .product_name = "ALC4040", + .ifnum = QUIRK_NO_INTERFACE + } +}, + #undef USB_DEVICE_VENDOR_SPEC diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index fb1c9ddc..9b3b9bd5 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -33,6 +33,11 @@ include config/utilities.mak # # Define EXTRA_CFLAGS=-m64 or EXTRA_CFLAGS=-m32 as appropriate for cross-builds. # +# Define EXCLUDE_EXTLIBS=-lmylib to exclude libmylib from the auto-generated +# EXTLIBS. +# +# Define EXTRA_PERFLIBS to pass extra libraries to PERFLIBS. +# # Define NO_DWARF if you do not want debug-info analysis feature at all. # # Define WERROR=0 to disable treating any warnings as errors. @@ -289,7 +294,8 @@ ifdef ASCIIDOC8 export ASCIIDOC8 endif -LIBS = -Wl,--whole-archive $(PERFLIBS) -Wl,--no-whole-archive -Wl,--start-group $(EXTLIBS) -Wl,--end-group +EXTLIBS := $(call filter-out,$(EXCLUDE_EXTLIBS),$(EXTLIBS)) +LIBS = -Wl,--whole-archive $(PERFLIBS) $(EXTRA_PERFLIBS) -Wl,--no-whole-archive -Wl,--start-group $(EXTLIBS) -Wl,--end-group export INSTALL SHELL_PATH