Co je velkokapacitní úložiště USB. Větší není vždy lepší. Funkce SCSI_Read10(), která je volána v události Data In

Pro označení Mass Storage „v každodenním životě“ se používají dvě zkratky – MSC a UMS. MSC (Mass Storage Class) je oficiální a UMS (možné možnosti dekódování: USB/Universal Mass Storage) je „lidové“. Neprotiřečí si, spíše se doplňují.

MSC uvádí, že protokol je jednou ze schválených standardních „tříd zařízení“ v rámci specifikace USB, a je tedy de jure průmyslovým standardem. UMS hovoří o univerzálnosti protokolu, který dnes podporuje většina operačních systémů a nespočet koncových zařízení, čímž se stává de facto standardem. Možnost dekódování UMS jako USB Mass Storage doplňuje tyto informace a objasňuje, že se používá jako fyzická linka rozhraní USB. Písmena MS (Mass Storage), společná všem zkratkám, naznačují, že se jedná o protokol určený pro práci s úložnými zařízeními pro velké objemy dat. Právě pro ně byl tento standard vyvinut.

Do třídy zařízení Velkokapacitní úložiště USB Patří mezi ně zařízení, která přenášejí soubory jedním nebo dvěma směry. Typičtí představitelé této třídy zařízení: pevné disky, CD, DVD a flash disky. Systém souborů umožňuje uživateli kopírovat, přesouvat a mazat soubory na zařízení.

