关于字符串描述符的地位仅次于设备/配置/接口/端点四大描述符,那四大设备必须得支持,而字符串描述符对设备来说则是可选的,这并不是就说字符串描述符不重要,对咱们来说,提供字符串描述符的设备要比没有提供的设备亲切的多,不会有人会专门去记前面使用lsusb列出的04b4表示的是Cypress Semiconductor Corp。
一提到字符串,不可避免就得提到字符串使用的语言。Spec里就说了,字符串描述符使用的就是UNICODE编码,usb设备里的字符串可以通过它来支持多种语言,不过你需要在向设备请求字符串描述符的时候指定一个你期望看到的一种语言,俗称语言ID,即Language ID。这个语言ID使用两个字节表示,所有可以使用的语言ID在http://www.usb.org/developers/docs/USB_LANGIDs.pdf 文档里都有列出来,从这个文档里你也可以明白为啥要使用两个字节,而不是一个字节表示。这么说吧,比如中文是0X04,但是中文还有好几种,所以就需要另外一个字节表示是哪种中文,简体就是0X02,注意合起来表示简体中文并不是0X0402或者0X0204,因为这两个字节并不是分的那么清,bit0~9一共10位去表示Primary语言ID,其余6位去表示Sub语言ID,毕竟一种语言的Sub语言不可能会特别的多,没必要分去整整一半8bits,所以简体中文的语言ID就是0X0804。
还是结合代码,从上节飘过的usb_cache_string说起,看看如何去获得一个字符串描述符,它在message.c里定义:
/**
* usb_cache_string - read a string descriptor and cache it for later use
* @udev: the device whose string descriptor is being read
* @index: the descriptor index
*
* Returns a pointer to a kmalloc'ed buffer containing the descriptor string,
* or NULL if the index is 0 or the string could not be read.
*/
char *usb_cache_string(struct usb_device *udev, int index)
{
char *buf;
char *smallbuf = NULL;
int len;
if (index > 0 && (buf = kmalloc(256, GFP_KERNEL)) != NULL) {
if ((len = usb_string(udev, index, buf, 256)) > 0) {
if ((smallbuf = kmalloc(++len, GFP_KERNEL)) == NULL)
return buf;
memcpy(smallbuf, buf, len);
}
kfree(buf);
}
return smallbuf;
}
每个字符串描述符都有一个序号(index),字符串描述符这个序号是不能重复的,不过这点不用你我操心,都是设备已经固化好了的东西,重复不重复也不是咱们要操心的事。
参数的index就是表示了你希望获得其中的第几个。但是不可疏忽大意的是,你不能指定index为0,0编号是有特殊用途的,你指定0了就什么也得不到。
有关这个函数,还需要明白两点,第一是它采用的方针策略,就是苦活儿累活儿找个usb_string()去做。这个usb_string()怎么工作的之后再看,现在只要注意下它的参数,比usb_cache_string()的参数多了两个,就是buf和size,也就是需要传递一个存放返回的字符串描述符的缓冲区。但是你调用usb_cache_string()的时候并没有指定一个明确的size,usb_cache_string()也就不知道你想要的那个字符串描述符有多大,于是它就采用了这么一个技巧,先申请一个足够大的缓冲区,这里是256字节,拿这个缓冲区去调用usb_string(),通过usb_string()的返回值会得到字符串描述符的真实大小,然后再拿这个真实的大小去申请一个缓冲区,并将大缓冲区里放的字符串描述符数据拷贝过来,这时那个大缓冲区当然就没什么利用价值了,于是再把它给释放掉。第二就是申请那个小缓冲区的时候,使用的并不是usb_string()的返回值,而是多了1个字节,也就是说要从大缓冲区里多拷一个字节到小缓冲区里,为什么?这牵涉到C里字符串方面那个人见人愁鬼见鬼哭的代码杀手——字符串结束符。字符串都需要那么一个结束符,这点是个正常人都知道的,但并不是每个正常人都能每时每刻的记得给字符串加上这么一个结束符。原因是spec说字符串描述符不是一个NULL-terminated字符串,意思也就是字符串描述符没有一个结束符,你从设备那里得到字符串之后得给它追加一个结束符。本来usb_string()里已经为buf追加好了,但是它返回的长度里还是没有包括进这个结束符的1个字节,所以usb_cache_string()为smallbuf申请内存的时候就得多准备那么一个字节,以便将buf里的那个结束符也给拷过来。现在就看看usb_string()的细节,定义在message.c里:
/**
* usb_string - returns ISO 8859-1 version of a string descriptor
* @dev: the device whose string descriptor is being retrieved
* @index: the number of the descriptor
* @buf: where to put the string
* @size: how big is "buf"?
* Context: !in_interrupt ()
*
* This converts the UTF-16LE encoded strings returned by devices, from
* usb_get_string_descriptor(), to null-terminated ISO-8859-1 encoded ones
* that are more usable in most kernel contexts. Note that all characters
* in the chosen descriptor that can't be encoded using ISO-8859-1
* are converted to the question

本文介绍了USB驱动开发中字符串描述符的重要性和使用,强调了语言ID在请求字符串描述符时的作用。详细阐述了如何获取和处理字符串描述符,包括USB设备的UNICODE编码、语言ID的表示以及如何从设备获取特定语言的字符串。还探讨了字符串结束符的处理和转换过程。
最低0.47元/天 解锁文章
2614

被折叠的 条评论
为什么被折叠?



