2
0

usbdescgen.pl 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832
  1. #!/usr/bin/perl
  2. #
  3. # Simple tool for generating a USB desciptor table or ROM/RAM
  4. #
  5. use integer;
  6. use strict;
  7. use File::Spec;
  8. use Encode;
  9. require 'langid.ph';
  10. # Some forward declarations
  11. my($mode, $infile, $outfile) = @ARGV;
  12. my $module_name;
  13. my $err = 0;
  14. # Descriptor types. This follows the document in which they were specified;
  15. # some USB documents specify in hex and others in decimal...
  16. our %DT = (
  17. 'device' => 1,
  18. 'configuration' => 2, 'config' => 2, 'conf' => 2,
  19. 'string' => 3, 'str' => '3',
  20. 'interface' => 4, 'if' => '4',
  21. 'endpoint' => 5, 'ep' => 5,
  22. 'device_qualifier' => 6, 'dq' => 6,
  23. 'other_speed_configuration' => 7, 'otherspeed' => 7, 'osc' => 7,
  24. 'interface_power' => 8, 'ifpwr' => 8,
  25. 'otg' => 9,
  26. 'debug' => 10,
  27. 'interface_association' => 11, 'iad' => 11,
  28. 'bos' => 15, 'binary_object_storage' => 15,
  29. 'device_capability' => 16, 'devcap' => 16,
  30. 'cs_interface' => 0x24, 'cs_if' => 0x24,
  31. 'cs_endpoint' => 0x25, 'cs_ep' => 0x25,
  32. 'superspeed_usb_endpoint_companion' => 48, 'usb_ep_companion' => 48,
  33. 'superspeedplus_isochronous_endpoint_companion' => 49, 'iso_ep_compation' => 49,
  34. );
  35. # Descriptor subtypes, where applicable
  36. our %DS = (
  37. # Under CS_INTERFACE
  38. 0x24 => {
  39. 'header' => 0x00,
  40. 'call' => 0x01, 'call_management' => 0x01,
  41. 'abstract_control' => 0x02, 'acm' => 0x02,
  42. 'direct_line' => 0x03,
  43. 'ringer' => 0x04,
  44. 'line_state' => 0x05,
  45. 'union' => 0x06,
  46. 'country' => 0x07,
  47. 'op_mode' => 0x08,
  48. 'terminal' => 0x09,
  49. 'net_channel' => 0x0a,
  50. 'protocol_unit' => 0x0b,
  51. 'extension_unit' => 0x0c,
  52. 'multi_channel' => 0x0d,
  53. 'capi' => 0x0e,
  54. 'ethernet' => 0x0f,
  55. 'atm' => 0x10,
  56. 'whcm' => 0x11, 'wireless_handset' => 0x11,
  57. 'mobile_line' => 0x12, 'mobile_direct_line' => 0x12,
  58. 'mdlm_detail' => 0x13, 'mdlm' => 0x13,
  59. 'device_management' => 0x14, 'device' => 0x14, 'mgmt' => 0x14,
  60. 'command_set' => 0x16,
  61. 'command_set_detail' => 0x17,
  62. 'telephone_control' => 0x18, 'tcm' => 0x18, 'phone' => 0x18,
  63. 'obex_service_identifier' => 0x19, 'obex' => 0x19
  64. }
  65. );
  66. #
  67. # Class, subclass, and protocol codes. Map a string to a number, then
  68. # use that number as an index to descend, if that entry exists. 0 is
  69. # the default; for some classes the subclass code is unused and so is
  70. # set to 0; no string to look up.
  71. #
  72. # Numbers below the class level are massively incomplete, feel free to add.
  73. #
  74. # Applies to all levels
  75. my %class_all_levels = (
  76. undef => 0x00, 'undef' => 0x00, '-' => 0x00,
  77. 'none' => 0x00, 'default' => 0x00,
  78. 'vendor_specific' => 0xff, 'vendor' => 0xff,
  79. );
  80. my $cdc_pstn_protocols = {
  81. 'v250' => 0x01, 'at' => 0x01, 'v25ter' => 0x01,
  82. 'pcca101' => 0x02,
  83. 'pcca101o' => 0x03, 'pcca' => 0x03,
  84. 'gsm707' => 0x04, 'gsm' => 0x04,
  85. '3gpp2707' => 0x05, '3gpp' => 0x05, '3g' => 0x05,
  86. 'ca00170' => 0x06, 'tia_cdma' => 0x06, 'cdma' => 0x06
  87. };
  88. my %class_codes = (
  89. 'multi' => 0x00, # Real class code in interface descriptors
  90. 'audio' => 0x01,
  91. 'cdc' => 0x02, 'communications' => 0x02,
  92. 0x02 => {
  93. 'dlcm' => 0x01, 'direct_line_control' => 0x01,
  94. 'acm' => 0x02, 'abstract_control' => 0x02,
  95. 0x02 => $cdc_pstn_protocols,
  96. 'tcm' => 0x03, 'telephone_control' => 0x03,
  97. 0x03 => $cdc_pstn_protocols,
  98. 'mccm' => 0x04, 'multi_channel_control' => 0x04,
  99. 0x04 => $cdc_pstn_protocols,
  100. 'ccm' => 0x05, 'capi' => 0x05, 'capi_control' => 0x05,
  101. 0x05 => $cdc_pstn_protocols,
  102. 'ecm' => 0x06, 'ethernet_control' => 0x06,
  103. 0x06 => $cdc_pstn_protocols,
  104. 'atm' => 0x07, 'ancm' => 0x07, 'atm_networking_control' => 0x07,
  105. 'whcm' => 0x08, 'wireless_handset_control' => 0x08,
  106. 0x08 => $cdc_pstn_protocols,
  107. 'device_management' => 0x09, 'mgmt' => 0x09, 'device' => 0x09,
  108. 'mdlm' => 0x0a, 'mobile_direct_line' => 0x0a,
  109. 'obex' => 0x0b,
  110. 'eem' => 0x0c, 'ethernet' => 0x0c, 'ethernet_emulation' => 0x0c,
  111. 0x0c => {
  112. 'ethernet' => 0x07, 'eem' => 0x07
  113. },
  114. 'ncm' => 0x0d, 'net' => 0x0d, 'network_control' => 0x0d,
  115. },
  116. 'hid' => 0x03,
  117. 'physical' => 0x05,
  118. 'imaging' => 0x06, 'photo' => 0x06, 'idc' => 0x06,
  119. 'printer' => 0x07,
  120. 'mass_storage' => 0x08, 'storage' => 0x08, 'disk' => 0x08,
  121. 'hub' => 0x09,
  122. 'cdc_data' => 0x0a, 'data' => 0x0a,
  123. 0x0a => {
  124. 0 => {
  125. 'ntb' => 0x01, 'network_transfer_block' => 0x01,
  126. 'isdn_bri' => 0x30, 'isdn' => 0x30,
  127. 'hdlc' => 0x31,
  128. 'transparent' => 0x32,
  129. 'q921_management' => 0x50, 'q921m' => 0x50,
  130. 'q921_datalink' => 0x51, 'q921' => 0x51,
  131. 'q921_tei_mux' => 0x52, 'q921tm' => 0x52,
  132. 'v42bis' => 0x90,
  133. 'euro_isdn' => 0x91, 'q931' => 0x91,
  134. 'v120' => 0x92, 'isdn_v24' => 0x92,
  135. 'capi' => 0x93,
  136. 'host' => 0xfd,
  137. 'external' => 0xfe,
  138. 'vendor' => 0xff
  139. },
  140. },
  141. 'smart_card' => 0x0b, 'smartcard' => 0x0b, 'scdc' => 0x0b,
  142. 'content_security' => 0x0d, 'drm' => 0x0d, 'csdc' => 0x0d,
  143. 'video' => 0x0e, 'vdc' => 0x0e,
  144. 'personal_healthcare' => 0x0f, 'healthcare' => 0x0f, 'health' => 0x0f, 'phdc' => 0x0f,
  145. 'audio_video' => 0x10, 'av' => 0x10, 'avdc' => 0x10,
  146. 'billboard' => 0x11, 'bdc' => 0x11,
  147. 'usb_c_bridge' => 0x12, 'usbc' => 0x12, 'usbcbdc' => 0x12,
  148. 'diagnostic' => 0xdc,
  149. 'wireless_controller' => 0xe0, 'wireless' => 0xe0,
  150. 'miscellaneous' => 0xef, 'misc' => 0xef,
  151. 'application_specific' => 0xfe, 'app_specific' => 0xfe, 'app' => 0xfe,
  152. );
  153. my %packfmt = ( 1 => 'C', 2 => 'v', 4 => 'V', 8 => 'Q<' );
  154. my $utf16le = find_encoding('utf16le');
  155. sub atom($@) {
  156. my($bytes,$b,$adj) = @_;
  157. my @o = ();
  158. $adj = toint($adj);
  159. my $t;
  160. while (($t = ref $b) eq 'REF') {
  161. $b = $$b;
  162. }
  163. if ($t eq 'SCALAR') {
  164. # To be resolved later
  165. push(@o, {'bytes' => $bytes, 'num' => $b, 'adj' => $adj });
  166. } elsif ($t eq '') {
  167. push(@o, pack($packfmt{$bytes}, $b+$adj));
  168. } else {
  169. push(@o, {'bytes' => $bytes, 'data' => $b});
  170. }
  171. return [@o];
  172. }
  173. sub byte(@) {
  174. return atom(1,@_);
  175. }
  176. sub word(@) {
  177. return atom(2,@_);
  178. }
  179. sub dword(@) {
  180. return atom(4,@_);
  181. }
  182. sub qword(@) {
  183. return atom(8,@_);
  184. }
  185. # Generate endpoint identifiers
  186. sub ep_i($) {
  187. my($n) = @_;
  188. return byte($n,0x80);
  189. }
  190. sub ep_o($) {
  191. my($n) = @_;
  192. return byte($n,0x00);
  193. }
  194. #
  195. # General handy stuff...
  196. #
  197. # Decode an integer in C notation
  198. sub toint($) {
  199. my($i) = @_;
  200. return ($i =~ /^0/) ? oct $i : ($i =~ /^[1-9]/) ? $i+0 : undef;
  201. }
  202. # For sorting
  203. sub numeric {
  204. return $a <=> $b;
  205. }
  206. # Generate class code triplets
  207. sub usb_class($;$$) {
  208. my @cl = @_;
  209. my $lvl = \%class_codes;
  210. my $cd = '';
  211. for (my $i = 0; $i < 3; $i++) {
  212. my $cs = shift(@cl);
  213. my $cc = defined($cs) ? toint($cs) : 0;
  214. if (!defined($cc)) {
  215. $cs = lc($cs);
  216. $cs =~ s/\P{Alnum}+/_/g;
  217. $cc = $lvl->{$cs} if (defined($lvl));
  218. $cc = $class_all_levels{$cs} unless (defined($cc));
  219. if (!defined($cc)) {
  220. print STDERR "$0: unknown class code ", join('.', @_), "\n";
  221. $err = 1;
  222. $cc = 0;
  223. }
  224. }
  225. $cd .= pack('C', $cc);
  226. $lvl = $lvl->{$cc};
  227. }
  228. return $cd;
  229. }
  230. sub datalen(@) {
  231. my $l = 0;
  232. foreach my $e (@_) {
  233. my $b = $e;
  234. my $t;
  235. while (($t = ref $b) eq 'REF') {
  236. $b = $$b;
  237. }
  238. if ($t eq 'HASH') {
  239. $l += $b->{'bytes'};
  240. } elsif ($t eq 'ARRAY') {
  241. $l += datalen(@$b);
  242. } elsif ($t eq 'SCALAR') {
  243. $l += length($$b);
  244. } elsif ($t eq '') {
  245. $l += length($b);
  246. } else {
  247. die;
  248. }
  249. }
  250. return $l;
  251. }
  252. sub makedata(@) {
  253. my $o = '';
  254. foreach my $e (@_) {
  255. my $b = $e;
  256. my $t;
  257. while (($t = ref $b) eq 'REF') {
  258. $b = $$b;
  259. }
  260. if ($t eq 'HASH') {
  261. unless (defined($b->{'raw'})) {
  262. if (defined(my $n = $b->{'num'})) {
  263. my $tt;
  264. my $adj = $b->{'adj'};
  265. while (($tt = ref $n) eq 'REF') {
  266. $n = $$n;
  267. }
  268. if ($tt eq 'SCALAR') {
  269. $b->{'raw'} = pack($packfmt{$b->{'bytes'}}, $$n+$adj);
  270. } else {
  271. $b->{'raw'} = makedata($n);
  272. }
  273. } elsif (defined($b->{'data'})) {
  274. $b->{'raw'} = makedata($b->{'data'});
  275. } else {
  276. die;
  277. }
  278. }
  279. $b->{'offs'} = length($o);
  280. $b->{'bytes'} = length($b->{'raw'});
  281. $o .= $b->{'raw'};
  282. } elsif ($t eq 'ARRAY') {
  283. $o .= makedata(@$b);
  284. } elsif ($t eq 'SCALAR') {
  285. $o .= makedata($$b);
  286. } elsif ($t eq '') {
  287. $o .= $b;
  288. } else {
  289. die;
  290. }
  291. }
  292. return $o;
  293. }
  294. # USB descriptor set
  295. my $u_self = { 'children' => \(my $children = 0), clist => [] };
  296. sub usb_dset(&) {
  297. my($contents) = @_;
  298. my $parent = $u_self;
  299. my $children = 0;
  300. my $index = ${$u_self->{'children'}}++;
  301. my $ds = { 'type' => 'dset',
  302. 'parent' => $parent,
  303. 'data' => undef,
  304. 'bytes' => undef,
  305. 'raw' => undef,
  306. 'children' => \$children,
  307. 'index' => \$index,
  308. 'offs' => undef,
  309. 'clist' => [] };
  310. $u_self = $ds;
  311. push(@{$parent->{'clist'}}, $ds);
  312. my @data = $contents->($ds, $parent);
  313. $ds->{'data'} = \@data;
  314. $ds->{'bytes'} = datalen(@data);
  315. $u_self = $parent;
  316. return $ds;
  317. }
  318. sub usb_totallen(;$) {
  319. my($r) = @_;
  320. $r = $u_self unless(defined($r));
  321. return \$r->{'bytes'};
  322. }
  323. sub usb_index(;$) {
  324. my($r) = @_;
  325. $r = $u_self unless(defined($r));
  326. return $r->{'index'};
  327. }
  328. sub usb_peers(;$) {
  329. my($r) = @_;
  330. $r = $u_self unless(defined($r));
  331. return $r->{'parent'}{'children'};
  332. }
  333. sub usb_children(;$) {
  334. my($r) = @_;
  335. $r = $u_self unless(defined($r));
  336. return $r->{'children'};
  337. }
  338. # USB descriptor
  339. sub usb_desc($@) {
  340. my($typestr, @data) = @_;
  341. my($type,$subtype) = split(/\./, $typestr, 2);
  342. my $tn;
  343. my $sn;
  344. my $hdr;
  345. my $dlen = datalen(@data);
  346. $tn = toint($type);
  347. $tn = $DT{lc($type)} unless (defined($tn));
  348. die "$0: unknown descriptor type: $typestr\n" unless (defined($tn));
  349. if (defined($subtype)) {
  350. $sn = toint($subtype);
  351. $sn = $DS{$tn}->{$subtype} unless (defined($sn));
  352. die "$0: unknown descriptor type: $typestr\n" unless (defined($sn));
  353. $dlen += 3;
  354. $hdr = pack('CCC', $dlen, $tn, $sn);
  355. } else {
  356. $dlen += 2;
  357. $hdr = pack('CC', $dlen, $tn);
  358. }
  359. return { 'type' => 'descriptor',
  360. 'data' => [$hdr, @data],
  361. 'bytes' => $dlen };
  362. }
  363. # Device top level
  364. my $device_dset;
  365. sub usb_device(&) {
  366. my($contents) = @_;
  367. $device_dset = usb_dset(\&$contents);
  368. }
  369. # Additional USB data
  370. my $additional_dset;
  371. sub usb_additional_data(&) {
  372. my($contents) = @_;
  373. $additional_dset = usb_dset(\&$contents);
  374. }
  375. my @langlist;
  376. my %lang;
  377. my @lang_mask = (0xffff, 0x03ff, 0); # Masks for language codes
  378. my $stringdata;
  379. my %stringoffs; # Pointer into stringdata
  380. # Reserved string descriptor numbers
  381. my $strdesc_lang = 0; # 0 = reserved for language descriptors
  382. my $strdesc_serial = 1; # 1 = reserved for serial number (see below)
  383. my $strdesc_msft = 0xee;
  384. my %special_strings = ($strdesc_lang => undef,
  385. $strdesc_serial => undef,
  386. $strdesc_msft => undef);
  387. # The index of string descriptors are sets of descriptors which
  388. # match for ALL languages so they can be given the same index.
  389. my %strdesci = ();
  390. my @strdescs = ();
  391. # Register a string into the string table and return a descriptor index byte.
  392. # Input should be a hash.
  393. sub usb_string(%) {
  394. my(%strh) = @_;
  395. my $descval = '';
  396. my %txts;
  397. my $found = 0;
  398. if (!%strh) {
  399. %strh = ( '' => '' ); # Null string descriptor
  400. }
  401. foreach my $l (keys(%strh)) {
  402. my $str = $strh{$l};
  403. my $co = langid($l);
  404. next unless (defined($co));
  405. foreach my $m (@lang_mask) {
  406. my $coi = $co & $m;
  407. next unless (defined($lang{$coi}));
  408. next if (defined($txts{$coi}));
  409. $txts{$coi} = $str;
  410. $found++;
  411. }
  412. }
  413. return pack('C', 0) unless ($found);
  414. foreach my $co (@langlist) {
  415. my $txt;
  416. foreach my $m (@lang_mask) {
  417. last if (defined($txt = $txts{$co & $m}));
  418. }
  419. my $utf16str = $utf16le->encode($txt, Encode::FB_WARN);
  420. unless (defined($stringoffs{$utf16str})) {
  421. $stringoffs{$utf16str} = length($stringdata);
  422. $stringdata .= pack('CC', length($utf16str)+2, $DT{string});
  423. $stringdata .= $utf16str;
  424. }
  425. $descval .= pack('v', $stringoffs{$utf16str});
  426. }
  427. my $descindex = $strdesci{$descval};
  428. unless (defined($descindex)) {
  429. $descindex = scalar(@strdescs) - 1;
  430. while (exists($special_strings{++$descindex})) {
  431. push(@strdescs, undef); # Skip reserved index
  432. }
  433. $strdesci{$descval} = $descindex;
  434. push(@strdescs, $descval);
  435. }
  436. return pack('C', $descindex);
  437. }
  438. #
  439. # Special string descriptors. Currently supports the serial number,
  440. # but could also be used for the M$ special 0xEE string descriptor
  441. # with a bit more infrastructure. Noteworthy is that the serial number
  442. # descriptor is quite likely to need to be patched in the field or
  443. # at runtime. Thus, these special string descriptors come at the
  444. # very beginning of the data, in index order.
  445. #
  446. # Language codes are not supported for special strings.
  447. #
  448. # Usage: usb_special_string(index,'default',length)
  449. #
  450. sub usb_special_string($@) {
  451. my($ix,$dft,$len) = @_;
  452. if (defined($dft) || $len || !defined($special_strings{$ix})) {
  453. $dft = $utf16le->encode($dft, Encode::FB_WARN);
  454. my $dchar = length($dft) >> 1;
  455. if (!$len) {
  456. $len = $dchar;
  457. } elsif ($len < $dchar) {
  458. $dft = substr($dft, 0, $len << 1);
  459. } elsif ($len > $dchar) {
  460. # Pad with spaces
  461. $dft .= $utf16le->encode(' ' x ($len - $dchar));
  462. }
  463. $special_strings{$ix} = pack('CC', ($len << 1)+2, $DT{string}).$dft;
  464. }
  465. return pack('C', $ix);
  466. }
  467. sub usb_serial($;$) {
  468. return usb_special_string($strdesc_serial, @_);
  469. }
  470. #
  471. # Register the possible languages
  472. #
  473. sub usb_languages(@) {
  474. my @langs = @_;
  475. # There are two differences between keys(%lang) and @langlist: the
  476. # former lists all possible language codes for which a lookup is
  477. # possible in no specific order, whereas @langlist is exactly the
  478. # items wanted in order in string descriptor zero.
  479. #
  480. # In other words, %lang is a reverse mapping of langlist, *plus*
  481. # permissible aliases.
  482. %lang = ();
  483. @langlist = ();
  484. foreach my $l (@langs) {
  485. my $co = langid($l);
  486. if (defined($co) && !defined($lang{$co})) {
  487. my $nlang = scalar(@langlist);
  488. push(@langlist, $co);
  489. # Defaults for partial languages?
  490. foreach my $m (@lang_mask) {
  491. if (!defined($lang{$co & $m})) {
  492. $lang{$co & $m} = $nlang;
  493. }
  494. }
  495. }
  496. }
  497. if (!scalar(@langlist)) {
  498. $stringdata = '';
  499. } else {
  500. $stringdata = pack("CCv*", scalar(@langlist)*2 + 2,
  501. $DT{string}, @langlist);
  502. }
  503. }
  504. my $descriptor_data;
  505. my @descriptor_ptrs;
  506. my $additional_offs;
  507. my $additional_len;
  508. sub generate_data()
  509. {
  510. my $data = '';
  511. my @ptrs = ();
  512. # Special string descriptors first
  513. foreach my $ssn (sort numeric keys(%special_strings)) {
  514. my $ss = $special_strings{$ssn};
  515. next unless defined($ss);
  516. push(@ptrs, {'type' => $DT{string}, 'dindex' => $ssn,
  517. 'offs' => length($data),
  518. 'len' => length($ss)});
  519. $data .= $ss;
  520. }
  521. # Device and configuration descriptors
  522. my $dev_offs = length($data);
  523. $data .= makedata($device_dset);
  524. foreach my $dc (@{$device_dset->{data}}) {
  525. my $offs = $dev_offs + $dc->{offs};
  526. # Length and type of the *first* descriptor in a set
  527. my($flen,$ftype) = unpack('CC', substr($data, $offs, 2));
  528. my $dindex = $dc->{index};
  529. $dindex = $$dindex while (ref $dindex eq 'SCALAR');
  530. push(@ptrs, {'type' => $ftype, 'dindex' => $dindex,
  531. 'offs' => $offs, 'len' => $dc->{bytes}});
  532. }
  533. my $string_offs = length($data);
  534. # 'Regular' string descriptors
  535. push(@ptrs, {'type' => $DT{string}, 'dindex' => 0,
  536. 'offs' => $string_offs,
  537. 'len' => unpack('C', substr($stringdata, 0, 1))});
  538. for (my $i = 1; $i < scalar(@strdescs); $i++) {
  539. my @sofs = unpack('v*', $strdescs[$i]);
  540. for (my $j = 0; $j < scalar(@sofs); $j++) {
  541. my $co = $langlist[$j];
  542. my $of = $sofs[$j];
  543. my $m = $lang_mask[0];
  544. # Widen the mask as much as possible
  545. foreach my $mx (@lang_mask) {
  546. last unless ($lang{$co & $mx} == $lang{$co & $m});
  547. $m = $mx;
  548. }
  549. push(@ptrs, {'type' => $DT{string}, 'dindex' => $i,
  550. 'windex' => $co & $m,
  551. 'wimask' => $m,
  552. 'offs' => $string_offs + $of,
  553. 'len' => unpack('C', substr($stringdata,$of,1))});
  554. }
  555. }
  556. $data .= $stringdata;
  557. $additional_offs = length($data);
  558. if (defined($additional_dset)) {
  559. $data .= makedata($additional_dset);
  560. }
  561. $additional_len = length($data) - $additional_offs;
  562. $descriptor_data = $data;
  563. @descriptor_ptrs = @ptrs;
  564. return $descriptor_data;
  565. }
  566. # Output a number and a mask as a Verilog case with ? as needed
  567. sub v_case($$$) {
  568. my($b,$v,$m) = @_;
  569. my $o = sprintf("%d'b", $b);
  570. for (my $i = $b-1; $i >= 0; $i--) {
  571. my $ix = 1 << $i;
  572. $o .= ($m & $ix) ? (($v & $ix) ? '1' : '0') : '?';
  573. $o .= '_' if ($i > 0 && ($i & 7) == 0);
  574. }
  575. return $o;
  576. }
  577. # Output a Verilog module
  578. sub output_verilog($) {
  579. my($out) = @_;
  580. my $bytes = length($descriptor_data);
  581. my $abits = 1;
  582. while ((1 << $abits) < $bytes) {
  583. $abits++;
  584. }
  585. my $amax = $abits-1;
  586. my $bmax = (1 << $abits)-1;
  587. my $afmt = sprintf("%d'h%%0%dx", $abits, ($abits+3) >> 2);
  588. my $abad = sprintf("%d'h", $abits) . ('x' x (($abits+3) >> 2));
  589. my $bfmt = "8'h%02x";
  590. my $bbad = "8'hxx";
  591. print $out <<"EOF";
  592. /*
  593. * Call it a ROM even through it can be optionally written to.
  594. * Trust the tools to figure out if we don't need part of the whole thing.
  595. */
  596. module ${module_name}_rom (
  597. input clk,
  598. input [$amax:0] usb_addr,
  599. output [7:0] usb_rdata,
  600. input cpu_clk,
  601. input [$amax:0] cpu_addr,
  602. output [7:0] cpu_rdata,
  603. input [7:0] cpu_wdata,
  604. input cpu_wren
  605. );
  606. reg [7:0] rom [0:$bmax];
  607. initial begin
  608. EOF
  609. my $addr = 0;
  610. foreach my $b (unpack("C*", $descriptor_data)) {
  611. printf $out "\t\trom[$afmt] = $bfmt;\n", $addr++, $b;
  612. }
  613. while ($addr < $bytes) {
  614. printf $out "\t\trom[$afmt] = $bbad;\n", $addr++;
  615. }
  616. print $out <<"EOF";
  617. end
  618. always \@(posedge clk) begin
  619. usb_rdata <= rom[usb_addr];
  620. end
  621. always \@(posedge cpu_clk) begin
  622. cpu_rdata <= rom[cpu_addr];
  623. if (cpu_wren)
  624. rom[cpu_addr] <= cpu_wdata;
  625. end
  626. endmodule
  627. module ${module_name}_index (
  628. input [7:0] dtype,
  629. input [7:0] dindex,
  630. input [15:0] windex,
  631. input additional,
  632. output reg [$amax:0] addr,
  633. output reg [$amax:0] len
  634. );
  635. always \@(\*)
  636. if (additional)
  637. EOF
  638. printf $out "\t\t{addr,len} = {$afmt,$afmt};\n", $additional_offs, $additional_len;
  639. print $out <<"EOF";
  640. else priority casez ({windex,dindex,dtype})
  641. EOF
  642. my @cases;
  643. foreach my $d ({ 'len' => 0 }, @descriptor_ptrs) {
  644. my $id = 0;
  645. my $mask = 0;
  646. if (defined($d->{type})) {
  647. $id |= $d->{type};
  648. $mask |= 0xff;
  649. }
  650. if (defined($d->{dindex})) {
  651. $id |= $d->{dindex} << 8;
  652. $mask |= 0xff << 8;
  653. }
  654. if (defined($d->{windex})) {
  655. $id |= $d->{windex} << 16;
  656. $mask |= $d->{wimask} << 16;
  657. }
  658. my $cs = "\t\t".v_case(32,$id,$mask).': {addr,len} = ';
  659. if ($d->{len}) {
  660. $cs .= sprintf("{$afmt,$afmt};\n", $d->{offs},$d->{len});
  661. } else {
  662. $cs .= sprintf("{$abad,$afmt};\n", $d->{len});
  663. }
  664. push(@cases, $cs);
  665. }
  666. # This relies on ? sorting after digits
  667. print $out sort @cases;
  668. print $out <<'EOF';
  669. endcase
  670. endmodule
  671. EOF
  672. }
  673. if (scalar(@ARGV) < 3) {
  674. die "Usage: $0 {bin|v} input output\n";
  675. }
  676. my @ofs = File::Spec->splitpath($outfile);
  677. $module_name = $ofs[2];
  678. $module_name =~ s/\..*$//;
  679. $module_name =~ s/\P{Alnum}+/_/g;
  680. unless (defined(do File::Spec->rel2abs($infile))) {
  681. die "$0: $infile: ".(($@ ne '') ? $@ : $!)."\n";
  682. }
  683. generate_data();
  684. exit ($err) if ($err);
  685. open(my $out, '>', $outfile)
  686. or die "$0: $outfile: $!\n";
  687. if ($mode eq 'bin') {
  688. print $out $descriptor_data;
  689. } elsif ($mode eq 'v') {
  690. output_verilog($out);
  691. } else {
  692. die "$0: $mode: unknown operations mode\n";
  693. }
  694. close($out);
  695. if ($err) {
  696. remove($outfile);
  697. }
  698. exit($err);