Téměř všechna velkokapacitní paměťová zařízení USB používají protokol hromadného přenosu (BOT, také nazývaný BBB). (Výjimkou jsou některé vysokorychlostní disketové jednotky, které používají několik typů datových přenosů: řídicí, hromadné a přerušovací přenosy (tento protokol se nazývá CBI). Velkokapacitní paměťová zařízení USB také používají příkazy SCSI definované v různé normy SCSI (Small Computer System Interface).

Protokol hromadného přenosu definuje způsob, jakým může hostitel USB odesílat příkazy a přijímat odpovědi pomocí hromadného přenosu, jak je definováno ve specifikaci USB. V protokolu pouze pro data vyžaduje každá výměna informací 2 nebo 3 přenosy dat USB. Při prvním přenosu hostitel odešle příkaz ve struktuře nazvané CBW (Command Block Wrapper). Po sadě CBW následuje přenos, který obsahuje data odeslaná hostitelem nebo zařízením. Při posledním přenosu vrátí zařízení stav ve struktuře nazvané CSW (Command Status Wrapper).

Výhody protokolu. Hlavní věc je jednoduchost: všechny operace se provádějí prostřednictvím standardních souborových shellů, vč. Průzkumník Windows(dirigent), pro práci s ním nejsou potřeba žádné další znalosti ani školení.

Rozšíření - již Windows Me a 2000 měly základní podporu protokolu, Windows XP ji podporovaly zcela. Mnoho dalších operačních systémů - MacOS, Linux atd. - kompatibilní s Mass Storage.

Je také důležité, aby existovala specifikace USB host (on the go), která vám umožní připojit zařízení Mass Storage k jiným přenosným (a nepřenosným) zařízením.

Specifikace:

221 Kb Engl USB Mass Storage Class – specifikace příkazu UFI
Třída velkokapacitního úložiště USB 103 Kb Engl – pouze hromadný přenos
103 kb v angličtině

grafalex 5. září 2017 v 18:01

Upgrade velkokapacitního paměťového zařízení USB na STM32F103 pomocí FreeRTOS a DMA

  • Programování mikrokontroléru,
  • Programování systému

Nedávno nebo v ruštině - jako flash disk. Zdá se, že je vše relativně jednoduché: v grafickém konfigurátoru STM32CubeMX jsem vygeneroval kód na pár kliknutí, přidal ovladač SD karty a voila - vše funguje. Jen velmi pomalu – 200 KB/s, a to i přesto, že šířka pásma sběrnice USB v režimu Full Speed ​​​​je mnohem vyšší – 12 Mbit/s (zhruba 1,2 MB/s). Navíc doba spouštění mého flash disku v operačním systému je asi 50 sekund, což je prostě nepohodlné. Když už jsem se ponořil do této oblasti, proč neopravit přenosovou rychlost.

Vlastně už jsem napsal vlastní ovladač pro SD kartu (přesněji SPI ovladač), který fungoval přes DMA a poskytoval rychlost až 500kb/s. Bohužel tento ovladač nefungoval v kontextu USB. Důvodem je samotný komunikační model USB - vše se děje na přerušeních, zatímco můj ovladač byl navržen tak, aby fungoval v běžném vláknu. Navíc je zaprášený synchronizačními primitivy FreeRTOS.

V tomto článku jsem udělal pár triků, které mi umožnily vytěžit maximum z kombinace USB a SD karet připojených k mikrokontroléru STM32F103 přes SPI. Nebudou chybět ani informace o FreeRTOS, synchronizačních objektech a obecných přístupech k přenosu dat přes DMA. Takže si myslím, že článek bude užitečný pro ty, kteří rozumí pouze STM32 kontrolérům a nástrojům jako DMA a přístupům při práci s FreeRTOS. Kód je postaven na základě knihoven HAL a USB Middleware z balíčku STM32Cube a také SdFat pro práci s SD kartou.

Přehled architektury

Aniž bych zacházel do detailů jednotlivé komponenty, pak je implementace Mass Storage Device (aka Mass Storage Class - MSC) na straně mikrokontroléru poměrně jednoduchá věc.

Na jedné straně je knihovna USB Core. Komunikuje s hostitelem, zajišťuje registraci zařízení a implementuje všemožné USB věci na nízké úrovni.

Ovladač Mass Storage (pomocí jádra USB) může přijímat a odesílat data hostiteli. Téměř jako COM port se pouze data přenášejí v blocích. Důležitý je zde sémantický obsah těchto dat: přenášejí se příkazy SCSI a data do nich. Navíc existuje jen několik typů příkazů: číst data, zapisovat data, zjistit velikost úložného zařízení, zjistit připravenost zařízení.

Úkolem ovladače MSC je interpretovat příkazy SCSI a přesměrovat volání na ovladač úložného zařízení. Může to být jakékoli úložné zařízení s blokovaným přístupem ( RAM disk, flash disk, síťové úložiště, CD atd.). V mém případě je úložným zařízením MicroSD karta připojená přes SPI. Sada funkcí, které jsou vyžadovány od ovladače, je přibližně stejná: čtení, zápis, zadávání velikosti a stav připravenosti.

A pak se jeden objeví důležitá nuance, což je vlastně důvod všeho toho povyku. Faktem je, že protokol USB je orientován na hostitele. Pouze hostitel může zahájit transakce, odesílat nebo přijímat data. Z pohledu mikrokontroléru to znamená, že veškerá činnost související s USB bude probíhat v kontextu přerušení. V tomto případě bude odpovídající handler zavolán z ovladače MSC.

Pokud jde o odesílání dat z mikrokontroléru do hostitele. Mikrokontrolér nemůže sám zahájit přenos dat. Maximálně může mikrokontrolér signalizovat jádru USB, že existují data, která může hostitel vzít.

Ani se samotnou SD kartou to není tak jednoduché. Faktem je, že karta je složité zařízení (zřejmě má vlastní mikrokontrolér) a komunikační protokol je velmi netriviální. Tito. neposílal/přijímal data na konkrétní adresu (jako je tomu u některých modulů I2C EEPROM). Komunikační protokol s kartou zajišťuje celou sadu různých příkazů a potvrzení, kontroly kontrolních součtů a dodržování nejrůznějších timeoutů.

Používám knihovnu SdFat. Implementuje práci s SD kartou na úrovni souborů FAT systémy, kterou ve svém zařízení aktivně používám. V případě připojení USB je zakázáno vše, co souvisí se systémem souborů (tato role přechází na hostitele). Co je ale důležité je, že knihovna samostatně poskytuje ovladači karty rozhraní, téměř stejné, jaké požaduje ovladač MSC – číst, zapisovat, zjišťovat velikost.


Ovladač karty implementuje protokol pro komunikaci s kartou přes SPI. Přesně ví, jaké příkazy má na kartu poslat, v jakém pořadí a jaké odpovědi očekávat. Samotný ovladač ale s hardwarem nekomunikuje. Pro tento účel je poskytována další úroveň abstrakce - SPI driver, který převádí požadavky na čtení/zápis jednotlivých bloků do samotného přenosu dat po SPI sběrnici. Právě na tomto místě jsem byl schopen zorganizovat přenos dat přes DMA, což zvýšilo rychlost přenosu dat v normální režim, ale rozbilo to všechno v případě USB (DMA nakonec muselo být zakázáno)

Ale nejdřív.

Jaký problém řešíme?

Můj kolega často klade tuto otázku, což velmi mate jeho partnery během technických diskusí.

Celá tato kuchyně má 2 problémy:

  • Nízká rychlost linky při práci z USB. Především díky použití synchronních operací čtení/zápisu
  • Vysoké zatížení procesoru (až 100 %) – zařízení se stává nepoužitelným. Důvodem je deaktivace DMA a potřeba řídit data pomocí procesoru.
Ale to je ze strany řadiče a existují také aspekty protokolu USB Mass Storage. Nainstaloval jsem USB sniffer Wireshark a podíval se přesně na to, jaké pakety běží na sběrnici, a viděl jsem nejméně 3 další důvody pro nízkou rychlost
  • Hostitel odesílá příliš mnoho transakcí
  • Transakce se časem prodlužují
  • Samotné operace čtení/zápisu probíhají synchronně a čekají na dokončení
Problém počtu transakcí se řeší celkem jednoduše. Ukázalo se, že když připojím své zařízení, operační systém načte celou tabulku FAT a provede mnoho dalších malých čtení adresáře a MBR. Mám 8gigový flash disk naformátovaný na FAT32 s velikostí clusteru 4kb. Ukazuje se, že tabulka FAT zabírá asi 8 MB. Při lineární přenosové rychlosti 200 kb/s to má za následek téměř 40 sekund.

Nejjednodušší způsob, jak snížit počet operací čtení při připojení zařízení, je zmenšit tabulku FAT. Stačí jednoduše přeformátovat flash disk a zvětšit velikost clusteru (tím snížit jejich počet a velikost tabulky). Kartu jsem naformátoval s velikostí clusteru nastavenou na 16 KB - velikost tabulky FAT byla těsně pod 2 MB a doba inicializace se zkrátila na 20 sekund.

Větší není vždy lepší

Uvědomil jsem si, že 8GB flash disk je pro mé zařízení příliš mnoho, tolik nepotřebuji. 1 gig, nebo dokonce 512 megabajtů, je dost. Jen takovou flashku ještě nemám po ruce. Navíc se teď ani neprodávají. Budete muset oškrábat dno sudu. Jakmile to najdu, vyzkouším.


V žádném případě přeformátování flash disku neřeší problém lineární rychlosti (rychlost, kterou se sekvenčně načítají velké soubory). Stále zůstává na úrovni 200kb/s a nejhůře zatěžuje procesor. Pojďme se podívat, co s tím můžeme udělat.

Co je špatného na DMA z USB?

Pojďme konečně ke kódu a podívejme se, jak čtu/zapisuji na flash kartu (ovladač SPI)

Ve svém projektu používám FreeRTOS. Toto je jen úžasný nástroj, který mi umožnil zpracovat každou funkci mého zařízení v samostatném vláknu (úkolu). Podařilo se mi zahodit obrovské státní automaty pro všechny příležitosti a kód se stal mnohem jednodušším a přehlednějším. Všechny úkoly fungují současně, vzájemně se ustupují a v případě potřeby synchronizují. Pokud všechna vlákna usnula čekáním na nějakou událost, můžete použít úsporné režimy mikrokontroléru.

Kód, který pracuje s kartou SD, také běží v samostatném vláknu. To nám umožnilo psát funkce čtení/zápisu velmi elegantním způsobem.

SPI ovladač pro čtení/zápis dat na SD kartu pomocí DMA

uint8_t SdFatSPIDriver::receive(uint8_t* buf, size_t n) ( // Zahájení přenosu dat memset(buf, 0xff, n); HAL_SPI_TransmitReceive_DMA(&spiHandle, buf, buf, n); // Počkejte, dokud se přenos nedokončí, xSemaphoreTakema x 100 návrat 0; // Stav OK ) void SdFatSPIDriver::send(const uint8_t* buf, velikost_t n) ( // Zahájení přenosu dat HAL_SPI_Transmit_DMA(&spiHandle, (uint8_t*)buf, n); // Počkejte, dokud nebude přenos dokončen. dokončeno xSemaphoreTake(xSema, 100 void SdFatSPIDriver::dmaTransferCompletedCB() ( // Obnovení vlákna SD xSemaphoreGiveFromISR(xSema, NULL); )


Krása je v tom, že když potřebujeme číst nebo zapisovat velký blok dat, tento kód nečeká na dokončení. Místo toho se spustí přenos dat přes DMA a samotné vlákno přejde do režimu spánku. V tomto případě může procesor pokračovat ve své činnosti a přenos řízení přechází na jiná vlákna. Po dokončení přenosu se zavolá přerušení DMA a probudí vlákno, které čekalo na přenos dat.

Problém je v tom, že tento přístup je obtížné aplikovat na model USB, kde se veškerá provozní logika vyskytuje v přerušeních a ne v běžném spouštěcím vláknu. Tito. Ukazuje se, že v přerušení obdržíme požadavek na čtení/zápis a ve stejném přerušení bude muset čekat i dokončení přenosu dat.

Samozřejmě můžeme zorganizovat předávání přes DMA v kontextu přerušení, ale bude to málo platné. DMA funguje dobře tam, kde můžete zahájit přenos a přepnout procesor na nějakou jinou užitečnou práci, dokud přenos dat neskončí. Ale po zahájení převodu z přerušení nebudeme moci přerušení přerušit (omlouvám se za tautologii) a věnovat se své práci. Budete tam muset viset a čekat na konec přenosu. Tito. provoz bude synchronní a celkový čas bude stejný jako v případě bez DMA.

Zde by bylo mnohem zajímavější zahájit přenos dat přes DMA na žádost hostitele a ukončit přerušení. A pak při příštím přerušení podat zprávu o vykonané práci.

Ale to není celý obrázek. Pokud by čtení z karty sestávalo pouze z odeslání bloku dat, pak by takový přístup nebylo těžké realizovat. Přenos přes SPI je ale samozřejmě tou nejdůležitější částí, ale ne jedinou. Pokud se podíváte na čtení/zápis datového bloku na úrovni ovladače karty, proces vypadá asi takto.

  • Odešlete příkaz na kartu, počkejte a zkontrolujte odpověď
  • Počkejte, až bude karta připravena
  • Odeslat data (stejnou funkcí, kterou jsem uvedl výše)
  • Vypočítat kontrolní součet a porovnejte jej s mapovým zobrazením
  • Kompletní převod
Vzhledem k tomu, že tento zdánlivě lineární algoritmus je implementován jako série vnořených volání funkcí, pak by nebylo příliš rozumné oříznout jej uprostřed. Budeme muset důkladně zpanikařit celou knihovnu. A pokud vezmeme v úvahu, že v některých případech může být přenos prováděn nikoli v jednom kuse, ale v cyklu v řadě malých bloků, pak se tento úkol stává zcela nemožným.

Ale není to všechno špatné. Pokud se podíváte ještě výš - na úrovni ovladače MSC - pak je vlastně jedno, jak přesně bude přenos dat probíhat - v jednom nebo několika bloku, s nebo bez DMA. Hlavní je přenést data a nahlásit stav.

Ideálním místem pro experimenty by byla vrstva mezi ovladačem MSC a ovladačem karty. Před všemi těmi výsměchy vypadala tato komponenta velmi triviálně - v podstatě je to adaptér mezi rozhraním, které chce ovladač MSC vidět, a tím, co vydává ovladač karty.

Původní implementace adaptéru

int8_t SD_MSC_Read (uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len) ( (void)lun; // Nepoužito if(!card.readBlocks(blk_addr, buf, blk_len)) return (USD_FAIL;) return (USD_FAIL;) return (USD_FAIL; SD_MSC_Write (uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len) ( (void)lun; // Nepoužito if(!card.writeBlocks(blk_addr, buf, blk_len)) return USBD_FAIL; return (US)


Jak jsem již řekl, ovladač karty nefunguje, pokud je volán z přerušení. Ale v normálním vláknu to funguje dobře. Založme pro to tedy samostatné vlákno.

Toto vlákno bude přijímat požadavky na čtení a zápis prostřednictvím fronty. Každý požadavek obsahuje informace o typu operace (čtení/zápis), číslo bloku ke čtení nebo zápisu, počet bloků a ukazatel na datovou vyrovnávací paměť. Vytvořil jsem také ukazatel na kontext operace - budeme ho potřebovat o něco později.

Fronta čtení/zápisu

enum IOOperation(IO_Read, IO_Write); struct IOMsg ( IOOperation op; uint32_t lba; uint8_t * buf; uint16_t len; void * kontext; ); // Fronta IO příkazů k provedení v samostatném vláknu QueueHandle_t sdCmdQueue = NULL; // Inicializace vlákna odpovědného za komunikaci s kartou SD bool initSDIOThread() ( // Inicializace synchronizace sdCmdQueue = xQueueCreate(1, sizeof(IOMsg)); bool res = card.begin(&spiDriver, PA4, SPI_FULL_SPEED); return res; ))


Samotné vlákno spí a čeká na příkazy. Pokud přijde příkaz, provede se požadovaná operace a synchronně. Na konci operace zavoláme zpětné volání, které v závislosti na implementaci po dokončení operace čtení/zápis udělá, co je potřeba.

Vlákno sloužící ke čtení/zápisu na kartu

extern "C" void cardReadCompletedCB(uint8_t res, void * kontext); extern "C" void cardWriteCompletedCB(uint8_t res, void * kontext); void xSDIOThread(void *pvParameters) ( while(true) ( ​​IOMsg msg; if(xQueueReceive(sdCmdQueue, &msg, portMAX_DELAY)) ( switch(msg.op) ( case IO_Read: ( bool res.s. = karta. lba, msg.buf, msg.len); cardWriteCompletedCB (res? 0: 0xff, msg.context) default: break;


Protože se to vše děje v rámci normálního vlákna, může ovladač karty interně používat synchronizaci DMA a FreeRTOS.

Funkce MSC se staly trochu složitějšími, ale ne o mnoho. Nyní, namísto přímého čtení nebo zápisu, tento kód odešle požadavek do příslušného vlákna.

Odesílání požadavků na čtení/zápis

int8_t SD_MSC_Read (uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len, void * context) ( // Odeslání příkazu čtení do vlákna IO exekutoru IOMsg msg; msg.op = IO_Read.; blklendr =s ? msg.op = io_write; (USBD_OK);


Je zde důležitý bod – sémantika těchto funkcí se změnila. Nyní jsou asynchronní, tzn. nečekejte na skutečné dokončení operace. Stále tedy budeme muset vyladit kód, který je volá, ale to uděláme o něco později.

Mezitím, abychom otestovali tyto funkce, uděláme ještě jeden testovací stream. Bude emulovat jádro USB a odesílat požadavky na čtení.

Testovací vlákno

uint8_t io_buf; static TaskHandle_t xTestTask = NULL; void cardReadCompletedCB(bool res, void * context) ( xTaskNotifyGive(xTestTask); ) void cardWriteCompletedCB(bool res, void * kontext) ( xTaskNotifyGive(xTestTask); ) void xSDTestThread(void *pvxTaskHestGameters =(uTainttskHestGameters)); předchozí = HAL_GetTick(); uint32_t opsPer1s = 0 uint32_t cardSize = card.cardSize(); 1000) ( předchozí = HAL_GetTick(); usbDebugWrite("Rychlost čtení: %d kbajtů/s\r\n", opsPer1s); opsPer1s = 0; ) ) while(true) ; )


Tento kód přečte celou kartu od začátku do konce v blocích o velikosti 1 kB a změří rychlost čtení. Každá operace čtení odešle požadavek do proudu SD karty. Tam probíhá čtení synchronně a hlásí dokončení prostřednictvím zpětného volání. Nahradil jsem vlastní implementaci tohoto zpětného volání, která jednoduše signalizuje testovacímu vláknu, že může pokračovat (testovací vlákno celou dobu spí ve funkci ulTaskNotifyTake()).

Ale co je nejdůležitější, rychlost čtení je v této verzi cca 450kb/s a procesor je vytížen jen na 3-4%. Podle mě to není špatné.

Aktualizace ovladače MSC

Takže jsme porazili ovladač karty povolením DMA. Ale sémantika čtení/zápisu se změnila ze synchronní na asynchronní. Nyní potřebujeme vyladit implementaci MSC a naučit ji pracovat s asynchronními voláními. Tito. Musíme začít přenášet přes DMA na první požadavek od hostitele a nějak reagovat na všechny následující, a to slovy „předchozí operace ještě neskončila, vraťte se později“.

Ve skutečnosti protokol USB poskytuje takový mechanismus hned po vybalení. Přijímající strana potvrzuje přenos dat s určitým stavem. Pokud jsou data přijata a zpracována úspěšně, přijímač potvrdí transakci stavem ACK. Pokud zařízení nemůže transakci zpracovat (není inicializováno, je v chybovém stavu nebo nefunguje z jakéhokoli jiného důvodu), odezva bude STALL.

Pokud však zařízení rozpoznalo transakci a je v provozuschopném stavu, ale data ještě nejsou připravena, může zařízení odpovědět NAK. V tomto případě je hostitel povinen kontaktovat zařízení s přesně stejným požadavkem o něco později. Tento stav bychom mohli využít pro zpožděné čtení/zápis – při prvním volání na hostitele zahájíme přenos dat přes DMA, ale odpovíme na transakci NAK. Když hostitel přijde s opakovanou transakcí a přenos přes DMA již skončil, odpovíme ACK.

Bohužel jsem nenašel dobrý způsob, jak poslat NAK signál v USB knihovně od ST. Návratové kódy funkcí se buď nekontrolují, nebo zvládnou pouze 2 stavy - vše v pořádku, nebo chyba. V druhém případě jsou všechny koncové body uzavřeny a všude je nastaven stav STALL.

Mám podezření, že na nejnižší úrovni ovladače USB se potvrzení NAK používá poměrně aktivně, ale nepřišel jsem na to, jak se správně připojit k NAK na úrovni ovladače třídy.

Podle všeho tvůrci ST knihoven místo různých potvrzování poskytli humánnější rozhraní. Pokud má zařízení něco poslat hostiteli, zavolá funkci USBD_LL_Transmit() – hostitel sám převezme poskytnutá data. A pokud funkce nebyla volána, zařízení automaticky odpoví odpověďmi NAK. U příjmu dat je situace přibližně stejná. Pokud je zařízení připraveno přijímat, zavolá funkci USBD_LL_PrepareReceive(). V opačném případě zařízení odpoví NAK, pokud se hostitel pokusí přenést data. Využijme tyto znalosti k implementaci našeho ovladače MSC.

Podívejme se, jaké transakce probíhají na sběrnici USB (analýza byla provedena před změnami v ovladači karty).

Zajímavé zde nejsou ani samotné transakce, ale jejich časová razítka. Na tomto obrázku jsem zvolil „lehké“ transakce – ty, které nevyžadují zpracování. Mikrokontrolér na takové požadavky bez dlouhého přemýšlení reaguje pevně zakódovanými odpověďmi. Důležité zde je, že hostitel neposílá nepřetržitý proud transakcí. Transakce neprobíhají více než jednou za 1 ms. I když je odpověď připravena okamžitě, hostitel ji vyzvedne až při další transakci po 1 ms.

A takto vypadá čtení jednoho bloku dat z hlediska transakcí na USB sběrnici.

Nejprve hostitel odešle příkaz pro čtení SCSI a poté přečte data (druhý řádek) a stav (třetí řádek) v samostatných transakcích. První transakce je nejdelší. Při zpracování této transakce se mikrokontrolér zabývá čtením z karty. A opět, hostitel mezi transakcemi pauzuje 1 ms.

Mimochodem.

V terminologii USB se směr od hostitele k zařízení nazývá OUT, ačkoli pro řadič je to příjem. Naopak směr od zařízení k hostiteli se nazývá IN, i když pro nás to znamená odesílání dat.


Algoritmus ovladače MSC na straně mikrokontroléru vypadá asi takto
    • Hostitel odešle příkaz pro čtení. Ze strany mikrokontroléru je volána funkce MSC_BOT_DataOut().
    • Příkaz je zpracován prostřednictvím funkčního řetězce MSC_BOT_DataOut() -> MSC_BOT_CBW_Decode() -> SCSI_ProcessCmd() -> SCSI_Read10()
    • Protože je ovladač ve stavu hmsc->bot_state == USBD_BOT_IDLE, je připravena procedura čtení: zkontrolují se parametry příkazu, zapamatuje se, kolik bloků je třeba přečíst, a poté se řízení přenese do funkce SCSI_ProcessRead() pomocí požadavek na přečtení prvního bloku
    • Funkce SCSI_ProcessRead() načítá data synchronní režimu. To je místo, kde je mikrokontrolér zaneprázdněn většinu z tohočas.
    • Když jsou data přijata, jsou přenesena (pomocí funkce USBD_LL_Transmit()) do výstupní vyrovnávací paměti koncového bodu MSC_IN, aby je hostitel mohl vyzvednout.
    • Ovladač zadá stav hmsc->bot_state = USBD_BOT_DATA_IN
  • Transakce SCSI: Data In
    • Hostitel načítá data z výstupní vyrovnávací paměti mikrokontroléru v 64bajtových paketech (maximální doporučená velikost paketu pro zařízení USB Full Speed ​​​​). To vše se děje na nejnižší úrovni v jádře USB, ovladač MSC se na tom nepodílí
    • Když hostitel shromáždí všechna data, dojde k události Data In. Řízení je předáno funkci MSC_BOT_DataIn(). Upozorňuji na to, že tato funkce se volá až po skutečném odeslání dat.
    • Ovladač je ve stavu hmsc->bot_state == USBD_BOT_DATA_IN, což znamená, že jsme stále v režimu čtení dat.
    • Pokud ještě nebyly přečteny všechny objednané bloky – začneme číst další díl a čekáme na dokončení, přeneste jej do výstupní vyrovnávací paměti a počkejte, až hostitel data vyzvedne. Algoritmus se opakuje
    • Pokud byly načteny všechny bloky, ovladač se přepne do stavu USBD_BOT_LAST_DATA_IN, aby odeslal konečný stav příkazu
  • SCSI transakce: Odezva
    • ovladač o tom pouze obdrží upozornění a přejde do stavu USBD_BOT_IDLE
Nejdelší operací v tomto schématu je vlastní čtení z karty. Podle mých měření trvá čtení v synchronním režimu cca 2-3ms. Navíc k přenosu dochází pomocí procesoru a to vše se děje v přerušení USB. Pro srovnání, čtení jednoho bloku délky 512 přes DMA trvá něco málo přes 1 ms.

Výrazně (řekněme až 1Mb/s) se mi nepodařilo zrychlit čtení dat - zřejmě jde o propustnost karty připojené přes SPI. Můžeme ale zkusit přidat do naší služby pauzu 1 ms mezi transakcemi.

Vidím to takto (trochu zjednodušeně)

  • SCSI transakce: čtení(10) LUN: 0x00 (LBA: 0x00000000, délka: 1)
    • Mikrokontrolér přijme příkaz čtení, zkontroluje všechny parametry, zapamatuje si počet bloků, které je třeba přečíst
    • Mikrokontrolér zahájí čtení prvního bloku v asynchronním režimu
    • Opustíme přerušení, aniž bychom čekali na konec čtení
  • Po dokončení čtení je voláno zpětné volání
    • Načtená data jsou odeslána do výstupní vyrovnávací paměti
    • Host je čte bez účasti ovladače MSC
  • Transakce SCSI: Data In
    • Je volána funkce zpětného volání DataIn(), která signalizuje, že hostitel shromáždil data a lze provést další čtení
    • Začínáme číst další blok. Algoritmus se opakuje počínaje zpětné volání o dokončení čtení
    • Pokud byly všechny bloky přečteny, odešleme stavový paket
  • SCSI transakce: Odezva
    • V tuto chvíli již byly tyto zásilky odeslány.
    • Příprava na další transakci
Pokusme se tento přístup implementovat, protože funkci SCSI_ProcessRead() lze snadno rozdělit na „před“ a „po“. To znamená, že kód, který začne číst, bude proveden v kontextu přerušení a zbývající kód se přesune do zpětného volání. Účelem tohoto zpětného volání je přesunout načtená data do výstupní vyrovnávací paměti (hostitel pak tato data nějakým způsobem načte s příslušnými požadavky)

Funkce SCSI_ProcessRead() přizpůsobená pro asynchronní čtení

/** * @brief SCSI_ProcessRead * Proces čtení popisovače * @param lun: Číslo logické jednotky * @retval status */ static int8_t SCSI_ProcessRead (USBD_HandleTypeDef *pdev, uint8_t lun) ( USBD_MSC_BOT_HandleTypeDef;utClassint3lenshmc2 =_tClasdeDef;usp3lenshms = MIN(hmsc->scsi_blk_len , MSC_MEDIA_PACKET) if(pdev->pClassSpecificInterfaceMSC->Read(lun , hmsc->bot_data, hmsc->scsi_blk_addr / hmsc->scsize,-v_blk_blk));< 0) { SCSI_SenseCode(pdev, lun, HARDWARE_ERROR, UNRECOVERED_READ_ERROR); return -1; } hmsc->bot_state = USBD_BOT_DATA_IN;


Ve zpětném volání potřebujete přistupovat k několika proměnným, které byly definovány ve funkci SCSI_ProcessRead() - ukazatel na USB handle, délka přenášeného bloku, LUN. Zde se hodí parametr kontextu. Je pravda, že jsem nepřenesl vše, ale pouze pdev a vše ostatní z něj lze extrahovat. Pokud jde o mě, tento přístup je jednodušší než přetahování celé struktury s požadovanými poli. A v každém případě je to lepší než mít několik globálních proměnných.

Přidejte dvojitý buffer

Tento přístup obecně fungoval, ale rychlost byla stále mírně vyšší než 200 kb/s (ačkoli zatížení procesoru bylo pevné a stalo se asi 2–3 %). Pojďme zjistit, co vám brání pracovat rychleji.

Podle rad v komentářích k jednomu z mých článků jsem si konečně pořídil osciloskop (i když levný). Ukázalo se, že je to velmi užitečné pro pochopení toho, co se tam děje. Vzal jsem nepoužitý pin a nastavil jsem ho na jedničku před zahájením čtení a na nulu po skončení čtení. Na osciloskopu proces čtení vypadal takto.

Tito. Samotné čtení 512 bajtů trvá o něco déle než 1 ms. Po ukončení čtení z karty se data přenesou do výstupní vyrovnávací paměti, odkud si je hostitel během následující 1ms vyzvedne. Tito. zde dochází buď ke čtení z karty nebo k přenosu přes USB sběrnici, ale ne současně.

Tato situace se obvykle řeší pomocí dvojitého ukládání do vyrovnávací paměti. Navíc USB periferie mikrokontrolérů STM32F103 již nabízejí mechanismy pro dvojitou vyrovnávací paměť. Ale nebudou nám vyhovovat ze dvou důvodů:

  1. Chcete-li použít dvojitou vyrovnávací paměť, kterou nabízí samotný mikrokontrolér, možná budete muset přepracovat jádro USB a implementaci MSC
  2. Velikost vyrovnávací paměti je pouze 64 bajtů, zatímco SD karta nemůže pracovat v blocích menších než 512 bajtů.
Budeme si tedy muset vymyslet vlastní implementaci. Nemělo by to však být těžké. Nejprve vyhradíme místo pro druhý buffer. Nevytvořil jsem pro to samostatnou proměnnou, ale jednoduše jsem zvýšil stávající vyrovnávací paměť 2krát. Také jsme museli vytvořit proměnnou bot_data_idx, která bude indikovat, která polovina tohoto dvojitého bufferu se aktuálně používá: 0 – první polovina, 1 – druhá.

Dvojitý buffer

typedef struct _USBD_MSC_BOT_HandleTypeDef ( ... USBD_MSC_BOT_CBWTypeDef cbw; USBD_MSC_BOT_CSWTypeDef csw; uint16_t bot_data_length; uint8_t bot_data; uint8_t bot_data_idx; ...; ...


Mimochodem, struktury cbw a csw jsou velmi citlivé na zarovnání. Některé hodnoty byly nesprávně zapsány nebo přečteny z polí těchto struktur. Proto jsme je museli přesunout výše, než jsou datové buffery.

Původní implementace fungovala na přerušení DataIn – signálu, že byla odeslána data. Tito. na příkaz hostitele bylo zahájeno čtení, po kterém byla data přenesena do výstupní vyrovnávací paměti. Čtení další části dat bylo „dobito“ přerušením DataIn. Tato varianta nám nevyhovuje. Začneme číst ihned po skončení předchozího čtení.

Čtení dobíjíme ihned po skončení předchozího

void cardReadCompletedCB(uint8_t res, void * kontext) ( USBD_HandleTypeDef * pdev = (USBD_HandleTypeDef *)kontext; USBD_MSC_BOT_HandleTypeDef *hmsc = pdev->pClassDataMSC; uint8_int b- MIN. 2 (hmsc-> scsi_blk _len, MSC_MEDIA_PACKET if(res != 0) ( SCSI_SenseCode(pdev, lun, HARDWARE_ERROR, UNRECOVERED_READ_ERROR); return; ) // Synchronizace, aby se zabránilo několika přenosům najednou // Toto musí být dokončeno jako předchozí, protože čeká. USB přenos / / zatímco níže uvedený kód připravuje další pdev->pClassSpecificInterfaceMSC->OnFinishOp(); // Uložte tyto hodnoty pro přenos dat uint8_t * txBuf = hmsc->bot_data + hmsc->bot_data_idx * MSC_MEDIA_PACKET správný stav // Poznámka: jsme v kontextu vlákna SD, nikoli přerušení USB // Takže hodnoty musí být správné, když dojde k přerušení DataIn hmsc->scsi_blk_addr += len /* případ 6: Hi = Di *; / hmsc->csw.dDataResidue -= len;


if (hmsc->scsi_blk_len == 0) ( hmsc->bot_state = USBD_BOT_LAST_DATA_IN; ) else ( hmsc->bot_data_idx ^= 1; hmsc->bot_data_length = MSC_MEDIA_PACKET; SCSI_de -ProcessRead (nekontroluje se chybový kód /vcessRead); SCSI_ProcessRead() již přechází do chybového stavu v případě selhání čtení ) // Nyní můžeme přenášet data načtená z SD USBD_LL_Transmit (pdev, MSC_IN_EP, txBuf, txSize); )

Ale to není všechno. Za druhé se změnila posloupnost akcí. Nyní je nejprve zpoplatněno čtení dalšího datového bloku a teprve poté je voláno USBD_LL_Transmit(). To je provedeno, protože funkce cardReadCompletedCB() je volána v kontextu běžného vlákna. Pokud nejprve zavoláte USBD_LL_Transmit() a poté změníte hodnoty polí hmsc, může být v tuto chvíli potenciálně vyvoláno přerušení z USB, které také chce změnit tato pole.

Za třetí jsme museli přidat další synchronizaci. Faktem je, že čtení z karty obvykle trvá o něco déle než přenos přes USB. Ale někdy se to stane obráceně a pak se volání USBD_LL_Transmit() pro další blok uskuteční dříve, než byl předchozí blok úplně odeslán. Jádro USB se z takové drzosti zblázní a data se posílají špatně.


Odeslání dat (Transmit) je potvrzeno událostí Data In, ale někdy dojde k několika přenosům za sebou. V takových případech je nutná synchronizace.

To lze vyřešit velmi jednoduše přidáním malé synchronizace. Do rozhraní USBD_StorageTypeDef jsem přidal pár funkcí s poměrně jednoduchou implementací (i když možná názvy nejsou moc dobré). Implementace využívá obvyklé . OnFinishOp(), která je volána ve zpětném volání cardReadCompletedCB(), přejde do režimu spánku a počká, dokud nebude odeslán předchozí datový paket.

Skutečnost odeslání je potvrzena událostí DataIn, která je zpracována funkcí SCSI_Read10(), která zavolá OnStartOp(), čímž se odemkne OnFinishOp(), která odešle další datový paket do domu, který Jack postavil. I když jsou funkce volány v opačném pořadí (a to je přesně to, co se stane během prvního čtení - nejprve SCSI_Read10(), poté cardReadCompletedCB()), pak bude vše také fungovat dobře (vlastnost semafor v režimu signal-wait) .

Implementace synchronizačních funkcí

void SD_MSC_OnStartOp() ( xSemaphoreGiveFromISR(usbTransmitSema, NULL); ) void SD_MSC_OnFinishOp() ( xSemaphoreTake(usbTransmitSema, portMAX_DELAY); )


S takovou synchronizací získá obrázek následující podobu.


Červené šipky označují synchronizaci. Poslední přenos čeká na předchozí Data In

Posledním kouskem skládačky je funkce SCSI_Read10().

Funkce SCSI_Read10(), která je volána v události Data In

/** * @brief SCSI_Read10 * Zpracovat příkaz Read10 * @param lun: Číslo logické jednotky * @param params: Parametry příkazu * @retval status */ static int8_t SCSI_Read10(USBD_HandleTypeDef *pdev, uint8_t lun , uint8_t USBOT_B_params) (THDMS_params) hmsc = pdev->pClassDataMSC // Synchronizace pro zamezení několika přenosů najednou pdev->pClassSpecificInterfaceMSC->OnStartOp(if(hmsc->bot_state == USBD_BOT_IDLE) /* Idle */ ( // Kontrola parametrů ...); hmsc- >scsi_blk_addr = ... hmsc->scsi_blk_len = ... hmsc->bot_state = USBD_BOT_DATA_IN; ... hmsc->bot_data_idx = 0 hmsc->bot_data_length = MSC_MEDIA_Process (sCPpACKET);


V původní implementaci SCSI_Read10() první volání funkce zkontrolovalo parametry a zahájilo proces čtení prvního bloku. Stejnou funkci později zavolá přerušení DataIn, když již byl odeslán předchozí paket a je třeba zahájit čtení dalšího. Obě větve začaly číst pomocí funkce SCSI_ProcessRead().

V nové implementaci se volání SCSI_ProcessRead() přesunulo do if a je voláno pouze pro čtení prvního bloku (bot_state == USBD_BOT_IDLE), zatímco čtení následujících bloků je spouštěno z cardReadCompletedCB().

Podívejme se, co z toho vzešlo. Schválně jsem přidal malé prodlevy mezi bloky čtení, abych viděl takové zářezy na osciloskopu. Ve skutečnosti je mezi čtením tak málo času, že to můj osciloskop nevidí.

Jak můžete vidět z tohoto obrázku, nápad byl úspěšný. Nová operacečtení začne okamžitě, jakmile skončí předchozí. Pauzy mezi čteními jsou poměrně malé a jsou diktovány hlavně hostitelem (stejná prodleva 1 ms mezi transakcemi). Průměrná rychlost čtení velké soubory dosahuje 400-440kb/s, což je docela dobré. A konečně, využití procesoru se pohybuje kolem 2 %.

A co nahrávka?

Tématu psaní do karty jsem se zatím taktně vyhýbal. Ale nyní se získanými znalostmi a pochopením ovladače MSC by implementace funkce nahrávání neměla být obtížná.

Původní implementace funguje asi takto.

  • Transakce zápisu SCSI
    • Příkaz je zpracován prostřednictvím funkčního řetězce MSC_BOT_DataOut() -> MSC_BOT_CBW_Decode() -> SCSI_ProcessCmd() -> SCSI_Write10()
    • Protože je ovladač ve stavu hmsc->bot_state == USBD_BOT_IDLE, je procedura zápisu připravena: zkontrolují se parametry příkazu a zapamatuje se, kolik bloků bude potřeba zapsat
    • Je volána funkce USBD_LL_PrepareReceive(), která připraví periferii USB na příjem datového bloku.
    • Ovladač přejde do stavu hmsc->bot_state = USBD_BOT_DATA_OUT
  • Transakce SCSI: Výstup dat
    • Zařízení přijímá data v 64bajtových paketech a ukládá data do poskytnuté vyrovnávací paměti. To vše se děje na nejnižší úrovni v jádře USB, ovladač MSC se na tom nepodílí
    • Po přijetí dat dojde k události Data Out a znovu se zavolá funkce SCSI_Write10().
    • Protože je ovladač ve stavu hmsc->bot_state == USBD_BOT_DATA_OUT, řízení přechází na funkci SCSI_ProcessWrite()
    • Zde je karta zapsána. synchronní režim
    • Pokud ještě nebyla přijata všechna data, pak se příjem „dobije“ voláním USBD_LL_PrepareReceive()
    • Pokud jsou zapsány všechny bloky, zavolá se funkce MSC_BOT_SendCSW(), která odešle hostiteli potvrzení (Control Status Word - CSW) a ovladač se přepne do stavu USBD_BOT_IDLE
  • SCSI transakce: Odezva
    • V tomto okamžiku již byl balíček stavu odeslán. Není vyžadována žádná akce
Nejprve přizpůsobme původní implementaci asynchronii funkce Write(). Stačí rozdělit funkci SCSI_ProcessWrite() a zavolat druhou polovinu ve zpětném volání.

Implementace funkce nahrávání

/ ** * @Brief SCSI_PROCESSWRITE * PROCES WRITE PROCES * @Param LUN: LOGICKÁ JEDNOTKA ČÍSLO * @RETVAL STATUS */ static Int8_T SCSI_PROCESSWRITE (usbd_handlepedef * pdev, uint8_t lun) (uint32_t len; usbd_msc_Bot_HanDleped * hmsc; = MIN(hmsc->scsi_blk_len , MSC_MEDIA_PACKET, if(pdev->pClassSpecificInterfaceMSC->Write(lun , hmsc->bot_data, hmsc->scsi_blk_addr, hmsc->scsibl_blk) / hmsc->scsibl_blkde));< 0) { SCSI_SenseCode(pdev, lun, HARDWARE_ERROR, WRITE_FAULT); return -1; } return 0; } return 0; } void cardWriteCompletedCB(uint8_t res, void * context) { USBD_HandleTypeDef * pdev = (USBD_HandleTypeDef *)context; USBD_MSC_BOT_HandleTypeDef *hmsc = pdev->pClassDataMSC;


uint8_t lun = hmsc->cbw.bLUN; uint32_t len ​​​​= MIN(hmsc->scsi_blk_len , MSC_MEDIA_PACKET);// Nejprve zkontrolujte kód chyby if(res != 0) ( SCSI_SenseCode(pdev, lun, HARDWARE_ERROR, WRITE_FAULT); return; ) hmsc->scsi_blk_addr += len;

Rychlost záznamu v tomto režimu je asi 90 kb/s a je omezena především rychlostí zápisu na kartu. To potvrzuje oscilogram - každý vrchol je záznamem jednoho bloku. Soudě podle obrázku trvá zápis 512 bajtů 3 až 6 ms (pokaždé jinak).

Navíc záznam se někdy může držet od 100ms do 0,5s - zřejmě je někde v kartě potřeba různých interních činností - přemapování bloků, mazání stránek a podobně.

Na základě toho je nepravděpodobné, že by dokončení dvojité vyrovnávací paměti radikálně zlepšilo situaci. Čistě ze sportovního zájmu se o to ale ještě pokusíme.

Takže podstatou cvičení je přijmout další blok od hostitele, zatímco ten předchozí se zapisuje na kartu. Možnost, která nás okamžitě napadne, je začít psát a přijímat další blok současně někde ve funkci SCSI_Write10(), tzn. na události DataOut (příjem dalšího bloku je dokončen). Nic nebude fungovat jen tak. protože příjem je mnohem rychlejší než zápis a lze přijmout více dat, než dokáže karta zapsat. Tito. následující data přepíší dříve přijatá, ale dosud nezpracovaná data.


V tomto schématu lze přijmout několik paketů za sebou, ale ne všechny stihnou zapsat na SD kartu. S největší pravděpodobností budou některá data oříznuta dalším blokem.

Musíte provést synchronizaci. Ale kde? V případě operace čtení jsme zorganizovali dvojité ukládání do vyrovnávací paměti a synchronizaci v místě, kde končí čtení z karty a data se přenášejí na USB. Tímto místem byla funkce cardReadCompletedCB(). V případě operace zápisu bude takovým centrálním místem funkce SCSI_Write10() - zde se ocitneme při příjmu dalšího bloku dat a zde začneme zapisovat na kartu.

Mezi funkcemi cardReadCompletedCB() a SCSI_Write10() je ale jeden zásadní rozdíl – první funguje v proudu SD karty a druhá v přerušení USB. Normální vlákno může být pozastaveno během čekání na nějakou událost nebo synchronizační objekt. Tento trik nebude fungovat s přerušením – všechny funkce FreeRTOS s příponou FromISR jsou neblokující. Buď fungují podle očekávání (zabaví zdroj, pokud je volný, odesílají/přijímají zprávy přes frontu, pokud je místo nebo požadovanou zprávu), nebo tyto funkce vrátí chybu. Ale nikdy nečekají.

Ale pokud není možné zorganizovat čekání v přerušení, můžete se pokusit zajistit, aby se přerušení vůbec nevolalo. Přesněji řečeno i takto: aby k přerušení došlo přesně tolikrát, kolikrát a v takových chvílích, kdy to potřebujeme.

Podívejme se na několik případů, které mohou nastat během přijímacího/zápisového řízení.

Případ č. 1: příjem prvního bloku. Jakmile je přijat první blok, můžete začít nahrávat tento blok. Zároveň můžete začít brát druhý blok. Tím se odstraní pauza, kdy nepřijmeme další blok, zatímco ten předchozí se zapisuje na kartu.

Případ č. 2: Přijetí bloku uprostřed transakce. S největší pravděpodobností budou oba buffery již plné. Někde ve streamu SD karty se zapisuje blok dat z prvního bloku, zatímco druhý blok jsme právě přijali od hostitele. V principu nic nebrání zpoplatnění záznamu druhého bloku - na vstupu je fronta (viz funkce SD_MSC_Read() výše), která reguluje vstupní požadavky a bude postupně zapisovat bloky. Jen se musíte ujistit, že je v této frontě místo pro 2 požadavky.

Jak ale regulovat příjem? Máme pouze 2 přijímací vyrovnávací paměti. Pokud ihned po přijetí druhého bloku začnete přijímat další blok, dojde k přepsání dat v první vyrovnávací paměti, kam se právě zapisuje karta. V tomto případě by bylo správnější začít přijímat další datový blok ve chvíli, kdy se vyrovnávací paměť uvolní – když záznam skončí (tedy při zpětném volání funkce nahrávání).

Konečně případ č. 3: musíte být schopni správně dokončit přijímací/registrační řízení. S posledním blokem je vše jasné – místo příjmu dalšího bloku je potřeba poslat hostiteli CSW, že data byla přijata a transakce může být uzavřena. Musíme si ale uvědomit, že na začátku transakce jsme již zorganizovali blok navíc, takže předposlední blok by neměl nařizovat příjem dalšího bloku.

Zde je obrázek, který tyto případy popisuje.


Případ 1: na prvním DataOut okamžitě začneme přijímat druhý blok. Případ 2: další blok začneme přijímat až po dokončení nahrávání a uvolnění vyrovnávací paměti. Případ 3: nezačneme přijímat na předposlední nahrávce, ale na poslední posíláme CSW

Zajímavý postřeh: pokud zápis na kartu přichází z první vyrovnávací paměti, pak po dokončení zápisu bude další blok přijat do stejné první vyrovnávací paměti. Přesně to samé s druhým bufferem. Této skutečnosti bych rád využil při své realizaci.

Pokusme se realizovat naše plány. Pro implementaci prvního případu (přijetí dalšího bloku) potřebujeme speciální stav

Nový stav pro příjem prvního bloku

#define USBD_BOT_DATA_OUT_1ST 6 /* Stav datového výstupu pro první přijímací blok */


A jeho zpracování

/** * @brief MSC_BOT_DataOut * Zpracování dat MSC OUT * @param pdev: instance zařízení * @param epnum: index koncového bodu * @retval Žádné */ void MSC_BOT_DataOut (USBD_HandleTypeDef *pdev, uint8_t pdevHm a USBT epnum_B> USBD_t epnum_B) pClassDataMSC; přepínač (hmsc->bot_state) ( případ USBD_BOT_IDLE: MSC_BOT_CBW_Decode(pdev); přerušení; případ USBD_BOT_DATA_OUT: případ USBD_BOT_DATA_OUT_1ST: if(SCSI_ProcessCmd (pdev)c->wbwsc.< 0) { MSC_BOT_SendCSW (pdev, USBD_CSW_CMD_FAILED); } break; default: break; } }


Chcete-li implementovat druhý případ (přijetí bloku po dokončení nahrávání), musíte nějakým způsobem předat určité množství informací zpětnému volání. Za tímto účelem jsem vytvořil strukturu s kontextem nahrávání a deklaroval 2 instance této struktury v rukojeti USB.

Kontext nahrávání

typedef struct (uint32_t next_write_len; uint8_t * buf; USBD_HandleTypeDef * pdev; ) USBD_WriteBlockContext; typedef struct _USBD_MSC_BOT_HandleTypeDef ( ... USBD_WriteBlockContext write_ctxt; ... ) USBD_MSC_BOT_HandleTypeDef;


Nezapomeňte změnit velikost záznamové fronty ve streamu SD karty

Inicializace fronty

// Inicializace vlákna zodpovědného za komunikaci s kartou SD bool initSDIOThread() ( // Inicializace synchronizace sdCmdQueue = xQueueCreate(2, sizeof(IOMsg)); … )


Funkce SCSI_Write10() se změnila jen málo, byla přidána pouze inicializace indexu dvojité vyrovnávací paměti a přechod do stavu USBD_BOT_DATA_OUT_1ST

Funkce SCSI_Write10().

/** * @brief SCSI_Write10 * Příkaz Process Write10 * @param lun: Číslo logické jednotky * @param params: Parametry příkazu * @retval status */ static int8_t SCSI_Write10 (USBD_HandleTypeDef *pdev, uint8_t lun , uint8_t lun , uint8_t USBDyt8_T *params_TyB) hmsc = pdev->pClassDataMSC; if (hmsc->bot_state == USBD_BOT_IDLE) /* Idle */ ( // Kontrola parametrů ... hmsc->scsi_blk_addr = ... hmsc->scsi_blk_len = ... /* Připravte EP pro příjem prvního datového paketu */ hmsc->bot_state = USBD_BOT_DATA_OUT_1ST; hmsc->bot_data_idx = 0; ( return SCSI_ProcessWrite(pdev, lun); ) return 0;


Veškerá nejzajímavější logika se soustředí ve funkci SCSI_ProcessWrite() – zde budou distribuovány vyrovnávací paměti a bude sestaven celý řetězec čtení a zápisu.

Funkce SCSI_ProcessWrite().

/** * @brief SCSI_ProcessWrite * Proces zápisu popisovačů * @param lun: Číslo logické jednotky * @retval status */ static int8_t SCSI_ProcessWrite (USBD_HandleTypeDef *pdev, uint8_t lun) ( USBD_MSC_BOT_Handle2TypeTypeDefin *hmmc_MSCata p-Dlasv ​​u *hmmc_MSCata p-lasv ​​u *hm = MIN (hmsc->scsi_blk_len , MSC_MEDIA_PACKET) USBD_WriteBlockContext * ctxt = hmsc->write_ctxt + hmsc->bot_data_idx // Zjistěte, co dělat po zapsání bloku if(hmsc->scsi_blk_len) (c-txt); next_write_len = 0xffffffff; else if(hmsc->scsi_blk_len == len + MSC_MEDIA_PACKET) ( ctxt->next_write_len = 0; ) else ( ctxt->next_write_len = MINET(hmsc->scsi_blk_len) / / Připravte další pole kontextu ctxt->buf = hmsc->bot_data + hmsc->bot_data_idx * MSC_MEDIA_PACKET; ctxt->pdev = pdev // Nepovolit několik příjemů najednou if(hmsc->bot_state != USBD_BOT_DATA_OUT_1; ) pdev ->pClassSpecificInterfaceMSC->OnStartOp( // Zápis přijatých dat if(pdev->pClassSpecificInterfaceMSC->Write(lun , ctxt->buf, hmsc->scsi_blk_addr / hmsc->scsi_blk_blks_); ctxt)< 0) { SCSI_SenseCode(pdev, lun, HARDWARE_ERROR, WRITE_FAULT); return -1; } // Switching blocks hmsc->bot_data_idx ^= 1;


hmsc->scsi_blk_addr += len;
  • hmsc->scsi_blk_len -= len;
  • /* případ 12: Ho = Do */ hmsc->csw.dDataResidue -= len;
  • // Provedení jednoho dalšího příjmu poprvé, aby bylo možné paralelně spouštět operace příjmu a zápisu if(hmsc->bot_state == USBD_BOT_DATA_OUT_1ST && hmsc->scsi_blk_len != 0) ( hmsc->bot_state = USBD_BOT_DATA_OUT; pERecev,Prepar MSC_OUT_EP, hmsc->bot_data + hmsc->bot_data_idx * MSC_MEDIA_PACKET, // MIN druhé vyrovnávací paměti (hmsc->scsi_blk_len, MSC_MEDIA_PACKET) ) návrat 0; )
Nejprve je zde připraven nahrávací kontext - informace, která bude předána zpětnému volání. Konkrétně se zde rozhodujeme, co uděláme, až záznam tohoto bloku skončí:

v obvyklém případě začneme přijímat další blok do stejného bufferu (případ č. 2 z výše popsaných)

Část odpovědi tohoto kódu je zpětné volání o dokončení zápisu na kartu. V závislosti na tom, který blok byl zaznamenán, se buď přijme další blok, odešle CSW, nebo se nic nestane.

Zpětné volání funkce nahrávání

void cardWriteCompletedCB(uint8_t res, void * kontext) ( USBD_WriteBlockContext * ctxt = (USBD_WriteBlockContext*)kontext; USBD_HandleTypeDef * pdev = ctxt->pdev; USBD_MSC_BOT_Handle_TypeTypeTypeFlassm8C ludeuxshm8 sc->cbw.bL OSN; / Nejprve zkontrolujte kód chyby if(res != 0) ( SCSI_SenseCode(pdev, lun, HARDWARE_ERROR, WRITE_FAULT); return; ) if (ctxt->next_write_len == 0xffffffff) ( MSC_BOT_SendCSW (pdev, USBD-ASSED_vD) else >pClassSpecificInterfaceMSC->OnFinishOp(ctxt->next_write_len != 0) ( /* Připravte EP pro příjem dalšího paketu */ USBD_LL_PrepareReceive (pdev, MSC_OUT_EP, ctxt->buf, ctxt->další)_zápis));


Závěrečným akordem je synchronizace, jejíž podstata je snadněji vidět na obrázku.

Velmi zřídka, ale přesto někdy nastane situace, kdy zápis na kartu skončí před přijetím dalšího paketu. V důsledku toho by kód (pokud nedošlo k synchronizaci) mohl požádat o přijetí dalšího paketu, ačkoli ten aktuální ještě nebyl plně přijat. Aby se to nestalo, museli jsme přidat synchronizaci. Nyní, než požádáte o přijetí dalšího bloku, kód počká, dokud nebude přijat předchozí. Synchronizační nástroje, které byly použity při čtení (OnStartOp()/OnFinishOp()), jsou docela vhodné.

Podmínky, za kterých je potřeba synchronizovat, jsou docela ošemetné. Přijetím dalšího bloku na začátku transakce dojde k synchronizaci s posunem o jeden blok. Proto zpětné volání pro zápis N-tého bloku čeká na příjem N+1 bloků. To zase znamená, že příjem prvního bloku (vyskytuje se v souvislosti s přerušením USB) a zápis posledního bloku (dochází v kontextu toku SD karty) nepotřebuje synchronizaci.

Může se zdát, že červená šipka duplikuje černou, která začne nahrávat další blok. Ale když se podíváte na kód, můžete vidět, že tomu tak není. Červená (synchronizace) synchronizuje kód v ovladači MSC (modrý čtverec), zatímco fronta je zpracovávána v ovladači karty (kde je smyčka hlavního vlákna SD karty). Opravdu jsem nechtěl zasahovat do kódu různých komponent.

Nastavil jsem nějaké logování ladění, záznam 4kb dat vypadá asi takto

Záznam protokolu ladění 4 kb blok

Spuštění operace zápisu pro LBA=0041C600, len=4096
Příjem prvního bloku do buf=1
Zápis bloku dat pro LBA=0041C600, len=512, buf=0
Bude to pravidelný blok
Příjem dalšího bloku do buf=1
Zápis bloku dat pro LBA=0041C800, len=512, buf=1
Bude to pravidelný blok


Zápis bloku dat pro LBA=0041CA00, len=512, buf=0
Bude to pravidelný blok


Zápis bloku dat pro LBA=0041CC00, len=512, buf=1
Bude to pravidelný blok
Zapište dokončené zpětné volání se stavem 0 (buf=0)
Příprava dalšího příjmu do buf=0
Zápis bloku dat pro LBA=0041CE00, len=512, buf=0
Bude to pravidelný blok
Zapište dokončené zpětné volání se stavem 0 (buf=1)
Příprava dalšího příjmu do buf=1
Zápis bloku dat pro LBA=0041D000, len=512, buf=1
Bude to pravidelný blok
Zapište dokončené zpětné volání se stavem 0 (buf=0)
Příprava dalšího příjmu do buf=0
Zápis bloku dat pro LBA=0041D200, len=512, buf=0
Toto bude jeden před posledním blokem
Zapište dokončené zpětné volání se stavem 0 (buf=1)
Příprava dalšího příjmu do buf=1
Zápis bloku dat pro LBA=0041D400, len=512, buf=1
Toto bude poslední blok
Zapište dokončené zpětné volání se stavem 0 (buf=0)
Zapište dokončené zpětné volání se stavem 0 (buf=1)
Zápis dokončen. Odesílání CSW


Podle očekávání to nepřidalo výrazné zvýšení rychlosti. Po úpravě byla rychlost 95-100 kb/s. Ale jak jsem řekl, bylo to všechno ze sportovního zájmu.

Je to možné ještě rychleji?

Zkusme to. Někde uprostřed práce jsem si náhodou všiml, že čtení jednoho bloku a čtení sekvence bloků jsou různé příkazy SD karty. Jsou dokonce reprezentovány různými metodami ovladače karet - readBlock() a readBlocks(). Stejně tak se liší příkazy pro zápis jednoho bloku a zápis řady bloků.

Protože je ovladač MSC navržen tak, aby ve výchozím nastavení pracoval s jedním blokem za jednotku času, mělo smysl nahradit readBlocks() za readBlock(). K mému překvapení se rychlost čtení dokonce zvýšila a dosáhla 480-500kb/s! Bohužel podobný trik s funkcemi nahrávání nepřinesl žádné zvýšení rychlosti.

Hned od začátku mě ale trápila jedna otázka. Podívejme se ještě jednou na obrázek čtení. Mezi zářezy (čtení jednoho bloku) - asi 2 ms.

Můj takt SPI je nastaven na 18 MHz (pomocí děliče frekvence jádra 72 MHz po 4). Teoreticky by přenos 512 bajtů měl trvat 512 bajtů * 8 bitů / 18 MHz = 228 µs. Ano, bude zde určitá režie pro synchronizaci několika vláken, řazení do fronty a další věci, ale to v žádném případě nevysvětluje rozdíl téměř 10krát!

Pomocí osciloskopu jsem změřil, jak dlouho jednotlivé části operace čtení skutečně trvaly.

K mému překvapení se ukázalo, že nejvíce dlouhý provoz nečte data vůbec, ale interval mezi příkazem read a potvrzením z karty, že je karta připravena a data lze načíst. Navíc tento interval dost silně kolísá v závislosti na různé parametry- frekvence požadavků, velikost čtených dat a adresa čteného bloku. Poslední bod je velmi zajímavý - čím dále od začátku mapy je blok, který je třeba přečíst, umístěn, tím rychleji se čte (alespoň to byl případ mé experimentální mapy)

Podobný (ale smutnější) snímek je pozorován při záznamu na kartu. Nebyl jsem schopen změřit všechna časování dostatečně dobře, protože... plavali v poměrně širokém rozmezí, ale vypadá to asi takto.

To vše umocňuje docela vysoká zátěž CPU – cca 75 %. Samotný zápis by měl teoreticky trvat stejně 228 mikrosekund jako čtení – jsou taktovány na stejných 18 MHz. Pouze v tomto případě se stále zobrazuje synchronizace vláken FreeRTOS. Zřejmě kvůli velkému zatížení procesoru a nutnosti přepnutí na jiná vlákna (s vyšší prioritou) je celkový čas mnohem delší.

Největší smutek je ale čekání, až bude karta hotová. Je mnohonásobně větší než v případě čtení. Navíc se zde karta může zaseknout na 100 nebo dokonce 500 ms. V ovladači karty je navíc tato část implementována aktivním čekáním, což vede k velmi vysoké zátěži procesoru

Aktivně čeká, až bude karta připravena

// čekání, až karta nebude zaneprázdněna bool SdSpiCard::waitNotBusy(uint16_t timeoutMS) ( uint16_t t0 = curTimeMS(); while (spiReceive() != 0XFF) ( if (isTimedOut(t0, timeoutMS)) ( return false; ) ) vrátit true )


V kódu jsou větve, které přidají volání SysCall::yield() do smyčky, ale obávám se, že to situaci nevyřeší. Toto volání jednoduše doporučuje, aby se plánovač úloh přepnul do jiného vlákna. Ale jelikož moje ostatní vlákna většinou spí, situaci to nijak dramaticky nezlepší - karta nepřestane být hloupá.

Další vtipný moment. Ve FreeRTOS se kontexty přepínají pomocí přerušení SysTick, které je standardně nastaveno na 1 ms. Z tohoto důvodu je mnoho operací na osciloskopu pečlivě zarovnáno na mřížce s násobkem kroku 1 ms. Pokud karta není pomalá a načtení jednoho bloku společně s čekáním trvá méně než 1 ms, pak se včetně všech vláken, synchronizací a front můžete otočit jedním tikem. Teoretická maximální rychlost čtení v takovém modelu je tedy přesně 500 kb/s (0,5 kb za 1 ms). Dobrou zprávou je, že se to daří!


Ale tahle věc se dá obejít. K vyrovnání 1 ms dochází z následujícího důvodu. Přerušení z USB nebo DMA není vázáno na nic a může nastat někde uprostřed tik. Pokud přerušení změnilo stav synchronizačního objektu (například odemklo semafor nebo přidalo zprávu do fronty), FreeRTOS se o tom okamžitě nedozví. Když přerušení vykoná svou práci, řízení se přenese na vlákno, které fungovalo před přerušením. Když tick skončí, zavolá se plánovač a v závislosti na stavu synchronizačního objektu se může přepnout na příslušné vlákno.

Ale právě pro takové případy má FreeRTOS mechanismus pro vynucení zavolání plánovače. Jak jsem řekl dříve, nemůžete přerušit přerušení. Můžete ale naznačit potřebu zavolat plánovači (zdůrazňuji: nevolejte plánovači, ale naznačte potřebu zavolat). Přesně to dělá funkce portYIELD_FROM_ISR().

Požádejte plánovače o přepnutí vláken ihned po přerušení

void SdFatSPIDriver::dmaTransferCompletedCB() ( // Obnovení vlákna SD BaseType_t xHigherPriorityTaskWoken; xSemaphoreGiveFromISR(xSema, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xTask)HigherenyPriority);


Nyní, když zpracování přerušení (řekněme z DMA) skončí, bude automaticky vyvoláno přerušení PendSV, v jehož handleru je volán plánovač. To zase násilně přepne kontext a přenese řízení na vlákno, které čekalo na semafor. Že. Dobu odezvy na přerušení lze výrazně zkrátit a ve výsledku vám tento trik umožňuje přetaktovat čtení na testovací kartě až na 600 kb/s!

Ale to je v případě, že se nebude dlouho čekat, než bude karta připravena. Bohužel pokud karta dlouho přemýšlí, tak se čtení protáhne o 2 tiky (a zápis o 4-6) a rychlost se ukáže být výrazně nižší. Navíc, pokud je do karty neustále vtloukán aktivní čekací kód a karta dlouhou dobu nereaguje, pak může projít celý tik. V tomto případě může plánovač OS rozhodnout, že toto vlákno běží příliš dlouho, a obecně přepne řízení na jiná vlákna. To může způsobit další zpoždění.

To vše jsem mimochodem testoval na 8GB kartě třídy 6. Zkoušel jsem i několik dalších karet, které jsem měl po ruce. Jiná karta, také 8GB, ale třída 10 z nějakého důvodu dávala jen 300-350 kb/s pro čtení, ale 120 kb/s pro zápis. Dokonce jsem risknul vsadit největší a rychlá mapa, který jsem měl, bylo 32 GB. S ním se nám podařilo dosáhnout maximální rychlosti - 650kb/s pro čtení a 120kb/s pro zápis. Mimochodem rychlosti, které uvádím, jsou průměrné. Neměl jsem čím měřit okamžitou rychlost.

Jaké závěry lze z této analýzy vyvodit?

  • Za prvé, SPI zjevně není nativní rozhraní pro SD karty. I cool karty jsou v nejběžnějších operacích nudné. Tady má smysl se dívat směrem k SDIO (už jsem si na poště vyzvedl balík s STM32F103RCT6 - má podporu SDIO z krabice)
  • Za druhé, mapa je jiná. Ten budete muset hledat. I když při připojení přes SDIO to nebude tak kritické
  • Za třetí, pokud máte dostatek paměti, můžete přejít na čtení ve větších blocích (řekněme 4k). Pak bude velká prodleva na začátku čtení/zápisu kompenzována vysokou přenosovou rychlostí. Zatím je 20kb paměti mého řadiče (STM32F103C8T6) téměř zaplněno a měl jsem problém najít i 512 bajtů pro dvojité vyrovnávací paměti

Závěr

V tomto článku jsem řekl, jak se mi podařilo upgradovat implementaci USB MSC od STMicroelectronics. Na rozdíl od jiných mikrokontrolérů řady STM32 nemá řada F103 vestavěnou podporu DMA pro USB. Ale s pomocí FreeRTOS jsem byl schopen povolit čtení/zápis na SD kartu přes DMA. No, abychom to využili co nejefektivněji propustnost Podařilo se mi nainstalovat dvojitou vyrovnávací paměť na USB sběrnici.

Výsledek předčil má očekávání. Zpočátku jsem cílil na rychlost kolem 400 kb/s, ale podařilo se mi vymáčknout až 650 kb/s. Pro mě ale nejsou důležité ani absolutní ukazatele rychlosti, ale fakt, že této rychlosti je dosaženo s minimálním zásahem procesoru. Takto jsou data přenášena pomocí DMA a USB periferie a procesor je připojen pouze k nabití další operace.

S nahráváním však nebylo možné získat superrychlosti - pouze 100-120 kb/s. Důvodem jsou obrovské časové limity samotné SD karty. Protože je karta připojena přes SPI, zdá se, že neexistuje žádný jiný způsob, jak zjistit, zda je karta připravena (kromě neustálého dotazování). Z tohoto důvodu existuje docela vysoké zatížení procesor při operacích zápisu. Tajně doufám, že připojením karty přes SDIO dosáhnete mnohem vyšších rychlostí.

Snažil jsem se nejen poskytnout kód, ale také říct, jak to funguje a proč to funguje tímto způsobem. Možná to pomůže udělat něco podobného pro jiné ovladače nebo knihovny. Nezvýrazňoval jsem to samostatná knihovna, protože tento kód závisí na jiných částech mého projektu a knihovně FreeRTOS. Navíc jsem svůj kód založil na . Pokud tedy chcete použít moji verzi, budete ji muset backportovat do původní knihovny.

  • hal
  • msc
  • hromadné úložiště
  • Přidejte značky

    O Režim USB Mass Storage, neznámé, kde „zmizelo“ z Androidu Zmrzlina Sandwichi, na internetu v dnešní době zuří spousta vášní. Všichni mluvili, jak ti, kteří vědí, ale zřídka mluví, tak ti, kteří nevědí, ale vždy mají svůj názor. Vášně zuří tak vážně, že jsem se dokonce musela „vrátit“ z blogerského důchodu, jít na LiveJournal a napsat pár řádků.

    Abych byl rychlý a k věci:
    - Režim USB Mass Storage je režim pro připojení SD karty k telefonu, kdy se telefon promění ve čtečku karet SD.
    - Režim USB Mass Storage vyžaduje samostatnou SD kartu v telefonu, vyměnitelnou nebo vestavěnou. To znamená, že záleží na konkrétní konfiguraci hardwaru v telefonu.
    - Podpora pro USB Mass Storage z Androidu 4.x ICS nezmizela - stačí nainstalovat ICS na Nexus S a uvidíte, že tam je a funguje skvěle.
    - Velkokapacitní úložiště USB není na Galaxy Nexus podporováno, opět kvůli jeho hardwarové konfiguraci. Někdo si prostě spletl hardware se softwarem a lidé to zvedli.
    - Pro ty, kteří se bojí o budoucí telefony: nebojte se. Výrobci telefonů si dobře uvědomují, že na mnoha trzích je pro kupující důležité mít SD kartu v telefonu a mnoho modelů bude obsahovat podporu vyměnitelné externí SD karty, často spolu s vestavěnou „interní“ SD kartou.


    Začněme z dálky. S konfigurací pevných disků se při sestavování domácího počítače setkali všichni geekové. Někdo si koupí jeden velký a rozdělí ho na více oddílů, někdo si koupí malé SSD, aby na něj nainstaloval systém a velké interní SATA na ukládání souborů, někdo koupí také externí USB pro ukládání/přenos filmů, fotografií a hudby. Musíte zjistit, jakou velikost disků koupit, jakou rychlost, jaké rozhraní připojení, jaká společnost je vyrobila atd. Je to známá situace? Výrobci telefonů se tedy při navrhování a výrobě svého dalšího modelu potýkají s podobnou situací.

    Co byste si měli uložit do telefonu? Ano, téměř totéž:
    1. Systémové soubory (OS)
    2. Nainstalované programy a jejich soubory
    3. Uživatelské soubory

    Řekněme, že se o to musíte ujistit různé typy data byla uložena na různých jednotkách: C, D a E. Mluvím o tomto rozdělení a jeho výhodách. Dovolte mi připomenout, že takové rozdělení poskytuje určitou „flexibilitu“, a to i na ploše. Potřebujeme například přeinstalovat systém – naformátujeme C, ale data na D a E nám zůstanou. Tehdy jsme mluvili hlavně o C a D. Teď pojďme mluvit o E.

    E je místo, kde vy a vaše programy ukládáte svá data, zvláště pokud tato data zabírají hodně místa. Obvykle tam dáváte svůj mediální obsah (filmy, hudbu, fotky), kamera tam zapisuje nové fotky a videa, GPS navigátory nahrávají si tam své gigabajtové karty atd. V systému Android je to vaše karta SD. Historicky byla „interní“ flash paměť pro C a D drahá, a pokud bylo potřeba uložit něco velkého, muselo to být provedeno na externí levnou microSD, tedy disk E. Další radostí z toho bylo, že uživatel dokázal upravit velikost „E drive“ a také použít microSD k výměně dat s jinými zařízeními, pro tento účel byl formátován pod souborovým systémem VFAT, srozumitelným pro každého. Něco jako kdybyste měli jednotku E jako externí pevný disk USB a připojili ji k jinému počítači, abyste si „stahovali filmy“.

    Pokračujeme v analogii mezi microSD a USB externím pevným diskem, řekněme, že proces připojení k jinému zařízení (počítač, přehrávač médií atd.) vyžadoval úplné odpojení od smartphonu. Pevný disk USB nelze zapojit do dvou zařízení najednou. Když byl tedy zapnut režim velkokapacitního úložiště USB, řadič čtení/zápisu na kartu SD se zcela přepnul na USB. V důsledku toho již telefon neměl přístup k SD kartě a jeho ovladač fungoval jako USB čtečka. Jako u každé čtečky pracoval počítač přímo s SD kartou. To znamená, že pokud by byla naformátována pro systém souborů, který operační systém běžící na počítači nezná, operační systém vám jednoduše sdělí, že byla narušena integrita karty SD a je třeba ji naformátovat. Takhle funguje každá čtečka, nerozumí struktuře souborů na disku, prostě dává přístup k „blokům“ (clusterům) na něm, zbytek musí „pochopit“ OS.

    Výrobci hodně experimentují. Do některých telefonů dávají rychlé, drahé malé C, méně rychlé, levnější a větší D a nechají uživatele vybrat E (Galaxy S), v některých C a D „žijí“ na stejném „fyzickém pevném disku“ a E stále žije na microSD. A v některých (tablety, Galaxy Nexus) - vše žije jako logické oddíly na jednom velkém čipu. A stává se, že výrobci instalují interní logické E a dávají uživateli možnost vložit jednotku F - externí microSD (Samsung Vibrant). Výběr konfigurace závisí na mnoha faktorech. Faktory se liší v závislosti na společnosti. Například v zařízeních od Applu bude vše žít na jednom interním drahém čipu. U ostatních bude konfigurace určována především cenou komponentů, přáním operátorů a prodejním trhem. Android (díky použití linuxového jádra) snadno podporuje jakoukoli konfiguraci.

    Nyní přejděme k linuxové terminologii, kde všechna zařízení (logické disky, disketové jednotky, CD-ROM atd.) jsou adresáře a nazývejme věci pravými jmény. C se změní na /system, D - /data, E - /sdcard. Na tabletech (mimochodem, proč tehdy nevzbudili všechen ten povyk? Kam hledali kritici Androidu?) a na Galaxy Nexus /sdcard je pouze adresář /data/media, připojený přes FUSE. (Ach, FUSE je úžasná věc, o tom jindy, pro úplně beznadějné geeky). V normálním jazyce můžeme říci, že /sdcard se stal takovým záludným odkazem na /data/media. To znamená, že jednotka E se stala odkazem na adresář na jednotce D. uživatelské programy a uživatelský mediální obsah sdílí společný prostor na disku. A neukazuje se, že pro hudbu zbývá ještě hodně místa, ale potřebujete prostor pro další program, ale teď už žádný nezbyl. Stejně jako na iPhonu.

    Proč pro tuto konfiguraci nelze povolit režim velkokapacitního úložiště USB? Totiž proto, že je nutné propojit celý " logický pohon"okamžitě. Ale ten váš není skutečný, protože je speciálně vyrobený tak, aby měl proměnnou velikost. A abyste se mohli připojit, musíte vědět, který kus čipu je přidělen pro požadovaný logický disk, tzn. , opravte jeho velikost, což se vám nechce „Proč ne celý čip,“ prominete mi, „připojím celý čip a já na to přijdu sám celý čip a nejen že máte /data a /system, ale je také dost pravděpodobné, že boot , recovery, bootloader je poměrně složitý systém několika oddílů (logických jednotek), z nichž několik je v málo srozumitelném ext4. souborový systém A pokud to celé připojíte k počítači jako externí USB disk, každý počítač vám nabídne naformátování tohoto nešvaru, který není třeba vysvětlovat, co se stane, když se disk C na vašem počítači náhle otočí k formátování.

    Jako náhrada je nám nabídnut protokol MTP (Media Transfer Protocol), při použití váš počítač vidí telefon jako MP3 přehrávač. V systému Windows tomuto protokolu rozumí systém Windows Přehrávač médií(a Windows sám tomu rozumí, počínaje Vista), což vám umožní přenášet mediální obsah z vaší knihovny médií (ze stejného přehrávače médií) do telefonu. Řešení je méně demokratické než USB Mass Storage, ale demokratičtější než iTunes. Rozhodnutí je kontroverzní a ne každému se bude líbit, ale zvažte následující:

    Galaxy Nexus, i když jde o vlajkovou loď pro Android Ice Cream Sandwich, byl vytvořen, aby demonstroval ne všechny možnosti operačního systému, ale pouze jejich část. A v žádném případě nejsou možnosti OS omezeny možnostmi tohoto telefonu.

    Zařízení Nexus jsou jakýmsi „hřištěm“ Google, platformou pro testování nových funkcí a nápadů. Je pravděpodobné, že někteří z nich někam půjdou a někteří ne. Ale stejně byste je měli vyzkoušet.

    Zařízení Nexus nejsou dostupná jiným výrobcům. Google neukládá žádná omezení na konfiguraci telefonu. I když Android nepodporuje konkrétní hardware, přidání podpory výrobce nic nestojí – zdrojový kód je otevřený, a to bylo provedeno více než jednou. Takže i kdyby podpora pro USB Mass Storage náhle zmizela z ICS na příkaz Google, byla by rychle přidána zpět (není to těžké, věřte mi).

    Na ICS budou telefony s vyměnitelnou SD kartou a USB Mass Storage. Pro trhy, kde jsou 3G připojení pomalá nebo mají špatné pokrytí (např. Indie), je SD karta v telefonu jedním z hlavních nákupních faktorů. A věřte, že výrobci telefonů to moc dobře vědí. A vědí, že to je poptávka nejen v Indii.

    Pokud jste skutečný geek, vždy najdete způsob, jak přenést jakékoli (nejen mediální) soubory do telefonu. Tip: adb push soubor /sdcard/

    P.S. Konspirační teoretici: Co si myslíte o myšlence, že Google zvolil tuto konfiguraci, aby se vyhnul použití VFAT (povinné při použití SD karty), v obavě ze soudního sporu od Microsoftu na základě 17 let starého patentu za používání dlouhých názvů souborů ve FAT? souborový systém?

    23. listopadu 2011 v 02:17

    O podpoře „USB Mass Storage“ v Ice Cream Sandwich

    • Vývoj Androidu

    Z prvních recenzí zařízení (jmenovitě Galaxy Nexus) na nové verzi Androidu 4.0 (aka ICS, aka „zmrzlinový sendvič“) se ukázalo, že nepodporují tak úžasnou funkci, jako je USB Mass Storage, tj. používání telefonu jako flash disku bez dalších triků. Uživatelé zařízení Android až do verze 3.0 „Honeycomb“ (a jak se ukázalo, v této verzi došlo ke změnám) vědí, že pro přenos souborů do nebo z telefonu stačilo jej jednoduše připojit k počítači bez s ohledem na to, jaký operační systém je na něm nainstalován systém nebo software. Je logické, že zpráva o zmizení této možnosti v nových verzích nevzbudila mezi uživateli Androidu nadšení a dokonce mnohé přiměla přemýšlet o přítomnosti nějakého problému nebo chyby. Naštěstí jeden z inženýrů Google Dan Morill(Dan Morrill) v komentářích k naštvanému příspěvku na redditu situaci objasnil a podrobně vysvětlil, co se vlastně stalo a proč. Podle mého názoru je to velmi zajímavé, proto níže překládám překlad jeho komentářů.

    Samotný ICS podporuje USB Mass Storage (UMS). Ale telefon Galaxy Nexus není. Je to stejný příběh jako u Honeycomb: HC podporuje UMS, ale tablet Xoom ne. Pokud má zařízení externí SD kartu, je podporován přístup k ní přes UMS. Pokud máte pouze vestavěnou paměť (jako Xoom a Galaxy Nexus), je přístup k paměti zařízení podporován pouze prostřednictvím MTP *(Protokol přenosu médií) a PTP *(Protokol přenosu obrázků). Je fyzicky nemožné podporovat UMS na zařízeních, která nemají vyhrazený oddíl pro ukládání informací (jako je externí karta SD nebo samostatný oddíl, jako v Nexus S). Důvodem je, že USM je nízkoúrovňový blokový protokol, který poskytuje hostitelskému počítači přímý přístup k fyzickým blokům média, což neumožňuje jeho současné připojení v systému Android. S novým modelem sjednoceného úložiště, který jsme představili v Honeycomb, je všech 32 GB (nebo všech 16 GB nebo všech N...) plně sdíleno mezi aplikacemi a mediálními soubory. Už nemusíte smutně zírat na volných 5 GB ve vašem Nexus S, zatímco je interní oddíl aplikací přeplněný – je to nyní jeden velký, šťastný oddíl. Bohužel, cena za to je ta, že Android si již nemůže dovolit dát PC přímý přístup a umožnit mu beztrestně toužit po USB paměťových médiích. Místo toho používáme MTP. Ve Windows (který má většina uživatelů) je v Průzkumníkovi zabudovaná podpora MTP a zařízení vypadá úplně stejně jako běžný disk. Na Linuxu a Macu bohužel není vše tak jednoduché, ale jsem si jistý, že se situace brzy zlepší. Celkově by tak mělo být používání telefonu mnohem pohodlnější.

    Na otázku, zda má Nexus S pouze vnitřní paměť, jak mohou programy jako správci souborů fungovat bez rootu, Dan vysvětlil:

    Kouzlo. ;)

    Nejprve vybereme adresář ve vnitřní paměti, který bude „SD karta“. Pak vezmeme souborový systém FUSE, který nedělá nic kromě toho, že tento adresář znovu připojí jako /sdcard s vypnutou kontrolou přístupu. Kromě přístupů je FUSE jednoduše end-to-end shell, který přenáší zápis a čtení přímo do/z adresáře. Jinými slovy, používáme falešný souborový systém FUSE k opětovnému připojení určitého adresáře, který se vydává za SD kartu. To je zcela transparentní pro aplikace, které si neuvědomují, že nemají přímý přístup k disku.

    Ano. Ve skutečnosti složka /sdcard běžící pod FAT32 (nebo, jak se nazývá v rozhraní API - „složka externích úložných médií“), ve skutečnosti nepodporuje omezený přístup, což je normální, protože se jedná o běžný výpis souborů otevřený všem , kde jedna aplikace může dupat po souborech jinou. Původně byl koncipován pro věci, jako je hudba a fotografie, a nikoli pro soukromá data, která „žijí“ v osobním úložišti aplikací, umístěném ve vnitřní paměti se sdíleným přístupem.
    Na zařízeních bez SD karty je jediným fyzickým souborovým systémem úložiště osobních dat aplikace. Vybereme tedy adresář, prohlásíme jej za výpis souborů, připojíme jej jako samostatný souborový systém FUSE, který ignoruje oprávnění, stejně jako jsou ignorována ve FAT32.

    Dan vysvětlil důvody všech těchto změn:

    (...) Neudělali jsme to, protože jsme chtěli přejít na ext3, i když jsme z toho těžili jako vedlejší efekt. Udělali jsme to, protože jsme chtěli spojit sdílený úložný prostor (jako je hudba a fotografie). vnitřní úložiště pro aplikace. Už nás nebaví vidět, že výrobci telefonů zahrnují gigabajty úložiště pro hudbu, ale uživatelům stále dochází místo pro aplikace a informace. Tento přístup umožňuje spojit vše do jedné sekce, což je mnohem lepší.

    Další osoba se zeptala, proč není možné použít oba přístupy, protože paměťový slot zabírá docela dost místa. K čemuž bylo řečeno něco o ideologii Androidu:

    Technicky není problém v hardwaru nainstalovat obojí. Problém je v tom, že z toho nemůžete udělat uživatelsky přívětivé rozhraní.
    Jedním ze základních principů Androidu je, že uživatel nepotřebuje správce souborů. Nikdy. Chtěli jsme se vyhnout syndromu, kdy každé kýchnutí způsobí, že se objeví dialogové okno pro výběr souboru, jak se často stává v jiných OS. Interní informace, se kterými mohou aplikace pracovat, by měly být jednoduše dostupné „kouzlem“ nebo uložené v cloudu. Při hledání souborů na SD kartě nemůžete uživatele přinutit, aby se zapojil do speleologie.

    Problém je v tom, že díky podpoře jak interní paměti, tak externí SD karty je najednou tento princip mnohem obtížnější dodržet. Má ji fotoaparát u konkrétní fotografie uložit na interních 16GB nebo na SD kartu? Aplikace z Marketu – mají se instalovat do interní paměti nebo na SD? A tak dále. Ano, můžeme to vyřešit tím, že donutíme uživatele vybrat nebo nastavit nastavení. Ale toto je nakonec dialog pro výběr souboru nebo něco podobného, ​​takže se nám to také nelíbí.

    Kromě toho to bude mít důsledky pro API - pokud vložíte SD kartu s fotografiemi, měl by je systémový poskytovatel mediálního obsahu indexovat?... Pokud ano, aplikace budou trpět, protože nebyly navrženy tak, aby to zohledňovaly fotografie se mohou náhle objevit a zmizet.
    V určitém okamžiku pravděpodobně přidáme koncept importu/exportu z připojených médií. Poté bude fotoaparát vždy ukládat fotografie do interních 16GB a po vložení SD karty (nebo připojení USB flash disk), budete moci spustit migraci nebo získat okno pro import/export. Ale do té doby bude mít většina zařízení buď SD kartu, nebo velkou vnitřní paměť, ale ne obojí. Dobře chápu, že mnoho lidí má rádo SD karty a mně samotnému chybí USB Mass Storage, ale proto je tak skvělé, že máme tolik zařízení, ze kterých si můžeme vybrat :)
    Obecně jde samozřejmě o spleť problémů. Už teď přemýšlíme o kompromisech pro budoucí verze.

    Doufám, že vám to také pomohlo trochu objasnit situaci a bylo zajímavé nahlédnout do vnitřního fungování vývojářů Androidu :)

    V předchozím článku jsme se pokusili popsat chaos, který nastal v letech 1998-2003. v oblasti protokolů pro párování přehrávačů s PC. Období 2003-2004 se stal dobou zefektivnění průmyslu. Mnoho proudů proprietárních protokolů bylo nahrazeno třemi harmonickými proudy. Přesněji dva a půl, protože... dva z těchto toků byly variacemi stejného protokolu.

    Byly to tyto:

    1. „čistý“ protokol Mass Storage
    2. Mass Storage protokol s přídavnými softwarovou konfiguraci
    3. protokol MTP.

    Nejvíce se používá Pure Mass Storage velký počet výrobci, zejména malé společnosti z Koreje nebo Číny. V Rusku se stal nejpopulárnějším protokolem. Náš dnešní příběh je o něm.

    Pro označení Mass Storage „v každodenním životě“ se používají dvě zkratky - MSC a UMS. MSC (Mass Storage Class) je oficiální a UMS (možné možnosti dekódování: USB/Universal Mass Storage) je „lidové“. Neprotiřečí si, spíše se doplňují.

    MSC uvádí, že protokol je jednou ze schválených standardních „tříd zařízení“ v rámci specifikace USB, a je tedy de jure průmyslovým standardem. UMS hovoří o univerzálnosti protokolu, který dnes podporuje většina operačních systémů a nespočet koncových zařízení, čímž se stává de facto standardem. Možnost dekódování UMS jako USB Mass Storage doplňuje tyto informace a specifikuje, že rozhraní USB se používá jako fyzická linka. Písmena MS (Mass Storage), společná všem zkratkám, naznačují, že se jedná o protokol určený pro práci s úložnými zařízeními pro velké objemy dat. Právě pro ně byl tento standard vyvinut - pro flash disky, čtečky karet, mobilní HDD disky. Jak se to dostalo do přenosných přehrávačů?

    ProtokolMšeÚložiště bylo koncipováno především pro podobná zařízení. Jeho vzhled vMP3 přehrávače byl vynucený krok

    V minulých materiálech jsme opakovaně mluvili o tom, jak se spontánně a nečekaně objevily a začaly vyvíjet přenosné audio přehrávače. Průmysl si jich prostě nechtěl všimnout, nejprve kvůli jejich marginálnosti, později kvůli přitaženému propojení těchto zařízení s digitálním pirátstvím. To mělo mnoho důsledků a jedním z nich bylo to, že při distribuci tříd USB zařízení byli „obcházení“ přehrávače.

    Podívejme se na seznam těchto tříd: existují externí zvukové karty, komunikační zařízení a samostatná třída pro periferní zařízení, jako jsou myši a klávesnice, existují také třídy pro tiskárny, rozbočovače USB, webové kamery a bezdrátové adaptéry. Existuje také třída pro digitální fotoaparáty. A v kategorii „ostatní“ zůstaly pouze audio a multimediální přehrávače.

    Mezi standardní třídyUSB má místo pro různá zařízení. Ne však pro multimediální přehrávače

    Dobrých pět let nikoho nenapadlo vybudovat pro ně samostatnou třídu. Výrobci museli vybírat z toho, co bylo k dispozici.

    Na tomto pozadí byl MSC/UMS jediným univerzálním řešením. Pokud své úkoly omezíte výhradně na „blbé“ načítání obsahu do přehrávače, pak už není potřeba nic. Protokol navíc umožnil proměnit přehrávač v mobilní úložiště. Běžní prodejci a kupující již nyní popisují přehrávače s tímto protokolem jako „fungující jako flash disk“, „připojující se jako flash disk“, „není třeba instalovat žádné programy“, „můžete ukládat soubory“ atd. atd.

    Jednoduchá hra – “MP3-Stick“ aMšeProtokol úložiště – vytvořený jeden pro druhého

    Tato dodatečná schopnost dobře zapadá do přístupu all-in-one přijatého asijskými výrobci MP3 přehrávačů. Byli průkopníky v adaptaci MSC/UMS do audio přehrávačů. Oni a Sigmatel, jehož platforma STMP3400 začala tento protokol podporovat na začátku roku 2003.

    leden 2003 –Sigmatel oznamuje podporuMšeÚložiště na jejich platformáchD-Hlavní

    Výhody protokolu. Hlavní věc je jednoduchost: všechny operace se provádějí prostřednictvím standardních souborových shellů, vč. Průzkumník Windows (Explorer), pro práci s ním nejsou potřeba žádné další znalosti ani školení.

    Rozšíření - Windows Me a 2000 již měly základní podporu protokolu, Windows XP ji podporovaly plně. Mnoho dalších operačních systémů - MacOS, Linux atd. – kompatibilní s Mass Storage.

    OS, který podporuje v té či oné forměMšeProtokol úložiště

    Dnes je obtížnější najít PC, které tento protokol nepodporuje. Podpora v tomto případě znamená přítomnost ovladačů protokolu jako součásti operačního systému.

    MšeÚložný přehrávač na 1,8” pevném diskuToshiba připojená k PC. JakZařízení MSC používá standardní ovladačUSBSTOR.SYS, který je součástí OS. Jako pohon také používá standardní ovladačeWindows. Není nutná žádná další instalace ovladače.

    Vzhledem k tomu, že veškerá práce s obsahem se také provádí pomocí standardních prostředků, prostřednictvím Průzkumníka Windows, uživatel nemusí instalovat vůbec nic: veškerá podpora protokolů je již zabudována v OS.

    Přehrávač je viditelný v PrůzkumníkuWindows je jako další pevný disk. Veškerá práce s obsahem se provádí v Průzkumníku nebo libovolném správci souborů podle výběru uživatele. Není nutná žádná další instalace softwaru

    Ukázalo se, že jde o skutečné Plug-and-Play: vyjměte jej z krabice, zapojte a použijte. Z hlediska parametrů, jako je transparentnost a neviditelnost pro uživatele, tento protokol prostě nemá obdoby.

    Z hlediska kompatibility s přenosnými zařízeními jsme na tom také dobře: protokol není závislý na souborových systémech a umí pracovat s kterýmkoli z nich, pokud je podporován OS.

    Je také důležité, aby existovala specifikace USB host (on the go), která vám umožní připojit zařízení Mass Storage k jiným přenosným (a nepřenosným) zařízením. Přehrávač kompatibilní s MSC lze dnes připojit k celé řadě zařízení, ať už je to herní konzole, stereo systém, autorádio, FM vysílač nebo jiný přehrávač.

    Auta získávají na popularitěFM vysílače, které vám umožní připojit jakékoliMšePřehrávač úložiště

    Nevýhody protokolu jsou pokračováním jeho výhod. Jeho funkčnost je základní, primitivní. Ve skutečnosti není schopen ničeho jiného než kopírování dat tam a zpět.

    Ale data, se kterými hráč pracuje, jsou víc než jen sada binárních znaků, jsou to obsah. Každý obsah, ať už jde o skladbu nebo soubor, má řadu vlastností, jako je název, formát, autor, trvání atd. Jednotlivé jednotky mohou být součástí složitějších agregátů, jako je album nebo seznam skladeb.

    Mass Storage o ničem takovém neví, což všechny starosti se správou obsahu klade buď na uživatele, nebo na vestavěný software přehrávače. Ten se nejčastěji nedokáže efektivně vypořádat s úkolem spravovat velké množství obsahu. Výsledkem je, že většina přehrávačů MSC/UMS má extrémně špatný mechanismus navigace – přes složky, podobně jako navigace v Průzkumníkovi Windows. Zároveň se nevyužívá značné množství informací obsažených v metadatech a značkách, což je vhodné pro klasifikaci obsahu.

    Informace, které mohou být obsaženy ve značkách (na příkladu programuMP3štítek). Málo z toho se používá vPřehrávače Mass Storage

    Uživatel je nucen organizovat svůj obsah samostatně pomocí systému podsložek. Zároveň, když si zvolil například systém klasifikace „píseň-album-autor“, nebude moci rychle a bezbolestně přejít na systém „žánr písně“ nebo „rok nahrávky“. bude muset otřást celou knihovnou.

    Organizace playlistů v takových přehrávačích je ve svých možnostech velmi slabá. Obvykle je možný pouze jeden seznam skladeb. Práce s playlisty je přiřazena výhradně zařízení samotnému a pokud byl některý ze souborů obsažených v playlistu smazán přes PC v režimu MSC/UMS, může to narušit chod celého seznamu jako celku. Taková prémiová funkce, jako je zobrazení obalu alba (obrázek alba nebo bunda), není v zásadě dostupná pro „čisté“ přehrávače MSC/UMS. Teoreticky to lze implementovat načtením grafický soubor ze složky, ale v praxi to zatím nikdo neudělal. A pokud to udělá, uživatel bude muset ručně vložit odpovídající obrázky do všech složek. Některé přehrávače mají možnost zobrazit slova písně (Text), ale tato slova nejsou převzata z metadat: uživatel je musí samostatně připravit pomocí speciálního programu.

    To je hlavní problém Mass Storage. Přehrávač je více než jen mobilní úložné zařízení, aby mohl efektivně fungovat, musí mít hluboké znalosti o tom, co je skutečně uloženo v jeho paměti. Jelikož jde o moderní multimediální zařízení určené pro široké spektrum uživatelů, nemůže jednoduše mumlat vše, co je na něm zaznamenáno. Musí umět vyprávět, co sledujeme nebo posloucháme, a to stručně, komplexně a nenápadně, jako špičkový bavič. Ten by nám jako zkušený knihovník měl pomoci rychle najít mezi tisíci písničkami přesně to, co potřebujeme, i když jsme zapomněli název. V tom všem mu není pomocí protokol MSC/UMS, který je ve svých možnostech extrémně omezený. A čerstvý zápalný zásah, diplomová práce a odkládací soubor Windows pro něj jsou jen anonymní pole dat. Tím se protokol mění v jakési odosobňující úzké hrdlo mezi dvěma výkonnými multimediálními systémy – přehrávačem a PC. Celé břemeno transformace anonymního toku informací do uživatelsky přívětivé podoby leží na bedrech těch druhých.

    Na PC vše závisí na samotném uživateli: pokud projeví vytrvalost a vynalézavost, uspořádá hudební knihovnu, kterou mu bude každý závidět. Nebo možná všechno vysypte do jedné složky, dokud nebude úplně nemožné tam něco najít.

    Na přehrávači vše závisí na vývojáři a ti vůbec netouží vynaložit velké úsilí na vývoj výkonného softwaru. Ve výsledku je bavič z takových přehrávačů takový – monotónním hlasem přečte název písně, autora, v lepším případě alba. Nebo možná jen název souboru.

    RozhraníiPod versus rozhraníMšeÚložiště přehrávačeiriverh300 vypadá více spartánsky, ale zároveň zobrazuje mnohem více informací o přehrávané skladbě. Přitom hráč ziriver je stále poměrně úspěšným příklademMšeÚložiště přehrávače

    A je to zbytečný knihovník - takže rukou naznačí směr, kam se dívat, ale nic víc.

    UživatelMšeÚložiště přehrávače (CowonX5 v tomto případě vlevo) se při hledání kompozice zájmu může řídit pouze logikou složek a souborů, které sám vytvořil. Pokud jsou použita jiná řešení (jako vTvůrčíZenTouch, right) mají schopnost bezplatné vyhledávání podle parametrů

    Jsou nějaké výjimky (například hráči z Archosu), ale není jich mnoho.

    Tato situace má velmi jednoduchý důsledek. Uživatelé, kteří se vyznají v počítači, zvyklí na koncept souborů a složek, nejsou příliš nároční vnější efekty a rychle se přizpůsobte novým věcem, pevně stojí za čistým velkokapacitním úložištěm. Transparentnost, otevřenost a rozšířenost protokolu jsou pro ně výhodami, oproti nimž všechny nevýhody blednou.

    Společnostiiriver aCowon vděčí za svou popularitu u určitých segmentů zákazníků v neposlední řadě podpoře „zdarma“Velkokapacitní úložiště

    Ale notoričtí „obyčejní“ uživatelé nejsou příliš spokojeni. Přehrávač pro ně stále není flash disk, nikoli úložiště souborů, ale přehrávač. Netouží po pečlivém budování pyramidy souborů a složek v hudební knihovně, ani se nechtějí potulovat v hlubinách této pyramidy na obrazovce přehrávače a soustředit se pouze na názvy složek. Navigace podle metadat, přehrávání s krásným obalem alba, automatické načítání nových skladeb do přehrávače – to vše je jim mnohem bližší. Značný počet přehrávačů MSC/UMS vrácených do obchodu a vyměněných za iPody ve Spojených státech je toho důkazem.

    Přesto je toto odvětví nastaveno výrobci, kteří nepoužívají čistéMšeSkladování

    Existuje další kategorie nespokojených s protokolem. Jedná se o nahrávací studia a filmová studia. Mass Storage, která je ke všemu lhostejná, rozhodně nerozezná „pirátskou“ skladbu od poctivě zakoupené. Výrobce, jehož přehrávače podporují stahování obsahu prostřednictvím „čistého“ Mass Storage, může jen stěží počítat s plodnou spoluprací s velkými vydavatelstvími. Malé firmy to samozřejmě nepotřebují. Ale velké korporace, které chtějí uživatelům poskytovat vertikální řešení včetně získávání obsahu, jsou nuceny tento faktor brát v úvahu.

    Výsledkem je, že výrobce chce vytvořit přehrávač, který:

      Atraktivní pro „jednoduchého uživatele“ díky snadné, automatizované práci s ním, pohodlné, rychlé a krásné navigaci, efektivnímu využití metadat

      Nezpůsobilo by to odmítnutí mezi filmovými a nahrávacími studii, což by umožnilo zorganizovat pohodlnou (a pro společnost ziskovou) akvizici obsahu pro tyto „běžné uživatele“

      Nevyžaduje velké investice do vývoje, jak časové, tak i do kvalifikace programátorů

    – nuceni hledat řešení přesahující možnosti „čistého“ Mass Storage.



    
    Nahoru