2005-04-17 02:20:36 +04:00
/*
* Copyright ( C ) 2003 David Brownell
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation ; either version 2.1 of the License , or
* ( at your option ) any later version .
*/
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/list.h>
# include <linux/string.h>
# include <linux/device.h>
# include <linux/init.h>
2006-12-17 02:34:53 +03:00
# include <linux/usb/ch9.h>
2007-10-05 05:05:17 +04:00
# include <linux/usb/gadget.h>
2005-04-17 02:20:36 +04:00
# include <asm/unaligned.h>
static int utf8_to_utf16le ( const char * s , __le16 * cp , unsigned len )
{
int count = 0 ;
u8 c ;
u16 uchar ;
/* this insists on correct encodings, though not minimal ones.
* BUT it currently rejects legit 4 - byte UTF - 8 code points ,
* which need surrogate pairs . ( Unicode 3.1 can use them . )
*/
while ( len ! = 0 & & ( c = ( u8 ) * s + + ) ! = 0 ) {
if ( unlikely ( c & 0x80 ) ) {
// 2-byte sequence:
// 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
if ( ( c & 0xe0 ) = = 0xc0 ) {
uchar = ( c & 0x1f ) < < 6 ;
c = ( u8 ) * s + + ;
2009-04-27 21:22:40 +04:00
if ( ( c & 0xc0 ) ! = 0x80 )
2005-04-17 02:20:36 +04:00
goto fail ;
c & = 0x3f ;
uchar | = c ;
// 3-byte sequence (most CJKV characters):
// zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
} else if ( ( c & 0xf0 ) = = 0xe0 ) {
uchar = ( c & 0x0f ) < < 12 ;
c = ( u8 ) * s + + ;
2009-04-27 21:22:40 +04:00
if ( ( c & 0xc0 ) ! = 0x80 )
2005-04-17 02:20:36 +04:00
goto fail ;
c & = 0x3f ;
uchar | = c < < 6 ;
c = ( u8 ) * s + + ;
2009-04-27 21:22:40 +04:00
if ( ( c & 0xc0 ) ! = 0x80 )
2005-04-17 02:20:36 +04:00
goto fail ;
c & = 0x3f ;
uchar | = c ;
/* no bogus surrogates */
if ( 0xd800 < = uchar & & uchar < = 0xdfff )
goto fail ;
// 4-byte sequence (surrogate pairs, currently rare):
// 11101110wwwwzzzzyy + 110111yyyyxxxxxx
// = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
// (uuuuu = wwww + 1)
// FIXME accept the surrogate code points (only)
} else
goto fail ;
} else
uchar = c ;
2008-04-29 12:03:40 +04:00
put_unaligned_le16 ( uchar , cp + + ) ;
2005-04-17 02:20:36 +04:00
count + + ;
len - - ;
}
return count ;
fail :
return - 1 ;
}
/**
* usb_gadget_get_string - fill out a string descriptor
* @ table : of c strings encoded using UTF - 8
* @ id : string id , from low byte of wValue in get string descriptor
* @ buf : at least 256 bytes
*
* Finds the UTF - 8 string matching the ID , and converts it into a
* string descriptor in utf16 - le .
* Returns length of descriptor ( always even ) or negative errno
*
* If your driver needs stings in multiple languages , you ' ll probably
* " switch (wIndex) { ... } " in your ep0 string descriptor logic ,
* using this routine after choosing which set of UTF - 8 strings to use .
* Note that US - ASCII is a strict subset of UTF - 8 ; any string bytes with
* the eighth bit set will be multibyte UTF - 8 characters , not ISO - 8859 / 1
* characters ( which are also widely used in C strings ) .
*/
int
usb_gadget_get_string ( struct usb_gadget_strings * table , int id , u8 * buf )
{
struct usb_string * s ;
int len ;
/* descriptor 0 has the language id */
if ( id = = 0 ) {
buf [ 0 ] = 4 ;
buf [ 1 ] = USB_DT_STRING ;
buf [ 2 ] = ( u8 ) table - > language ;
buf [ 3 ] = ( u8 ) ( table - > language > > 8 ) ;
return 4 ;
}
for ( s = table - > strings ; s & & s - > s ; s + + )
if ( s - > id = = id )
break ;
/* unrecognized: stall. */
if ( ! s | | ! s - > s )
return - EINVAL ;
/* string descriptors have length, tag, then UTF16-LE text */
len = min ( ( size_t ) 126 , strlen ( s - > s ) ) ;
memset ( buf + 2 , 0 , 2 * len ) ; /* zero all the bytes */
len = utf8_to_utf16le ( s - > s , ( __le16 * ) & buf [ 2 ] , len ) ;
if ( len < 0 )
return - EINVAL ;
buf [ 0 ] = ( len + 1 ) * 2 ;
buf [ 1 ] = USB_DT_STRING ;
return buf [ 0 ] ;
}