123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283 |
- /* Copyright (c) 2013, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only 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.
- */
- #include <linux/completion.h>
- #include <linux/debugfs.h>
- #include <linux/export.h>
- #include <linux/fs.h>
- #include <linux/if_ether.h>
- #include <linux/ioctl.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/msm_ipa.h>
- #include <linux/mutex.h>
- #include <linux/skbuff.h>
- #include <linux/types.h>
- #include <mach/ipa.h>
- #include <mach/sps.h>
- #include "ipa_i.h"
- #define TETH_BRIDGE_DRV_NAME "ipa_tethering_bridge"
- #define TETH_DBG(fmt, args...) \
- pr_debug(TETH_BRIDGE_DRV_NAME " %s:%d " fmt, \
- __func__, __LINE__, ## args)
- #define TETH_DBG_FUNC_ENTRY() \
- pr_debug(TETH_BRIDGE_DRV_NAME " %s:%d ENTRY\n", __func__, __LINE__)
- #define TETH_DBG_FUNC_EXIT() \
- pr_debug(TETH_BRIDGE_DRV_NAME " %s:%d EXIT\n", __func__, __LINE__)
- #define TETH_ERR(fmt, args...) \
- pr_err(TETH_BRIDGE_DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args)
- #define USB_ETH_HDR_NAME_IPV4 "usb_bridge_ipv4"
- #define USB_ETH_HDR_NAME_IPV6 "usb_bridge_ipv6"
- #define A2_ETH_HDR_NAME_IPV4 "a2_bridge_ipv4"
- #define A2_ETH_HDR_NAME_IPV6 "a2_bridge_ipv6"
- #define USB_TO_A2_RT_TBL_NAME_IPV4 "usb_a2_rt_tbl_ipv4"
- #define A2_TO_USB_RT_TBL_NAME_IPV4 "a2_usb_rt_tbl_ipv4"
- #define USB_TO_A2_RT_TBL_NAME_IPV6 "usb_a2_rt_tbl_ipv6"
- #define A2_TO_USB_RT_TBL_NAME_IPV6 "a2_usb_rt_tbl_ipv6"
- #define MBIM_HEADER_NAME "mbim_header"
- #define TETH_DEFAULT_AGGR_TIME_LIMIT 1
- #define ETHERTYPE_IPV4 0x0800
- #define ETHERTYPE_IPV6 0x86DD
- #define TETH_AGGR_MAX_DATAGRAMS_DEFAULT 16
- #define TETH_AGGR_MAX_AGGR_PACKET_SIZE_DEFAULT (8*1024)
- #define TETH_MTU_BYTE 1500
- #define TETH_INACTIVITY_TIME_MSEC (1000)
- #define TETH_WORKQUEUE_NAME "tethering_bridge_wq"
- #define TETH_TOTAL_HDR_ENTRIES 6
- #define TETH_TOTAL_RT_ENTRIES_IP 3
- #define TETH_TOTAL_FLT_ENTRIES_IP 2
- #define TETH_IP_FAMILIES 2
- /**
- * struct mac_addresses_type - store host PC and device MAC addresses
- * @host_pc_mac_addr: MAC address of the host PC
- * @host_pc_mac_addr_known: is the MAC address of the host PC known ?
- * @device_mac_addr: MAC address of the device
- * @device_mac_addr_known: is the MAC address of the device known ?
- */
- struct mac_addresses_type {
- u8 host_pc_mac_addr[ETH_ALEN];
- bool host_pc_mac_addr_known;
- u8 device_mac_addr[ETH_ALEN];
- bool device_mac_addr_known;
- };
- /**
- * struct stats - driver statistics, viewable using debugfs
- * @a2_to_usb_num_sw_tx_packets: number of packets bridged from A2 to USB using
- * the SW bridge
- * @usb_to_a2_num_sw_tx_packets: number of packets bridged from USB to A2 using
- * the SW bridge
- * @num_sw_tx_packets_during_resource_wakeup: number of packets bridged during a
- * resource wakeup period, there is a special treatment for these kind of
- * packets
- */
- struct stats {
- u64 a2_to_usb_num_sw_tx_packets;
- u64 usb_to_a2_num_sw_tx_packets;
- u64 num_sw_tx_packets_during_resource_wakeup;
- };
- /**
- * struct teth_bridge_ctx - Tethering bridge driver context information
- * @class: kernel class pointer
- * @dev_num: kernel device number
- * @dev: kernel device struct pointer
- * @cdev: kernel character device struct
- * @usb_ipa_pipe_hdl: USB to IPA pipe handle
- * @ipa_usb_pipe_hdl: IPA to USB pipe handle
- * @a2_ipa_pipe_hdl: A2 to IPA pipe handle
- * @ipa_a2_pipe_hdl: IPA to A2 pipe handle
- * @is_connected: is the tethered bridge connected ?
- * @link_protocol: IP / Ethernet
- * @mac_addresses: Struct which holds host pc and device MAC addresses, relevant
- * in ethernet mode only
- * @is_hw_bridge_complete: is HW bridge setup ?
- * @aggr_params: aggregation parmeters
- * @aggr_params_known: are the aggregation parameters known ?
- * @tethering_mode: Rmnet / MBIM
- * @is_bridge_prod_up: completion object signaled when the bridge producer
- * finished its resource request procedure
- * @is_bridge_prod_down: completion object signaled when the bridge producer
- * finished its resource release procedure
- * @comp_hw_bridge_work: used for setting up the HW bridge using a workqueue
- * @comp_hw_bridge_in_progress: true when the HW bridge setup is in progress
- * @aggr_caps: aggregation capabilities
- * @stats: statistics, how many packets were transmitted using the SW bridge
- * @teth_wq: dedicated workqueue, used for setting up the HW bridge and for
- * sending packets using the SW bridge when the system is waking up from power
- * collapse
- * @a2_ipa_hdr_len: A2 to IPA header length, used for configuring the A2
- * endpoint for header removal
- * @ipa_a2_hdr_len: IPA to A2 header length, used for configuring the A2
- * endpoint for header removal
- * @hdr_del: array to store the headers handles in order to delete them later
- * @routing_del: array of routing rules handles, one array for IPv4 and one for
- * IPv6
- * @filtering_del: array of routing rules handles, one array for IPv4 and one
- * for IPv6
- */
- struct teth_bridge_ctx {
- struct class *class;
- dev_t dev_num;
- struct device *dev;
- struct cdev cdev;
- u32 usb_ipa_pipe_hdl;
- u32 ipa_usb_pipe_hdl;
- u32 a2_ipa_pipe_hdl;
- u32 ipa_a2_pipe_hdl;
- bool is_connected;
- enum teth_link_protocol_type link_protocol;
- struct mac_addresses_type mac_addresses;
- bool is_hw_bridge_complete;
- struct teth_aggr_params aggr_params;
- bool aggr_params_known;
- enum teth_tethering_mode tethering_mode;
- struct completion is_bridge_prod_up;
- struct completion is_bridge_prod_down;
- struct work_struct comp_hw_bridge_work;
- bool comp_hw_bridge_in_progress;
- struct teth_aggr_capabilities *aggr_caps;
- struct stats stats;
- struct workqueue_struct *teth_wq;
- u16 a2_ipa_hdr_len;
- u16 ipa_a2_hdr_len;
- struct ipa_ioc_del_hdr *hdr_del;
- struct ipa_ioc_del_rt_rule *routing_del[TETH_IP_FAMILIES];
- struct ipa_ioc_del_flt_rule *filtering_del[TETH_IP_FAMILIES];
- };
- static struct teth_bridge_ctx *teth_ctx;
- enum teth_packet_direction {
- TETH_USB_TO_A2,
- TETH_A2_TO_USB,
- };
- /**
- * struct teth_work - wrapper for an skb which is sent using a workqueue
- * @work: used by the workqueue
- * @skb: pointer to the skb to be sent
- * @dir: direction of send, A2 to USB or USB to A2
- */
- struct teth_work {
- struct work_struct work;
- struct sk_buff *skb;
- enum teth_packet_direction dir;
- };
- #ifdef CONFIG_DEBUG_FS
- #define TETH_MAX_MSG_LEN 512
- static char dbg_buff[TETH_MAX_MSG_LEN];
- #endif
- /**
- * add_eth_hdrs() - add Ethernet headers to IPA
- * @hdr_name_ipv4: header name for IPv4
- * @hdr_name_ipv6: header name for IPv6
- * @src_mac_addr: source MAC address
- * @dst_mac_addr: destination MAC address
- *
- * This function is called only when link protocol is Ethernet
- */
- static int add_eth_hdrs(char *hdr_name_ipv4, char *hdr_name_ipv6,
- u8 *src_mac_addr, u8 *dst_mac_addr)
- {
- int res;
- struct ipa_ioc_add_hdr *hdrs;
- struct ethhdr hdr_ipv4;
- struct ethhdr hdr_ipv6;
- int idx1;
- TETH_DBG_FUNC_ENTRY();
- memcpy(hdr_ipv4.h_source, src_mac_addr, ETH_ALEN);
- memcpy(hdr_ipv4.h_dest, dst_mac_addr, ETH_ALEN);
- hdr_ipv4.h_proto = htons(ETHERTYPE_IPV4);
- memcpy(hdr_ipv6.h_source, src_mac_addr, ETH_ALEN);
- memcpy(hdr_ipv6.h_dest, dst_mac_addr, ETH_ALEN);
- hdr_ipv6.h_proto = htons(ETHERTYPE_IPV6);
- /* Add headers to the header insertion tables */
- hdrs = kzalloc(sizeof(struct ipa_ioc_add_hdr) +
- 2 * sizeof(struct ipa_hdr_add), GFP_KERNEL);
- if (hdrs == NULL) {
- TETH_ERR("Failed allocating memory for headers !\n");
- return -ENOMEM;
- }
- hdrs->commit = 0;
- hdrs->num_hdrs = 2;
- /* Ethernet IPv4 header */
- strlcpy(hdrs->hdr[0].name, hdr_name_ipv4, IPA_RESOURCE_NAME_MAX);
- hdrs->hdr[0].hdr_len = ETH_HLEN;
- memcpy(hdrs->hdr[0].hdr, &hdr_ipv4, ETH_HLEN);
- /* Ethernet IPv6 header */
- strlcpy(hdrs->hdr[1].name, hdr_name_ipv6, IPA_RESOURCE_NAME_MAX);
- hdrs->hdr[1].hdr_len = ETH_HLEN;
- memcpy(hdrs->hdr[1].hdr, &hdr_ipv6, ETH_HLEN);
- res = ipa_add_hdr(hdrs);
- if (res || hdrs->hdr[0].status || hdrs->hdr[1].status)
- TETH_ERR("Header insertion failed\n");
- /* Save the headers handles in order to delete them later */
- for (idx1 = 0; idx1 < hdrs->num_hdrs; idx1++) {
- int idx2 = teth_ctx->hdr_del->num_hdls++;
- teth_ctx->hdr_del->hdl[idx2].hdl = hdrs->hdr[idx1].hdr_hdl;
- }
- kfree(hdrs);
- TETH_DBG_FUNC_EXIT();
- return res;
- }
- static int configure_ipa_header_block_internal(u32 usb_ipa_hdr_len,
- u32 a2_ipa_hdr_len,
- u32 ipa_usb_hdr_len,
- u32 ipa_a2_hdr_len)
- {
- struct ipa_ep_cfg_hdr hdr_cfg;
- int res;
- TETH_DBG_FUNC_ENTRY();
- /* Configure header removal for the USB->IPA pipe and A2->IPA pipe */
- memset(&hdr_cfg, 0, sizeof(hdr_cfg));
- hdr_cfg.hdr_len = usb_ipa_hdr_len;
- res = ipa_cfg_ep_hdr(teth_ctx->usb_ipa_pipe_hdl, &hdr_cfg);
- if (res) {
- TETH_ERR("Header removal config for USB->IPA pipe failed\n");
- goto bail;
- }
- hdr_cfg.hdr_len = a2_ipa_hdr_len;
- teth_ctx->a2_ipa_hdr_len = a2_ipa_hdr_len;
- res = ipa_cfg_ep_hdr(teth_ctx->a2_ipa_pipe_hdl, &hdr_cfg);
- if (res) {
- TETH_ERR("Header removal config for A2->IPA pipe failed\n");
- goto bail;
- }
- /* Configure header insertion for the IPA->USB pipe and IPA->A2 pipe */
- hdr_cfg.hdr_len = ipa_usb_hdr_len;
- res = ipa_cfg_ep_hdr(teth_ctx->ipa_usb_pipe_hdl, &hdr_cfg);
- if (res) {
- TETH_ERR("Header insertion config for IPA->USB pipe failed\n");
- goto bail;
- }
- hdr_cfg.hdr_len = ipa_a2_hdr_len;
- teth_ctx->ipa_a2_hdr_len = ipa_a2_hdr_len;
- res = ipa_cfg_ep_hdr(teth_ctx->ipa_a2_pipe_hdl, &hdr_cfg);
- if (res) {
- TETH_ERR("Header insertion config for IPA->A2 pipe failed\n");
- goto bail;
- }
- TETH_DBG_FUNC_EXIT();
- bail:
- return res;
- }
- static int add_mbim_hdr(void)
- {
- int res;
- struct ipa_ioc_add_hdr *mbim_hdr;
- u8 mbim_stream_id = 0;
- int idx;
- TETH_DBG_FUNC_ENTRY();
- mbim_hdr = kzalloc(sizeof(struct ipa_ioc_add_hdr) +
- sizeof(struct ipa_hdr_add),
- GFP_KERNEL);
- if (!mbim_hdr) {
- TETH_ERR("Failed allocating memory for MBIM header\n");
- return -ENOMEM;
- }
- mbim_hdr->commit = 0;
- mbim_hdr->num_hdrs = 1;
- strlcpy(mbim_hdr->hdr[0].name, MBIM_HEADER_NAME, IPA_RESOURCE_NAME_MAX);
- memcpy(mbim_hdr->hdr[0].hdr, &mbim_stream_id, sizeof(u8));
- mbim_hdr->hdr[0].hdr_len = sizeof(u8);
- mbim_hdr->hdr[0].is_partial = false;
- res = ipa_add_hdr(mbim_hdr);
- if (res || mbim_hdr->hdr[0].status) {
- TETH_ERR("Failed adding MBIM header\n");
- res = -EFAULT;
- } else {
- TETH_DBG("Added MBIM stream ID header\n");
- }
- /* Save the header handle in order to delete it later */
- idx = teth_ctx->hdr_del->num_hdls++;
- teth_ctx->hdr_del->hdl[idx].hdl = mbim_hdr->hdr[0].hdr_hdl;
- kfree(mbim_hdr);
- TETH_DBG_FUNC_EXIT();
- return res;
- }
- /**
- * configure_ipa_header_block() - adds headers and configures endpoint registers
- *
- * - For IP link protocol and MBIM aggregation, configure MBIM header
- * - For Ethernet link protocol, configure Ethernet headers
- */
- static int configure_ipa_header_block(void)
- {
- int res;
- u32 hdr_len = 0;
- u32 ipa_usb_hdr_len = 0;
- TETH_DBG_FUNC_ENTRY();
- if (teth_ctx->link_protocol == TETH_LINK_PROTOCOL_IP) {
- /*
- * Create a new header for MBIM stream ID and associate it with
- * the IPA->USB routing table
- */
- if (teth_ctx->aggr_params.dl.aggr_prot ==
- TETH_AGGR_PROTOCOL_MBIM) {
- ipa_usb_hdr_len = 1;
- res = add_mbim_hdr();
- if (res) {
- TETH_ERR("Failed adding MBIM header\n");
- goto bail;
- }
- }
- } else if (teth_ctx->link_protocol == TETH_LINK_PROTOCOL_ETHERNET) {
- /* Add a header entry for USB */
- res = add_eth_hdrs(USB_ETH_HDR_NAME_IPV4,
- USB_ETH_HDR_NAME_IPV6,
- teth_ctx->mac_addresses.device_mac_addr,
- teth_ctx->mac_addresses.host_pc_mac_addr);
- if (res) {
- TETH_ERR("Failed adding USB Ethernet header\n");
- goto bail;
- }
- TETH_DBG("Added USB Ethernet headers (IPv4 / IPv6)\n");
- /* Add a header entry for A2 */
- res = add_eth_hdrs(A2_ETH_HDR_NAME_IPV4,
- A2_ETH_HDR_NAME_IPV6,
- teth_ctx->mac_addresses.host_pc_mac_addr,
- teth_ctx->mac_addresses.device_mac_addr);
- if (res) {
- TETH_ERR("Failed adding A2 Ethernet header\n");
- goto bail;
- }
- TETH_DBG("Added A2 Ethernet headers (IPv4 / IPv6\n");
- hdr_len = ETH_HLEN;
- ipa_usb_hdr_len = ETH_HLEN;
- }
- res = configure_ipa_header_block_internal(hdr_len,
- hdr_len,
- ipa_usb_hdr_len,
- hdr_len);
- if (res) {
- TETH_ERR("Configuration of header removal/insertion failed\n");
- goto bail;
- }
- TETH_DBG_FUNC_EXIT();
- bail:
- return res;
- }
- static int configure_routing_by_ip(char *hdr_name,
- char *rt_tbl_name,
- enum ipa_client_type dst,
- enum ipa_ip_type ip_address_family)
- {
- struct ipa_ioc_add_rt_rule *rt_rule;
- struct ipa_ioc_get_hdr hdr_info;
- int res;
- int idx;
- TETH_DBG_FUNC_ENTRY();
- /* Get the header handle */
- memset(&hdr_info, 0, sizeof(hdr_info));
- strlcpy(hdr_info.name, hdr_name, IPA_RESOURCE_NAME_MAX);
- ipa_get_hdr(&hdr_info);
- rt_rule = kzalloc(sizeof(struct ipa_ioc_add_rt_rule) +
- 1 * sizeof(struct ipa_rt_rule_add),
- GFP_KERNEL);
- if (!rt_rule) {
- TETH_ERR("Memory allocation failure");
- return -ENOMEM;
- }
- /* Match all, do not commit to HW*/
- rt_rule->commit = 0;
- rt_rule->num_rules = 1;
- rt_rule->ip = ip_address_family;
- strlcpy(rt_rule->rt_tbl_name, rt_tbl_name, IPA_RESOURCE_NAME_MAX);
- rt_rule->rules[0].rule.dst = dst;
- rt_rule->rules[0].rule.hdr_hdl = hdr_info.hdl;
- rt_rule->rules[0].rule.attrib.attrib_mask = 0; /* Match all */
- res = ipa_add_rt_rule(rt_rule);
- if (res || rt_rule->rules[0].status)
- TETH_ERR("Failed adding routing rule\n");
- /* Save the routing rule handle in order to delete it later */
- idx = teth_ctx->routing_del[ip_address_family]->num_hdls++;
- teth_ctx->routing_del[ip_address_family]->hdl[idx].hdl =
- rt_rule->rules[0].rt_rule_hdl;
- kfree(rt_rule);
- TETH_DBG_FUNC_EXIT();
- return res;
- }
- static int configure_routing(char *hdr_name_ipv4,
- char *rt_tbl_name_ipv4,
- char *hdr_name_ipv6,
- char *rt_tbl_name_ipv6,
- enum ipa_client_type dst)
- {
- int res;
- TETH_DBG_FUNC_ENTRY();
- /* Configure IPv4 routing table */
- res = configure_routing_by_ip(hdr_name_ipv4,
- rt_tbl_name_ipv4,
- dst,
- IPA_IP_v4);
- if (res) {
- TETH_ERR("Failed adding IPv4 routing table\n");
- goto bail;
- }
- /* Configure IPv6 routing table */
- res = configure_routing_by_ip(hdr_name_ipv6,
- rt_tbl_name_ipv6,
- dst,
- IPA_IP_v6);
- if (res) {
- TETH_ERR("Failed adding IPv6 routing table\n");
- goto bail;
- }
- TETH_DBG_FUNC_EXIT();
- bail:
- return res;
- }
- /**
- * configure_ipa_routing_block() - Configure the IPA routing block
- *
- * This function configures IPA for:
- * - Route all packets from USB to A2
- * - Route all packets from A2 to USB
- * - Use the correct headers in Ethernet or MBIM cases
- */
- static int configure_ipa_routing_block(void)
- {
- int res;
- char hdr_name_ipv4[IPA_RESOURCE_NAME_MAX];
- char hdr_name_ipv6[IPA_RESOURCE_NAME_MAX];
- TETH_DBG_FUNC_ENTRY();
- hdr_name_ipv4[0] = '\0';
- hdr_name_ipv6[0] = '\0';
- /* Configure USB -> A2 routing table */
- if (teth_ctx->link_protocol == TETH_LINK_PROTOCOL_ETHERNET) {
- strlcpy(hdr_name_ipv4,
- A2_ETH_HDR_NAME_IPV4,
- IPA_RESOURCE_NAME_MAX);
- strlcpy(hdr_name_ipv6,
- A2_ETH_HDR_NAME_IPV6,
- IPA_RESOURCE_NAME_MAX);
- }
- res = configure_routing(hdr_name_ipv4,
- USB_TO_A2_RT_TBL_NAME_IPV4,
- hdr_name_ipv6,
- USB_TO_A2_RT_TBL_NAME_IPV6,
- IPA_CLIENT_A2_TETHERED_CONS);
- if (res) {
- TETH_ERR("USB to A2 routing block configuration failed\n");
- goto bail;
- }
- /* Configure A2 -> USB routing table */
- if (teth_ctx->link_protocol == TETH_LINK_PROTOCOL_ETHERNET) {
- strlcpy(hdr_name_ipv4,
- USB_ETH_HDR_NAME_IPV4,
- IPA_RESOURCE_NAME_MAX);
- strlcpy(hdr_name_ipv6,
- USB_ETH_HDR_NAME_IPV6,
- IPA_RESOURCE_NAME_MAX);
- } else if (teth_ctx->aggr_params.dl.aggr_prot ==
- TETH_AGGR_PROTOCOL_MBIM) {
- strlcpy(hdr_name_ipv4,
- MBIM_HEADER_NAME,
- IPA_RESOURCE_NAME_MAX);
- strlcpy(hdr_name_ipv6,
- MBIM_HEADER_NAME,
- IPA_RESOURCE_NAME_MAX);
- }
- res = configure_routing(hdr_name_ipv4,
- A2_TO_USB_RT_TBL_NAME_IPV4,
- hdr_name_ipv6,
- A2_TO_USB_RT_TBL_NAME_IPV6,
- IPA_CLIENT_USB_CONS);
- if (res) {
- TETH_ERR("A2 to USB routing block configuration failed\n");
- goto bail;
- }
- TETH_DBG_FUNC_EXIT();
- bail:
- return res;
- }
- static int configure_filtering_by_ip(char *rt_tbl_name,
- enum ipa_client_type src,
- enum ipa_ip_type ip_address_family)
- {
- struct ipa_ioc_add_flt_rule *flt_tbl;
- struct ipa_ioc_get_rt_tbl rt_tbl_info;
- int res;
- int idx;
- TETH_DBG_FUNC_ENTRY();
- /* Get the needed routing table handle */
- rt_tbl_info.ip = ip_address_family;
- strlcpy(rt_tbl_info.name, rt_tbl_name, IPA_RESOURCE_NAME_MAX);
- res = ipa_get_rt_tbl(&rt_tbl_info);
- if (res) {
- TETH_ERR("Failed getting routing table handle\n");
- goto bail;
- }
- flt_tbl = kzalloc(sizeof(struct ipa_ioc_add_flt_rule) +
- 1 * sizeof(struct ipa_flt_rule_add), GFP_KERNEL);
- if (!flt_tbl) {
- TETH_ERR("Filtering table memory allocation failure\n");
- return -ENOMEM;
- }
- flt_tbl->commit = 0;
- flt_tbl->ep = src;
- flt_tbl->global = 0;
- flt_tbl->ip = ip_address_family;
- flt_tbl->num_rules = 1;
- flt_tbl->rules[0].rule.action = IPA_PASS_TO_ROUTING;
- flt_tbl->rules[0].rule.rt_tbl_hdl = rt_tbl_info.hdl;
- flt_tbl->rules[0].rule.attrib.attrib_mask = 0; /* Match all */
- res = ipa_add_flt_rule(flt_tbl);
- if (res || flt_tbl->rules[0].status)
- TETH_ERR("Failed adding filtering table\n");
- /* Save the filtering rule handle in order to delete it later */
- idx = teth_ctx->filtering_del[ip_address_family]->num_hdls++;
- teth_ctx->filtering_del[ip_address_family]->hdl[idx].hdl =
- flt_tbl->rules[0].flt_rule_hdl;
- kfree(flt_tbl);
- TETH_DBG_FUNC_EXIT();
- bail:
- return res;
- }
- static int configure_filtering(char *rt_tbl_name_ipv4,
- char *rt_tbl_name_ipv6,
- enum ipa_client_type src)
- {
- int res;
- TETH_DBG_FUNC_ENTRY();
- res = configure_filtering_by_ip(rt_tbl_name_ipv4, src, IPA_IP_v4);
- if (res) {
- TETH_ERR("Failed adding IPv4 filtering table\n");
- goto bail;
- }
- res = configure_filtering_by_ip(rt_tbl_name_ipv6, src, IPA_IP_v6);
- if (res) {
- TETH_ERR("Failed adding IPv4 filtering table\n");
- goto bail;
- }
- TETH_DBG_FUNC_EXIT();
- bail:
- return res;
- }
- /**
- * configure_ipa_filtering_block() - Configures IPA filtering block
- *
- * This function configures IPA for:
- * - Filter all traffic coming from USB to A2 pointing routing table
- * - Filter all traffic coming from A2 to USB pointing routing table
- */
- static int configure_ipa_filtering_block(void)
- {
- int res;
- TETH_DBG_FUNC_ENTRY();
- /* Filter all traffic coming from USB to A2 */
- res = configure_filtering(USB_TO_A2_RT_TBL_NAME_IPV4,
- USB_TO_A2_RT_TBL_NAME_IPV6,
- IPA_CLIENT_USB_PROD);
- if (res) {
- TETH_ERR("USB_PROD ep filtering configuration failed\n");
- goto bail;
- }
- /* Filter all traffic coming from A2 to USB */
- res = configure_filtering(A2_TO_USB_RT_TBL_NAME_IPV4,
- A2_TO_USB_RT_TBL_NAME_IPV6,
- IPA_CLIENT_A2_TETHERED_PROD);
- if (res) {
- TETH_ERR("A2_PROD filtering configuration failed\n");
- goto bail;
- }
- TETH_DBG_FUNC_EXIT();
- bail:
- return res;
- }
- static int prepare_ipa_aggr_struct(
- const struct teth_aggr_params_link *teth_aggr_params,
- struct ipa_ep_cfg_aggr *ipa_aggr_params,
- bool client_is_prod)
- {
- TETH_DBG_FUNC_ENTRY();
- memset(ipa_aggr_params, 0, sizeof(*ipa_aggr_params));
- switch (teth_aggr_params->aggr_prot) {
- case TETH_AGGR_PROTOCOL_NONE:
- ipa_aggr_params->aggr_en = IPA_BYPASS_AGGR;
- break;
- case TETH_AGGR_PROTOCOL_MBIM:
- ipa_aggr_params->aggr = IPA_MBIM_16;
- ipa_aggr_params->aggr_en = (client_is_prod) ?
- IPA_ENABLE_DEAGGR : IPA_ENABLE_AGGR;
- break;
- case TETH_AGGR_PROTOCOL_TLP:
- ipa_aggr_params->aggr = IPA_TLP;
- ipa_aggr_params->aggr_en = (client_is_prod) ?
- IPA_ENABLE_DEAGGR : IPA_ENABLE_AGGR;
- break;
- default:
- TETH_ERR("Unsupported aggregation protocol\n");
- return -EFAULT;
- }
- /*
- * Due to a HW 'feature', the maximal aggregated packet size may be the
- * requested aggr_byte_limit plus the MTU. Therefore, the MTU is
- * subtracted from the requested aggr_byte_limit so that the requested
- * byte limit is honored .
- */
- ipa_aggr_params->aggr_byte_limit =
- (teth_aggr_params->max_transfer_size_byte - TETH_MTU_BYTE) /
- 1024;
- ipa_aggr_params->aggr_time_limit = TETH_DEFAULT_AGGR_TIME_LIMIT;
- TETH_DBG_FUNC_EXIT();
- return 0;
- }
- static int teth_set_aggr_per_ep(
- const struct teth_aggr_params_link *teth_aggr_params,
- bool client_is_prod,
- u32 pipe_hdl)
- {
- struct ipa_ep_cfg_aggr agg_params;
- int res;
- TETH_DBG_FUNC_ENTRY();
- res = prepare_ipa_aggr_struct(teth_aggr_params,
- &agg_params,
- client_is_prod);
- if (res) {
- TETH_ERR("prepare_ipa_aggregation_struct() failed\n");
- goto bail;
- }
- res = ipa_cfg_ep_aggr(pipe_hdl, &agg_params);
- if (res) {
- TETH_ERR("ipa_cfg_ep_aggr() failed\n");
- goto bail;
- }
- TETH_DBG_FUNC_EXIT();
- bail:
- return res;
- }
- static void aggr_prot_to_str(enum teth_aggr_protocol_type aggr_prot,
- char *buff,
- uint buff_size)
- {
- switch (aggr_prot) {
- case TETH_AGGR_PROTOCOL_NONE:
- strlcpy(buff, "NONE", buff_size);
- break;
- case TETH_AGGR_PROTOCOL_MBIM:
- strlcpy(buff, "MBIM", buff_size);
- break;
- case TETH_AGGR_PROTOCOL_TLP:
- strlcpy(buff, "TLP", buff_size);
- break;
- default:
- strlcpy(buff, "ERROR", buff_size);
- break;
- }
- }
- /**
- * teth_set_aggregation() - set aggregation parameters to IPA
- *
- * The parameters to this function are passed in the context variable ipa_ctx.
- */
- static int teth_set_aggregation(void)
- {
- int res;
- char aggr_prot_str[20];
- TETH_DBG_FUNC_ENTRY();
- if (!teth_ctx->aggr_params_known) {
- TETH_ERR("Aggregation parameters unknown.\n");
- return -EINVAL;
- }
- if ((teth_ctx->usb_ipa_pipe_hdl == 0) ||
- (teth_ctx->ipa_usb_pipe_hdl == 0))
- return 0;
- /*
- * Returning 0 in case pipe handles are 0 becuase aggregation
- * params will be set later
- */
- if (teth_ctx->aggr_params.ul.aggr_prot == TETH_AGGR_PROTOCOL_MBIM ||
- teth_ctx->aggr_params.dl.aggr_prot == TETH_AGGR_PROTOCOL_MBIM) {
- res = ipa_set_aggr_mode(IPA_MBIM);
- if (res) {
- TETH_ERR("ipa_set_aggr_mode() failed\n");
- goto bail;
- }
- res = ipa_set_single_ndp_per_mbim(false);
- if (res) {
- TETH_ERR("ipa_set_single_ndp_per_mbim() failed\n");
- goto bail;
- }
- }
- aggr_prot_to_str(teth_ctx->aggr_params.ul.aggr_prot,
- aggr_prot_str,
- sizeof(aggr_prot_str)-1);
- TETH_DBG("Setting %s aggregation on UL\n", aggr_prot_str);
- aggr_prot_to_str(teth_ctx->aggr_params.dl.aggr_prot,
- aggr_prot_str,
- sizeof(aggr_prot_str)-1);
- TETH_DBG("Setting %s aggregation on DL\n", aggr_prot_str);
- /* Configure aggregation on UL producer (USB->IPA) */
- res = teth_set_aggr_per_ep(&teth_ctx->aggr_params.ul,
- true,
- teth_ctx->usb_ipa_pipe_hdl);
- if (res) {
- TETH_ERR("teth_set_aggregation_per_ep() failed\n");
- goto bail;
- }
- /* Configure aggregation on DL consumer (IPA->USB) */
- res = teth_set_aggr_per_ep(&teth_ctx->aggr_params.dl,
- false,
- teth_ctx->ipa_usb_pipe_hdl);
- if (res) {
- TETH_ERR("teth_set_aggregation_per_ep() failed\n");
- goto bail;
- }
- TETH_DBG_FUNC_EXIT();
- bail:
- return res;
- }
- /**
- * teth_request_resource() - wrapper function to
- * ipa_rm_inactivity_timer_request_resource()
- *
- * - initialize the is_bridge_prod_up completion object
- * - request the resource
- * - error handling
- */
- static int teth_request_resource(void)
- {
- int res;
- INIT_COMPLETION(teth_ctx->is_bridge_prod_up);
- res = ipa_rm_inactivity_timer_request_resource(
- IPA_RM_RESOURCE_BRIDGE_PROD);
- if (res < 0) {
- if (res == -EINPROGRESS)
- wait_for_completion(&teth_ctx->is_bridge_prod_up);
- else
- return res;
- }
- return 0;
- }
- /**
- * complete_hw_bridge() - setup the HW bridge from USB to A2 and back through
- * IPA
- */
- static void complete_hw_bridge(struct work_struct *work)
- {
- int res;
- TETH_DBG_FUNC_ENTRY();
- TETH_DBG("Completing HW bridge in %s mode\n",
- (teth_ctx->link_protocol == TETH_LINK_PROTOCOL_ETHERNET) ?
- "ETHERNET" :
- "IP");
- res = teth_request_resource();
- if (res) {
- TETH_ERR("request_resource() failed.\n");
- goto bail;
- }
- res = teth_set_aggregation();
- if (res) {
- TETH_ERR("Failed setting aggregation params\n");
- goto bail;
- }
- res = configure_ipa_header_block();
- if (res) {
- TETH_ERR("Configuration of IPA header block Failed\n");
- goto bail;
- }
- res = configure_ipa_routing_block();
- if (res) {
- TETH_ERR("Configuration of IPA routing block Failed\n");
- goto bail;
- }
- res = configure_ipa_filtering_block();
- if (res) {
- TETH_ERR("Configuration of IPA filtering block Failed\n");
- goto bail;
- }
- /*
- * Commit all the data to HW, including header, routing and filtering
- * blocks, IPv4 and IPv6
- */
- res = ipa_commit_hdr();
- if (res) {
- TETH_ERR("Failed committing headers / routing / filtering.\n");
- goto bail;
- }
- teth_ctx->is_hw_bridge_complete = true;
- bail:
- teth_ctx->comp_hw_bridge_in_progress = false;
- ipa_rm_inactivity_timer_release_resource(IPA_RM_RESOURCE_BRIDGE_PROD);
- TETH_DBG_FUNC_EXIT();
- return;
- }
- static void mac_addr_to_str(u8 mac_addr[ETH_ALEN],
- char *buff,
- uint buff_size)
- {
- scnprintf(buff, buff_size, "%02x-%02x-%02x-%02x-%02x-%02x",
- mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3],
- mac_addr[4], mac_addr[5]);
- }
- /**
- * check_to_complete_hw_bridge() - can HW bridge be set up ?
- * @param skb: pointer to socket buffer
- * @param my_mac_addr: pointer to write 'my' extracted MAC address to
- * @param my_mac_addr_known: pointer to update whether 'my' extracted MAC
- * address is known
- * @param peer_mac_addr_known: pointer to update whether the 'peer' extracted
- * MAC address is known
- *
- * This function is used by both A2 and USB callback functions, therefore the
- * meaning of 'my' and 'peer' changes according to the context.
- * Extracts MAC address from the packet in Ethernet link protocol,
- * Sets up the HW bridge in case all conditions are met.
- */
- static void check_to_complete_hw_bridge(struct sk_buff *skb,
- u8 *my_mac_addr,
- bool *my_mac_addr_known,
- bool *peer_mac_addr_known)
- {
- bool both_mac_addresses_known;
- char mac_addr_str[20];
- if ((teth_ctx->link_protocol == TETH_LINK_PROTOCOL_ETHERNET) &&
- (!(*my_mac_addr_known))) {
- memcpy(my_mac_addr, &skb->data[ETH_ALEN], ETH_ALEN);
- mac_addr_to_str(my_mac_addr,
- mac_addr_str,
- sizeof(mac_addr_str)-1);
- TETH_DBG("Extracted MAC addr: %s\n", mac_addr_str);
- *my_mac_addr_known = true;
- }
- both_mac_addresses_known = *my_mac_addr_known && *peer_mac_addr_known;
- if ((both_mac_addresses_known ||
- (teth_ctx->link_protocol == TETH_LINK_PROTOCOL_IP)) &&
- (!teth_ctx->comp_hw_bridge_in_progress) &&
- (teth_ctx->aggr_params_known)) {
- INIT_WORK(&teth_ctx->comp_hw_bridge_work, complete_hw_bridge);
- teth_ctx->comp_hw_bridge_in_progress = true;
- queue_work(teth_ctx->teth_wq, &teth_ctx->comp_hw_bridge_work);
- }
- }
- /**
- * teth_send_skb_work() - workqueue function for sending a packet
- */
- static void teth_send_skb_work(struct work_struct *work)
- {
- struct teth_work *work_data =
- container_of(work, struct teth_work, work);
- int res;
- res = teth_request_resource();
- if (res) {
- TETH_ERR("Packet send failure, dropping packet !\n");
- goto bail;
- }
- switch (work_data->dir) {
- case TETH_USB_TO_A2:
- res = a2_mux_write(A2_MUX_TETHERED_0, work_data->skb);
- if (res) {
- TETH_ERR("Packet send failure, dropping packet !\n");
- goto bail;
- }
- teth_ctx->stats.usb_to_a2_num_sw_tx_packets++;
- break;
- case TETH_A2_TO_USB:
- res = ipa_tx_dp(IPA_CLIENT_USB_CONS, work_data->skb, NULL);
- if (res) {
- TETH_ERR("Packet send failure, dropping packet !\n");
- goto bail;
- }
- teth_ctx->stats.a2_to_usb_num_sw_tx_packets++;
- break;
- default:
- TETH_ERR("Unsupported direction to send !\n");
- WARN_ON(1);
- }
- ipa_rm_inactivity_timer_release_resource(IPA_RM_RESOURCE_BRIDGE_PROD);
- kfree(work_data);
- teth_ctx->stats.num_sw_tx_packets_during_resource_wakeup++;
- return;
- bail:
- ipa_rm_inactivity_timer_release_resource(IPA_RM_RESOURCE_BRIDGE_PROD);
- dev_kfree_skb(work_data->skb);
- kfree(work_data);
- }
- /**
- * defer_skb_send() - defer sending an skb using the SW bridge to a workqueue
- * @param skb: pointer to the socket buffer
- * @param dir: direction of send
- *
- * In case where during a packet send, the A2 or USB needs to wake up from power
- * collapse, defer the send and return the context to IPA driver. This is
- * important since IPA driver has a single threaded Rx path.
- */
- static void defer_skb_send(struct sk_buff *skb, enum teth_packet_direction dir)
- {
- struct teth_work *work = kmalloc(sizeof(struct teth_work), GFP_KERNEL);
- if (!work) {
- TETH_ERR("No mem, dropping packet\n");
- dev_kfree_skb(skb);
- ipa_rm_inactivity_timer_release_resource
- (IPA_RM_RESOURCE_BRIDGE_PROD);
- return;
- }
- /*
- * Since IPA uses a single Rx thread, we don't
- * want to wait for completion here
- */
- INIT_WORK(&work->work, teth_send_skb_work);
- work->dir = dir;
- work->skb = skb;
- queue_work(teth_ctx->teth_wq, &work->work);
- }
- /**
- * usb_notify_cb() - callback function for sending packets from USB to A2
- * @param priv: private data
- * @param evt: event - RECEIVE or WRITE_DONE
- * @param data: pointer to skb to be sent
- *
- * This callback function is installed by the IPA driver, it is invoked in 2
- * cases:
- * 1. When a packet comes from the USB pipe and is routed to A5 (SW bridging)
- * 2. After a packet has been bridged from USB to A2 and its skb should be freed
- *
- * Invocation: sps driver --> IPA driver --> bridge driver
- *
- * In the event of IPA_RECEIVE:
- * - Checks whether the HW bridge can be set up..
- * - Requests the BRIDGE_PROD resource so that A2 and USB are not in power
- * collapse. In case where the resource is waking up, defer the send operation
- * to a workqueue in order to not block the IPA driver single threaded Rx path.
- * - Sends the packets to A2 using a2_service driver API.
- * - Releases the BRIDGE_PROD resource.
- *
- * In the event of IPA_WRITE_DONE:
- * - Frees the skb memory
- */
- static void usb_notify_cb(void *priv,
- enum ipa_dp_evt_type evt,
- unsigned long data)
- {
- struct sk_buff *skb = (struct sk_buff *)data;
- int res;
- switch (evt) {
- case IPA_RECEIVE:
- if (!teth_ctx->is_hw_bridge_complete)
- check_to_complete_hw_bridge(
- skb,
- teth_ctx->mac_addresses.host_pc_mac_addr,
- &teth_ctx->mac_addresses.host_pc_mac_addr_known,
- &teth_ctx->mac_addresses.device_mac_addr_known);
- /*
- * Request the BRIDGE_PROD resource, send the packet and release
- * the resource
- */
- res = ipa_rm_inactivity_timer_request_resource(
- IPA_RM_RESOURCE_BRIDGE_PROD);
- if (res < 0) {
- if (res == -EINPROGRESS) {
- /* The resource is waking up */
- defer_skb_send(skb, TETH_USB_TO_A2);
- } else {
- TETH_ERR(
- "Packet send failure, dropping packet !\n");
- dev_kfree_skb(skb);
- }
- ipa_rm_inactivity_timer_release_resource(
- IPA_RM_RESOURCE_BRIDGE_PROD);
- return;
- }
- res = a2_mux_write(A2_MUX_TETHERED_0, skb);
- if (res) {
- TETH_ERR("Packet send failure, dropping packet !\n");
- dev_kfree_skb(skb);
- ipa_rm_inactivity_timer_release_resource(
- IPA_RM_RESOURCE_BRIDGE_PROD);
- return;
- }
- teth_ctx->stats.usb_to_a2_num_sw_tx_packets++;
- ipa_rm_inactivity_timer_release_resource(
- IPA_RM_RESOURCE_BRIDGE_PROD);
- break;
- case IPA_WRITE_DONE:
- dev_kfree_skb(skb);
- break;
- default:
- TETH_ERR("Unsupported IPA event !\n");
- WARN_ON(1);
- }
- return;
- }
- /**
- * a2_notify_cb() - callback function for sending packets from A2 to USB
- * @param user_data: private data
- * @param event: event - RECEIVE or WRITE_DONE
- * @param data: pointer to skb to be sent
- *
- * This callback function is installed by the IPA driver, it is invoked in 2
- * cases:
- * 1. When a packet comes from the A2 pipe and is routed to A5 (SW bridging)
- * 2. After a packet has been bridged from A2 to USB and its skb should be freed
- *
- * Invocation: sps driver --> IPA driver --> a2_service driver --> bridge driver
- *
- * In the event of A2_MUX_RECEIVE:
- * - Checks whether the HW bridge can be set up..
- * - Requests the BRIDGE_PROD resource so that A2 and USB are not in power
- * collapse. In case where the resource is waking up, defer the send operation
- * to a workqueue in order to not block the IPA driver single threaded Rx path.
- * - Sends the packets to USB using IPA drivers ipa_tx_dp() API.
- * - Releases the BRIDGE_PROD resource.
- *
- * In the event of A2_MUX_WRITE_DONE:
- * - Frees the skb memory
- */
- static void a2_notify_cb(void *user_data,
- enum a2_mux_event_type event,
- unsigned long data)
- {
- struct sk_buff *skb = (struct sk_buff *)data;
- int res;
- switch (event) {
- case A2_MUX_RECEIVE:
- if (!teth_ctx->is_hw_bridge_complete)
- check_to_complete_hw_bridge(
- skb,
- teth_ctx->mac_addresses.device_mac_addr,
- &teth_ctx->mac_addresses.device_mac_addr_known,
- &teth_ctx->
- mac_addresses.host_pc_mac_addr_known);
- /*
- * Request the BRIDGE_PROD resource, send the packet and release
- * the resource
- */
- res = ipa_rm_inactivity_timer_request_resource(
- IPA_RM_RESOURCE_BRIDGE_PROD);
- if (res < 0) {
- if (res == -EINPROGRESS) {
- /* The resource is waking up */
- defer_skb_send(skb, TETH_A2_TO_USB);
- } else {
- TETH_ERR(
- "Packet send failure, dropping packet !\n");
- dev_kfree_skb(skb);
- }
- ipa_rm_inactivity_timer_release_resource(
- IPA_RM_RESOURCE_BRIDGE_PROD);
- return;
- }
- res = ipa_tx_dp(IPA_CLIENT_USB_CONS, skb, NULL);
- if (res) {
- TETH_ERR("Packet send failure, dropping packet !\n");
- dev_kfree_skb(skb);
- ipa_rm_inactivity_timer_release_resource(
- IPA_RM_RESOURCE_BRIDGE_PROD);
- return;
- }
- teth_ctx->stats.a2_to_usb_num_sw_tx_packets++;
- ipa_rm_inactivity_timer_release_resource(
- IPA_RM_RESOURCE_BRIDGE_PROD);
- break;
- case A2_MUX_WRITE_DONE:
- dev_kfree_skb(skb);
- break;
- default:
- TETH_ERR("Unsupported IPA event !\n");
- WARN_ON(1);
- }
- return;
- }
- /**
- * bridge_prod_notify_cb() - IPA Resource Manager callback function
- * @param notify_cb_data: private data
- * @param event: RESOURCE_GRANTED / RESOURCE_RELEASED
- * @param data: not used in this case
- *
- * This callback function is called by IPA resource manager to notify the
- * BRIDGE_PROD entity of events like RESOURCE_GRANTED and RESOURCE_RELEASED.
- */
- static void bridge_prod_notify_cb(void *notify_cb_data,
- enum ipa_rm_event event,
- unsigned long data)
- {
- switch (event) {
- case IPA_RM_RESOURCE_GRANTED:
- complete(&teth_ctx->is_bridge_prod_up);
- break;
- case IPA_RM_RESOURCE_RELEASED:
- complete(&teth_ctx->is_bridge_prod_down);
- break;
- default:
- TETH_ERR("Unsupported notification!\n");
- WARN_ON(1);
- break;
- }
- return;
- }
- static void a2_prod_notify_cb(void *notify_cb_data,
- enum ipa_rm_event event,
- unsigned long data)
- {
- int res;
- struct ipa_ep_cfg ipa_ep_cfg;
- switch (event) {
- case IPA_RM_RESOURCE_GRANTED:
- res = a2_mux_get_tethered_client_handles(
- A2_MUX_TETHERED_0,
- &teth_ctx->ipa_a2_pipe_hdl,
- &teth_ctx->a2_ipa_pipe_hdl);
- if (res) {
- TETH_ERR(
- "a2_mux_get_tethered_client_handles() failed, res = %d\n",
- res);
- return;
- }
- /* Reset the various endpoints configuration */
- memset(&ipa_ep_cfg, 0, sizeof(ipa_ep_cfg));
- ipa_ep_cfg.hdr.hdr_len = teth_ctx->ipa_a2_hdr_len;
- ipa_cfg_ep(teth_ctx->ipa_a2_pipe_hdl, &ipa_ep_cfg);
- memset(&ipa_ep_cfg, 0, sizeof(ipa_ep_cfg));
- ipa_ep_cfg.hdr.hdr_len = teth_ctx->a2_ipa_hdr_len;
- ipa_cfg_ep(teth_ctx->a2_ipa_pipe_hdl, &ipa_ep_cfg);
- break;
- case IPA_RM_RESOURCE_RELEASED:
- break;
- default:
- TETH_ERR("Unsupported notification!\n");
- WARN_ON(1);
- break;
- }
- return;
- }
- /**
- * teth_bridge_init() - Initialize the Tethering bridge driver
- * @usb_notify_cb_ptr: Callback function which should be used by the caller.
- * Output parameter.
- * @private_data_ptr: Data for the callback function. Should be used by the
- * caller. Output parameter.
- *
- * USB driver gets a pointer to a callback function (usb_notify_cb) and an
- * associated data. USB driver installs this callback function in the call to
- * ipa_connect().
- *
- * Builds IPA resource manager dependency graph.
- *
- * Return codes: 0: success,
- * -EINVAL - Bad parameter
- * Other negative value - Failure
- */
- int teth_bridge_init(ipa_notify_cb *usb_notify_cb_ptr, void **private_data_ptr)
- {
- int res = 0;
- struct ipa_rm_register_params a2_prod_reg_params;
- TETH_DBG_FUNC_ENTRY();
- if (usb_notify_cb_ptr == NULL) {
- TETH_ERR("Bad parameter\n");
- res = -EINVAL;
- goto bail;
- }
- *usb_notify_cb_ptr = usb_notify_cb;
- *private_data_ptr = NULL;
- /* Build IPA Resource manager dependency graph */
- res = ipa_rm_add_dependency(IPA_RM_RESOURCE_BRIDGE_PROD,
- IPA_RM_RESOURCE_USB_CONS);
- if (res && res != -EINPROGRESS) {
- TETH_ERR("ipa_rm_add_dependency() failed\n");
- goto bail;
- }
- res = ipa_rm_add_dependency(IPA_RM_RESOURCE_BRIDGE_PROD,
- IPA_RM_RESOURCE_A2_CONS);
- if (res && res != -EINPROGRESS) {
- TETH_ERR("ipa_rm_add_dependency() failed\n");
- goto fail_add_dependency_1;
- }
- res = ipa_rm_add_dependency(IPA_RM_RESOURCE_USB_PROD,
- IPA_RM_RESOURCE_A2_CONS);
- if (res && res != -EINPROGRESS) {
- TETH_ERR("ipa_rm_add_dependency() failed\n");
- goto fail_add_dependency_2;
- }
- res = ipa_rm_add_dependency(IPA_RM_RESOURCE_A2_PROD,
- IPA_RM_RESOURCE_USB_CONS);
- if (res && res != -EINPROGRESS) {
- TETH_ERR("ipa_rm_add_dependency() failed\n");
- goto fail_add_dependency_3;
- }
- /* Register for A2_PROD resource notifications */
- a2_prod_reg_params.user_data = NULL;
- a2_prod_reg_params.notify_cb = a2_prod_notify_cb;
- res = ipa_rm_register(IPA_RM_RESOURCE_A2_PROD, &a2_prod_reg_params);
- if (res) {
- TETH_ERR("ipa_rm_register() failed\n");
- goto fail_add_dependency_4;
- }
- /* Return 0 as EINPROGRESS is a valid return value at this point */
- res = 0;
- goto bail;
- fail_add_dependency_4:
- ipa_rm_delete_dependency(IPA_RM_RESOURCE_A2_PROD,
- IPA_RM_RESOURCE_USB_CONS);
- fail_add_dependency_3:
- ipa_rm_delete_dependency(IPA_RM_RESOURCE_USB_PROD,
- IPA_RM_RESOURCE_A2_CONS);
- fail_add_dependency_2:
- ipa_rm_delete_dependency(IPA_RM_RESOURCE_BRIDGE_PROD,
- IPA_RM_RESOURCE_A2_CONS);
- fail_add_dependency_1:
- ipa_rm_delete_dependency(IPA_RM_RESOURCE_BRIDGE_PROD,
- IPA_RM_RESOURCE_USB_CONS);
- bail:
- TETH_DBG_FUNC_EXIT();
- return res;
- }
- EXPORT_SYMBOL(teth_bridge_init);
- /**
- * initialize_context() - Initialize the ipa_ctx struct
- */
- static void initialize_context(void)
- {
- TETH_DBG_FUNC_ENTRY();
- /* Initialize context variables */
- teth_ctx->usb_ipa_pipe_hdl = 0;
- teth_ctx->ipa_a2_pipe_hdl = 0;
- teth_ctx->a2_ipa_pipe_hdl = 0;
- teth_ctx->ipa_usb_pipe_hdl = 0;
- teth_ctx->is_connected = false;
- /* The default link protocol is Ethernet */
- teth_ctx->link_protocol = TETH_LINK_PROTOCOL_ETHERNET;
- memset(&teth_ctx->mac_addresses, 0, sizeof(teth_ctx->mac_addresses));
- teth_ctx->is_hw_bridge_complete = false;
- memset(&teth_ctx->aggr_params, 0, sizeof(teth_ctx->aggr_params));
- teth_ctx->aggr_params_known = false;
- teth_ctx->tethering_mode = 0;
- INIT_COMPLETION(teth_ctx->is_bridge_prod_up);
- INIT_COMPLETION(teth_ctx->is_bridge_prod_down);
- teth_ctx->comp_hw_bridge_in_progress = false;
- memset(&teth_ctx->stats, 0, sizeof(teth_ctx->stats));
- teth_ctx->a2_ipa_hdr_len = 0;
- teth_ctx->ipa_a2_hdr_len = 0;
- memset(teth_ctx->hdr_del,
- 0,
- sizeof(struct ipa_ioc_del_hdr) + TETH_TOTAL_HDR_ENTRIES *
- sizeof(struct ipa_hdr_del));
- memset(teth_ctx->routing_del[IPA_IP_v4],
- 0,
- sizeof(struct ipa_ioc_del_rt_rule) +
- TETH_TOTAL_RT_ENTRIES_IP * sizeof(struct ipa_rt_rule_del));
- teth_ctx->routing_del[IPA_IP_v4]->ip = IPA_IP_v4;
- memset(teth_ctx->routing_del[IPA_IP_v6],
- 0,
- sizeof(struct ipa_ioc_del_rt_rule) +
- TETH_TOTAL_RT_ENTRIES_IP * sizeof(struct ipa_rt_rule_del));
- teth_ctx->routing_del[IPA_IP_v6]->ip = IPA_IP_v6;
- memset(teth_ctx->filtering_del[IPA_IP_v4],
- 0,
- sizeof(struct ipa_ioc_del_flt_rule) +
- TETH_TOTAL_FLT_ENTRIES_IP * sizeof(struct ipa_flt_rule_del));
- teth_ctx->filtering_del[IPA_IP_v4]->ip = IPA_IP_v4;
- memset(teth_ctx->filtering_del[IPA_IP_v6],
- 0,
- sizeof(struct ipa_ioc_del_flt_rule) +
- TETH_TOTAL_FLT_ENTRIES_IP * sizeof(struct ipa_flt_rule_del));
- teth_ctx->filtering_del[IPA_IP_v6]->ip = IPA_IP_v6;
- TETH_DBG_FUNC_EXIT();
- }
- /**
- * teth_bridge_disconnect() - Disconnect tethering bridge module
- */
- int teth_bridge_disconnect(void)
- {
- int res;
- struct ipa_rm_register_params a2_prod_reg_params;
- TETH_DBG_FUNC_ENTRY();
- if (!teth_ctx->is_connected) {
- TETH_ERR(
- "Trying to disconnect an already disconnected bridge\n");
- goto bail;
- }
- /*
- * Delete part of IPA resource manager dependency graph. Only the
- * BRIDGE_PROD <-> A2 dependency remains intact
- */
- res = ipa_rm_delete_dependency(IPA_RM_RESOURCE_BRIDGE_PROD,
- IPA_RM_RESOURCE_USB_CONS);
- if ((res != 0) && (res != -EINPROGRESS))
- TETH_ERR(
- "Failed deleting ipa_rm dependency BRIDGE_PROD <-> USB_CONS\n");
- res = ipa_rm_delete_dependency(IPA_RM_RESOURCE_USB_PROD,
- IPA_RM_RESOURCE_A2_CONS);
- if ((res != 0) && (res != -EINPROGRESS))
- TETH_ERR(
- "Failed deleting ipa_rm dependency USB_PROD <-> A2_CONS\n");
- res = ipa_rm_delete_dependency(IPA_RM_RESOURCE_A2_PROD,
- IPA_RM_RESOURCE_USB_CONS);
- if ((res != 0) && (res != -EINPROGRESS))
- TETH_ERR(
- "Failed deleting ipa_rm dependency A2_PROD <-> USB_CONS\n");
- /* Request the BRIDGE_PROD resource, A2 and IPA should power up */
- res = teth_request_resource();
- if (res) {
- TETH_ERR("request_resource() failed.\n");
- goto bail;
- }
- /* Close the channel to A2 */
- if (a2_mux_close_channel(A2_MUX_TETHERED_0))
- TETH_ERR("a2_mux_close_channel() failed\n");
- /* Teardown the IPA HW bridge */
- if (teth_ctx->is_hw_bridge_complete) {
- /* Delete header entries */
- if (ipa_del_hdr(teth_ctx->hdr_del))
- TETH_ERR("ipa_del_hdr() failed\n");
- /* Delete installed routing rules */
- if (ipa_del_rt_rule(teth_ctx->routing_del[IPA_IP_v4]))
- TETH_ERR("ipa_del_rt_rule() failed\n");
- if (ipa_del_rt_rule(teth_ctx->routing_del[IPA_IP_v6]))
- TETH_ERR("ipa_del_rt_rule() failed\n");
- /* Delete installed filtering rules */
- if (ipa_del_flt_rule(teth_ctx->filtering_del[IPA_IP_v4]))
- TETH_ERR("ipa_del_flt_rule() failed\n");
- if (ipa_del_flt_rule(teth_ctx->filtering_del[IPA_IP_v6]))
- TETH_ERR("ipa_del_flt_rule() failed\n");
- /*
- * Commit all the data to HW, including header, routing and
- * filtering blocks, IPv4 and IPv6
- */
- if (ipa_commit_hdr())
- TETH_ERR("Failed committing headers\n");
- }
- initialize_context();
- ipa_rm_inactivity_timer_release_resource(IPA_RM_RESOURCE_BRIDGE_PROD);
- /* Delete the last ipa_rm dependency - BRIDGE_PROD <-> A2 */
- res = ipa_rm_delete_dependency(IPA_RM_RESOURCE_BRIDGE_PROD,
- IPA_RM_RESOURCE_A2_CONS);
- if ((res != 0) && (res != -EINPROGRESS))
- TETH_ERR(
- "Failed deleting ipa_rm dependency BRIDGE_PROD <-> A2_CONS\n");
- /* Deregister from A2_PROD notifications */
- a2_prod_reg_params.user_data = NULL;
- a2_prod_reg_params.notify_cb = a2_prod_notify_cb;
- res = ipa_rm_deregister(IPA_RM_RESOURCE_A2_PROD, &a2_prod_reg_params);
- if (res)
- TETH_ERR("Failed deregistering from A2_prod notifications.\n");
- teth_ctx->is_connected = false;
- bail:
- TETH_DBG_FUNC_EXIT();
- return 0;
- }
- EXPORT_SYMBOL(teth_bridge_disconnect);
- /**
- * teth_bridge_connect() - Connect bridge for a tethered Rmnet / MBIM call
- * @connect_params: Connection info
- *
- * Return codes: 0: success
- * -EINVAL: invalid parameters
- * -EPERM: Operation not permitted as the bridge is already
- * connected
- */
- int teth_bridge_connect(struct teth_bridge_connect_params *connect_params)
- {
- int res;
- struct ipa_ep_cfg ipa_ep_cfg;
- TETH_DBG_FUNC_ENTRY();
- if (teth_ctx->is_connected) {
- TETH_ERR("Trying to connect an already connected bridge !\n");
- return -EPERM;
- }
- if (connect_params == NULL ||
- connect_params->ipa_usb_pipe_hdl <= 0 ||
- connect_params->usb_ipa_pipe_hdl <= 0 ||
- connect_params->tethering_mode >= TETH_TETHERING_MODE_MAX ||
- connect_params->tethering_mode < 0)
- return -EINVAL;
- teth_ctx->ipa_usb_pipe_hdl = connect_params->ipa_usb_pipe_hdl;
- teth_ctx->usb_ipa_pipe_hdl = connect_params->usb_ipa_pipe_hdl;
- teth_ctx->tethering_mode = connect_params->tethering_mode;
- res = teth_request_resource();
- if (res) {
- TETH_ERR("request_resource() failed.\n");
- goto bail;
- }
- res = a2_mux_open_channel(A2_MUX_TETHERED_0,
- NULL,
- a2_notify_cb);
- if (res) {
- TETH_ERR("a2_mux_open_channel() failed\n");
- goto bail;
- }
- res = a2_mux_get_tethered_client_handles(A2_MUX_TETHERED_0,
- &teth_ctx->ipa_a2_pipe_hdl,
- &teth_ctx->a2_ipa_pipe_hdl);
- if (res) {
- TETH_ERR(
- "a2_mux_get_tethered_client_handles() failed, res = %d\n", res);
- goto bail;
- }
- /* Reset the various endpoints configuration */
- memset(&ipa_ep_cfg, 0, sizeof(ipa_ep_cfg));
- ipa_cfg_ep(teth_ctx->ipa_usb_pipe_hdl, &ipa_ep_cfg);
- ipa_cfg_ep(teth_ctx->usb_ipa_pipe_hdl, &ipa_ep_cfg);
- ipa_cfg_ep(teth_ctx->ipa_a2_pipe_hdl, &ipa_ep_cfg);
- ipa_cfg_ep(teth_ctx->a2_ipa_pipe_hdl, &ipa_ep_cfg);
- teth_ctx->is_connected = true;
- if (teth_ctx->tethering_mode == TETH_TETHERING_MODE_MBIM)
- teth_ctx->link_protocol = TETH_LINK_PROTOCOL_IP;
- if (teth_ctx->aggr_params_known) {
- res = teth_set_aggregation();
- if (res) {
- TETH_ERR("Failed setting aggregation params\n");
- goto bail;
- }
- }
- /* In case of IP link protocol, complete HW bridge */
- if ((teth_ctx->link_protocol == TETH_LINK_PROTOCOL_IP) &&
- (!teth_ctx->comp_hw_bridge_in_progress) &&
- (teth_ctx->aggr_params_known) &&
- (!teth_ctx->is_hw_bridge_complete)) {
- INIT_WORK(&teth_ctx->comp_hw_bridge_work, complete_hw_bridge);
- teth_ctx->comp_hw_bridge_in_progress = true;
- queue_work(teth_ctx->teth_wq, &teth_ctx->comp_hw_bridge_work);
- }
- bail:
- ipa_rm_inactivity_timer_release_resource(IPA_RM_RESOURCE_BRIDGE_PROD);
- TETH_DBG_FUNC_EXIT();
- return res;
- }
- EXPORT_SYMBOL(teth_bridge_connect);
- static void set_aggr_default_params(struct teth_aggr_params_link *params)
- {
- if (params->max_datagrams == 0)
- params->max_datagrams =
- TETH_AGGR_MAX_DATAGRAMS_DEFAULT;
- if (params->max_transfer_size_byte == 0)
- params->max_transfer_size_byte =
- TETH_AGGR_MAX_AGGR_PACKET_SIZE_DEFAULT;
- }
- /**
- * teth_set_bridge_mode() - set the link protocol (IP / Ethernet)
- */
- static void teth_set_bridge_mode(enum teth_link_protocol_type link_protocol)
- {
- teth_ctx->link_protocol = link_protocol;
- teth_ctx->is_hw_bridge_complete = false;
- memset(&teth_ctx->mac_addresses, 0, sizeof(teth_ctx->mac_addresses));
- }
- /**
- * teth_bridge_set_aggr_params() - kernel API to set aggregation parameters
- * @param aggr_params: aggregation parmeters for uplink and downlink
- *
- * Besides setting the aggregation parameters, the function enforces max tranfer
- * size which is less then 8K and also forbids Ethernet link protocol with MBIM
- * aggregation which is not supported by HW.
- */
- int teth_bridge_set_aggr_params(struct teth_aggr_params *aggr_params)
- {
- int res;
- TETH_DBG_FUNC_ENTRY();
- if (!aggr_params) {
- TETH_ERR("Invalid parameter\n");
- return -EINVAL;
- }
- /*
- * In case the requested max transfer size is larger than 8K, set it to
- * to the default 8K
- */
- if (aggr_params->dl.max_transfer_size_byte >
- TETH_AGGR_MAX_AGGR_PACKET_SIZE_DEFAULT)
- aggr_params->dl.max_transfer_size_byte =
- TETH_AGGR_MAX_AGGR_PACKET_SIZE_DEFAULT;
- if (aggr_params->ul.max_transfer_size_byte >
- TETH_AGGR_MAX_AGGR_PACKET_SIZE_DEFAULT)
- aggr_params->ul.max_transfer_size_byte =
- TETH_AGGR_MAX_AGGR_PACKET_SIZE_DEFAULT;
- /* Ethernet link protocol and MBIM aggregation is not supported */
- if (teth_ctx->link_protocol == TETH_LINK_PROTOCOL_ETHERNET &&
- (aggr_params->dl.aggr_prot == TETH_AGGR_PROTOCOL_MBIM ||
- aggr_params->ul.aggr_prot == TETH_AGGR_PROTOCOL_MBIM)) {
- TETH_ERR("Ethernet with MBIM is not supported.\n");
- return -EINVAL;
- }
- res = teth_request_resource();
- if (res) {
- TETH_ERR("request_resource() failed.\n");
- return res;
- }
- memcpy(&teth_ctx->aggr_params,
- aggr_params,
- sizeof(struct teth_aggr_params));
- set_aggr_default_params(&teth_ctx->aggr_params.dl);
- set_aggr_default_params(&teth_ctx->aggr_params.ul);
- teth_ctx->aggr_params_known = true;
- res = teth_set_aggregation();
- if (res)
- TETH_ERR("Failed setting aggregation params\n");
- ipa_rm_inactivity_timer_release_resource(IPA_RM_RESOURCE_BRIDGE_PROD);
- TETH_DBG_FUNC_EXIT();
- return res;
- }
- EXPORT_SYMBOL(teth_bridge_set_aggr_params);
- static long teth_bridge_ioctl(struct file *filp,
- unsigned int cmd,
- unsigned long arg)
- {
- int res = 0;
- struct teth_aggr_params aggr_params;
- TETH_DBG("cmd=%x nr=%d\n", cmd, _IOC_NR(cmd));
- if ((_IOC_TYPE(cmd) != TETH_BRIDGE_IOC_MAGIC) ||
- (_IOC_NR(cmd) >= TETH_BRIDGE_IOCTL_MAX)) {
- TETH_ERR("Invalid ioctl\n");
- return -ENOIOCTLCMD;
- }
- switch (cmd) {
- case TETH_BRIDGE_IOC_SET_BRIDGE_MODE:
- TETH_DBG("TETH_BRIDGE_IOC_SET_BRIDGE_MODE ioctl called\n");
- if (teth_ctx->link_protocol != arg)
- teth_set_bridge_mode(arg);
- break;
- case TETH_BRIDGE_IOC_SET_AGGR_PARAMS:
- TETH_DBG("TETH_BRIDGE_IOC_SET_AGGR_PARAMS ioctl called\n");
- res = copy_from_user(&aggr_params,
- (struct teth_aggr_params *)arg,
- sizeof(struct teth_aggr_params));
- if (res) {
- TETH_ERR("Error, res = %d\n", res);
- res = -EFAULT;
- break;
- }
- res = teth_bridge_set_aggr_params(&aggr_params);
- if (res)
- break;
- /* In case of IP link protocol, complete HW bridge */
- if ((teth_ctx->link_protocol == TETH_LINK_PROTOCOL_IP) &&
- (!teth_ctx->comp_hw_bridge_in_progress) &&
- (!teth_ctx->is_hw_bridge_complete)) {
- INIT_WORK(&teth_ctx->comp_hw_bridge_work,
- complete_hw_bridge);
- teth_ctx->comp_hw_bridge_in_progress = true;
- queue_work(teth_ctx->teth_wq,
- &teth_ctx->comp_hw_bridge_work);
- }
- break;
- case TETH_BRIDGE_IOC_GET_AGGR_PARAMS:
- TETH_DBG("TETH_BRIDGE_IOC_GET_AGGR_PARAMS ioctl called\n");
- if (copy_to_user((u8 *)arg, (u8 *)&teth_ctx->aggr_params,
- sizeof(struct teth_aggr_params))) {
- res = -EFAULT;
- break;
- }
- break;
- case TETH_BRIDGE_IOC_GET_AGGR_CAPABILITIES:
- {
- u16 sz;
- u16 pyld_sz;
- struct teth_aggr_capabilities caps;
- TETH_DBG("GET_AGGR_CAPABILITIES ioctl called\n");
- sz = sizeof(struct teth_aggr_capabilities);
- if (copy_from_user(&caps,
- (struct teth_aggr_capabilities *)arg,
- sz)) {
- res = -EFAULT;
- break;
- }
- if (caps.num_protocols != teth_ctx->aggr_caps->num_protocols) {
- caps.num_protocols = teth_ctx->aggr_caps->num_protocols;
- if (copy_to_user((struct teth_aggr_capabilities *)arg,
- &caps,
- sz)) {
- res = -EFAULT;
- break;
- }
- TETH_DBG("Not enough space allocated.\n");
- res = -EAGAIN;
- break;
- }
- pyld_sz = sz + caps.num_protocols *
- sizeof(struct teth_aggr_params_link);
- if (copy_to_user((u8 *)arg,
- (u8 *)(teth_ctx->aggr_caps),
- pyld_sz)) {
- res = -EFAULT;
- break;
- }
- }
- break;
- }
- return res;
- }
- /**
- * set_aggr_capabilities() - allocates and fills the aggregation capabilities
- * struct
- */
- static int set_aggr_capabilities(void)
- {
- u16 NUM_PROTOCOLS = 2;
- teth_ctx->aggr_caps = kzalloc(sizeof(struct teth_aggr_capabilities) +
- NUM_PROTOCOLS *
- sizeof(struct teth_aggr_params_link),
- GFP_KERNEL);
- if (!teth_ctx->aggr_caps) {
- TETH_ERR("Memory alloc failed for aggregation capabilities.\n");
- return -ENOMEM;
- }
- teth_ctx->aggr_caps->num_protocols = NUM_PROTOCOLS;
- teth_ctx->aggr_caps->prot_caps[0].aggr_prot = TETH_AGGR_PROTOCOL_MBIM;
- set_aggr_default_params(&teth_ctx->aggr_caps->prot_caps[0]);
- teth_ctx->aggr_caps->prot_caps[1].aggr_prot = TETH_AGGR_PROTOCOL_TLP;
- set_aggr_default_params(&teth_ctx->aggr_caps->prot_caps[1]);
- return 0;
- }
- /**
- * teth_bridge_get_client_handles() - Get USB <--> IPA pipe handles
- * @producer_handle: USB --> IPA pipe handle
- * @consumer_handle: IPA --> USB pipe handle
- */
- void teth_bridge_get_client_handles(u32 *producer_handle,
- u32 *consumer_handle)
- {
- if (producer_handle == NULL || consumer_handle == NULL)
- return;
- *producer_handle = teth_ctx->usb_ipa_pipe_hdl;
- *consumer_handle = teth_ctx->ipa_usb_pipe_hdl;
- }
- #ifdef CONFIG_DEBUG_FS
- static struct dentry *dent;
- static struct dentry *dfile_link_protocol;
- static struct dentry *dfile_get_aggr_params;
- static struct dentry *dfile_set_aggr_protocol;
- static struct dentry *dfile_stats;
- static struct dentry *dfile_is_hw_bridge_complete;
- static ssize_t teth_debugfs_read_link_protocol(struct file *file,
- char __user *ubuf,
- size_t count,
- loff_t *ppos)
- {
- int nbytes;
- nbytes = scnprintf(dbg_buff, TETH_MAX_MSG_LEN, "Link protocol = %s\n",
- (teth_ctx->link_protocol ==
- TETH_LINK_PROTOCOL_ETHERNET) ?
- "ETHERNET" :
- "IP");
- return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
- }
- static ssize_t teth_debugfs_write_link_protocol(struct file *file,
- const char __user *ubuf,
- size_t count,
- loff_t *ppos)
- {
- unsigned long missing;
- enum teth_link_protocol_type link_protocol;
- if (sizeof(dbg_buff) < count + 1)
- return -EFAULT;
- missing = copy_from_user(dbg_buff, ubuf, count);
- if (missing)
- return -EFAULT;
- if (count > 0)
- dbg_buff[count-1] = '\0';
- if (strcmp(dbg_buff, "ETHERNET") == 0) {
- link_protocol = TETH_LINK_PROTOCOL_ETHERNET;
- } else if (strcmp(dbg_buff, "IP") == 0) {
- link_protocol = TETH_LINK_PROTOCOL_IP;
- } else {
- TETH_ERR("Bad link protocol, got %s,\n"
- "Use <ETHERNET> or <IP>.\n", dbg_buff);
- return count;
- }
- teth_set_bridge_mode(link_protocol);
- return count;
- }
- static ssize_t teth_debugfs_read_aggr_params(struct file *file,
- char __user *ubuf,
- size_t count,
- loff_t *ppos)
- {
- int nbytes = 0;
- char aggr_str[20];
- aggr_prot_to_str(teth_ctx->aggr_params.ul.aggr_prot,
- aggr_str,
- sizeof(aggr_str)-1);
- nbytes += scnprintf(&dbg_buff[nbytes], TETH_MAX_MSG_LEN - nbytes,
- "Aggregation parameters for uplink:\n");
- nbytes += scnprintf(&dbg_buff[nbytes], TETH_MAX_MSG_LEN - nbytes,
- " Aggregation protocol: %s\n",
- aggr_str);
- nbytes += scnprintf(&dbg_buff[nbytes], TETH_MAX_MSG_LEN - nbytes,
- " Max transfer size [byte]: %d\n",
- teth_ctx->aggr_params.ul.max_transfer_size_byte);
- nbytes += scnprintf(&dbg_buff[nbytes], TETH_MAX_MSG_LEN - nbytes,
- " Max datagrams: %d\n",
- teth_ctx->aggr_params.ul.max_datagrams);
- aggr_prot_to_str(teth_ctx->aggr_params.dl.aggr_prot,
- aggr_str,
- sizeof(aggr_str)-1);
- nbytes += scnprintf(&dbg_buff[nbytes], TETH_MAX_MSG_LEN,
- "Aggregation parameters for downlink:\n");
- nbytes += scnprintf(&dbg_buff[nbytes], TETH_MAX_MSG_LEN - nbytes,
- " Aggregation protocol: %s\n",
- aggr_str);
- nbytes += scnprintf(&dbg_buff[nbytes], TETH_MAX_MSG_LEN - nbytes,
- " Max transfer size [byte]: %d\n",
- teth_ctx->aggr_params.dl.max_transfer_size_byte);
- nbytes += scnprintf(&dbg_buff[nbytes], TETH_MAX_MSG_LEN - nbytes,
- " Max datagrams: %d\n",
- teth_ctx->aggr_params.dl.max_datagrams);
- return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
- }
- static ssize_t teth_debugfs_set_aggr_protocol(struct file *file,
- const char __user *ubuf,
- size_t count, loff_t *ppos)
- {
- unsigned long missing;
- enum teth_aggr_protocol_type aggr_prot;
- int res;
- if (sizeof(dbg_buff) < count + 1)
- return -EFAULT;
- missing = copy_from_user(dbg_buff, ubuf, count);
- if (missing)
- return -EFAULT;
- if (count > 0)
- dbg_buff[count-1] = '\0';
- set_aggr_default_params(&teth_ctx->aggr_params.dl);
- set_aggr_default_params(&teth_ctx->aggr_params.ul);
- if (strcmp(dbg_buff, "NONE") == 0) {
- aggr_prot = TETH_AGGR_PROTOCOL_NONE;
- } else if (strcmp(dbg_buff, "MBIM") == 0) {
- aggr_prot = TETH_AGGR_PROTOCOL_MBIM;
- } else if (strcmp(dbg_buff, "TLP") == 0) {
- aggr_prot = TETH_AGGR_PROTOCOL_TLP;
- } else {
- TETH_ERR("Bad aggregation protocol, got %s,\n"
- "Use <NONE>, <MBIM> or <TLP>.\n", dbg_buff);
- return count;
- }
- teth_ctx->aggr_params.dl.aggr_prot = aggr_prot;
- teth_ctx->aggr_params.ul.aggr_prot = aggr_prot;
- teth_ctx->aggr_params_known = true;
- res = teth_set_aggregation();
- if (res)
- TETH_ERR("Failed setting aggregation params\n");
- return count;
- }
- static ssize_t teth_debugfs_stats(struct file *file,
- char __user *ubuf,
- size_t count,
- loff_t *ppos)
- {
- int nbytes = 0;
- nbytes += scnprintf(&dbg_buff[nbytes],
- TETH_MAX_MSG_LEN - nbytes,
- "USB to A2 SW Tx packets: %lld\n",
- teth_ctx->stats.usb_to_a2_num_sw_tx_packets);
- nbytes += scnprintf(&dbg_buff[nbytes],
- TETH_MAX_MSG_LEN - nbytes,
- "A2 to USB SW Tx packets: %lld\n",
- teth_ctx->stats.a2_to_usb_num_sw_tx_packets);
- nbytes += scnprintf(
- &dbg_buff[nbytes],
- TETH_MAX_MSG_LEN - nbytes,
- "SW Tx packets sent during resource wakeup: %lld\n",
- teth_ctx->stats.num_sw_tx_packets_during_resource_wakeup);
- return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
- }
- static ssize_t teth_debugfs_hw_bridge_status(struct file *file,
- char __user *ubuf,
- size_t count,
- loff_t *ppos)
- {
- int nbytes = 0;
- if (teth_ctx->is_hw_bridge_complete)
- nbytes += scnprintf(&dbg_buff[nbytes],
- TETH_MAX_MSG_LEN - nbytes,
- "HW bridge is in use.\n");
- else
- nbytes += scnprintf(&dbg_buff[nbytes],
- TETH_MAX_MSG_LEN - nbytes,
- "SW bridge is in use. HW bridge not complete yet.\n");
- return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
- }
- const struct file_operations teth_link_protocol_ops = {
- .read = teth_debugfs_read_link_protocol,
- .write = teth_debugfs_write_link_protocol,
- };
- const struct file_operations teth_get_aggr_params_ops = {
- .read = teth_debugfs_read_aggr_params,
- };
- const struct file_operations teth_set_aggr_protocol_ops = {
- .write = teth_debugfs_set_aggr_protocol,
- };
- const struct file_operations teth_stats_ops = {
- .read = teth_debugfs_stats,
- };
- const struct file_operations teth_hw_bridge_status_ops = {
- .read = teth_debugfs_hw_bridge_status,
- };
- void teth_debugfs_init(void)
- {
- const mode_t read_only_mode = S_IRUSR | S_IRGRP | S_IROTH;
- const mode_t read_write_mode = S_IRUSR | S_IRGRP | S_IROTH |
- S_IWUSR | S_IWGRP | S_IWOTH;
- dent = debugfs_create_dir("ipa_teth", 0);
- if (IS_ERR(dent)) {
- IPAERR("fail to create folder ipa_teth debug_fs.\n");
- return;
- }
- dfile_link_protocol =
- debugfs_create_file("link_protocol", read_write_mode, dent, 0,
- &teth_link_protocol_ops);
- if (!dfile_link_protocol || IS_ERR(dfile_link_protocol)) {
- IPAERR("fail to create file link_protocol\n");
- goto fail;
- }
- dfile_get_aggr_params =
- debugfs_create_file("get_aggr_params", read_only_mode, dent, 0,
- &teth_get_aggr_params_ops);
- if (!dfile_get_aggr_params || IS_ERR(dfile_get_aggr_params)) {
- IPAERR("fail to create file get_aggr_params\n");
- goto fail;
- }
- dfile_set_aggr_protocol =
- debugfs_create_file("set_aggr_protocol", read_only_mode, dent,
- 0, &teth_set_aggr_protocol_ops);
- if (!dfile_set_aggr_protocol || IS_ERR(dfile_set_aggr_protocol)) {
- IPAERR("fail to create file set_aggr_protocol\n");
- goto fail;
- }
- dfile_stats =
- debugfs_create_file("stats", read_only_mode, dent,
- 0, &teth_stats_ops);
- if (!dfile_stats || IS_ERR(dfile_stats)) {
- IPAERR("fail to create file stats\n");
- goto fail;
- }
- dfile_is_hw_bridge_complete =
- debugfs_create_file("is_hw_bridge_complete", read_only_mode,
- dent, 0, &teth_hw_bridge_status_ops);
- if (!dfile_is_hw_bridge_complete ||
- IS_ERR(dfile_is_hw_bridge_complete)) {
- IPAERR("fail to create file is_hw_bridge_complete\n");
- goto fail;
- }
- return;
- fail:
- debugfs_remove_recursive(dent);
- }
- #else
- void teth_debugfs_init(void) {}
- #endif /* CONFIG_DEBUG_FS */
- static const struct file_operations teth_bridge_drv_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = teth_bridge_ioctl,
- };
- /**
- * teth_bridge_driver_init() - Initialize tethering bridge driver
- *
- */
- int teth_bridge_driver_init(void)
- {
- int res;
- struct ipa_rm_create_params bridge_prod_params;
- TETH_DBG("Tethering bridge driver init\n");
- teth_ctx = kzalloc(sizeof(*teth_ctx), GFP_KERNEL);
- if (!teth_ctx) {
- TETH_ERR("kzalloc err.\n");
- return -ENOMEM;
- }
- res = set_aggr_capabilities();
- if (res) {
- TETH_ERR("kzalloc err.\n");
- goto fail_alloc_aggr_caps;
- }
- res = -ENOMEM;
- teth_ctx->hdr_del = kzalloc(sizeof(struct ipa_ioc_del_hdr) +
- TETH_TOTAL_HDR_ENTRIES *
- sizeof(struct ipa_hdr_del),
- GFP_KERNEL);
- if (!teth_ctx->hdr_del) {
- TETH_ERR("kzalloc err.\n");
- goto fail_alloc_hdr_del;
- }
- teth_ctx->routing_del[IPA_IP_v4] =
- kzalloc(sizeof(struct ipa_ioc_del_rt_rule) +
- TETH_TOTAL_RT_ENTRIES_IP *
- sizeof(struct ipa_rt_rule_del),
- GFP_KERNEL);
- if (!teth_ctx->routing_del[IPA_IP_v4]) {
- TETH_ERR("kzalloc err.\n");
- goto fail_alloc_routing_del_ipv4;
- }
- teth_ctx->routing_del[IPA_IP_v6] =
- kzalloc(sizeof(struct ipa_ioc_del_rt_rule) +
- TETH_TOTAL_RT_ENTRIES_IP *
- sizeof(struct ipa_rt_rule_del),
- GFP_KERNEL);
- if (!teth_ctx->routing_del[IPA_IP_v6]) {
- TETH_ERR("kzalloc err.\n");
- goto fail_alloc_routing_del_ipv6;
- }
- teth_ctx->filtering_del[IPA_IP_v4] =
- kzalloc(sizeof(struct ipa_ioc_del_flt_rule) +
- TETH_TOTAL_FLT_ENTRIES_IP *
- sizeof(struct ipa_flt_rule_del),
- GFP_KERNEL);
- if (!teth_ctx->filtering_del[IPA_IP_v4]) {
- TETH_ERR("kzalloc err.\n");
- goto fail_alloc_filtering_del_ipv4;
- }
- teth_ctx->filtering_del[IPA_IP_v6] =
- kzalloc(sizeof(struct ipa_ioc_del_flt_rule) +
- TETH_TOTAL_FLT_ENTRIES_IP *
- sizeof(struct ipa_flt_rule_del),
- GFP_KERNEL);
- if (!teth_ctx->filtering_del[IPA_IP_v6]) {
- TETH_ERR("kzalloc err.\n");
- goto fail_alloc_filtering_del_ipv6;
- }
- teth_ctx->class = class_create(THIS_MODULE, TETH_BRIDGE_DRV_NAME);
- res = alloc_chrdev_region(&teth_ctx->dev_num, 0, 1,
- TETH_BRIDGE_DRV_NAME);
- if (res) {
- TETH_ERR("alloc_chrdev_region err.\n");
- res = -ENODEV;
- goto fail_alloc_chrdev_region;
- }
- teth_ctx->dev = device_create(teth_ctx->class, NULL, teth_ctx->dev_num,
- teth_ctx, TETH_BRIDGE_DRV_NAME);
- if (IS_ERR(teth_ctx->dev)) {
- TETH_ERR(":device_create err.\n");
- res = -ENODEV;
- goto fail_device_create;
- }
- cdev_init(&teth_ctx->cdev, &teth_bridge_drv_fops);
- teth_ctx->cdev.owner = THIS_MODULE;
- teth_ctx->cdev.ops = &teth_bridge_drv_fops;
- res = cdev_add(&teth_ctx->cdev, teth_ctx->dev_num, 1);
- if (res) {
- TETH_ERR(":cdev_add err=%d\n", -res);
- res = -ENODEV;
- goto fail_cdev_add;
- }
- teth_debugfs_init();
- /* Create BRIDGE_PROD entity in IPA Resource Manager */
- bridge_prod_params.name = IPA_RM_RESOURCE_BRIDGE_PROD;
- bridge_prod_params.reg_params.user_data = NULL;
- bridge_prod_params.reg_params.notify_cb = bridge_prod_notify_cb;
- res = ipa_rm_create_resource(&bridge_prod_params);
- if (res) {
- TETH_ERR("ipa_rm_create_resource() failed\n");
- goto fail_cdev_add;
- }
- init_completion(&teth_ctx->is_bridge_prod_up);
- init_completion(&teth_ctx->is_bridge_prod_down);
- res = ipa_rm_inactivity_timer_init(IPA_RM_RESOURCE_BRIDGE_PROD,
- TETH_INACTIVITY_TIME_MSEC);
- if (res) {
- TETH_ERR("ipa_rm_inactivity_timer_init() failed, res=%d\n",
- res);
- goto fail_cdev_add;
- }
- teth_ctx->teth_wq = create_workqueue(TETH_WORKQUEUE_NAME);
- if (!teth_ctx->teth_wq) {
- TETH_ERR("workqueue creation failed\n");
- goto fail_cdev_add;
- }
- initialize_context();
- TETH_DBG("Tethering bridge driver init OK\n");
- return 0;
- fail_cdev_add:
- device_destroy(teth_ctx->class, teth_ctx->dev_num);
- fail_device_create:
- unregister_chrdev_region(teth_ctx->dev_num, 1);
- fail_alloc_chrdev_region:
- kfree(teth_ctx->filtering_del[IPA_IP_v6]);
- fail_alloc_filtering_del_ipv6:
- kfree(teth_ctx->filtering_del[IPA_IP_v4]);
- fail_alloc_filtering_del_ipv4:
- kfree(teth_ctx->routing_del[IPA_IP_v6]);
- fail_alloc_routing_del_ipv6:
- kfree(teth_ctx->routing_del[IPA_IP_v4]);
- fail_alloc_routing_del_ipv4:
- kfree(teth_ctx->hdr_del);
- fail_alloc_hdr_del:
- kfree(teth_ctx->aggr_caps);
- fail_alloc_aggr_caps:
- kfree(teth_ctx);
- teth_ctx = NULL;
- return res;
- }
- EXPORT_SYMBOL(teth_bridge_driver_init);
- MODULE_LICENSE("GPL v2");
- MODULE_DESCRIPTION("Tethering bridge driver");
|