ツヌむンワンUSBホストず耇合USBデバむス





画像










少し前、蚘事「Pastilda-オヌプンハヌドりェアパスワヌドマネヌゞャヌ」が公開されたした 。 このプロゞェクトは公開されおいるので、蚭蚈プロセス、盎面しおいるタスク、盎面しおいる困難に぀いお小さなメモを曞くず面癜いず刀断したした。



Pastildaの䞻な本質は、キヌボヌドずPCの間の䞀皮のアダプタヌであるこずです。 したがっお、圌女は次のこずができるはずです。



この機胜はプロゞェクトの骚子であるため、最初のメモは圌に捧げられたす。





USBホストの実装



そのため、たず、USBデバむスに接続されたキヌボヌドを認識しお通信できるように、USBデバむスにホストを実装する必芁がありたした。 私の仕事ではEclipse + GNU ARM Eclipse + libopencm3バンドルを䜿甚しおいるため、既補でできればlibopencm3ラむブラリを䜿甚しお䜜成されたものを探しおいたした。 私の怜玢は成功するず信じない最埌の瞬間たで、私の欲求は非垞に倪っおいたした。 しかし、営業日の終わりに、むンタヌネットを䞋にスクロヌルしお、私は突然これに出䌚いたした 。 libusbhost ほんず たた、libopencm3 usbに基づいお蚘述されたホストだけでなく、プロゞェクトで䜿甚するこずにしたSTM32F4の䞋でも蚘述されたした。 䞀般に、星は収束し、私の喜びは際限がありたせんでした。 ずころで、このプロゞェクトはlibopencm3の䞀郚ずしお䜜成されたこずが刀明したしたが、ラむブラリには決しお远加されたせんでした。



ラむブラリずしお、私はlibusbhostをコンパむルしたせんでした。必芁な゜ヌスを取埗し、キヌボヌド甚のドラむバヌを䜜成し、䞀般的にはすべおを駆動したした。 しかし、たず最初に。



libusbhostから次のファむルを取埗したした。



たた、usart_helpers。[Ch]ファむルがあり、その助けを借りお、デバむスからホストに送られるすべおのメッセヌゞず倚くの異なるデバッグ情報をUARTを介しお端末に送信するこずができたした。 この機胜を詊したしたが、プロゞェクトから削陀したした。



usbh_driver_hid_mouse。[Ch]ず同様に、キヌボヌド甚のドラむバヌを䜜成したしたusbh_driver_hid_kbd。[Ch]。



次に、単玔なクラスが実装され、ホストで動䜜したす。



USBホストクラス
constexpr uint8_t USB_HOST_TIMER_NUMBER = 6; constexpr uint16_t USB_HOST_TIMER_PRESCALER = (8400 - 1); constexpr uint16_t USB_HOST_TIMER_PERIOD = (65535); typedef void (*redirect)(uint8_t *data, uint8_t len); typedef void (*control_interception)(); static redirect redirect_callback; static control_interception control_interception_callback; class USB_host { public: USB_host(redirect redirect_callback, control_interception control_interception_callback); void poll(); static void kbd_in_message_handler(uint8_t data_len, const uint8_t *data); static constexpr hid_kbd_config_t kbd_config = { &kbd_in_message_handler }; static constexpr usbh_dev_driver_t *device_drivers[] = { (usbh_dev_driver_t *)&usbh_hid_kbd_driver }; private: TIMER_ext *_timer; void timer_setup(); uint32_t get_time_us(); void oth_hs_setup(); };
      
      







ここではすべおが透明です。 デバむスはキヌボヌドをリッスンし、䞀連の特別なキヌの組み合わせがログむンおよびパスワヌド遞択モヌドに入るのを埅぀必芁がありたす。 これは、キヌボヌド割り蟌みハンドラヌkbd_in_message_handleruint8_t data_len、const uint8_t * dataで発生したす。 むベントの開発には2぀のオプションがありたす。





耇合USBデバむスの実装



次に、デバむスマネヌゞャヌにデバむスをキヌボヌドずディスクドラむブの䞡方ずしお衚瀺する必芁がありたした。 ここで、蚘述子のすべおの魔法= このドキュメントの第9章で、USBデバむスフレヌムワヌクに぀いお詳しく説明したす。 この章は非垞に泚意深く読んで、それに応じおデバむス蚘述子を説明する必芁がありたす。 私の堎合、次のこずが起こりたした。



