From 64c81e28468dbd98b09454fa13c9bee8c89c7a05 Mon Sep 17 00:00:00 2001 From: Sebastian Sumpf Date: Sun, 3 Jul 2022 17:49:17 +0200 Subject: [PATCH] usb_net: Add cdc_ether ECM support * Enable ECM devices * Allow disconnect of devices * Handle link state correctly * Required by PinePhone's USB modem issue #4557 --- repos/dde_linux/lib/mk/usb_net_include.mk | 2 +- repos/dde_linux/ports/dde_linux.hash | 2 +- repos/dde_linux/src/drivers/usb_net/driver.h | 53 +++ repos/dde_linux/src/drivers/usb_net/dummies.c | 93 +---- .../dde_linux/src/drivers/usb_net/lx_emul.cc | 89 ++++- repos/dde_linux/src/drivers/usb_net/lx_emul.h | 10 + repos/dde_linux/src/drivers/usb_net/lxc.c | 359 ++++++++++++++++++ repos/dde_linux/src/drivers/usb_net/main.cc | 28 +- repos/dde_linux/src/drivers/usb_net/target.mk | 5 + repos/dde_linux/usb_net.list | 6 + 10 files changed, 555 insertions(+), 92 deletions(-) diff --git a/repos/dde_linux/lib/mk/usb_net_include.mk b/repos/dde_linux/lib/mk/usb_net_include.mk index 6629578512..0c83605915 100644 --- a/repos/dde_linux/lib/mk/usb_net_include.mk +++ b/repos/dde_linux/lib/mk/usb_net_include.mk @@ -8,7 +8,7 @@ LX_EMUL_H := $(REP_DIR)/src/drivers/usb_net/lx_emul.h # of these header files we create a symlink to 'lx_emul.h'. # SCAN_DIRS := $(addprefix $(USB_NET_CONTRIB_DIR)/include/, asm-generic linux uapi net) \ - $(addprefix $(USB_NET_CONTRIB_DIR)/, drivers net) + $(addprefix $(USB_NET_CONTRIB_DIR)/, drivers net lib) GEN_INCLUDES := $(shell grep -rIh "^\#include .*\/" $(SCAN_DIRS) |\ sed "s/^\#include [^<\"]*[<\"]\([^>\"]*\)[>\"].*/\1/" |\ sort | uniq) diff --git a/repos/dde_linux/ports/dde_linux.hash b/repos/dde_linux/ports/dde_linux.hash index 1551b47f15..c5b7b44a2b 100644 --- a/repos/dde_linux/ports/dde_linux.hash +++ b/repos/dde_linux/ports/dde_linux.hash @@ -1 +1 @@ -447158aa8aa24d4e2925a0479d8710a2d7d738e9 +f56657b5edae9fb069db1b5846bca4aebe7ce542 diff --git a/repos/dde_linux/src/drivers/usb_net/driver.h b/repos/dde_linux/src/drivers/usb_net/driver.h index 3505117f13..e1e265c8e6 100644 --- a/repos/dde_linux/src/drivers/usb_net/driver.h +++ b/repos/dde_linux/src/drivers/usb_net/driver.h @@ -26,6 +26,10 @@ /* Linux emulation environment includes */ #include +#include +#include +#include +#include struct usb_device_id; struct usb_interface; @@ -93,6 +97,55 @@ struct Driver } }; + + class Sync_packet : public Usb::Completion + { + private: + + Usb::Session_client & _usb; + Usb::Packet_descriptor _packet { _usb.source()->alloc_packet(0) }; + completion _comp; + + public: + + Sync_packet(Usb::Session_client &usb) : _usb(usb) + { + init_completion(&_comp); + } + + virtual ~Sync_packet() + { + _usb.source()->release_packet(_packet); + } + + void complete(Usb::Packet_descriptor &p) override + { + ::complete(&_comp); + } + + void send() + { + _packet.completion = this; + _usb.source()->submit_packet(_packet); + wait_for_completion(&_comp); + } + + void config(int configuration) + { + _packet.type = Usb::Packet_descriptor::CONFIG; + _packet.number = configuration; + send(); + } + + void alt_setting(int interface, int alt_setting) + { + _packet.type = Usb::Packet_descriptor::ALT_SETTING; + _packet.interface.number = interface; + _packet.interface.alt_setting = alt_setting; + send(); + } + }; + Devices devices; Genode::Env &env; Genode::Entrypoint &ep { env.ep() }; diff --git a/repos/dde_linux/src/drivers/usb_net/dummies.c b/repos/dde_linux/src/drivers/usb_net/dummies.c index d5c4c9f5d9..a17d52d16f 100644 --- a/repos/dde_linux/src/drivers/usb_net/dummies.c +++ b/repos/dde_linux/src/drivers/usb_net/dummies.c @@ -27,14 +27,6 @@ u16 bitrev16(u16 in) return -1; } -struct usb_cdc_parsed_header; -struct usb_interface; -int cdc_parse_cdc_header(struct usb_cdc_parsed_header *hdr, struct usb_interface *intf, u8 *buffer, int buflen) -{ - TRACE_AND_STOP; - return -1; -} - u16 crc16(u16 crc, const u8 *buffer, size_t len) { TRACE_AND_STOP; @@ -53,24 +45,12 @@ __wsum csum_partial(const void *buff, int len, __wsum sum) return -1; } -void * dev_get_drvdata(const struct device *dev) -{ - TRACE_AND_STOP; - return NULL; -} - int device_set_wakeup_enable(struct device *dev, bool enable) { TRACE_AND_STOP; return -1; } -struct sk_buff; -void dev_kfree_skb_any(struct sk_buff *skb) -{ - TRACE_AND_STOP; -} - void dst_release(struct dst_entry *dst) { TRACE_AND_STOP; @@ -95,11 +75,6 @@ bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap return -1; } -void free_netdev(struct net_device * ndev) -{ - TRACE_AND_STOP; -} - void free_uid(struct user_struct * user) { TRACE_AND_STOP; @@ -129,12 +104,6 @@ bool gfp_pfmemalloc_allowed(gfp_t gfp_flags) return -1; } -int hex2bin(u8 *dst, const char *src, size_t count) -{ - TRACE_AND_STOP; - return -1; -} - int in_irq() { TRACE_AND_STOP; @@ -255,11 +224,6 @@ void read_unlock_bh(rwlock_t * l) TRACE_AND_STOP; } -void unregister_netdev(struct net_device * dev) -{ - TRACE_AND_STOP; -} - void secpath_reset(struct sk_buff * skb) { TRACE; @@ -267,7 +231,7 @@ void secpath_reset(struct sk_buff * skb) void __set_current_state(int state) { - TRACE_AND_STOP; + TRACE; } void sg_init_table(struct scatterlist *sg, unsigned int arg) @@ -318,7 +282,7 @@ size_t strlcpy(char *dest, const char *src, size_t size) void tasklet_kill(struct tasklet_struct *t) { - TRACE_AND_STOP; + TRACE; } void trace_consume_skb(struct sk_buff *skb) @@ -351,17 +315,21 @@ int usb_clear_halt(struct usb_device *dev, int pipe) } struct usb_driver; -int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void *priv) -{ - TRACE_AND_STOP; - return -1; -} - +struct usb_interface; void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface) { - TRACE_AND_STOP; + TRACE; } + +/* only called to kill interrupt urb in usbnet.c */ +struct urb; +void usb_kill_urb(struct urb *urb) +{ + TRACE; +} + + struct usb_anchor; struct urb *usb_get_from_anchor(struct usb_anchor *anchor) { @@ -369,12 +337,6 @@ struct urb *usb_get_from_anchor(struct usb_anchor *anchor) return NULL; } -struct urb *usb_get_urb(struct urb *urb) -{ - TRACE_AND_STOP; - return NULL; -} - int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout) { TRACE_AND_STOP; @@ -383,13 +345,7 @@ int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, void usb_scuttle_anchored_urbs(struct usb_anchor *anchor) { - TRACE_AND_STOP; -} - -int usb_string(struct usb_device *dev, int index, char *buf, size_t size) -{ - TRACE_AND_STOP; - return -1; + TRACE; } ktime_t ktime_get_real(void) @@ -419,29 +375,20 @@ void put_page(struct page *page) TRACE_AND_STOP; } -struct usb_interface *usb_ifnum_to_if(const struct usb_device *dev, unsigned ifnum) -{ - TRACE_AND_STOP; - return NULL; -} - -void usb_kill_urb(struct urb *urb) -{ - TRACE_AND_STOP; -} - -int usb_set_interface(struct usb_device *dev, int ifnum, int alternate) +int usb_unlink_urb(struct urb *urb) { TRACE; return 0; } -int usb_unlink_urb(struct urb *urb) + +struct urb *usb_get_urb(struct urb *urb) { - TRACE_AND_STOP; - return -1; + TRACE; + return NULL; } + void usleep_range(unsigned long min, unsigned long max) { TRACE; diff --git a/repos/dde_linux/src/drivers/usb_net/lx_emul.cc b/repos/dde_linux/src/drivers/usb_net/lx_emul.cc index 04deeb88dd..70a7cc1e9e 100644 --- a/repos/dde_linux/src/drivers/usb_net/lx_emul.cc +++ b/repos/dde_linux/src/drivers/usb_net/lx_emul.cc @@ -30,6 +30,8 @@ #include #include +#include + static int usb_match_device(struct usb_device *dev, const struct usb_device_id *id) @@ -203,12 +205,6 @@ Genode::Ram_dataspace_capability Lx::backend_alloc(Genode::addr_t size, } -Genode::addr_t Lx::backend_dma_addr(Genode::Ram_dataspace_capability) -{ - return 0; -} - - int usb_register_driver(struct usb_driver * driver, struct module *, const char *) { INIT_LIST_HEAD(&driver->dynids.list); @@ -217,25 +213,66 @@ int usb_register_driver(struct usb_driver * driver, struct module *, const char } +Genode::addr_t Lx::backend_dma_addr(Genode::Ram_dataspace_capability) +{ + return 0; +} + + +int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void *priv) +{ + usb_device *udev = interface_to_usbdev(iface); + Usb::Connection *usb = reinterpret_cast(udev->bus->controller); + try { + usb->claim_interface(iface->cur_altsetting->desc.bInterfaceNumber); + } catch (...) { + return -1; + } + return 0; +} + + +int usb_set_interface(struct usb_device *udev, int ifnum, int alternate) +{ + Usb::Connection *usb = reinterpret_cast(udev->bus->controller); + Driver::Sync_packet packet { *usb }; + packet.alt_setting(ifnum, alternate); + usb_interface *iface = udev->config->interface[ifnum]; + iface->cur_altsetting = &iface->altsetting[alternate]; + + return 0; +} + void Driver::Device::probe_interface(usb_interface * iface, usb_device_id * id) { using Le = Genode::List_element; for (Le *le = Lx_driver::list().first(); le; le = le->next()) { usb_device_id * id = le->object()->match(iface); - if (id && le->object()->probe(iface, id)) return; + + if (id) { + int ret = le->object()->probe(iface, id); + if (ret == 0) return; + } } } void Driver::Device::remove_interface(usb_interface * iface) { - to_usb_driver(iface->dev.driver)->disconnect(iface); + /* we might not drive this interface */ + if (iface->dev.driver) { + usbnet *dev =(usbnet* )usb_get_intfdata(iface); + usbnet_link_change(dev, 0, 0); + to_usb_driver(iface->dev.driver)->disconnect(iface); + } + for (unsigned i = 0; i < iface->num_altsetting; i++) { if (iface->altsetting[i].extra) kfree(iface->altsetting[i].extra); kfree(iface->altsetting[i].endpoint); - kfree(iface->altsetting); } + + kfree(iface->altsetting); kfree(iface); } @@ -350,6 +387,16 @@ struct net_device *alloc_etherdev(int sizeof_priv) } +void free_netdev(struct net_device * ndev) +{ + if (!ndev) return; + + kfree(ndev->priv); + kfree(ndev->dev_addr); + kfree(ndev); +} + + void *__alloc_percpu(size_t size, size_t align) { return kmalloc(size, 0); @@ -379,16 +426,25 @@ int register_netdev(struct net_device *dev) dev->state |= 1 << __LINK_STATE_START; int err = dev->netdev_ops->ndo_open(dev); + if (err) return err; if (dev->netdev_ops->ndo_set_rx_mode) dev->netdev_ops->ndo_set_rx_mode(dev); - single_net_device = dev; return 0; }; +void unregister_netdev(struct net_device * dev) +{ + if (dev->netdev_ops->ndo_stop) + dev->netdev_ops->ndo_stop(dev); + + single_net_device = NULL; +} + + net_device * Linux_network_session_base:: _register_session(Linux_network_session_base &session, @@ -431,6 +487,12 @@ int dev_set_drvdata(struct device *dev, void *data) } +void * dev_get_drvdata(const struct device *dev) +{ + return dev->driver_data; +} + + int netif_running(const struct net_device *dev) { return dev->state & (1 << __LINK_STATE_START); @@ -485,6 +547,11 @@ int netif_rx(struct sk_buff * skb) } +void dev_kfree_skb_any(struct sk_buff *skb) +{ + dev_kfree_skb(skb); +} + int is_valid_ether_addr(const u8 * a) { for (unsigned i = 0; i < ETH_ALEN; i++) @@ -551,3 +618,5 @@ void page_frag_free(void *addr) Lx::Malloc::dma().free_large(page->addr); kfree(page); } + + diff --git a/repos/dde_linux/src/drivers/usb_net/lx_emul.h b/repos/dde_linux/src/drivers/usb_net/lx_emul.h index b4a9b85761..e26d349da1 100644 --- a/repos/dde_linux/src/drivers/usb_net/lx_emul.h +++ b/repos/dde_linux/src/drivers/usb_net/lx_emul.h @@ -120,6 +120,8 @@ int dev_set_drvdata(struct device *dev, void *data); #define pr_notice(fmt, ...) printk(KERN_NOTICE fmt, ##__VA_ARGS__) #define pr_emerg(fmt, ...) printk(KERN_INFO fmt, ##__VA_ARGS__) +#define try_then_request_module(x, mod...) (x) + struct bus_type { int (*match)(struct device *dev, struct device_driver *drv); @@ -190,6 +192,7 @@ const char *dev_name(const struct device *dev); struct __una_u16 { u16 x; } __attribute__((packed)); struct __una_u32 { u32 x; } __attribute__((packed)); +#define get_unaligned(ptr) (*ptr) u16 get_unaligned_le16(const void *p); u32 get_unaligned_le32(const void *p); @@ -534,6 +537,12 @@ const void *of_get_mac_address(struct device_node *np); u16 bitrev16(u16 in); u16 crc16(u16 crc, const u8 *buffer, size_t len); int hex2bin(u8 *dst, const char *src, size_t count); +char *hex_byte_pack(char *buf, u8 byte); + +#define hex_asc_upper_lo(x) hex_asc_upper[((x) & 0x0f)] +#define hex_asc_lo(x) hex_asc[((x) & 0x0f)] +#define hex_asc_hi(x) hex_asc[((x) & 0xf0) >> 4] + #define this_cpu_ptr(ptr) ptr @@ -918,6 +927,7 @@ void *kmap_atomic(struct page *page); void kunmap_atomic(void *addr); #define CONFIG_LOCKDEP 1 +#define CONFIG_NLS_DEFAULT "iso8859-1" struct partial_page { diff --git a/repos/dde_linux/src/drivers/usb_net/lxc.c b/repos/dde_linux/src/drivers/usb_net/lxc.c index 3f595cfea8..42a59f5234 100644 --- a/repos/dde_linux/src/drivers/usb_net/lxc.c +++ b/repos/dde_linux/src/drivers/usb_net/lxc.c @@ -13,7 +13,13 @@ /* linux includes */ #include +#include +#include #include +#include +#include +#include +#include /* local includes */ #include @@ -63,3 +69,356 @@ unsigned char *lxc_skb_put(struct sk_buff *skb, size_t len) { return skb_put(skb, len); } + + +/** + * cdc_parse_cdc_header - parse the extra headers present in CDC devices + * @hdr: the place to put the results of the parsing + * @intf: the interface for which parsing is requested + * @buffer: pointer to the extra headers to be parsed + * @buflen: length of the extra headers + * + * This evaluates the extra headers present in CDC devices which + * bind the interfaces for data and control and provide details + * about the capabilities of the device. + * + * Return: number of descriptors parsed or -EINVAL + * if the header is contradictory beyond salvage + */ + +int cdc_parse_cdc_header(struct usb_cdc_parsed_header *hdr, + struct usb_interface *intf, + u8 *buffer, + int buflen) +{ + /* duplicates are ignored */ + struct usb_cdc_union_desc *union_header = NULL; + + /* duplicates are not tolerated */ + struct usb_cdc_header_desc *header = NULL; + struct usb_cdc_ether_desc *ether = NULL; + struct usb_cdc_mdlm_detail_desc *detail = NULL; + struct usb_cdc_mdlm_desc *desc = NULL; + + unsigned int elength; + int cnt = 0; + + memset(hdr, 0x00, sizeof(struct usb_cdc_parsed_header)); + hdr->phonet_magic_present = false; + while (buflen > 0) { + elength = buffer[0]; + if (!elength) { + dev_err(&intf->dev, "skipping garbage byte\n"); + elength = 1; + goto next_desc; + } + if ((buflen < elength) || (elength < 3)) { + dev_err(&intf->dev, "invalid descriptor buffer length\n"); + break; + } + if (buffer[1] != USB_DT_CS_INTERFACE) { + dev_err(&intf->dev, "skipping garbage\n"); + goto next_desc; + } + + switch (buffer[2]) { + case USB_CDC_UNION_TYPE: /* we've found it */ + if (elength < sizeof(struct usb_cdc_union_desc)) + goto next_desc; + if (union_header) { + dev_err(&intf->dev, "More than one union descriptor, skipping ...\n"); + goto next_desc; + } + union_header = (struct usb_cdc_union_desc *)buffer; + break; + case USB_CDC_COUNTRY_TYPE: + if (elength < sizeof(struct usb_cdc_country_functional_desc)) + goto next_desc; + hdr->usb_cdc_country_functional_desc = + (struct usb_cdc_country_functional_desc *)buffer; + break; + case USB_CDC_HEADER_TYPE: + if (elength != sizeof(struct usb_cdc_header_desc)) + goto next_desc; + if (header) + return -EINVAL; + header = (struct usb_cdc_header_desc *)buffer; + break; + case USB_CDC_ACM_TYPE: + if (elength < sizeof(struct usb_cdc_acm_descriptor)) + goto next_desc; + hdr->usb_cdc_acm_descriptor = + (struct usb_cdc_acm_descriptor *)buffer; + break; + case USB_CDC_ETHERNET_TYPE: + if (elength != sizeof(struct usb_cdc_ether_desc)) + goto next_desc; + if (ether) + return -EINVAL; + ether = (struct usb_cdc_ether_desc *)buffer; + break; + case USB_CDC_CALL_MANAGEMENT_TYPE: + if (elength < sizeof(struct usb_cdc_call_mgmt_descriptor)) + goto next_desc; + hdr->usb_cdc_call_mgmt_descriptor = + (struct usb_cdc_call_mgmt_descriptor *)buffer; + break; + case USB_CDC_DMM_TYPE: + if (elength < sizeof(struct usb_cdc_dmm_desc)) + goto next_desc; + hdr->usb_cdc_dmm_desc = + (struct usb_cdc_dmm_desc *)buffer; + break; + case USB_CDC_MDLM_TYPE: + if (elength < sizeof(struct usb_cdc_mdlm_desc *)) + goto next_desc; + if (desc) + return -EINVAL; + desc = (struct usb_cdc_mdlm_desc *)buffer; + break; + case USB_CDC_MDLM_DETAIL_TYPE: + if (elength < sizeof(struct usb_cdc_mdlm_detail_desc *)) + goto next_desc; + if (detail) + return -EINVAL; + detail = (struct usb_cdc_mdlm_detail_desc *)buffer; + break; + case USB_CDC_NCM_TYPE: + if (elength < sizeof(struct usb_cdc_ncm_desc)) + goto next_desc; + hdr->usb_cdc_ncm_desc = (struct usb_cdc_ncm_desc *)buffer; + break; + case USB_CDC_MBIM_TYPE: + if (elength < sizeof(struct usb_cdc_mbim_desc)) + goto next_desc; + + hdr->usb_cdc_mbim_desc = (struct usb_cdc_mbim_desc *)buffer; + break; + case USB_CDC_MBIM_EXTENDED_TYPE: + if (elength < sizeof(struct usb_cdc_mbim_extended_desc)) + break; + hdr->usb_cdc_mbim_extended_desc = + (struct usb_cdc_mbim_extended_desc *)buffer; + break; + case CDC_PHONET_MAGIC_NUMBER: + hdr->phonet_magic_present = true; + break; + default: + /* + * there are LOTS more CDC descriptors that + * could legitimately be found here. + */ + dev_dbg(&intf->dev, "Ignoring descriptor: type %02x, length %ud\n", + buffer[2], elength); + goto next_desc; + } + cnt++; +next_desc: + buflen -= elength; + buffer += elength; + } + hdr->usb_cdc_union_desc = union_header; + hdr->usb_cdc_header_desc = header; + hdr->usb_cdc_mdlm_detail_desc = detail; + hdr->usb_cdc_mdlm_desc = desc; + hdr->usb_cdc_ether_desc = ether; + return cnt; +} + +EXPORT_SYMBOL(cdc_parse_cdc_header); + +struct usb_interface *usb_ifnum_to_if(const struct usb_device *dev, + unsigned ifnum) +{ + struct usb_host_config *config = dev->actconfig; + int i; + + if (!config) { + lx_printf("No config for %u\n", ifnum); + return NULL; + } + + for (i = 0; i < config->desc.bNumInterfaces; i++) { + if (config->interface[i]->altsetting[0] + .desc.bInterfaceNumber == ifnum) { + return config->interface[i]; + } + } + + lx_printf("No interface for %u\n"); + return NULL; +} + +static int usb_get_string(struct usb_device *dev, unsigned short langid, + unsigned char index, void *buf, int size) +{ + int i; + int result; + + if (size <= 0) /* No point in asking for no data */ + return -EINVAL; + + for (i = 0; i < 3; ++i) { + /* retry on length 0 or stall; some devices are flakey */ + result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, + (USB_DT_STRING << 8) + index, langid, buf, size, + USB_CTRL_GET_TIMEOUT); + if (result == 0 || result == -EPIPE) + continue; + if (result > 1 && ((u8 *) buf)[1] != USB_DT_STRING) { + result = -ENODATA; + continue; + } + break; + } + return result; +} + + +static void usb_try_string_workarounds(unsigned char *buf, int *length) +{ + int newlength, oldlength = *length; + + for (newlength = 2; newlength + 1 < oldlength; newlength += 2) + if (!isprint(buf[newlength]) || buf[newlength + 1]) + break; + + if (newlength > 2) { + buf[0] = newlength; + *length = newlength; + } +} + +static int usb_string_sub(struct usb_device *dev, unsigned int langid, + unsigned int index, unsigned char *buf) +{ + int rc; + + /* Try to read the string descriptor by asking for the maximum + * possible number of bytes */ + if (dev->quirks & USB_QUIRK_STRING_FETCH_255) + rc = -EIO; + else + rc = usb_get_string(dev, langid, index, buf, 255); + + /* If that failed try to read the descriptor length, then + * ask for just that many bytes */ + if (rc < 2) { + rc = usb_get_string(dev, langid, index, buf, 2); + if (rc == 2) + rc = usb_get_string(dev, langid, index, buf, buf[0]); + } + + if (rc >= 2) { + if (!buf[0] && !buf[1]) + usb_try_string_workarounds(buf, &rc); + + /* There might be extra junk at the end of the descriptor */ + if (buf[0] < rc) + rc = buf[0]; + + rc = rc - (rc & 1); /* force a multiple of two */ + } + + if (rc < 2) + rc = (rc < 0 ? rc : -EINVAL); + + return rc; +} + +static int usb_get_langid(struct usb_device *dev, unsigned char *tbuf) +{ + int err; + + if (dev->have_langid) + return 0; + + if (dev->string_langid < 0) + return -EPIPE; + + err = usb_string_sub(dev, 0, 0, tbuf); + + /* If the string was reported but is malformed, default to english + * (0x0409) */ + if (err == -ENODATA || (err > 0 && err < 4)) { + dev->string_langid = 0x0409; + dev->have_langid = 1; + dev_err(&dev->dev, + "language id specifier not provided by device, defaulting to English\n"); + return 0; + } + + /* In case of all other errors, we assume the device is not able to + * deal with strings at all. Set string_langid to -1 in order to + * prevent any string to be retrieved from the device */ + if (err < 0) { + dev_info(&dev->dev, "string descriptor 0 read error: %d\n", + err); + dev->string_langid = -1; + return -EPIPE; + } + + /* always use the first langid listed */ + dev->string_langid = tbuf[2] | (tbuf[3] << 8); + dev->have_langid = 1; + dev_dbg(&dev->dev, "default language 0x%04x\n", + dev->string_langid); + return 0; +} + +/** + * usb_string - returns UTF-8 version of a string descriptor + * @dev: the device whose string descriptor is being retrieved + * @index: the number of the descriptor + * @buf: where to put the string + * @size: how big is "buf"? + * Context: !in_interrupt () + * + * This converts the UTF-16LE encoded strings returned by devices, from + * usb_get_string_descriptor(), to null-terminated UTF-8 encoded ones + * that are more usable in most kernel contexts. Note that this function + * chooses strings in the first language supported by the device. + * + * This call is synchronous, and may not be used in an interrupt context. + * + * Return: length of the string (>= 0) or usb_control_msg status (< 0). + */ +int usb_string(struct usb_device *dev, int index, char *buf, size_t size) +{ + unsigned char *tbuf; + int err; + + if (dev->state == USB_STATE_SUSPENDED) + return -EHOSTUNREACH; + if (size <= 0 || !buf || !index) + return -EINVAL; + buf[0] = 0; + tbuf = kmalloc(256, GFP_NOIO); + if (!tbuf) + return -ENOMEM; + + err = usb_get_langid(dev, tbuf); + if (err < 0) + goto errout; + + err = usb_string_sub(dev, dev->string_langid, index, tbuf); + if (err < 0) + goto errout; + + size--; /* leave room for trailing NULL char in output buffer */ + err = utf16s_to_utf8s((wchar_t *) &tbuf[2], (err - 2) / 2, + UTF16_LITTLE_ENDIAN, buf, size); + buf[err] = 0; + + if (tbuf[1] != USB_DT_STRING) + dev_dbg(&dev->dev, + "wrong descriptor type %02x for string %d (\"%s\")\n", + tbuf[1], index, buf); + + errout: + kfree(tbuf); + return err; +} +EXPORT_SYMBOL_GPL(usb_string); + diff --git a/repos/dde_linux/src/drivers/usb_net/main.cc b/repos/dde_linux/src/drivers/usb_net/main.cc index 52c528b25c..2e2e740740 100644 --- a/repos/dde_linux/src/drivers/usb_net/main.cc +++ b/repos/dde_linux/src/drivers/usb_net/main.cc @@ -39,15 +39,22 @@ void Driver::Device::scan_altsettings(usb_interface * iface, if (iface_desc.active) iface->cur_altsetting = &iface->altsetting[alt_idx]; + Usb::Interface_extra iface_extra; + if (usb.interface_extra(iface_idx, alt_idx, &iface_extra)) { + iface->altsetting[alt_idx].extra = (unsigned char *)kzalloc(iface_extra.length, 0); + Genode::memcpy(iface->altsetting[alt_idx].extra, iface_extra.data, + iface_extra.length); + iface->altsetting[alt_idx].extralen = iface_extra.length; + } iface->altsetting[alt_idx].endpoint = (usb_host_endpoint*) kzalloc(sizeof(usb_host_endpoint)*iface->altsetting[alt_idx].desc.bNumEndpoints, GFP_KERNEL); for (unsigned i = 0; i < iface->altsetting[alt_idx].desc.bNumEndpoints; i++) { - Usb::Endpoint_descriptor ep_desc; - usb.endpoint_descriptor(iface_idx, alt_idx, i, &ep_desc); + Usb::Endpoint_descriptor ep_desc[7]; + usb.endpoint_descriptor(iface_idx, alt_idx, i, ep_desc); Genode::memcpy(&iface->altsetting[alt_idx].endpoint[i].desc, - &ep_desc, sizeof(usb_endpoint_descriptor)); + ep_desc, sizeof(usb_endpoint_descriptor)); int epnum = usb_endpoint_num(&iface->altsetting[alt_idx].endpoint[i].desc); if (usb_endpoint_dir_out(&iface->altsetting[alt_idx].endpoint[i].desc)) udev->ep_out[epnum] = &iface->altsetting[alt_idx].endpoint[i]; @@ -70,11 +77,7 @@ void Driver::Device::scan_interfaces(unsigned iface_idx) for (unsigned i = 0; i < iface->num_altsetting; i++) scan_altsettings(iface, iface_idx, i); - struct usb_device_id id; - probe_interface(iface, &id); udev->config->interface[iface_idx] = iface; - - driver.activate_network_session(); }; @@ -101,6 +104,17 @@ void Driver::Device::register_device() for (unsigned i = 0; i < config_desc.num_interfaces; i++) scan_interfaces(i); + + udev->actconfig = udev->config; + udev->config->desc.bNumInterfaces = config_desc.num_interfaces; + + /* probe */ + for (unsigned i = 0; i < config_desc.num_interfaces; i++) { + struct usb_device_id id; + probe_interface(udev->config->interface[i], &id); + } + + driver.activate_network_session(); } diff --git a/repos/dde_linux/src/drivers/usb_net/target.mk b/repos/dde_linux/src/drivers/usb_net/target.mk index 1c85626d29..0d850a42d9 100644 --- a/repos/dde_linux/src/drivers/usb_net/target.mk +++ b/repos/dde_linux/src/drivers/usb_net/target.mk @@ -20,9 +20,14 @@ SRC_C += drivers/net/usb/cdc_ether.c SRC_C += drivers/net/usb/rndis_host.c SRC_C += drivers/net/usb/smsc95xx.c SRC_C += drivers/net/usb/usbnet.c +SRC_C += fs/nls/nls_base.c +SRC_C += lib/ctype.c +SRC_C += lib/hexdump.c SRC_C += net/core/skbuff.c SRC_C += net/ethernet/eth.c +CC_OPT += -Wno-address-of-packed-member + CC_C_OPT += -Wno-comment -Wno-int-conversion -Wno-incompatible-pointer-types \ -Wno-unused-variable -Wno-pointer-sign -Wno-uninitialized \ -Wno-maybe-uninitialized -Wno-format -Wno-discarded-qualifiers \ diff --git a/repos/dde_linux/usb_net.list b/repos/dde_linux/usb_net.list index 5f4123b80f..b625f3ab00 100644 --- a/repos/dde_linux/usb_net.list +++ b/repos/dde_linux/usb_net.list @@ -8,6 +8,9 @@ linux-x.x.x/drivers/net/usb/rndis_host.c linux-x.x.x/drivers/net/usb/smsc95xx.h linux-x.x.x/drivers/net/usb/smsc95xx.c linux-x.x.x/drivers/net/usb/usbnet.c +linux-x.x.x/fs/nls/nls_base.c +linux-x.x.x/lib/ctype.c +linux-x.x.x/lib/hexdump.c linux-x.x.x/net/core/skbuff.c linux-x.x.x/net/ethernet/eth.c linux-x.x.x/include/asm-generic/atomic64.h @@ -18,6 +21,7 @@ linux-x.x.x/include/asm-generic/bitops/fls.h linux-x.x.x/include/asm-generic/bitops/fls64.h linux-x.x.x/include/asm-generic/bitops/non-atomic.h linux-x.x.x/include/linux/cgroup-defs.h +linux-x.x.x/include/linux/ctype.h linux-x.x.x/include/linux/errqueue.h linux-x.x.x/include/linux/ethtool.h linux-x.x.x/include/linux/if_ether.h @@ -30,6 +34,7 @@ linux-x.x.x/include/linux/mdio.h linux-x.x.x/include/linux/mod_devicetable.h linux-x.x.x/include/linux/netdev_features.h linux-x.x.x/include/linux/net.h +linux-x.x.x/include/linux/nls.h linux-x.x.x/include/linux/phy.h linux-x.x.x/include/linux/rbtree.h linux-x.x.x/include/linux/rculist.h @@ -42,6 +47,7 @@ linux-x.x.x/include/linux/swab.h linux-x.x.x/include/linux/usb.h linux-x.x.x/include/linux/usb/ch9.h linux-x.x.x/include/linux/usb/cdc.h +linux-x.x.x/include/linux/usb/quirks.h linux-x.x.x/include/linux/usb/rndis_host.h linux-x.x.x/include/linux/usb/usbnet.h linux-x.x.x/include/net/dst.h