ソースを参照

usbdescgen: fix language mask handling

H. Peter Anvin 3 年 前
コミット
14f9fd012d
4 ファイル変更54 行追加24 行削除
  1. 2 2
      fpga/usb/usb_desc.conf
  2. 3 3
      tools/perlinc/langid.ph
  3. 1 0
      tools/perlinc/lcid.ph
  4. 48 19
      tools/usbdescgen.pl

+ 2 - 2
fpga/usb/usb_desc.conf

@@ -10,8 +10,8 @@ my $device_id    = 0x6149;
 my $version_id   = 0x0100;
 
 my $serial       = usb_serial('?' x 16);
-my $manufacturer = usb_string('' => 'Peter & Per');
-my $product      = usb_string('' => 'MAX80');
+my $manufacturer = usb_string(''   => 'Peter & Per');
+my $product      = usb_string(''   => 'MAX80 I/O card for ABC');
 
 my $mgmt_if, $data_if;
 

+ 3 - 3
tools/perlinc/langid.ph

@@ -12,12 +12,12 @@ sub langid($) {
     $s = lc($s);
     $s =~ s/\P{Alnum}+/-/g;
 
-    while ($s ne '') {
+    while (1) {
 	my $lcid = $langid{$s};
 	return $lcid if (defined($lcid));
 
-	last unless ($lcid =~ /(.*)\-[^-]+$/);
-	$lcid = $1;
+	last unless ($s =~ /(.*)\-[^-]+$/);
+	$s = $1;
     }
     return undef;
 }

+ 1 - 0
tools/perlinc/lcid.ph

@@ -1,4 +1,5 @@
 %langid = (
+	''		=> 0x0000,
 	'af'            => 0x0036,
 	'af-za'         => 0x0436,
 	'am'            => 0x005e,

+ 48 - 19
tools/usbdescgen.pl

@@ -411,6 +411,7 @@ sub usb_device(&) {
 
 my @langlist;
 my %lang;
+my @lang_mask = (0xffff, 0x03ff, 0); # Masks for language codes
 my $stringdata;
 my %stringoffs;			# Pointer into stringdata
 
@@ -433,23 +434,30 @@ sub usb_string(%) {
     my(%strh) = @_;
 
     my $descval = '';
-    my @txts = (undef) x scalar(@langlist);
-
-    my $found = $strh{''} ne ''; # Default string
+    my %txts;
+    my $found = 0;
 
     foreach my $l (keys(%strh)) {
+	my $str = $strh{$l};
 	my $co = langid($l);
 	next unless (defined($co));
-	$txts[$lang{$co}] = $strh{$l};
-	$found += $strh{$l} ne '';
+
+	foreach my $m (@lang_mask) {
+	    my $coi = $co & $m;
+	    next unless (defined($lang{$coi}));
+	    next if (defined($txts{$coi}));
+	    $txts{$coi} = $str;
+	    $found++;
+	}
     }
 
     return pack('C', 0) unless ($found);
 
-    for (my $i = 0; $i < scalar(@langlist); $i++) {
-	my $co  = $langlist[$i];
-	my $txt = $txts[$i];
-	$txt = $strh{''} unless defined($txt);
+    foreach my $co (@langlist) {
+	my $txt;
+	foreach my $m (@lang_mask) {
+	    last if (defined($txt = $txts{$co & $m}));
+	}
 
 	my $utf16str = $utf16le->encode($txt, Encode::FB_WARN);
 	unless (defined($stringoffs{$utf16str})) {
@@ -458,7 +466,7 @@ sub usb_string(%) {
 	    $stringdata .= $utf16str;
 	}
 
-	$descval .= pack('vv', $co, $stringoffs{$utf16str});
+	$descval .= pack('v', $stringoffs{$utf16str});
     }
 
     my $descindex = $strdesci{$descval};
@@ -516,13 +524,27 @@ sub usb_serial($;$) {
 sub usb_languages(@) {
     my @langs = @_;
 
+    # There are two differences between keys(%lang) and @langlist: the
+    # former lists all possible language codes for which a lookup is
+    # possible in no specific order, whereas @langlist is exactly the
+    # items wanted in order in string descriptor zero.
+    #
+    # In other words, %lang is a reverse mapping of langlist, *plus*
+    # permissible aliases.
     %lang = ();
     @langlist = ();
     foreach my $l (@langs) {
 	my $co = langid($l);
 	if (defined($co) && !defined($lang{$co})) {
-	    $lang{$co} = scalar(@langlist);
+	    my $nlang = scalar(@langlist);
 	    push(@langlist, $co);
+
+	    # Defaults for partial languages?
+	    foreach my $m (@lang_mask) {
+		if (!defined($lang{$co & $m})) {
+		    $lang{$co & $m} = $nlang;
+		}
+	    }
 	}
     }
 
@@ -576,16 +598,23 @@ sub generate_data()
 		     'len' => unpack('C', substr($stringdata, 0, 1))});
 
     for (my $i = 1; $i < scalar(@strdescs); $i++) {
-	my @sds = unpack("v*", $strdescs[$i]);
-	while (scalar @sds >= 2) {
-	    my $co = shift(@sds);
-	    my $offs = shift(@sds);
+	my @sofs = unpack('v*', $strdescs[$i]);
+	for (my $j = 0; $j < scalar(@sofs); $j++) {
+	    my $co = $langlist[$j];
+	    my $of = $sofs[$j];
+	    my $m  = $lang_mask[0];
+
+	    # Widen the mask as much as possible
+	    foreach my $mx (@lang_mask) {
+		last unless ($lang{$co & $mx} == $lang{$co & $m});
+		$m = $mx;
+	    }
 
 	    push(@ptrs, {'type' => $DT{string}, 'dindex' => $i,
-			     'windex' => $co, 'wimask' => 0x03ff, # FIX THIS
-			     'offs' => $string_offs + $offs,
-			     'len' =>
-			     unpack('C', substr($stringdata, $offs, 1))});
+			     'windex' => $co & $m,
+			     'wimask' => $m,
+			     'offs' => $string_offs + $of,
+			     'len' => unpack('C', substr($stringdata,$of,1))});
 	}
     }
     $data .= $stringdata;