耇合USB蚘述子
 static constexpr uint8_t keyboard_report_descriptor[] = { 0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x05, 0x07, 0x19, 0xE0, 0x29, 0xE7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x95, 0x01, 0x75, 0x08, 0x81, 0x01, 0x95, 0x03, 0x75, 0x01, 0x05, 0x08, 0x19, 0x01, 0x29, 0x03, 0x91, 0x02, 0x95, 0x05, 0x75, 0x01, 0x91, 0x01, 0x95, 0x06, 0x75, 0x08, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x05, 0x07, 0x19, 0x00, 0x2A, 0xFF, 0x00, 0x81, 0x00, 0xC0 }; static constexpr char usb_strings[][30] = { "Third Pin", "Composite Device", "Pastilda" }; static constexpr struct usb_device_descriptor dev = { USB_DT_DEVICE_SIZE, //bLength USB_DT_DEVICE, //bDescriptorType 0x0110, //bcdUSB 0x0, //bDeviceClass 0x00, //bDeviceSubClass 0x00, //bDeviceProtocol 64, //bMaxPacketSize0 0x0483, //idVendor 0x5741, //idProduct 0x0200, //bcdDevice 1, //iManufacturer 2, //iProduct 3, //iSerialNumber 1 //bNumConfigurations }; typedef struct __attribute__((packed)) { struct usb_hid_descriptor hid_descriptor; struct { uint8_t bReportDescriptorType; uint16_t wDescriptorLength; } __attribute__((packed)) hid_report; } type_hid_function; static constexpr type_hid_function keyboard_hid_function = { { 9, //bLength USB_DT_HID, //bDescriptorType 0x0111, //bcdHID 0, //bCountryCode 1 //bNumDescriptors }, { USB_DT_REPORT, sizeof(keyboard_report_descriptor) } }; static constexpr struct usb_endpoint_descriptor hid_endpoint = { USB_DT_ENDPOINT_SIZE, //bLength USB_DT_ENDPOINT, //bDescriptorType Endpoint::E_KEYBOARD, //bEndpointAddress USB_ENDPOINT_ATTR_INTERRUPT, //bmAttributes 64, //wMaxPacketSize 0x20 //bInterval }; static constexpr struct usb_endpoint_descriptor msc_endpoint[] = { { USB_DT_ENDPOINT_SIZE, //bLength USB_DT_ENDPOINT, //bDescriptorType Endpoint::E_MASS_STORAGE_IN, //bEndpointAddress USB_ENDPOINT_ATTR_BULK, //bmAttributes 64, //wMaxPacketSize 0 //bInterval }, { USB_DT_ENDPOINT_SIZE, //bLength USB_DT_ENDPOINT, //bDescriptorType Endpoint::E_MASS_STORAGE_OUT, //bEndpointAddress USB_ENDPOINT_ATTR_BULK, //bmAttributes 64, //wMaxPacketSize 0 //bInterval } }; static constexpr struct usb_interface_descriptor iface[] = { { USB_DT_INTERFACE_SIZE, //bLength USB_DT_INTERFACE, //bDescriptorType Interface::I_KEYBOARD, //bInterfaceNumber 0, //bAlternateSetting 1, //bNumEndpoints USB_CLASS_HID, //bInterfaceClass 1, //bInterfaceSubClass 1, //bInterfaceProtocol 0, //iInterface &hid_endpoint, &keyboard_hid_function, sizeof(keyboard_hid_function) }, { USB_DT_INTERFACE_SIZE, //bLength USB_DT_INTERFACE, //bDescriptorType Interface::I_MASS_STORAGE, //bInterfaceNumber 0, //bAlternateSetting 2, //bNumEndpoints USB_CLASS_MSC, //bInterfaceClass USB_MSC_SUBCLASS_SCSI, //bInterfaceSubClass USB_MSC_PROTOCOL_BBB, //bInterfaceProtocol 0x00, //iInterface msc_endpoint, 0, 0 }, }; static constexpr struct usb_config_descriptor::usb_interface ifaces[] { { (uint8_t *)0, //cur_altsetting 1, //num_altsetting (usb_iface_assoc_descriptor*)0, //iface_assoc &iface[Interface::I_KEYBOARD] //altsetting }, { (uint8_t *)0, //cur_altsetting 1, //num_altsetting (usb_iface_assoc_descriptor*)0, //iface_assoc &iface[Interface::I_MASS_STORAGE] //altsetting }, }; static constexpr struct usb_config_descriptor config_descr = { USB_DT_CONFIGURATION_SIZE, //bLength USB_DT_CONFIGURATION, //bDescriptorType 0, //wTotalLength 2, //bNumInterfaces 1, //bConfigurationValue 0, //iConfiguration 0x80, //bmAttributes 0x50, //bMaxPower ifaces };
      
      







keyboard_report_descriptorは、ヒュヌマンむンタヌフェむスデバむスHIDのデバむスクラス定矩 、付録E.6レポヌト蚘述子キヌボヌドから取埗されたした。 正盎なずころ、私はレポヌトの構造に぀いおあたり理解しおいたせんでした、私はドキュメントを信じおいたした䞀般的に、ここに特別な泚意を払う必芁があるいく぀かのポむントがありたす



それは、説明的な芳点から、おそらくすべおです。 蚘述子がラむブラリヌにどのように蚘述されおいるかに泚意するしかありたせんその蚘述はusbstd.hファむルにありたす。 ドキュメントにはすべおが明確に蚘茉されおいたす。 「耇合デバむスをどのように説明できたすか」などの質問がなかったため、これによりタスクが倧幅に簡玠化されたず思われたす。 すべおがすぐに明らかになりたした。



耇合デバむスを操䜜するために、USB_compositeクラスを以䞋に蚘述したした。



コンポゞットUSBクラス
 extern "C" void USB_OTG_IRQ(); int USB_control_callback(usbd_device *usbd_dev, struct usb_setup_data *req, uint8_t **buf, uint16_t *len, usbd_control_complete_callback *complete); void USB_set_config_callback(usbd_device *usbd_dev, uint16_t wValue); static uint8_t keyboard_protocol = 1; static uint8_t keyboard_idle = 0; static uint8_t keyboard_leds = 0; class USB_composite { public: uint8_t usbd_control_buffer[500]; UsbCompositeDescriptors *descriptors; uint8_t usb_ready = 0; usbd_device *my_usb_device; USB_composite(const uint32_t block_count, int (*read_block)(uint32_t lba, uint8_t *copy_to), int (*write_block)(uint32_t lba, const uint8_t *copy_from)); void usb_send_packet(const void *buf, int len); int hid_control_request(usbd_device *usbd_dev, struct usb_setup_data *req, uint8_t **buf, uint16_t *len, void (**complete)(usbd_device *usbd_dev, struct usb_setup_data *req)); void hid_set_config(usbd_device *usbd_dev, uint16_t wValue); };
      
      







このクラスのキヌは2぀の関数です。



以䞋は、実装のオプションです。



コヌルバック
 int USB_composite::hid_control_request(usbd_device *usbd_dev, struct usb_setup_data *req, uint8_t **buf, uint16_t *len, void (**complete)(usbd_device *usbd_dev, struct usb_setup_data *req)) { (void)complete; (void)usbd_dev; if ((req->bmRequestType & USB_REQ_TYPE_DIRECTION) == USB_REQ_TYPE_IN) { if ((req->bmRequestType & USB_REQ_TYPE_TYPE) == USB_REQ_TYPE_STANDARD) { if (req->bRequest == USB_REQ_GET_DESCRIPTOR) { if (req->wValue == 0x2200) { *buf = (uint8_t *)descriptors->keyboard_report_descriptor; *len = sizeof(descriptors->keyboard_report_descriptor); return (USBD_REQ_HANDLED); } else if (req->wValue == 0x2100) { *buf = (uint8_t *)&descriptors->keyboard_hid_function; *len = sizeof(descriptors->keyboard_hid_function); return (USBD_REQ_HANDLED); } return (USBD_REQ_NOTSUPP); } } else if ((req->bmRequestType & USB_REQ_TYPE_TYPE) == USB_REQ_TYPE_CLASS) { if (req->bRequest == HidRequest::GET_REPORT) { *buf = (uint8_t*)&boot_key_report; *len = sizeof(boot_key_report); return (USBD_REQ_HANDLED); } else if (req->bRequest == HidRequest::GET_IDLE) { *buf = &keyboard_idle; *len = sizeof(keyboard_idle); return (USBD_REQ_HANDLED); } else if (req->bRequest == HidRequest::GET_PROTOCOL) { *buf = &keyboard_protocol; *len = sizeof(keyboard_protocol); return (USBD_REQ_HANDLED); } return (USBD_REQ_NOTSUPP); } } else { if ((req->bmRequestType & USB_REQ_TYPE_TYPE) == USB_REQ_TYPE_CLASS) { if (req->bRequest == HidRequest::SET_REPORT) { if (*len == 1) { keyboard_leds = (*buf)[0]; } return (USBD_REQ_HANDLED); } else if (req->bRequest == HidRequest::SET_IDLE) { keyboard_idle = req->wValue >> 8; return (USBD_REQ_HANDLED); } else if (req->bRequest == HidRequest::SET_PROTOCOL) { keyboard_protocol = req->wValue; return (USBD_REQ_HANDLED); } } return (USBD_REQ_NOTSUPP); } return (USBD_REQ_NEXT_CALLBACK); } int USB_control_callback(usbd_device *usbd_dev, struct usb_setup_data *req, uint8_t **buf, uint16_t *len, usbd_control_complete_callback *complete) { return(usb_pointer->hid_control_request(usbd_dev, req, buf, len, complete)); } void USB_composite::hid_set_config(usbd_device *usbd_dev, uint16_t wValue) { (void)wValue; (void)usbd_dev; usbd_ep_setup(usbd_dev, Endpoint::E_KEYBOARD, USB_ENDPOINT_ATTR_INTERRUPT, 8, 0); usbd_register_control_callback(usbd_dev, USB_REQ_TYPE_INTERFACE, USB_REQ_TYPE_RECIPIENT, USB_control_callback ); } void USB_set_config_callback(usbd_device *usbd_dev, uint16_t wValue) { usb_pointer->hid_set_config(usbd_dev, wValue) ; }
      
      







通垞、control_requestおよびset_config関数は、デバむスごずに明瀺的に蚘述する必芁がありたす。 ただし、このルヌルには䟋倖がありたす倧容量蚘憶装眮。 そのため、USB_Compositeクラスのコンストラクタヌを扱いたす。



たず、USB OTG FSレッグを初期化したす。

 GPIO_ext uf_p(PA11); GPIO_ext uf_m(PA12); uf_p.mode_setup(Mode::ALTERNATE_FUNCTION, PullMode::NO_PULL); uf_m.mode_setup(Mode::ALTERNATE_FUNCTION, PullMode::NO_PULL); uf_p.set_af(AF_Number::AF10); uf_m.set_af(AF_Number::AF10);
      
      





次に、耇合デバむスを初期化し、䞊蚘のUSB_set_config_callbackを登録し、割り蟌みを有効にする必芁がありたす。

  my_usb_device = usbd_init(&otgfs_usb_driver, &(UsbCompositeDescriptors::dev), &(UsbCompositeDescriptors::config_descr), (const char**)UsbCompositeDescriptors::usb_strings, 3, usbd_control_buffer, sizeof(usbd_control_buffer)); usbd_register_set_config_callback(my_usb_device, USB_set_config_callback); nvic_enable_irq(NVIC_OTG_FS_IRQ);
      
      





これは、デバむスマネヌゞャヌでデバむスを認識するのに十分です。



ただし、「USB Mass Storage Device」には、デバむスが正垞に動䜜しおいないずいう譊告が衚瀺されたす。 問題は、他のUSBデバむスずは異なり、libopencm3ラむブラリのusb_msc.cファむルに蚘述されおいるusb_msc_init関数を介しお、倧容量蚘憶装眮が少し異なっお初期化されるこずです。 MSDの堎合、control_request関数ずset_config関数を明瀺的に蚘述する必芁はないこずを前述したした。 これは、usb_msc_init関数がすべおを行うためです。゚ンドポむントを構成し、すべおのコヌルバックを登録したす。 したがっお、コンストラクタをもう1行远加する必芁がありたす。

  usb_msc_init(my_usb_device, Endpoint::E_MASS_STORAGE_IN, 64, Endpoint::E_MASS_STORAGE_OUT, 64, "ThirdPin", "Pastilda", "0.00", block_count, read_block, write_block);
      
      





ここで、MSDを初期化するずきに、メモリを操䜜するための最小限のAPIを枡す必芁があるこずがわかりたす。



Pastildaでは、倖郚フラッシュSST25VF064Cを䜿甚したす。 このチップのドラむバはこちらにありたす 。 将来、このドラむバヌに基づいお、ファむルシステムはフラッシュに実装されたす。 ほずんどの堎合、私の同僚がこれに぀いお詳现にある皋床詳しく曞いおいたす。 しかし、MSDの動䜜をすばやくテストしたかったので、ファむルシステムの芜を曞きたした= ここで叫ぶこずができたす 。



だからここに。 USB_Compositeクラスのコンストラクタヌが远加されたので、プロゞェクトをアセンブルし、デバむスをフラッシュしお、「USBストレヌゞデバむス」に譊告のマヌクが付いおいないこずを確認し、「ディスクデバむス」タブで「ThirdPin Pastilda USBデバむス」を芋぀けるこずができたす。 そしお、すべおが順調だず思われたす。 しかし、ない=さらに問題がありたす



1.ディスクにアクセスするこずはできたせん。 これを行おうずするず、すべおがハングし、死に、コンピュヌタヌが非垞に悪くなりたす。

2.デバむスをディスクずしお認識するには、2分以䞊かかりたす。



これらの問題ず、健康に害を及がさずにそれらを解決する方法に぀いおは、 USB倧容量ストレヌゞデバむスずlibopencm3で説明しおいたす。



そしお、ああ、奇跡 スポットなし=これですべおが機胜したす。 USBホストず耇合USBデバむスがありたす。 圌らの仕事を組み合わせるだけです。



ホストず耇合デバむスの組み合わせ



私たちの目暙





これをすべお実装するコヌドは、スティックのように簡単です。



App.cpp
 App *app_pointer; App::App() { app_pointer = this; clock_setup(); systick_init(); _leds_api = new LEDS_api(); _flash = new FlashMemory(); usb_host = new USB_host(redirect, control_interception); usb_composite = new USB_composite(_flash->flash_blocks(), _flash->flash_read, _flash->flash_write); } void App::process() { _leds_api->toggle(); usb_host->poll(); } void App::redirect(uint8_t *data, uint8_t len) { app_pointer->usb_composite->usb_send_packet(data, len); } void App::control_interception() { memset(app_pointer->key, 0, 8); app_pointer->key[2] = KEY_W; app_pointer->key[3] = KEY_O; app_pointer->key[4] = KEY_N; app_pointer->key[5] = KEY_D; app_pointer->key[6] = KEY_E; app_pointer->key[7] = KEY_R; app_pointer->usb_composite->usb_send_packet(app_pointer->key, 8); app_pointer->key[2] = 0; app_pointer->key[3] = 0; app_pointer->key[4] = 0; app_pointer->key[5] = 0; app_pointer->key[6] = 0; app_pointer->key[7] = 0; app_pointer->usb_composite->usb_send_packet(app_pointer->key, 8); app_pointer->key[2] = KEY_SPACEBAR; app_pointer->key[3] = KEY_W; app_pointer->key[4] = KEY_O; app_pointer->key[5] = KEY_M; app_pointer->key[6] = KEY_A; app_pointer->key[7] = KEY_N; app_pointer->usb_composite->usb_send_packet(app_pointer->key, 8); app_pointer->key[2] = 0; app_pointer->key[3] = 0; app_pointer->key[4] = 0; app_pointer->key[5] = 0; app_pointer->key[6] = 0; app_pointer->key[7] = 0; app_pointer->usb_composite->usb_send_packet(app_pointer->key, 8); }
      
      







コンストラクタヌで、必芁なものをすべお初期化したす。

  1. 点滅するLED。
  2. フラッシュ。ディスク䞊のファむルを䜜成/削陀できたす。
  3. ホスト。リダむレクト関数​​組み合わせがない堎合の凊理​​およびcontrol_interception組み合わせが抌された堎合の凊理​​を枡したす。
  4. メモリの読み取り/曞き蟌み機胜を枡す耇合デバむス。


そしお、それだけです。 開始され、デバむスのスケルトンが䜜成されたした。 ファむルシステムは、Ctrl + Shift +〜の組み合わせを抌すこずで、すぐに確定したす。1行のメニュヌが衚瀺され、暗号化されたパスワヌドデヌタベヌスがフラッシュに保存されたす。



コメントや提案があれば嬉しいです。



そしお、もちろん、 githubぞのリンク。



UPD 06/27/2017

  1. Pastildaプロゞェクトリポゞトリはここに移動したした 。 最近、リリヌス1.0が公開されたした。
  2. プロゞェクトの最新ニュヌスはこちらでご芧いただけたす 。
  3. そしお぀いにプロゞェクトサむトを立ち䞊げたした 



All Articles