1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905 |
- /****************************************************************************
- * drivers/usbdev/rndis.c
- *
- * Copyright (C) 2011-2018 Gregory Nutt. All rights reserved.
- * Authors: Sakari Kapanen <sakari.m.kapanen@gmail.com>,
- * Petteri Aimonen <jpa@git.mail.kapsi.fi>
- *
- * References:
- * [MS-RNDIS]:
- * Remote Network Driver Interface Specification (RNDIS) Protocol
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * 3. Neither the name NuttX nor the names of its contributors may be
- * used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- ****************************************************************************/
- /****************************************************************************
- * Included Files
- ****************************************************************************/
- #include <queue.h>
- #include <errno.h>
- #include <string.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <nuttx/net/net.h>
- #include <nuttx/net/netdev.h>
- #include <nuttx/net/arp.h>
- #include <nuttx/kmalloc.h>
- #include <nuttx/arch.h>
- #include <nuttx/usb/usb.h>
- #include <nuttx/usb/cdc.h>
- #include <nuttx/usb/usbdev.h>
- #include <nuttx/usb/usbdev_trace.h>
- #include <nuttx/usb/rndis.h>
- #include <nuttx/wdog.h>
- #include <nuttx/wqueue.h>
- #include "rndis_std.h"
- /****************************************************************************
- * Pre-processor definitions
- ****************************************************************************/
- #define CONFIG_RNDIS_EP0MAXPACKET 64
- #ifndef CONFIG_RNDIS_NWRREQS
- # define CONFIG_RNDIS_NWRREQS (2)
- #endif
- #define RNDIS_PACKET_HDR_SIZE (sizeof(struct rndis_packet_msg))
- #define CONFIG_RNDIS_BULKIN_REQLEN \
- (CONFIG_NET_ETH_PKTSIZE + CONFIG_NET_GUARDSIZE + RNDIS_PACKET_HDR_SIZE)
- #define CONFIG_RNDIS_BULKOUT_REQLEN CONFIG_RNDIS_BULKIN_REQLEN
- #define RNDIS_NCONFIGS (1)
- #define RNDIS_CONFIGID (1)
- #define RNDIS_CONFIGIDNONE (0)
- #define RNDIS_NINTERFACES (2)
- #define RNDIS_EPINTIN_ADDR USB_EPIN(3)
- #define RNDIS_EPBULKIN_ADDR USB_EPIN(1)
- #define RNDIS_EPBULKOUT_ADDR USB_EPOUT(2)
- #define RNDIS_NUM_EPS (3)
- #define RNDIS_MANUFACTURERSTRID (1)
- #define RNDIS_PRODUCTSTRID (2)
- #define RNDIS_SERIALSTRID (3)
- #define RNDIS_STR_LANGUAGE (0x0409) /* en-us */
- #define RNDIS_MXDESCLEN (128)
- #define RNDIS_MAXSTRLEN (RNDIS_MXDESCLEN-2)
- #define RNDIS_CTRLREQ_LEN (512)
- #define RNDIS_BUFFER_SIZE CONFIG_NET_ETH_PKTSIZE
- #define RNDIS_BUFFER_COUNT 4
- /* TX poll delay = 1 seconds. CLK_TCK is the number of clock ticks per second */
- #define RNDIS_WDDELAY (1*CLK_TCK)
- /* Work queue to use for network operations. LPWORK should be used here */
- #define ETHWORK LPWORK
- #ifndef min
- # define min(a,b) ((a)<(b)?(a):(b))
- #endif
- #ifndef max
- # define max(a,b) ((a)>(b)?(a):(b))
- #endif
- /****************************************************************************
- * Private Types
- ****************************************************************************/
- /* Container to support a list of requests */
- struct rndis_req_s
- {
- FAR struct rndis_req_s *flink; /* Implements a singly linked list */
- FAR struct usbdev_req_s *req; /* The contained request */
- };
- /* This structure describes the internal state of the driver */
- struct rndis_dev_s
- {
- struct net_driver_s netdev; /* Network driver structure */
- struct usbdev_devinfo_s devinfo;
- FAR struct usbdev_s *usbdev; /* usbdev driver pointer */
- FAR struct usbdev_ep_s *epintin; /* Interrupt IN endpoint structure */
- FAR struct usbdev_ep_s *epbulkin; /* Bulk IN endpoint structure */
- FAR struct usbdev_ep_s *epbulkout; /* Bulk OUT endpoint structure */
- FAR struct usbdev_req_s *ctrlreq; /* Pointer to preallocated control request */
- FAR struct usbdev_req_s *epintin_req; /* Pointer to preallocated interrupt in endpoint request */
- FAR struct usbdev_req_s *rdreq; /* Pointer to Preallocated control endpoint read request */
- struct sq_queue_s reqlist; /* List of free write request containers */
- /* Preallocated USB request buffers */
- struct rndis_req_s wrreqs[CONFIG_RNDIS_NWRREQS];
- struct work_s rxwork; /* Worker for dispatching RX packets */
- WDOG_ID txpoll; /* TX poll watchdog */
- struct work_s pollwork; /* TX poll worker */
- bool registered; /* Has netdev_register() been called */
- uint8_t config; /* USB Configuration number */
- FAR struct rndis_req_s *net_req; /* Pointer to request whose buffer is assigned to network */
- FAR struct rndis_req_s *rx_req; /* Pointer request container that holds RX buffer */
- size_t current_rx_received; /* Number of bytes of current RX datagram received over USB */
- size_t current_rx_datagram_size; /* Total number of bytes of the current RX datagram */
- size_t current_rx_datagram_offset; /* Offset of current RX datagram */
- size_t current_rx_msglen; /* Length of the entire message to be received */
- bool rdreq_submitted; /* Indicates if the read request is submitted */
- bool rx_blocked; /* Indicates if we can receive packets on bulk in endpoint */
- bool ctrlreq_has_encap_response; /* Indicates if ctrlreq buffer holds a response */
- bool connected; /* Connection status indicator */
- uint32_t rndis_packet_filter; /* RNDIS packet filter value */
- uint32_t rndis_host_tx_count; /* TX packet counter */
- uint32_t rndis_host_rx_count; /* RX packet counter */
- uint8_t host_mac_address[6]; /* Host side MAC address */
- };
- /* The internal version of the class driver */
- struct rndis_driver_s
- {
- struct usbdevclass_driver_s drvr;
- FAR struct rndis_dev_s *dev;
- };
- /* This is what is allocated */
- struct rndis_alloc_s
- {
- struct rndis_dev_s dev;
- struct rndis_driver_s drvr;
- };
- /* RNDIS USB configuration descriptor */
- struct rndis_cfgdesc_s
- {
- #ifndef CONFIG_RNDIS_COMPOSITE
- struct usb_cfgdesc_s cfgdesc; /* Configuration descriptor */
- #endif
- struct usb_iaddesc_s assoc_desc; /* Interface association descriptor */
- struct usb_ifdesc_s comm_ifdesc; /* Communication interface descriptor */
- struct usb_epdesc_s epintindesc; /* Interrupt endpoint descriptor */
- struct usb_ifdesc_s data_ifdesc; /* Data interface descriptor */
- struct usb_epdesc_s epbulkindesc; /* Bulk in interface descriptor */
- struct usb_epdesc_s epbulkoutdesc; /* Bulk out interface descriptor */
- };
- /* RNDIS object ID - value pair structure */
- struct rndis_oid_value_s
- {
- uint32_t objid;
- uint32_t length;
- uint32_t value;
- FAR const void *data; /* Data pointer overrides value if non-NULL. */
- };
- /****************************************************************************
- * Private Function Prototypes
- ****************************************************************************/
- /* Netdev driver callbacks */
- static int rndis_ifup(FAR struct net_driver_s *dev);
- static int rndis_ifdown(FAR struct net_driver_s *dev);
- static int rndis_txavail(FAR struct net_driver_s *dev);
- static int rndis_transmit(FAR struct rndis_dev_s *priv);
- static int rndis_txpoll(FAR struct net_driver_s *dev);
- static void rndis_polltimer(int argc, uint32_t arg, ...);
- /* usbclass callbacks */
- static int usbclass_setup(FAR struct usbdevclass_driver_s *driver,
- FAR struct usbdev_s *dev,
- FAR const struct usb_ctrlreq_s *ctrl,
- FAR uint8_t *dataout, size_t outlen);
- static int usbclass_bind(FAR struct usbdevclass_driver_s *driver,
- FAR struct usbdev_s *dev);
- static void usbclass_unbind(FAR struct usbdevclass_driver_s *driver,
- FAR struct usbdev_s *dev);
- static void usbclass_disconnect(FAR struct usbdevclass_driver_s *driver,
- FAR struct usbdev_s *dev);
- static int usbclass_setconfig(FAR struct rndis_dev_s *priv, uint8_t config);
- static void usbclass_resetconfig(FAR struct rndis_dev_s *priv);
- /* usbclass helpers */
- static int usbclass_copy_epdesc(int epid, FAR struct usb_epdesc_s *epdesc,
- FAR struct usbdev_devinfo_s *devinfo,
- bool hispeed);
- /****************************************************************************
- * Private Data
- ****************************************************************************/
- /* USB driver operations */
- const static struct usbdevclass_driverops_s g_driverops =
- {
- &usbclass_bind,
- &usbclass_unbind,
- &usbclass_setup,
- &usbclass_disconnect,
- NULL,
- NULL
- };
- #ifndef CONFIG_RNDIS_COMPOSITE
- static const struct usb_devdesc_s g_devdesc =
- {
- USB_SIZEOF_DEVDESC, /* len */
- USB_DESC_TYPE_DEVICE, /* type */
- {LSBYTE(0x0200), MSBYTE(0x0200)}, /* usb */
- 0, /* classid */
- 0, /* subclass */
- 0, /* protocol */
- CONFIG_RNDIS_EP0MAXPACKET, /* maxpacketsize */
- { LSBYTE(CONFIG_RNDIS_VENDORID), /* vendor */
- MSBYTE(CONFIG_RNDIS_VENDORID) },
- { LSBYTE(CONFIG_RNDIS_PRODUCTID), /* product */
- MSBYTE(CONFIG_RNDIS_PRODUCTID) },
- { LSBYTE(CONFIG_RNDIS_VERSIONNO), /* device */
- MSBYTE(CONFIG_RNDIS_VERSIONNO) },
- RNDIS_MANUFACTURERSTRID, /* imfgr */
- RNDIS_PRODUCTSTRID, /* iproduct */
- RNDIS_SERIALSTRID, /* serno */
- RNDIS_NCONFIGS /* nconfigs */
- };
- #endif
- const static struct rndis_cfgdesc_s g_rndis_cfgdesc =
- {
- #ifndef CONFIG_RNDIS_COMPOSITE
- {
- .len = USB_SIZEOF_CFGDESC,
- .type = USB_DESC_TYPE_CONFIG,
- .totallen = {0, 0},
- .ninterfaces = RNDIS_NINTERFACES,
- .cfgvalue = RNDIS_CONFIGID,
- .icfg = 0,
- .attr = USB_CONFIG_ATTR_ONE | USB_CONFIG_ATTR_SELFPOWER,
- .mxpower = (CONFIG_USBDEV_MAXPOWER + 1) / 2
- },
- #endif
- {
- .len = USB_SIZEOF_IADDESC,
- .type = USB_DESC_TYPE_INTERFACEASSOCIATION,
- .firstif = 0,
- .nifs = RNDIS_NINTERFACES,
- .classid = 0xef,
- .subclass = 0x04,
- .protocol = 0x01,
- .ifunction = 0
- },
- {
- .len = USB_SIZEOF_IFDESC,
- .type = USB_DESC_TYPE_INTERFACE,
- .ifno = 0,
- .alt = 0,
- .neps = 1,
- .classid = USB_CLASS_CDC,
- .subclass = CDC_SUBCLASS_ACM,
- .protocol = CDC_PROTO_VENDOR,
- .iif = 0
- },
- {
- .len = USB_SIZEOF_EPDESC,
- .type = USB_DESC_TYPE_ENDPOINT,
- .addr = RNDIS_EPINTIN_ADDR,
- .attr = USB_EP_ATTR_XFER_INT,
- .mxpacketsize = { LSBYTE(16), MSBYTE(16) },
- .interval = 1
- },
- {
- .len = USB_SIZEOF_IFDESC,
- .type = USB_DESC_TYPE_INTERFACE,
- .ifno = 1,
- .alt = 0,
- .neps = 2,
- .classid = USB_CLASS_CDC_DATA,
- .subclass = 0,
- .protocol = 0,
- .iif = 0
- },
- {
- .len = USB_SIZEOF_EPDESC,
- .type = USB_DESC_TYPE_ENDPOINT,
- .addr = RNDIS_EPBULKIN_ADDR,
- .attr = USB_EP_ATTR_XFER_BULK,
- #ifdef CONFIG_USBDEV_DUALSPEED
- .mxpacketsize = { LSBYTE(512), MSBYTE(512) },
- .interval = 0
- #else
- .mxpacketsize = { LSBYTE(64), MSBYTE(64) },
- .interval = 1
- #endif
- },
- {
- .len = USB_SIZEOF_EPDESC,
- .type = USB_DESC_TYPE_ENDPOINT,
- .addr = RNDIS_EPBULKOUT_ADDR,
- .attr = USB_EP_ATTR_XFER_BULK,
- #ifdef CONFIG_USBDEV_DUALSPEED
- .mxpacketsize = { LSBYTE(512), MSBYTE(512) },
- .interval = 0
- #else
- .mxpacketsize = { LSBYTE(64), MSBYTE(64) },
- .interval = 1
- #endif
- }
- };
- /* Default MAC address given to the host side of the interface. */
- static uint8_t g_rndis_default_mac_addr[6] =
- {
- 0x02, 0x00, 0x00, 0x11, 0x22, 0x33
- };
- /* These lists give dummy responses to be returned to PC. The values are
- * chosen so that Windows is happy - other operating systems don't really care
- * much.
- */
- static const uint32_t g_rndis_supported_oids[] =
- {
- RNDIS_OID_GEN_SUPPORTED_LIST,
- RNDIS_OID_GEN_HARDWARE_STATUS,
- RNDIS_OID_GEN_MEDIA_SUPPORTED,
- RNDIS_OID_GEN_MEDIA_IN_USE,
- RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE,
- RNDIS_OID_GEN_LINK_SPEED,
- RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE,
- RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE,
- RNDIS_OID_GEN_VENDOR_ID,
- RNDIS_OID_GEN_VENDOR_DESCRIPTION,
- RNDIS_OID_GEN_VENDOR_DRIVER_VERSION,
- RNDIS_OID_GEN_CURRENT_PACKET_FILTER,
- RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE,
- RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
- RNDIS_OID_GEN_PHYSICAL_MEDIUM,
- RNDIS_OID_GEN_XMIT_OK,
- RNDIS_OID_GEN_RCV_OK,
- RNDIS_OID_GEN_XMIT_ERROR,
- RNDIS_OID_GEN_RCV_ERROR,
- RNDIS_OID_GEN_RCV_NO_BUFFER,
- RNDIS_OID_802_3_PERMANENT_ADDRESS,
- RNDIS_OID_802_3_CURRENT_ADDRESS,
- RNDIS_OID_802_3_MULTICAST_LIST,
- RNDIS_OID_802_3_MAC_OPTIONS,
- RNDIS_OID_802_3_MAXIMUM_LIST_SIZE,
- RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT,
- RNDIS_OID_802_3_XMIT_ONE_COLLISION,
- RNDIS_OID_802_3_XMIT_MORE_COLLISION,
- };
- static const struct rndis_oid_value_s g_rndis_oid_values[] =
- {
- {RNDIS_OID_GEN_SUPPORTED_LIST, sizeof(g_rndis_supported_oids), 0, g_rndis_supported_oids},
- {RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE, 4, CONFIG_NET_ETH_PKTSIZE, NULL},
- #ifdef CONFIG_USBDEV_DUALSPEED
- {RNDIS_OID_GEN_LINK_SPEED, 4, 100000, NULL},
- #else
- {RNDIS_OID_GEN_LINK_SPEED, 4, 2000000, NULL},
- #endif
- {RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE, 4, CONFIG_NET_ETH_PKTSIZE, NULL},
- {RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE, 4, CONFIG_NET_ETH_PKTSIZE, NULL},
- {RNDIS_OID_GEN_VENDOR_ID, 4, 0x00FFFFFF, NULL},
- {RNDIS_OID_GEN_VENDOR_DESCRIPTION, 6, 0, "RNDIS"},
- {RNDIS_OID_GEN_CURRENT_PACKET_FILTER, 4, 0, NULL},
- {RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE, 4, 2048, NULL},
- {RNDIS_OID_GEN_XMIT_OK, 4, 0, NULL},
- {RNDIS_OID_GEN_RCV_OK, 4, 0, NULL},
- {RNDIS_OID_802_3_PERMANENT_ADDRESS, 6, 0, NULL},
- {RNDIS_OID_802_3_CURRENT_ADDRESS, 6, 0, NULL},
- {RNDIS_OID_802_3_MULTICAST_LIST, 4, 0xE0000000, NULL},
- {RNDIS_OID_802_3_MAXIMUM_LIST_SIZE, 4, 1, NULL},
- {0x0, 4, 0, NULL}, /* Default fallback */
- };
- /****************************************************************************
- * Private Data
- ****************************************************************************/
- /****************************************************************************
- * Buffering of data is implemented in the following manner:
- *
- * RNDIS driver holds a number of preallocated bulk IN endpoint write
- * requests along with buffers large enough to hold an Ethernet packet and
- * the corresponding RNDIS header.
- *
- * One of these is always reserved for packet reception - when data arrives
- * on the bulk OUT endpoint, it is copied to the reserved request buffer.
- * When the reception of an Ethernet packet is complete, a worker to process
- * the packet is scheduled and bulk OUT endpoint is set to NAK.
- *
- * The processing worker passes the buffer to the network. When the network is
- * done processing the packet, the buffer might contain data to be sent.
- * If so, the corresponding write request is queued on the bulk IN endpoint.
- * The NAK state on bulk OUT endpoint is cleared to allow new packets to
- * arrive. If there's no data to send, the request is returned to the list of
- * free requests.
- *
- * When a bulk IN write operation is complete, the request is added to the
- * list of free requests.
- *
- ****************************************************************************/
- /****************************************************************************
- * Name: rndis_submit_rdreq
- *
- * Description:
- * Submits the bulk OUT read request. Takes care not to submit the request
- * when the RX packet buffer is already in use.
- *
- * Input Parameters:
- * priv: pointer to RNDIS device driver structure
- *
- * Returned Value:
- * The return value of the EP_SUBMIT operation
- *
- ****************************************************************************/
- static int rndis_submit_rdreq(FAR struct rndis_dev_s *priv)
- {
- irqstate_t flags = enter_critical_section();
- int ret = OK;
- if (!priv->rdreq_submitted && !priv->rx_blocked)
- {
- priv->rdreq->len = priv->epbulkout->maxpacket;
- ret = EP_SUBMIT(priv->epbulkout, priv->rdreq);
- if (ret != OK)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDSUBMIT),
- (uint16_t)-priv->rdreq->result);
- }
- else
- {
- priv->rdreq_submitted = true;
- }
- }
- leave_critical_section(flags);
- return ret;
- }
- /****************************************************************************
- * Name: rndis_cancel_rdreq
- *
- * Description:
- * Cancels the bulk OUT endpoint read request.
- *
- * Input Parameters:
- * priv: pointer to RNDIS device driver structure
- *
- ****************************************************************************/
- static void rndis_cancel_rdreq(FAR struct rndis_dev_s *priv)
- {
- irqstate_t flags = enter_critical_section();
- if (priv->rdreq_submitted)
- {
- EP_CANCEL(priv->epbulkout, priv->rdreq);
- priv->rdreq_submitted = false;
- }
- leave_critical_section(flags);
- }
- /****************************************************************************
- * Name: rndis_block_rx
- *
- * Description:
- * Blocks reception of further bulk OUT endpoint data.
- *
- * Input Parameters:
- * priv: pointer to RNDIS device driver structure
- *
- ****************************************************************************/
- static void rndis_block_rx(FAR struct rndis_dev_s *priv)
- {
- irqstate_t flags = enter_critical_section();
- priv->rx_blocked = true;
- rndis_cancel_rdreq(priv);
- leave_critical_section(flags);
- }
- /****************************************************************************
- * Name: rndis_unblock_rx
- *
- * Description:
- * Unblocks reception of bulk OUT endpoint data.
- *
- * Input Parameters:
- * priv: pointer to RNDIS device driver structure
- *
- * Assumptions:
- * Called from critical section
- *
- ****************************************************************************/
- static void rndis_unblock_rx(FAR struct rndis_dev_s *priv)
- {
- priv->rx_blocked = false;
- }
- /****************************************************************************
- * Name: rndis_allocwrreq
- *
- * Description:
- * Allocates a bulk IN endpoint request from the list of free request
- * buffers.
- *
- * Input Parameters:
- * priv: pointer to RNDIS device driver structure
- *
- * Returned Value:
- * NULL if allocation failed; pointer to allocated request if succeeded
- *
- * Assumptions:
- * Called from critical section
- *
- ****************************************************************************/
- static FAR struct rndis_req_s *rndis_allocwrreq(FAR struct rndis_dev_s *priv)
- {
- return (FAR struct rndis_req_s *)sq_remfirst(&priv->reqlist);
- }
- /****************************************************************************
- * Name: rndis_hasfreereqs
- *
- * Description:
- * Checks if there are free requests usable for TX data.
- *
- * Input Parameters:
- * priv: pointer to RNDIS device driver structure
- *
- * Returned Value:
- * true if requests available; false if no requests available
- *
- * Assumptions:
- * Called from critical section
- *
- ****************************************************************************/
- static bool rndis_hasfreereqs(FAR struct rndis_dev_s *priv)
- {
- return sq_count(&priv->reqlist) > 1;
- }
- /****************************************************************************
- * Name: rndis_freewrreq
- *
- * Description:
- * Returns a bulk IN endpoint write requests to the list of free requests.
- *
- * Input Parameters:
- * priv: pointer to RNDIS device driver structure
- * req: pointer to the request
- *
- * Assumptions:
- * Called with interrupts disabled.
- *
- ****************************************************************************/
- static void rndis_freewrreq(FAR struct rndis_dev_s *priv,
- FAR struct rndis_req_s *req)
- {
- DEBUGASSERT(req != NULL);
- sq_addlast((FAR sq_entry_t *)req, &priv->reqlist);
- rndis_submit_rdreq(priv);
- }
- /****************************************************************************
- * Name: rndis_allocnetreq
- *
- * Description:
- * Allocates a request buffer to be used on the network.
- *
- * Input Parameters:
- * priv: pointer to RNDIS device driver structure
- *
- * Returned Value:
- * true if succeeded; false if failed
- *
- * Assumptions:
- * Caller holds the network lock
- *
- ****************************************************************************/
- static bool rndis_allocnetreq(FAR struct rndis_dev_s *priv)
- {
- irqstate_t flags = enter_critical_section();
- DEBUGASSERT(priv->net_req == NULL);
- if (!rndis_hasfreereqs(priv))
- {
- leave_critical_section(flags);
- return false;
- }
- priv->net_req = rndis_allocwrreq(priv);
- if (priv->net_req)
- {
- priv->netdev.d_buf = &priv->net_req->req->buf[RNDIS_PACKET_HDR_SIZE];
- priv->netdev.d_len = CONFIG_NET_ETH_PKTSIZE;
- }
- leave_critical_section(flags);
- return priv->net_req != NULL;
- }
- /****************************************************************************
- * Name: rndis_sendnetreq
- *
- * Description:
- * Submits the request buffer held by the network.
- *
- * Input Parameters:
- * priv: pointer to RNDIS device driver structure
- *
- * Assumptions:
- * Caller holds the network lock
- *
- ****************************************************************************/
- static void rndis_sendnetreq(FAR struct rndis_dev_s *priv)
- {
- irqstate_t flags = enter_critical_section();
- DEBUGASSERT(priv->net_req != NULL);
- priv->net_req->req->priv = priv->net_req;
- EP_SUBMIT(priv->epbulkin, priv->net_req->req);
- priv->net_req = NULL;
- priv->netdev.d_buf = NULL;
- priv->netdev.d_len = 0;
- leave_critical_section(flags);
- }
- /****************************************************************************
- * Name: rndis_freenetreq
- *
- * Description:
- * Frees the request buffer held by the network.
- *
- * Input Parameters:
- * priv: pointer to RNDIS device driver structure
- *
- * Assumptions:
- * Caller holds the network lock
- *
- ****************************************************************************/
- static void rndis_freenetreq(FAR struct rndis_dev_s *priv)
- {
- irqstate_t flags = enter_critical_section();
- rndis_freewrreq(priv, priv->net_req);
- priv->net_req = NULL;
- priv->netdev.d_buf = NULL;
- priv->netdev.d_len = 0;
- leave_critical_section(flags);
- }
- /****************************************************************************
- * Name: rndis_allocrxreq
- *
- * Description:
- * Allocates a buffer for packet reception if there already isn't one.
- *
- * Input Parameters:
- * priv: pointer to RNDIS device driver structure
- *
- * Returned Value:
- * true if succeeded; false if failed
- *
- * Assumptions:
- * Called from critical section
- *
- ****************************************************************************/
- static bool rndis_allocrxreq(FAR struct rndis_dev_s *priv)
- {
- if (priv->rx_req != NULL)
- {
- return true;
- }
- priv->rx_req = rndis_allocwrreq(priv);
- return priv->rx_req != NULL;
- }
- /****************************************************************************
- * Name: rndis_giverxreq
- *
- * Description:
- * Passes the RX packet buffer to the network
- *
- * Input Parameters:
- * priv: pointer to RNDIS device driver structure
- *
- * Assumptions:
- * Caller holds the network lock
- *
- ****************************************************************************/
- static void rndis_giverxreq(FAR struct rndis_dev_s *priv)
- {
- DEBUGASSERT(priv->rx_req != NULL);
- DEBUGASSERT(priv->net_req == NULL);
- priv->net_req = priv->rx_req;
- priv->netdev.d_buf = &priv->net_req->req->buf[RNDIS_PACKET_HDR_SIZE];
- priv->netdev.d_len = CONFIG_NET_ETH_PKTSIZE;
- priv->rx_req = NULL;
- }
- /****************************************************************************
- * Name: rndis_fillrequest
- *
- * Description:
- * Fills the RNDIS header to the request buffer
- *
- * Input Parameters:
- * priv: pointer to RNDIS device driver structure
- * req: the request whose buffer we should fill
- *
- * Returned Value:
- * The total length of the request data
- *
- * Assumptions:
- * Caller holds the network lock
- *
- ****************************************************************************/
- static uint16_t rndis_fillrequest(FAR struct rndis_dev_s *priv,
- FAR struct usbdev_req_s *req)
- {
- size_t datalen;
- req->len = 0;
- datalen = min(priv->netdev.d_len,
- CONFIG_RNDIS_BULKIN_REQLEN - RNDIS_PACKET_HDR_SIZE);
- if (datalen > 0)
- {
- /* Send the required headers */
- FAR struct rndis_packet_msg *msg = (FAR struct rndis_packet_msg *)req->buf;
- memset(msg, 0, RNDIS_PACKET_HDR_SIZE);
- msg->msgtype = RNDIS_PACKET_MSG;
- msg->msglen = RNDIS_PACKET_HDR_SIZE + datalen;
- msg->dataoffset = RNDIS_PACKET_HDR_SIZE - 8;
- msg->datalen = datalen;
- req->flags = USBDEV_REQFLAGS_NULLPKT;
- req->len = datalen + RNDIS_PACKET_HDR_SIZE;
- }
- return req->len;
- }
- /****************************************************************************
- * Name: rndis_rxdispatch
- *
- * Description:
- * Processes the received Ethernet packet. Called from work queue.
- *
- * Input Parameters:
- * arg: pointer to RNDIS device driver structure
- *
- ****************************************************************************/
- static void rndis_rxdispatch(FAR void *arg)
- {
- FAR struct rndis_dev_s *priv = (FAR struct rndis_dev_s *)arg;
- FAR struct eth_hdr_s *hdr;
- irqstate_t flags;
- net_lock();
- flags = enter_critical_section();
- rndis_giverxreq(priv);
- priv->netdev.d_len = priv->current_rx_datagram_size;
- leave_critical_section(flags);
- hdr = (FAR struct eth_hdr_s *)priv->netdev.d_buf;
- /* We only accept IP packets of the configured type and ARP packets */
- #ifdef CONFIG_NET_IPv4
- if (hdr->type == HTONS(ETHTYPE_IP))
- {
- NETDEV_RXIPV4(&priv->netdev);
- /* Handle ARP on input then give the IPv4 packet to the network
- * layer
- */
- arp_ipin(&priv->netdev);
- ipv4_input(&priv->netdev);
- if (priv->netdev.d_len > 0)
- {
- /* Update the Ethernet header with the correct MAC address */
- #ifdef CONFIG_NET_IPv6
- if (IFF_IS_IPv4(priv->netdev.d_flags))
- #endif
- {
- arp_out(&priv->netdev);
- }
- #ifdef CONFIG_NET_IPv6
- else
- {
- neighbor_out(&priv->netdev);
- }
- #endif
- /* And send the packet */
- rndis_transmit(priv);
- }
- }
- else
- #endif
- #ifdef CONFIG_NET_IPv6
- if (hdr->type == HTONS(ETHTYPE_IP6))
- {
- NETDEV_RXIPV6(&priv->netdev);
- /* Give the IPv6 packet to the network layer */
- arp_ipin(&priv->netdev);
- ipv6_input(&priv->netdev);
- if (priv->netdev.d_len > 0)
- {
- /* Update the Ethernet header with the correct MAC address */
- #ifdef CONFIG_NET_IPv4
- if (IFF_IS_IPv4(priv->netdev.d_flags))
- {
- arp_out(&priv->netdev);
- }
- else
- #endif
- #ifdef CONFIG_NET_IPv6
- {
- neighbor_out(&priv->netdev);
- }
- #endif
- /* And send the packet */
- rndis_transmit(priv);
- }
- }
- else
- #endif
- #ifdef CONFIG_NET_ARP
- if (hdr->type == htons(ETHTYPE_ARP))
- {
- NETDEV_RXARP(&priv->netdev);
- arp_arpin(&priv->netdev);
- if (priv->netdev.d_len > 0)
- {
- rndis_transmit(priv);
- }
- }
- else
- #endif
- {
- uerr("ERROR: Unsupported packet type dropped (%02x)\n", htons(hdr->type));
- NETDEV_RXDROPPED(&priv->netdev);
- priv->netdev.d_len = 0;
- }
- priv->current_rx_datagram_size = 0;
- rndis_unblock_rx(priv);
- if (priv->net_req != NULL)
- {
- rndis_freenetreq(priv);
- }
- net_unlock();
- }
- /****************************************************************************
- * Name: rndis_txpoll
- *
- * Description:
- * Sends the packet that is stored in the network packet buffer. Called
- * from work queue by e.g. txavail and txpoll callbacks.
- *
- * Input Parameters:
- * dev: pointer to network driver structure
- *
- * Assumptions:
- * Caller holds the network lock
- *
- ****************************************************************************/
- static int rndis_txpoll(FAR struct net_driver_s *dev)
- {
- FAR struct rndis_dev_s *priv = (FAR struct rndis_dev_s *)dev->d_private;
- int ret = OK;
- if (!priv->connected)
- {
- return -EBUSY;
- }
- /* If the polling resulted in data that should be sent out on the network,
- * the field d_len is set to a value > 0.
- */
- ninfo("Poll result: d_len=%d\n", priv->netdev.d_len);
- if (priv->netdev.d_len > 0)
- {
- /* Look up the destination MAC address and add it to the Ethernet
- * header.
- */
- #ifdef CONFIG_NET_IPv4
- #ifdef CONFIG_NET_IPv6
- if (IFF_IS_IPv4(priv->netdev.d_flags))
- #endif
- {
- arp_out(&priv->netdev);
- }
- #endif /* CONFIG_NET_IPv4 */
- #ifdef CONFIG_NET_IPv6
- #ifdef CONFIG_NET_IPv4
- else
- #endif
- {
- neighbor_out(&priv->netdev);
- }
- #endif /* CONFIG_NET_IPv6 */
- if (!devif_loopback(&priv->netdev))
- {
- ret = rndis_transmit(priv);
- }
- }
- /* If zero is returned, the polling will continue until all connections have
- * been examined.
- */
- return ret;
- }
- /****************************************************************************
- * Name: rndis_transmit
- *
- * Description:
- * Start hardware transmission.
- *
- ****************************************************************************/
- static int rndis_transmit(FAR struct rndis_dev_s *priv)
- {
- int ret = OK;
- /* Queue the packet */
- rndis_fillrequest(priv, priv->net_req->req);
- rndis_sendnetreq(priv);
- if (!rndis_allocnetreq(priv))
- {
- ret = -EBUSY;
- }
- return ret;
- }
- /****************************************************************************
- * Name: rndis_pollworker
- *
- * Description:
- * Worker function called by txpoll worker.
- *
- ****************************************************************************/
- static void rndis_pollworker(FAR void *arg)
- {
- FAR struct rndis_dev_s *priv = (struct rndis_dev_s *)arg;
- DEBUGASSERT(priv != NULL);
- net_lock();
- if (rndis_allocnetreq(priv))
- {
- devif_timer(&priv->netdev, rndis_txpoll);
- if (priv->net_req != NULL)
- {
- rndis_freenetreq(priv);
- }
- }
- net_unlock();
- }
- /****************************************************************************
- * Name: rndis_polltimer
- *
- * Description:
- * Network poll watchdog timer callback
- *
- ****************************************************************************/
- static void rndis_polltimer(int argc, uint32_t arg, ...)
- {
- FAR struct rndis_dev_s *priv = (FAR struct rndis_dev_s *)arg;
- int ret;
- if (work_available(&priv->pollwork))
- {
- ret = work_queue(ETHWORK, &priv->pollwork, rndis_pollworker,
- (FAR void *)priv, 0);
- DEBUGASSERT(ret == OK);
- UNUSED(ret);
- }
- /* Setup the watchdog poll timer again */
- (void)wd_start(priv->txpoll, RNDIS_WDDELAY, rndis_polltimer, 1,
- (wdparm_t)arg);
- }
- /****************************************************************************
- * Name: rndis_ifup
- *
- * Description:
- * Network ifup callback
- *
- ****************************************************************************/
- static int rndis_ifup(FAR struct net_driver_s *dev)
- {
- FAR struct rndis_dev_s *priv = (FAR struct rndis_dev_s *)dev->d_private;
- (void)wd_start(priv->txpoll, RNDIS_WDDELAY, rndis_polltimer,
- 1, (wdparm_t)priv);
- return OK;
- }
- /****************************************************************************
- * Name: rndis_ifdown
- *
- * Description:
- * Network ifdown callback
- *
- ****************************************************************************/
- static int rndis_ifdown(FAR struct net_driver_s *dev)
- {
- FAR struct rndis_dev_s *priv = (FAR struct rndis_dev_s *)dev->d_private;
- wd_cancel(priv->txpoll);
- return OK;
- }
- /****************************************************************************
- * Name: rndis_txavail_work
- *
- * Description:
- * txavail worker function
- *
- ****************************************************************************/
- static void rndis_txavail_work(FAR void *arg)
- {
- FAR struct rndis_dev_s *priv = (FAR struct rndis_dev_s *)arg;
- net_lock();
- if (rndis_allocnetreq(priv))
- {
- devif_poll(&priv->netdev, rndis_txpoll);
- if (priv->net_req != NULL)
- {
- rndis_freenetreq(priv);
- }
- }
- net_unlock();
- }
- /****************************************************************************
- * Name: rndis_txavail
- *
- * Description:
- * Network txavail callback that's called when there are buffers available
- * for sending data. May be called from an interrupt, so we must queue a
- * worker to do the actual processing.
- *
- ****************************************************************************/
- static int rndis_txavail(FAR struct net_driver_s *dev)
- {
- FAR struct rndis_dev_s *priv = (FAR struct rndis_dev_s *)dev->d_private;
- if (work_available(&priv->pollwork))
- {
- work_queue(ETHWORK, &priv->pollwork, rndis_txavail_work, priv, 0);
- }
- return OK;
- }
- /************************************************************************************
- * Name: rndis_recvpacket
- *
- * Description:
- * Handles a USB packet arriving on the data bulk out endpoint.
- *
- * Assumptions:
- * Called from the USB interrupt handler with interrupts disabled.
- *
- ************************************************************************************/
- static inline int rndis_recvpacket(FAR struct rndis_dev_s *priv,
- FAR uint8_t *reqbuf, uint16_t reqlen)
- {
- if (!rndis_allocrxreq(priv))
- {
- return -ENOMEM;
- }
- if (!priv->connected)
- {
- return -EBUSY;
- }
- if (!priv->current_rx_datagram_size)
- {
- if (reqlen < 16)
- {
- /* Packet too small to contain a message header */
- }
- else
- {
- /* The packet contains a RNDIS packet message header */
- FAR struct rndis_packet_msg *msg = (FAR struct rndis_packet_msg *)reqbuf;
- if (msg->msgtype == RNDIS_PACKET_MSG)
- {
- priv->current_rx_received = reqlen;
- priv->current_rx_datagram_size = msg->datalen;
- priv->current_rx_msglen = msg->msglen;
- /* According to RNDIS-over-USB send, if the message length is a
- * multiple of endpoint max packet size, the host must send an
- * additional single-byte zero packet. Take that in account here.
- */
- if ((priv->current_rx_msglen % priv->epbulkout->maxpacket) == 0)
- {
- priv->current_rx_msglen += 1;
- }
- /* Data offset is defined as an offset from the beginning of the
- * offset field itself
- */
- priv->current_rx_datagram_offset = msg->dataoffset + 8;
- if (priv->current_rx_datagram_offset < reqlen)
- {
- memcpy(&priv->rx_req->req->buf[RNDIS_PACKET_HDR_SIZE],
- &reqbuf[priv->current_rx_datagram_offset],
- reqlen - priv->current_rx_datagram_offset);
- }
- }
- else
- {
- uerr("Unknown RNDIS message type %u\n", msg->msgtype);
- }
- }
- }
- else
- {
- if (priv->current_rx_received >= priv->current_rx_datagram_offset &&
- priv->current_rx_received <= priv->current_rx_datagram_size +
- priv->current_rx_datagram_offset)
- {
- size_t index = priv->current_rx_received - priv->current_rx_datagram_offset;
- size_t copysize = min(reqlen, priv->current_rx_datagram_size - index);
- /* Check if the received packet exceeds request buffer */
- if ((index + copysize) <= CONFIG_NET_ETH_PKTSIZE)
- {
- memcpy(&priv->rx_req->req->buf[RNDIS_PACKET_HDR_SIZE + index], reqbuf,
- copysize);
- }
- else
- {
- uerr("The packet exceeds request buffer (reqlen=%d) \n", reqlen);
- }
- }
- priv->current_rx_received += reqlen;
- }
- if (priv->current_rx_received >= priv->current_rx_msglen)
- {
- /* Check for a usable packet length (4 added for the CRC) */
- if (priv->current_rx_datagram_size > (CONFIG_NET_ETH_PKTSIZE + 4) ||
- priv->current_rx_datagram_size <= (ETH_HDRLEN + 4))
- {
- uerr("ERROR: Bad packet size dropped (%d)\n",
- priv->current_rx_datagram_size);
- NETDEV_RXERRORS(&priv->netdev);
- priv->current_rx_datagram_size = 0;
- }
- else
- {
- int ret;
- DEBUGASSERT(work_available(&priv->rxwork));
- ret = work_queue(ETHWORK, &priv->rxwork, rndis_rxdispatch,
- priv, 0);
- DEBUGASSERT(ret == 0);
- UNUSED(ret);
- rndis_block_rx(priv);
- priv->rndis_host_tx_count++;
- return -EBUSY;
- }
- }
- return OK;
- }
- /****************************************************************************
- * Name: rndis_prepare_response
- *
- * Description:
- * Passes the RX packet buffer to the network
- *
- * Input Parameters:
- * priv: pointer to RNDIS device driver structure
- *
- * Assumptions:
- * Called from critical section
- *
- ****************************************************************************/
- static bool rndis_prepare_response(FAR struct rndis_dev_s *priv, size_t size,
- FAR struct rndis_command_header *request_hdr)
- {
- FAR struct rndis_response_header *hdr =
- (FAR struct rndis_response_header *)priv->ctrlreq->buf;
- hdr->msgtype = request_hdr->msgtype | RNDIS_MSG_COMPLETE;
- hdr->msglen = size;
- hdr->reqid = request_hdr->reqid;
- hdr->status = RNDIS_STATUS_SUCCESS;
- priv->ctrlreq_has_encap_response = true;
- return true;
- }
- /****************************************************************************
- * Name: rndis_send_encapsulated_response
- *
- * Description:
- * Give a notification to the host that there is an encapsulated response
- * available.
- *
- * Input Parameters:
- * priv: pointer to RNDIS device driver structure
- *
- * Assumptions:
- * Called from critical section
- *
- ****************************************************************************/
- static int rndis_send_encapsulated_response(FAR struct rndis_dev_s *priv)
- {
- FAR struct rndis_notification *notif =
- (FAR struct rndis_notification *)priv->epintin_req->buf;
- notif->notification = RNDIS_NOTIFICATION_RESPONSE_AVAILABLE;
- notif->reserved = 0;
- priv->epintin_req->len = sizeof(struct rndis_notification);
- EP_CANCEL(priv->epintin, priv->epintin_req);
- EP_SUBMIT(priv->epintin, priv->epintin_req);
- return OK;
- }
- /****************************************************************************
- * Name: rndis_handle_control_message
- *
- * Description:
- * Handle a RNDIS control message.
- *
- * Input Parameters:
- * priv: pointer to RNDIS device driver structure
- *
- * Assumptions:
- * Called from critical section
- *
- ****************************************************************************/
- static int rndis_handle_control_message(FAR struct rndis_dev_s *priv,
- FAR uint8_t *dataout, uint16_t outlen)
- {
- FAR struct rndis_command_header *cmd_hdr =
- (FAR struct rndis_command_header *)dataout;
- switch (cmd_hdr->msgtype)
- {
- case RNDIS_INITIALIZE_MSG:
- {
- FAR struct rndis_initialize_cmplt *resp;
- rndis_prepare_response(priv, sizeof(struct rndis_initialize_cmplt), cmd_hdr);
- resp = (FAR struct rndis_initialize_cmplt *)priv->ctrlreq->buf;
- resp->major = RNDIS_MAJOR_VERSION;
- resp->minor = RNDIS_MINOR_VERSION;
- resp->devflags = RNDIS_DEVICEFLAGS;
- resp->medium = RNDIS_MEDIUM_802_3;
- resp->pktperxfer = 1;
- resp->xfrsize = (4 + 44 + 22) + RNDIS_BUFFER_SIZE;
- resp->pktalign = 2;
- rndis_send_encapsulated_response(priv);
- }
- break;
- case RNDIS_HALT_MSG:
- {
- priv->connected = false;
- }
- break;
- case RNDIS_QUERY_MSG:
- {
- int i;
- size_t max_reply_size = sizeof(struct rndis_query_cmplt) +
- sizeof(g_rndis_supported_oids);
- rndis_prepare_response(priv, max_reply_size, cmd_hdr);
- FAR struct rndis_query_msg *req =
- (FAR struct rndis_query_msg *)dataout;
- FAR struct rndis_query_cmplt *resp =
- (FAR struct rndis_query_cmplt *)priv->ctrlreq->buf;
- resp->hdr.msglen = sizeof(struct rndis_query_cmplt);
- resp->bufoffset = 0;
- resp->buflen = 0;
- resp->hdr.status = RNDIS_STATUS_NOT_SUPPORTED;
- for (i = 0;
- i < sizeof(g_rndis_oid_values)/sizeof(g_rndis_oid_values[0]);
- i++)
- {
- bool match = (g_rndis_oid_values[i].objid == req->objid);
- if (!match && g_rndis_oid_values[i].objid == 0)
- {
- int j;
- /* Check whether to apply the fallback entry */
- for (j = 0; j < sizeof(g_rndis_supported_oids)/sizeof(uint32_t); j++)
- {
- if (g_rndis_supported_oids[j] == req->objid)
- {
- match = true;
- break;
- }
- }
- }
- if (match)
- {
- resp->hdr.status = RNDIS_STATUS_SUCCESS;
- resp->bufoffset = 16;
- resp->buflen = g_rndis_oid_values[i].length;
- if (req->objid == RNDIS_OID_GEN_CURRENT_PACKET_FILTER)
- {
- resp->buffer[0] = priv->rndis_packet_filter;
- }
- else if (req->objid == RNDIS_OID_GEN_XMIT_OK)
- {
- resp->buffer[0] = priv->rndis_host_tx_count;
- }
- else if (req->objid == RNDIS_OID_GEN_RCV_OK)
- {
- resp->buffer[0] = priv->rndis_host_rx_count;
- }
- else if (req->objid == RNDIS_OID_802_3_CURRENT_ADDRESS ||
- req->objid == RNDIS_OID_802_3_PERMANENT_ADDRESS)
- {
- memcpy(resp->buffer, priv->host_mac_address, 6);
- }
- else if (g_rndis_oid_values[i].data)
- {
- memcpy(resp->buffer, g_rndis_oid_values[i].data,
- resp->buflen);
- }
- else
- {
- memcpy(resp->buffer, &g_rndis_oid_values[i].value,
- resp->buflen);
- }
- break;
- }
- }
- uinfo("RNDIS Query RID=%08x OID=%08x LEN=%d DAT=%08x",
- (unsigned)req->hdr.reqid, (unsigned)req->objid,
- (int)resp->buflen, (unsigned)resp->buffer[0]);
- resp->hdr.msglen += resp->buflen;
- rndis_send_encapsulated_response(priv);
- }
- break;
- case RNDIS_SET_MSG:
- {
- FAR struct rndis_set_msg *req;
- FAR struct rndis_response_header *resp;
- rndis_prepare_response(priv, sizeof(struct rndis_response_header),
- cmd_hdr);
- req = (FAR struct rndis_set_msg *)dataout;
- resp = (FAR struct rndis_response_header *)priv->ctrlreq->buf;
- uinfo("RNDIS SET RID=%08x OID=%08x LEN=%d DAT=%08x",
- (unsigned)req->hdr.reqid, (unsigned)req->objid,
- (int)req->buflen, (unsigned)req->buffer[0]);
- if (req->objid == RNDIS_OID_GEN_CURRENT_PACKET_FILTER)
- {
- priv->rndis_packet_filter = req->buffer[0];
- if (req->buffer[0] == 0)
- {
- priv->connected = false;
- }
- else
- {
- uinfo("RNDIS is now connected");
- priv->connected = true;
- }
- }
- else if (req->objid == RNDIS_OID_802_3_MULTICAST_LIST)
- {
- uinfo("RNDIS multicast list ignored");
- }
- else
- {
- uinfo("RNDIS unsupported set %08x", (unsigned)req->objid);
- resp->status = RNDIS_STATUS_NOT_SUPPORTED;
- }
- rndis_send_encapsulated_response(priv);
- }
- break;
- case RNDIS_RESET_MSG:
- {
- FAR struct rndis_reset_cmplt *resp;
- rndis_prepare_response(priv, sizeof(struct rndis_reset_cmplt),
- cmd_hdr);
- resp = (FAR struct rndis_reset_cmplt *)priv->ctrlreq->buf;
- resp->addreset = 0;
- priv->connected = false;
- rndis_send_encapsulated_response(priv);
- }
- break;
- case RNDIS_KEEPALIVE_MSG:
- {
- rndis_prepare_response(priv, sizeof(struct rndis_response_header),
- cmd_hdr);
- rndis_send_encapsulated_response(priv);
- }
- break;
- default:
- uwarn("Unsupported RNDIS control message: %u\n", cmd_hdr->msgtype);
- }
- return OK;
- }
- /****************************************************************************
- * Name: rndis_rdcomplete
- *
- * Description:
- * Handle completion of read request on the bulk OUT endpoint.
- *
- ****************************************************************************/
- static void rndis_rdcomplete(FAR struct usbdev_ep_s *ep,
- FAR struct usbdev_req_s *req)
- {
- FAR struct rndis_dev_s *priv;
- irqstate_t flags;
- int ret;
- /* Sanity check */
- #ifdef CONFIG_DEBUG_FEATURES
- if (!ep || !ep->priv || !req)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
- return;
- }
- #endif
- /* Extract references to private data */
- priv = (FAR struct rndis_dev_s *)ep->priv;
- /* Process the received data unless this is some unusual condition */
- ret = OK;
- flags = enter_critical_section();
- priv->rdreq_submitted = false;
- switch (req->result)
- {
- case 0: /* Normal completion */
- ret = rndis_recvpacket(priv, req->buf, req->xfrd);
- DEBUGASSERT(ret != -ENOMEM);
- break;
- case -ESHUTDOWN: /* Disconnection */
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDSHUTDOWN), 0);
- leave_critical_section(flags);
- return;
- default: /* Some other error occurred */
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDUNEXPECTED), (uint16_t)-req->result);
- break;
- };
- if (ret == OK)
- {
- rndis_submit_rdreq(priv);
- }
- leave_critical_section(flags);
- }
- /****************************************************************************
- * Name: rndis_wrcomplete
- *
- * Description:
- * Handle completion of write request. This function probably executes
- * in the context of an interrupt handler.
- *
- ****************************************************************************/
- static void rndis_wrcomplete(FAR struct usbdev_ep_s *ep,
- FAR struct usbdev_req_s *req)
- {
- FAR struct rndis_dev_s *priv;
- FAR struct rndis_req_s *reqcontainer;
- irqstate_t flags;
- /* Sanity check */
- #ifdef CONFIG_DEBUG_FEATURES
- if (!ep || !ep->priv || !req || !req->priv)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
- return;
- }
- #endif
- /* Extract references to our private data */
- priv = (FAR struct rndis_dev_s *)ep->priv;
- reqcontainer = (FAR struct rndis_req_s *)req->priv;
- /* Return the write request to the free list */
- flags = enter_critical_section();
- rndis_freewrreq(priv, reqcontainer);
- if (rndis_hasfreereqs(priv))
- {
- rndis_txavail(&priv->netdev);
- }
- switch (req->result)
- {
- case OK: /* Normal completion */
- priv->rndis_host_rx_count++;
- break;
- case -ESHUTDOWN: /* Disconnection */
- break;
- default: /* Some other error occurred */
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_WRUNEXPECTED),
- (uint16_t)-req->result);
- break;
- }
- leave_critical_section(flags);
- }
- /****************************************************************************
- * Name: usbclass_ep0incomplete
- *
- * Description:
- * Handle completion of EP0 control operations
- *
- ****************************************************************************/
- static void usbclass_ep0incomplete(FAR struct usbdev_ep_s *ep,
- FAR struct usbdev_req_s *req)
- {
- if (req->result || req->xfrd != req->len)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_REQRESULT),
- (uint16_t)-req->result);
- }
- else if (req->len > 0)
- {
- struct rndis_dev_s *priv = (FAR struct rndis_dev_s *)ep->priv;
- priv->ctrlreq_has_encap_response = false;
- }
- }
- /****************************************************************************
- * Name: usbclass_ep0incomplete
- *
- * Description:
- * Handle completion of interrupt IN endpoint operations
- *
- ****************************************************************************/
- static void usbclass_epintin_complete(FAR struct usbdev_ep_s *ep,
- FAR struct usbdev_req_s *req)
- {
- if (req->result || req->xfrd != req->len)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_REQRESULT),
- (uint16_t)-req->result);
- }
- }
- /****************************************************************************
- * Name: usbclass_freereq
- *
- * Description:
- * Free a request instance along with its buffer
- *
- ****************************************************************************/
- static void usbclass_freereq(FAR struct usbdev_ep_s *ep,
- FAR struct usbdev_req_s *req)
- {
- if (ep != NULL && req != NULL)
- {
- if (req->buf != NULL)
- {
- EP_FREEBUFFER(ep, req->buf);
- }
- EP_FREEREQ(ep, req);
- }
- }
- /****************************************************************************
- * Name: usbclass_allocreq
- *
- * Description:
- * Allocate a request instance along with its buffer
- *
- ****************************************************************************/
- static FAR struct usbdev_req_s *usbclass_allocreq(FAR struct usbdev_ep_s *ep,
- uint16_t len)
- {
- FAR struct usbdev_req_s *req;
- req = EP_ALLOCREQ(ep);
- if (req != NULL)
- {
- req->len = len;
- req->buf = EP_ALLOCBUFFER(ep, len);
- if (req->buf == NULL)
- {
- EP_FREEREQ(ep, req);
- req = NULL;
- }
- }
- return req;
- }
- /****************************************************************************
- * Name: usbclass_mkstrdesc
- *
- * Description:
- * Construct a string descriptor
- *
- ****************************************************************************/
- static int usbclass_mkstrdesc(uint8_t id, FAR struct usb_strdesc_s *strdesc)
- {
- FAR const char *str;
- int len;
- int ndata;
- int i;
- switch (id)
- {
- #ifndef CONFIG_RNDIS_COMPOSITE
- case 0:
- {
- /* Descriptor 0 is the language id */
- strdesc->len = 4;
- strdesc->type = USB_DESC_TYPE_STRING;
- strdesc->data[0] = LSBYTE(RNDIS_STR_LANGUAGE);
- strdesc->data[1] = MSBYTE(RNDIS_STR_LANGUAGE);
- return 4;
- }
- case RNDIS_MANUFACTURERSTRID:
- str = CONFIG_RNDIS_VENDORSTR;
- break;
- case RNDIS_PRODUCTSTRID:
- str = CONFIG_RNDIS_PRODUCTSTR;
- break;
- case RNDIS_SERIALSTRID:
- str = CONFIG_RNDIS_SERIALSTR;
- break;
- #endif
- default:
- return -EINVAL;
- }
- /* The string is utf16-le. The poor man's utf-8 to utf16-le
- * conversion below will only handle 7-bit en-us ascii
- */
- len = strlen(str);
- if (len > (RNDIS_MAXSTRLEN / 2))
- {
- len = (RNDIS_MAXSTRLEN / 2);
- }
- for (i = 0, ndata = 0; i < len; i++, ndata += 2)
- {
- strdesc->data[ndata] = str[i];
- strdesc->data[ndata+1] = 0;
- }
- strdesc->len = ndata+2;
- strdesc->type = USB_DESC_TYPE_STRING;
- return strdesc->len;
- }
- /****************************************************************************
- * Name: usbclass_copy_epdesc
- *
- * Description:
- * Copies the requested Endpoint Description into the buffer given.
- * Returns the number of Bytes filled in ( sizeof(struct usb_epdesc_s) ).
- *
- ****************************************************************************/
- static int usbclass_copy_epdesc(int epid, FAR struct usb_epdesc_s *epdesc,
- FAR struct usbdev_devinfo_s *devinfo,
- bool hispeed)
- {
- #ifndef CONFIG_USBDEV_DUALSPEED
- UNUSED(hispeed);
- #endif
- switch (epid)
- {
- case RNDIS_EP_INTIN_IDX: /* Interrupt IN endpoint */
- {
- epdesc->len = USB_SIZEOF_EPDESC; /* Descriptor length */
- epdesc->type = USB_DESC_TYPE_ENDPOINT; /* Descriptor type */
- epdesc->addr = RNDIS_MKEPINTIN(devinfo); /* Endpoint address */
- epdesc->attr = RNDIS_EPINTIN_ATTR; /* Endpoint attributes */
- #ifdef CONFIG_USBDEV_DUALSPEED
- if (hispeed)
- {
- /* Maximum packet size (high speed) */
- epdesc->mxpacketsize[0] = LSBYTE(CONFIG_RNDIS_EPINTIN_HSSIZE);
- epdesc->mxpacketsize[1] = MSBYTE(CONFIG_RNDIS_EPINTIN_HSSIZE);
- }
- else
- #endif
- {
- /* Maximum packet size (full speed) */
- epdesc->mxpacketsize[0] = LSBYTE(CONFIG_RNDIS_EPINTIN_FSSIZE);
- epdesc->mxpacketsize[1] = MSBYTE(CONFIG_RNDIS_EPINTIN_FSSIZE);
- }
- epdesc->interval = 10; /* Interval */
- }
- break;
- case RNDIS_EP_BULKOUT_IDX: /* Bulk OUT endpoint */
- {
- epdesc->len = USB_SIZEOF_EPDESC; /* Descriptor length */
- epdesc->type = USB_DESC_TYPE_ENDPOINT; /* Descriptor type */
- epdesc->addr = RNDIS_MKEPBULKOUT(devinfo); /* Endpoint address */
- epdesc->attr = RNDIS_EPOUTBULK_ATTR; /* Endpoint attributes */
- #ifdef CONFIG_USBDEV_DUALSPEED
- if (hispeed)
- {
- /* Maximum packet size (high speed) */
- epdesc->mxpacketsize[0] = LSBYTE(CONFIG_RNDIS_EPBULKOUT_HSSIZE);
- epdesc->mxpacketsize[1] = MSBYTE(CONFIG_RNDIS_EPBULKOUT_HSSIZE);
- }
- else
- #endif
- {
- /* Maximum packet size (full speed) */
- epdesc->mxpacketsize[0] = LSBYTE(CONFIG_RNDIS_EPBULKOUT_FSSIZE);
- epdesc->mxpacketsize[1] = MSBYTE(CONFIG_RNDIS_EPBULKOUT_FSSIZE);
- }
- epdesc->interval = 0; /* Interval */
- }
- break;
- case RNDIS_EP_BULKIN_IDX: /* Bulk IN endpoint */
- {
- epdesc->len = USB_SIZEOF_EPDESC; /* Descriptor length */
- epdesc->type = USB_DESC_TYPE_ENDPOINT; /* Descriptor type */
- epdesc->addr = RNDIS_MKEPBULKIN(devinfo); /* Endpoint address */
- epdesc->attr = RNDIS_EPINBULK_ATTR; /* Endpoint attributes */
- #ifdef CONFIG_USBDEV_DUALSPEED
- if (hispeed)
- {
- /* Maximum packet size (high speed) */
- epdesc->mxpacketsize[0] = LSBYTE(CONFIG_RNDIS_EPBULKIN_HSSIZE);
- epdesc->mxpacketsize[1] = MSBYTE(CONFIG_RNDIS_EPBULKIN_HSSIZE);
- }
- else
- #endif
- {
- /* Maximum packet size (full speed) */
- epdesc->mxpacketsize[0] = LSBYTE(CONFIG_RNDIS_EPBULKIN_FSSIZE);
- epdesc->mxpacketsize[1] = MSBYTE(CONFIG_RNDIS_EPBULKIN_FSSIZE);
- }
- epdesc->interval = 0; /* Interval */
- }
- break;
- default:
- return 0;
- }
- return sizeof(struct usb_epdesc_s);
- }
- /****************************************************************************
- * Name: usbclass_mkcfgdesc
- *
- * Description:
- * Construct the configuration descriptor
- *
- ****************************************************************************/
- static int16_t usbclass_mkcfgdesc(FAR uint8_t *buf,
- FAR struct usbdev_devinfo_s *devinfo)
- {
- FAR struct rndis_cfgdesc_s *dest = (FAR struct rndis_cfgdesc_s *)buf;
- uint16_t totallen;
- /* This is the total length of the configuration (not necessarily the
- * size that we will be sending now).
- */
- totallen = sizeof(g_rndis_cfgdesc);
- memcpy(dest, &g_rndis_cfgdesc, totallen);
- #ifndef CONFIG_RNDIS_COMPOSITE
- /* For a stand-alone device, just fill in the total length */
- dest->cfgdesc.totallen[0] = LSBYTE(totallen);
- dest->cfgdesc.totallen[1] = MSBYTE(totallen);
- #else
- /* For composite device, apply possible offset to the interface numbers */
- dest->assoc_desc.firstif += devinfo->ifnobase;
- dest->comm_ifdesc.ifno += devinfo->ifnobase;
- dest->epintindesc.addr = USB_EPIN(devinfo->epno[RNDIS_EP_INTIN_IDX]);
- dest->data_ifdesc.ifno += devinfo->ifnobase;
- dest->epbulkindesc.addr = USB_EPIN(devinfo->epno[RNDIS_EP_BULKIN_IDX]);
- dest->epbulkoutdesc.addr = USB_EPOUT(devinfo->epno[RNDIS_EP_BULKOUT_IDX]);
- #endif
- return totallen;
- }
- /****************************************************************************
- * Name: usbclass_bind
- *
- * Description:
- * Invoked when the driver is bound to a USB device driver
- *
- ****************************************************************************/
- static int usbclass_bind(FAR struct usbdevclass_driver_s *driver,
- FAR struct usbdev_s *dev)
- {
- FAR struct rndis_dev_s *priv = ((FAR struct rndis_driver_s *)driver)->dev;
- FAR struct rndis_req_s *reqcontainer;
- irqstate_t flags;
- uint16_t reqlen;
- int ret;
- int i;
- usbtrace(TRACE_CLASSBIND, 0);
- /* Bind the structures */
- priv->usbdev = dev;
- /* Save the reference to our private data structure in EP0 so that it
- * can be recovered in ep0 completion events (Unless we are part of
- * a composite device and, in that case, the composite device owns
- * EP0).
- */
- dev->ep0->priv = priv;
- /* Preallocate control request */
- priv->ctrlreq = usbclass_allocreq(dev->ep0, RNDIS_CTRLREQ_LEN);
- if (priv->ctrlreq == NULL)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_ALLOCCTRLREQ), 0);
- ret = -ENOMEM;
- goto errout;
- }
- priv->ctrlreq->callback = usbclass_ep0incomplete;
- /* Pre-allocate all endpoints... the endpoints will not be functional
- * until the SET CONFIGURATION request is processed in usbclass_setconfig.
- * This is done here because there may be calls to kmm_malloc and the SET
- * CONFIGURATION processing probably occurs within interrupt handling
- * logic where kmm_malloc calls will fail.
- */
- /* Pre-allocate the IN interrupt endpoint */
- priv->epintin = DEV_ALLOCEP(dev, USB_EPIN(priv->devinfo.epno[RNDIS_EP_INTIN_IDX]),
- true, USB_EP_ATTR_XFER_INT);
- if (!priv->epintin)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPINTINALLOCFAIL), 0);
- ret = -ENODEV;
- goto errout;
- }
- priv->epintin->priv = priv;
- priv->epintin_req = usbclass_allocreq(priv->epintin, sizeof(struct rndis_notification));
- if (priv->epintin_req == NULL)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDALLOCREQ), -ENOMEM);
- ret = -ENOMEM;
- goto errout;
- }
- priv->epintin_req->callback = usbclass_epintin_complete;
- /* Pre-allocate the IN bulk endpoint */
- priv->epbulkin = DEV_ALLOCEP(dev, USB_EPIN(priv->devinfo.epno[RNDIS_EP_BULKIN_IDX]),
- true, USB_EP_ATTR_XFER_BULK);
- if (!priv->epbulkin)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPBULKINALLOCFAIL), 0);
- ret = -ENODEV;
- goto errout;
- }
- priv->epbulkin->priv = priv;
- /* Pre-allocate the OUT bulk endpoint */
- priv->epbulkout = DEV_ALLOCEP(dev, USB_EPOUT(priv->devinfo.epno[RNDIS_EP_BULKOUT_IDX]),
- false, USB_EP_ATTR_XFER_BULK);
- if (!priv->epbulkout)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPBULKOUTALLOCFAIL), 0);
- ret = -ENODEV;
- goto errout;
- }
- priv->epbulkout->priv = priv;
- /* Pre-allocate read requests. The buffer size is one full packet. */
- reqlen = 64;
- if (CONFIG_RNDIS_BULKOUT_REQLEN > reqlen)
- {
- reqlen = CONFIG_RNDIS_BULKOUT_REQLEN;
- }
- priv->rdreq = usbclass_allocreq(priv->epbulkout, reqlen);
- if (priv->rdreq == NULL)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDALLOCREQ), -ENOMEM);
- ret = -ENOMEM;
- goto errout;
- }
- priv->rdreq->callback = rndis_rdcomplete;
- /* Pre-allocate write request containers and put in a free list.
- * The buffer size should be larger than a full packet. Otherwise,
- * we will send a bogus null packet at the end of each packet.
- *
- * Pick the larger of the max packet size and the configured request
- * size.
- */
- reqlen = 64;
- if (CONFIG_RNDIS_BULKIN_REQLEN > reqlen)
- {
- reqlen = CONFIG_RNDIS_BULKIN_REQLEN;
- }
- for (i = 0; i < CONFIG_RNDIS_NWRREQS; i++)
- {
- reqcontainer = &priv->wrreqs[i];
- reqcontainer->req = usbclass_allocreq(priv->epbulkin, reqlen);
- if (reqcontainer->req == NULL)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_WRALLOCREQ), -ENOMEM);
- ret = -ENOMEM;
- goto errout;
- }
- reqcontainer->req->priv = reqcontainer;
- reqcontainer->req->callback = rndis_wrcomplete;
- flags = enter_critical_section();
- sq_addlast((FAR sq_entry_t *)reqcontainer, &priv->reqlist);
- leave_critical_section(flags);
- }
- /* Report if we are selfpowered */
- #ifndef CONFIG_RNDIS_COMPOSITE
- #ifdef CONFIG_USBDEV_SELFPOWERED
- DEV_SETSELFPOWERED(dev);
- #endif
- /* And pull-up the data line for the soft connect function */
- DEV_CONNECT(dev);
- #endif
- return OK;
- errout:
- usbclass_unbind(driver, dev);
- return ret;
- }
- /****************************************************************************
- * Name: usbclass_unbind
- *
- * Description:
- * Invoked when the driver is unbound from a USB device driver
- *
- ****************************************************************************/
- static void usbclass_unbind(FAR struct usbdevclass_driver_s *driver,
- FAR struct usbdev_s *dev)
- {
- FAR struct rndis_dev_s *priv;
- FAR struct rndis_req_s *reqcontainer;
- irqstate_t flags;
- usbtrace(TRACE_CLASSUNBIND, 0);
- #ifdef CONFIG_DEBUG_FEATURES
- if (!driver || !dev || !dev->ep0)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
- return;
- }
- #endif
- /* Extract reference to private data */
- priv = ((FAR struct rndis_driver_s *)driver)->dev;
- #ifdef CONFIG_DEBUG_FEATURES
- if (!priv)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EP0NOTBOUND), 0);
- return;
- }
- #endif
- /* Make sure that we are not already unbound */
- if (priv != NULL)
- {
- /* Make sure that the endpoints have been unconfigured. If
- * we were terminated gracefully, then the configuration should
- * already have been reset. If not, then calling usbclass_resetconfig
- * should cause the endpoints to immediately terminate all
- * transfers and return the requests to us (with result == -ESHUTDOWN)
- */
- usbclass_resetconfig(priv);
- up_mdelay(50);
- /* Free the interrupt IN endpoint */
- if (priv->epintin)
- {
- DEV_FREEEP(dev, priv->epintin);
- priv->epintin = NULL;
- }
- /* Free the bulk IN endpoint */
- if (priv->epbulkin)
- {
- DEV_FREEEP(dev, priv->epbulkin);
- priv->epbulkin = NULL;
- }
- /* Free the pre-allocated control request */
- if (priv->ctrlreq != NULL)
- {
- usbclass_freereq(dev->ep0, priv->ctrlreq);
- priv->ctrlreq = NULL;
- }
- if (priv->epintin_req != NULL)
- {
- usbclass_freereq(priv->epintin, priv->epintin_req);
- priv->epintin_req = NULL;
- }
- /* Free pre-allocated read requests (which should all have
- * been returned to the free list at this time -- we don't check)
- */
- if (priv->rdreq)
- {
- usbclass_freereq(priv->epbulkout, priv->rdreq);
- }
- /* Free the bulk OUT endpoint */
- if (priv->epbulkout)
- {
- DEV_FREEEP(dev, priv->epbulkout);
- priv->epbulkout = NULL;
- }
- netdev_unregister(&priv->netdev);
- /* Free write requests that are not in use (which should be all
- * of them
- */
- flags = enter_critical_section();
- while (!sq_empty(&priv->reqlist))
- {
- reqcontainer = (struct rndis_req_s *)sq_remfirst(&priv->reqlist);
- if (reqcontainer->req != NULL)
- {
- usbclass_freereq(priv->epbulkin, reqcontainer->req);
- }
- }
- leave_critical_section(flags);
- }
- }
- /****************************************************************************
- * Name: usbclass_setup
- *
- * Description:
- * Invoked for ep0 control requests. This function probably executes
- * in the context of an interrupt handler.
- *
- ****************************************************************************/
- static int usbclass_setup(FAR struct usbdevclass_driver_s *driver,
- FAR struct usbdev_s *dev,
- FAR const struct usb_ctrlreq_s *ctrl,
- FAR uint8_t *dataout, size_t outlen)
- {
- FAR struct rndis_dev_s *priv;
- FAR struct usbdev_req_s *ctrlreq;
- uint16_t value;
- uint16_t len;
- int ret = -EOPNOTSUPP;
- #ifdef CONFIG_DEBUG_FEATURES
- if (!driver || !dev || !dev->ep0 || !ctrl)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
- return -EIO;
- }
- #endif
- /* Extract reference to private data */
- usbtrace(TRACE_CLASSSETUP, ctrl->req);
- priv = ((FAR struct rndis_driver_s *)driver)->dev;
- #ifdef CONFIG_DEBUG_FEATURES
- if (!priv || !priv->ctrlreq)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EP0NOTBOUND), 0);
- return -ENODEV;
- }
- #endif
- ctrlreq = priv->ctrlreq;
- /* Extract the little-endian 16-bit values to host order */
- value = GETUINT16(ctrl->value);
- len = GETUINT16(ctrl->len);
- uinfo("type=%02x req=%02x value=%04x len=%04x\n",
- ctrl->type, ctrl->req, value, len);
- switch (ctrl->type & USB_REQ_TYPE_MASK)
- {
- /***********************************************************************
- * Standard Requests
- ***********************************************************************/
- case USB_REQ_TYPE_STANDARD:
- {
- switch (ctrl->req)
- {
- case USB_REQ_GETDESCRIPTOR:
- {
- /* The value field specifies the descriptor type in the MS byte and the
- * descriptor index in the LS byte (order is little endian)
- */
- switch (ctrl->value[1])
- {
- #ifndef CONFIG_RNDIS_COMPOSITE
- case USB_DESC_TYPE_DEVICE:
- {
- ret = USB_SIZEOF_DEVDESC;
- memcpy(ctrlreq->buf, &g_devdesc, ret);
- }
- break;
- #endif
- case USB_DESC_TYPE_CONFIG:
- {
- ret = usbclass_mkcfgdesc(ctrlreq->buf, &priv->devinfo);
- }
- break;
- case USB_DESC_TYPE_STRING:
- {
- /* index == language code. */
- ret = usbclass_mkstrdesc(ctrl->value[0],
- (FAR struct usb_strdesc_s *)ctrlreq->buf);
- }
- break;
- default:
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_GETUNKNOWNDESC), value);
- }
- break;
- }
- }
- break;
- case USB_REQ_SETCONFIGURATION:
- {
- if (ctrl->type == 0)
- {
- ret = usbclass_setconfig(priv, value);
- }
- }
- break;
- case USB_REQ_GETCONFIGURATION:
- {
- if (ctrl->type == USB_DIR_IN)
- {
- *(FAR uint8_t *)ctrlreq->buf = priv->config;
- ret = 1;
- }
- }
- break;
- default:
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDSTDREQ), ctrl->req);
- break;
- }
- }
- break;
- /* Class requests */
- case USB_REQ_TYPE_CLASS:
- {
- if ((ctrl->type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_INTERFACE)
- {
- if (ctrl->req == RNDIS_SEND_ENCAPSULATED_COMMAND)
- {
- ret = rndis_handle_control_message(priv, dataout, outlen);
- }
- else if (ctrl->req == RNDIS_GET_ENCAPSULATED_RESPONSE)
- {
- if (!priv->ctrlreq_has_encap_response)
- {
- ret = 1;
- ctrlreq->buf[0] = 0;
- }
- else
- {
- /* There is data prepared in the ctrlreq buffer.
- * Just assign the length.
- */
- FAR struct rndis_response_header *hdr =
- (struct rndis_response_header *)ctrlreq->buf;
- ret = hdr->msglen;
- }
- }
- }
- }
- break;
- default:
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_UNSUPPORTEDTYPE), ctrl->type);
- break;
- }
- /* Respond to the setup command if data was returned. On an error return
- * value (ret < 0), the USB driver will stall.
- */
- if (ret >= 0)
- {
- ctrlreq->len = min(len, ret);
- ctrlreq->flags = USBDEV_REQFLAGS_NULLPKT;
- ret = EP_SUBMIT(dev->ep0, ctrlreq);
- if (ret < 0)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPRESPQ), (uint16_t)-ret);
- ctrlreq->result = OK;
- usbclass_ep0incomplete(dev->ep0, ctrlreq);
- }
- }
- return ret;
- }
- /****************************************************************************
- * Name: usbclass_disconnect
- *
- * Description:
- * Invoked after all transfers have been stopped, when the host is
- * disconnected. This function is probably called from the context of an
- * interrupt handler.
- *
- ****************************************************************************/
- static void usbclass_disconnect(FAR struct usbdevclass_driver_s *driver,
- FAR struct usbdev_s *dev)
- {
- FAR struct rndis_dev_s *priv;
- irqstate_t flags;
- usbtrace(TRACE_CLASSDISCONNECT, 0);
- #ifdef CONFIG_DEBUG_FEATURES
- if (!driver || !dev || !dev->ep0)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
- return;
- }
- #endif
- /* Extract reference to private data */
- priv = ((FAR struct rndis_driver_s *)driver)->dev;
- #ifdef CONFIG_DEBUG_FEATURES
- if (!priv)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EP0NOTBOUND), 0);
- return;
- }
- #endif
- /* Inform the "upper half" network driver that we have lost the USB
- * connection.
- */
- priv->netdev.d_ifdown(&priv->netdev);
- flags = enter_critical_section();
- /* Reset the configuration */
- usbclass_resetconfig(priv);
- leave_critical_section(flags);
- /* Perform the soft connect function so that we will we can be
- * re-enumerated.
- */
- DEV_CONNECT(dev);
- }
- /****************************************************************************
- * Name: usbclass_resetconfig
- *
- * Description:
- * Mark the device as not configured and disable all endpoints.
- *
- ****************************************************************************/
- static void usbclass_resetconfig(FAR struct rndis_dev_s *priv)
- {
- /* Are we configured? */
- if (priv->config != RNDIS_CONFIGIDNONE)
- {
- /* Yes.. but not anymore */
- priv->config = RNDIS_CONFIGIDNONE;
- priv->netdev.d_ifdown(&priv->netdev);
- /* Disable endpoints. This should force completion of all pending
- * transfers.
- */
- EP_DISABLE(priv->epintin);
- EP_DISABLE(priv->epbulkin);
- EP_DISABLE(priv->epbulkout);
- }
- }
- /****************************************************************************
- * Name: usbclass_setconfig
- *
- * Description:
- * Set the device configuration by allocating and configuring endpoints and
- * by allocating and queue read and write requests.
- *
- ****************************************************************************/
- static int usbclass_setconfig(FAR struct rndis_dev_s *priv, uint8_t config)
- {
- struct usb_epdesc_s epdesc;
- bool hispeed = false;
- int ret = 0;
- #ifdef CONFIG_DEBUG_FEATURES
- if (priv == NULL)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
- return -EIO;
- }
- #endif
- #ifdef CONFIG_USBDEV_DUALSPEED
- hispeed = (priv->usbdev->speed == USB_SPEED_HIGH);
- #endif
- if (config == priv->config)
- {
- /* Already configured -- Do nothing */
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_ALREADYCONFIGURED), 0);
- return 0;
- }
- /* Discard the previous configuration data */
- usbclass_resetconfig(priv);
- /* Was this a request to simply discard the current configuration? */
- if (config == RNDIS_CONFIGIDNONE)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_CONFIGNONE), 0);
- return 0;
- }
- /* We only accept one configuration */
- if (config != RNDIS_CONFIGID)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_CONFIGIDBAD), 0);
- return -EINVAL;
- }
- /* Configure the IN interrupt endpoint */
- usbclass_copy_epdesc(RNDIS_EP_INTIN_IDX, &epdesc, &priv->devinfo, hispeed);
- ret = EP_CONFIGURE(priv->epintin, &epdesc, false);
- if (ret < 0)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPINTINCONFIGFAIL), 0);
- goto errout;
- }
- priv->epintin->priv = priv;
- /* Configure the IN bulk endpoint */
- usbclass_copy_epdesc(RNDIS_EP_BULKIN_IDX, &epdesc, &priv->devinfo, hispeed);
- ret = EP_CONFIGURE(priv->epbulkin, &epdesc, false);
- if (ret < 0)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPBULKINCONFIGFAIL), 0);
- goto errout;
- }
- priv->epbulkin->priv = priv;
- /* Configure the OUT bulk endpoint */
- usbclass_copy_epdesc(RNDIS_EP_BULKOUT_IDX, &epdesc, &priv->devinfo, hispeed);
- ret = EP_CONFIGURE(priv->epbulkout, &epdesc, true);
- if (ret < 0)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPBULKOUTCONFIGFAIL), 0);
- goto errout;
- }
- priv->epbulkout->priv = priv;
- /* Queue read requests in the bulk OUT endpoint */
- priv->rdreq->callback = rndis_rdcomplete;
- ret = rndis_submit_rdreq(priv);
- if (ret != OK)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_RDSUBMIT), (uint16_t)-ret);
- goto errout;
- }
- /* We are successfully configured */
- priv->config = config;
- if (priv->netdev.d_ifup(&priv->netdev) == OK)
- {
- priv->netdev.d_flags |= IFF_UP;
- }
- return OK;
- errout:
- usbclass_resetconfig(priv);
- return ret;
- }
- /****************************************************************************
- * Name: usbclass_classobject
- *
- * Description:
- * Allocate memory for the RNDIS driver class object
- *
- * Returned Value:
- * 0 on success, negative error code on failure.
- *
- ****************************************************************************/
- static int usbclass_classobject(int minor,
- FAR struct usbdev_devinfo_s *devinfo,
- FAR struct usbdevclass_driver_s **classdev)
- {
- FAR struct rndis_alloc_s *alloc;
- FAR struct rndis_dev_s *priv;
- FAR struct rndis_driver_s *drvr;
- int ret;
- /* Allocate the structures needed */
- alloc = (FAR struct rndis_alloc_s *)kmm_zalloc(sizeof(struct rndis_alloc_s));
- if (!alloc)
- {
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_ALLOCDEVSTRUCT), 0);
- return -ENOMEM;
- }
- /* Convenience pointers into the allocated blob */
- priv = &alloc->dev;
- drvr = &alloc->drvr;
- *classdev = &drvr->drvr;
- #ifdef CONFIG_RNDIS_COMPOSITE
- priv->devinfo = *devinfo;
- #else
- priv->devinfo.epno[RNDIS_EP_INTIN_IDX] = USB_EPNO(RNDIS_EPINTIN_ADDR);
- priv->devinfo.epno[RNDIS_EP_BULKIN_IDX] = USB_EPNO(RNDIS_EPBULKIN_ADDR);
- priv->devinfo.epno[RNDIS_EP_BULKOUT_IDX] = USB_EPNO(RNDIS_EPBULKOUT_ADDR);
- #endif
- /* Initialize the USB ethernet driver structure */
- sq_init(&priv->reqlist);
- memcpy(priv->host_mac_address, g_rndis_default_mac_addr, 6);
- priv->txpoll = wd_create();
- priv->netdev.d_private = priv;
- priv->netdev.d_ifup = &rndis_ifup;
- priv->netdev.d_ifdown = &rndis_ifdown;
- priv->netdev.d_txavail = &rndis_txavail;
- /* MAC address filtering is purposefully left out of this driver. Since
- * in the RNDIS USB scenario there are only two devices in the network
- * (host and us), there shouldn't be any packets received that don't
- * belong to us.
- */
- /* Initialize the USB class driver structure */
- drvr->drvr.speed = USB_SPEED_FULL;
- drvr->drvr.ops = &g_driverops;
- drvr->dev = priv;
- ret = netdev_register(&priv->netdev, NET_LL_ETHERNET);
- if (ret)
- {
- uerr("Failed to register net device");
- return ret;
- }
- drvr->dev->registered = true;
- return OK;
- }
- static void usbclass_uninitialize(FAR struct usbdevclass_driver_s *classdev)
- {
- FAR struct rndis_driver_s *drvr = (FAR struct rndis_driver_s *)classdev;
- FAR struct rndis_alloc_s *alloc = (FAR struct rndis_alloc_s *)drvr->dev;
- if (drvr->dev->registered)
- {
- netdev_unregister(&drvr->dev->netdev);
- drvr->dev->registered = false;
- }
- else
- {
- kmm_free(alloc);
- }
- }
- /****************************************************************************
- * Public Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: usbdev_rndis_initialize
- *
- * Description:
- * Initialize the RNDIS USB device driver.
- *
- * Input Parameters:
- * mac_address: pointer to an array of six octets which is the MAC address
- * of the host side of the interface. May be NULL to use the
- * default MAC address.
- *
- * Returned Value:
- * 0 on success, -errno on failure
- *
- ****************************************************************************/
- #ifndef CONFIG_RNDIS_COMPOSITE
- int usbdev_rndis_initialize(FAR const uint8_t *mac_address)
- {
- int ret;
- FAR struct usbdevclass_driver_s *classdev;
- FAR struct rndis_driver_s *drvr;
- ret = usbclass_classobject(0, NULL, &classdev);
- if (ret)
- {
- nerr("usbclass_classobject failed: %d\n", ret);
- return ret;
- }
- drvr = (FAR struct rndis_driver_s *)classdev;
- if (mac_address)
- {
- memcpy(drvr->dev->host_mac_address, mac_address, 6);
- }
- ret = usbdev_register(&drvr->drvr);
- if (ret)
- {
- nerr("usbdev_register failed: %d\n", ret);
- usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_DEVREGISTER), (uint16_t)-ret);
- usbclass_uninitialize(classdev);
- return ret;
- }
- return OK;
- }
- #endif
- /****************************************************************************
- * Name: usbdev_rndis_set_host_mac_addr
- *
- * Description:
- * Set host MAC address. Mainly for use with composite devices where
- * the MAC cannot be given directly to usbdev_rndis_initialize().
- *
- * Input Parameters:
- * netdev: pointer to the network interface. Can be obtained from
- * e.g. netdev_findbyname().
- *
- * mac_address: pointer to an array of six octets which is the MAC address
- * of the host side of the interface. May be NULL to use the
- * default MAC address.
- *
- * Returned Value:
- * 0 on success, -errno on failure
- *
- ****************************************************************************/
- int usbdev_rndis_set_host_mac_addr(FAR struct net_driver_s *netdev,
- FAR const uint8_t *mac_address)
- {
- FAR struct rndis_dev_s *dev = (FAR struct rndis_dev_s *)netdev;
- if (mac_address)
- {
- memcpy(dev->host_mac_address, mac_address, 6);
- }
- else
- {
- memcpy(dev->host_mac_address, g_rndis_default_mac_addr, 6);
- }
- return OK;
- }
- /****************************************************************************
- * Name: usbdev_rndis_get_composite_devdesc
- *
- * Description:
- * Helper function to fill in some constants into the composite
- * configuration struct.
- *
- * Input Parameters:
- * dev - Pointer to the configuration struct we should fill
- *
- * Returned Value:
- * None
- *
- ****************************************************************************/
- #ifdef CONFIG_RNDIS_COMPOSITE
- void usbdev_rndis_get_composite_devdesc(struct composite_devdesc_s *dev)
- {
- memset(dev, 0, sizeof(struct composite_devdesc_s));
- dev->mkconfdesc = usbclass_mkcfgdesc;
- dev->mkstrdesc = usbclass_mkstrdesc;
- dev->classobject = usbclass_classobject;
- dev->uninitialize = usbclass_uninitialize;
- dev->nconfigs = RNDIS_NCONFIGS;
- dev->configid = RNDIS_CONFIGID;
- dev->cfgdescsize = sizeof(g_rndis_cfgdesc);
- dev->devinfo.ninterfaces = RNDIS_NINTERFACES;
- dev->devinfo.nstrings = 0;
- dev->devinfo.nendpoints = RNDIS_NUM_EPS;
- /* Default endpoint indexes, board-specific logic can override these */
- dev->devinfo.epno[RNDIS_EP_INTIN_IDX] = USB_EPNO(RNDIS_EPINTIN_ADDR);
- dev->devinfo.epno[RNDIS_EP_BULKIN_IDX] = USB_EPNO(RNDIS_EPBULKIN_ADDR);
- dev->devinfo.epno[RNDIS_EP_BULKOUT_IDX] = USB_EPNO(RNDIS_EPBULKOUT_ADDR);
- }
- #endif
|