2011-04-07 19:45:52 +04:00
/*
* kinect sensor device camera , gspca driver
*
* Copyright ( C ) 2011 Antonio Ospite < ospite @ studenti . unina . it >
*
* Based on the OpenKinect project and libfreenect
* http : //openkinect.org/wiki/Init_Analysis
*
* Special thanks to Steven Toth and kernellabs . com for sponsoring a Kinect
* sensor device which I tested the driver on .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
2011-08-22 02:56:57 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2011-04-07 19:45:52 +04:00
# define MODULE_NAME "kinect"
# include "gspca.h"
# define CTRL_TIMEOUT 500
MODULE_AUTHOR ( " Antonio Ospite <ospite@studenti.unina.it> " ) ;
MODULE_DESCRIPTION ( " GSPCA/Kinect Sensor Device USB Camera Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
2014-06-25 13:27:57 +04:00
static bool depth_mode ;
2011-04-07 19:45:52 +04:00
struct pkt_hdr {
uint8_t magic [ 2 ] ;
uint8_t pad ;
uint8_t flag ;
uint8_t unk1 ;
uint8_t seq ;
uint8_t unk2 ;
uint8_t unk3 ;
uint32_t timestamp ;
} ;
struct cam_hdr {
uint8_t magic [ 2 ] ;
uint16_t len ;
uint16_t cmd ;
uint16_t tag ;
} ;
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev ; /* !! must be the first item */
uint16_t cam_tag ; /* a sequence number for packets */
2011-04-21 13:51:35 +04:00
uint8_t stream_flag ; /* to identify different stream types */
2011-04-21 13:51:34 +04:00
uint8_t obuf [ 0x400 ] ; /* output buffer for control commands */
uint8_t ibuf [ 0x200 ] ; /* input buffer for control commands */
2011-04-07 19:45:52 +04:00
} ;
# define MODE_640x480 0x0001
# define MODE_640x488 0x0002
# define MODE_1280x1024 0x0004
# define FORMAT_BAYER 0x0010
# define FORMAT_UYVY 0x0020
# define FORMAT_Y10B 0x0040
# define FPS_HIGH 0x0100
2014-06-25 13:27:57 +04:00
static const struct v4l2_pix_format depth_camera_mode [ ] = {
{ 640 , 480 , V4L2_PIX_FMT_Y10BPACK , V4L2_FIELD_NONE ,
. bytesperline = 640 * 10 / 8 ,
. sizeimage = 640 * 480 * 10 / 8 ,
. colorspace = V4L2_COLORSPACE_SRGB ,
. priv = MODE_640x488 | FORMAT_Y10B } ,
} ;
2011-04-07 19:45:52 +04:00
static const struct v4l2_pix_format video_camera_mode [ ] = {
{ 640 , 480 , V4L2_PIX_FMT_SGRBG8 , V4L2_FIELD_NONE ,
. bytesperline = 640 ,
. sizeimage = 640 * 480 ,
. colorspace = V4L2_COLORSPACE_SRGB ,
. priv = MODE_640x480 | FORMAT_BAYER | FPS_HIGH } ,
{ 640 , 480 , V4L2_PIX_FMT_UYVY , V4L2_FIELD_NONE ,
. bytesperline = 640 * 2 ,
. sizeimage = 640 * 480 * 2 ,
. colorspace = V4L2_COLORSPACE_SRGB ,
. priv = MODE_640x480 | FORMAT_UYVY } ,
{ 1280 , 1024 , V4L2_PIX_FMT_SGRBG8 , V4L2_FIELD_NONE ,
. bytesperline = 1280 ,
. sizeimage = 1280 * 1024 ,
. colorspace = V4L2_COLORSPACE_SRGB ,
. priv = MODE_1280x1024 | FORMAT_BAYER } ,
{ 640 , 488 , V4L2_PIX_FMT_Y10BPACK , V4L2_FIELD_NONE ,
. bytesperline = 640 * 10 / 8 ,
. sizeimage = 640 * 488 * 10 / 8 ,
. colorspace = V4L2_COLORSPACE_SRGB ,
. priv = MODE_640x488 | FORMAT_Y10B | FPS_HIGH } ,
{ 1280 , 1024 , V4L2_PIX_FMT_Y10BPACK , V4L2_FIELD_NONE ,
. bytesperline = 1280 * 10 / 8 ,
. sizeimage = 1280 * 1024 * 10 / 8 ,
. colorspace = V4L2_COLORSPACE_SRGB ,
. priv = MODE_1280x1024 | FORMAT_Y10B } ,
} ;
static int kinect_write ( struct usb_device * udev , uint8_t * data ,
uint16_t wLength )
{
return usb_control_msg ( udev ,
usb_sndctrlpipe ( udev , 0 ) ,
0x00 ,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE ,
0 , 0 , data , wLength , CTRL_TIMEOUT ) ;
}
static int kinect_read ( struct usb_device * udev , uint8_t * data , uint16_t wLength )
{
return usb_control_msg ( udev ,
usb_rcvctrlpipe ( udev , 0 ) ,
0x00 ,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE ,
0 , 0 , data , wLength , CTRL_TIMEOUT ) ;
}
static int send_cmd ( struct gspca_dev * gspca_dev , uint16_t cmd , void * cmdbuf ,
unsigned int cmd_len , void * replybuf , unsigned int reply_len )
{
struct sd * sd = ( struct sd * ) gspca_dev ;
struct usb_device * udev = gspca_dev - > dev ;
int res , actual_len ;
2011-04-21 13:51:34 +04:00
uint8_t * obuf = sd - > obuf ;
uint8_t * ibuf = sd - > ibuf ;
2011-04-07 19:45:52 +04:00
struct cam_hdr * chdr = ( void * ) obuf ;
struct cam_hdr * rhdr = ( void * ) ibuf ;
if ( cmd_len & 1 | | cmd_len > ( 0x400 - sizeof ( * chdr ) ) ) {
2011-08-22 02:56:57 +04:00
pr_err ( " send_cmd: Invalid command length (0x%x) \n " , cmd_len ) ;
2011-04-07 19:45:52 +04:00
return - 1 ;
}
chdr - > magic [ 0 ] = 0x47 ;
chdr - > magic [ 1 ] = 0x4d ;
chdr - > cmd = cpu_to_le16 ( cmd ) ;
chdr - > tag = cpu_to_le16 ( sd - > cam_tag ) ;
chdr - > len = cpu_to_le16 ( cmd_len / 2 ) ;
memcpy ( obuf + sizeof ( * chdr ) , cmdbuf , cmd_len ) ;
res = kinect_write ( udev , obuf , cmd_len + sizeof ( * chdr ) ) ;
PDEBUG ( D_USBO , " Control cmd=%04x tag=%04x len=%04x: %d " , cmd ,
sd - > cam_tag , cmd_len , res ) ;
if ( res < 0 ) {
2011-08-22 02:56:57 +04:00
pr_err ( " send_cmd: Output control transfer failed (%d) \n " , res ) ;
2011-04-07 19:45:52 +04:00
return res ;
}
do {
actual_len = kinect_read ( udev , ibuf , 0x200 ) ;
} while ( actual_len = = 0 ) ;
2013-12-30 20:41:46 +04:00
PDEBUG ( D_USBO , " Control reply: %d " , actual_len ) ;
2011-04-07 19:45:52 +04:00
if ( actual_len < sizeof ( * rhdr ) ) {
2013-12-30 20:41:46 +04:00
pr_err ( " send_cmd: Input control transfer failed (%d) \n " ,
actual_len ) ;
2013-12-30 20:41:45 +04:00
return actual_len < 0 ? actual_len : - EREMOTEIO ;
2011-04-07 19:45:52 +04:00
}
actual_len - = sizeof ( * rhdr ) ;
if ( rhdr - > magic [ 0 ] ! = 0x52 | | rhdr - > magic [ 1 ] ! = 0x42 ) {
2011-08-22 02:56:57 +04:00
pr_err ( " send_cmd: Bad magic %02x %02x \n " ,
rhdr - > magic [ 0 ] , rhdr - > magic [ 1 ] ) ;
2011-04-07 19:45:52 +04:00
return - 1 ;
}
if ( rhdr - > cmd ! = chdr - > cmd ) {
2011-08-22 02:56:57 +04:00
pr_err ( " send_cmd: Bad cmd %02x != %02x \n " ,
rhdr - > cmd , chdr - > cmd ) ;
2011-04-07 19:45:52 +04:00
return - 1 ;
}
if ( rhdr - > tag ! = chdr - > tag ) {
2011-08-22 02:56:57 +04:00
pr_err ( " send_cmd: Bad tag %04x != %04x \n " ,
rhdr - > tag , chdr - > tag ) ;
2011-04-07 19:45:52 +04:00
return - 1 ;
}
if ( cpu_to_le16 ( rhdr - > len ) ! = ( actual_len / 2 ) ) {
2011-08-22 02:56:57 +04:00
pr_err ( " send_cmd: Bad len %04x != %04x \n " ,
cpu_to_le16 ( rhdr - > len ) , ( int ) ( actual_len / 2 ) ) ;
2011-04-07 19:45:52 +04:00
return - 1 ;
}
if ( actual_len > reply_len ) {
2011-08-22 02:56:57 +04:00
pr_warn ( " send_cmd: Data buffer is %d bytes long, but got %d bytes \n " ,
reply_len , actual_len ) ;
2011-04-07 19:45:52 +04:00
memcpy ( replybuf , ibuf + sizeof ( * rhdr ) , reply_len ) ;
} else {
memcpy ( replybuf , ibuf + sizeof ( * rhdr ) , actual_len ) ;
}
sd - > cam_tag + + ;
return actual_len ;
}
static int write_register ( struct gspca_dev * gspca_dev , uint16_t reg ,
uint16_t data )
{
uint16_t reply [ 2 ] ;
uint16_t cmd [ 2 ] ;
int res ;
cmd [ 0 ] = cpu_to_le16 ( reg ) ;
cmd [ 1 ] = cpu_to_le16 ( data ) ;
PDEBUG ( D_USBO , " Write Reg 0x%04x <= 0x%02x " , reg , data ) ;
res = send_cmd ( gspca_dev , 0x03 , cmd , 4 , reply , 4 ) ;
if ( res < 0 )
return res ;
if ( res ! = 2 ) {
2011-08-22 02:56:57 +04:00
pr_warn ( " send_cmd returned %d [%04x %04x], 0000 expected \n " ,
res , reply [ 0 ] , reply [ 1 ] ) ;
2011-04-07 19:45:52 +04:00
}
return 0 ;
}
/* this function is called at probe time */
2014-06-25 13:27:57 +04:00
static int sd_config_video ( struct gspca_dev * gspca_dev ,
2011-04-07 19:45:52 +04:00
const struct usb_device_id * id )
{
struct sd * sd = ( struct sd * ) gspca_dev ;
struct cam * cam ;
sd - > cam_tag = 0 ;
sd - > stream_flag = 0x80 ;
cam = & gspca_dev - > cam ;
cam - > cam_mode = video_camera_mode ;
cam - > nmodes = ARRAY_SIZE ( video_camera_mode ) ;
2014-06-25 13:27:57 +04:00
gspca_dev - > xfer_ep = 0x81 ;
2011-04-07 19:45:52 +04:00
#if 0
2011-04-21 13:51:36 +04:00
/* Setting those values is not needed for video stream */
2011-04-07 19:45:52 +04:00
cam - > npkt = 15 ;
gspca_dev - > pkt_size = 960 * 2 ;
# endif
return 0 ;
}
2014-06-25 13:27:57 +04:00
static int sd_config_depth ( struct gspca_dev * gspca_dev ,
const struct usb_device_id * id )
{
struct sd * sd = ( struct sd * ) gspca_dev ;
struct cam * cam ;
sd - > cam_tag = 0 ;
sd - > stream_flag = 0x70 ;
cam = & gspca_dev - > cam ;
cam - > cam_mode = depth_camera_mode ;
cam - > nmodes = ARRAY_SIZE ( depth_camera_mode ) ;
gspca_dev - > xfer_ep = 0x82 ;
return 0 ;
}
2011-04-07 19:45:52 +04:00
/* this function is called at probe and resume time */
static int sd_init ( struct gspca_dev * gspca_dev )
{
PDEBUG ( D_PROBE , " Kinect Camera device. " ) ;
return 0 ;
}
2014-06-25 13:27:57 +04:00
static int sd_start_video ( struct gspca_dev * gspca_dev )
2011-04-07 19:45:52 +04:00
{
int mode ;
uint8_t fmt_reg , fmt_val ;
uint8_t res_reg , res_val ;
uint8_t fps_reg , fps_val ;
uint8_t mode_val ;
mode = gspca_dev - > cam . cam_mode [ gspca_dev - > curr_mode ] . priv ;
if ( mode & FORMAT_Y10B ) {
fmt_reg = 0x19 ;
res_reg = 0x1a ;
fps_reg = 0x1b ;
mode_val = 0x03 ;
} else {
fmt_reg = 0x0c ;
res_reg = 0x0d ;
fps_reg = 0x0e ;
mode_val = 0x01 ;
}
/* format */
if ( mode & FORMAT_UYVY )
fmt_val = 0x05 ;
else
fmt_val = 0x00 ;
if ( mode & MODE_1280x1024 )
res_val = 0x02 ;
else
res_val = 0x01 ;
if ( mode & FPS_HIGH )
fps_val = 0x1e ;
else
fps_val = 0x0f ;
/* turn off IR-reset function */
write_register ( gspca_dev , 0x105 , 0x00 ) ;
/* Reset video stream */
write_register ( gspca_dev , 0x05 , 0x00 ) ;
/* Due to some ridiculous condition in the firmware, we have to start
* and stop the depth stream before the camera will hand us 1280 x1024
* IR . This is a stupid workaround , but we ' ve yet to find a better
* solution .
*
* Thanks to Drew Fisher for figuring this out .
*/
if ( mode & ( FORMAT_Y10B | MODE_1280x1024 ) ) {
write_register ( gspca_dev , 0x13 , 0x01 ) ;
write_register ( gspca_dev , 0x14 , 0x1e ) ;
write_register ( gspca_dev , 0x06 , 0x02 ) ;
write_register ( gspca_dev , 0x06 , 0x00 ) ;
}
write_register ( gspca_dev , fmt_reg , fmt_val ) ;
write_register ( gspca_dev , res_reg , res_val ) ;
write_register ( gspca_dev , fps_reg , fps_val ) ;
/* Start video stream */
write_register ( gspca_dev , 0x05 , mode_val ) ;
/* disable Hflip */
write_register ( gspca_dev , 0x47 , 0x00 ) ;
return 0 ;
}
2014-06-25 13:27:57 +04:00
static int sd_start_depth ( struct gspca_dev * gspca_dev )
{
/* turn off IR-reset function */
write_register ( gspca_dev , 0x105 , 0x00 ) ;
/* reset depth stream */
write_register ( gspca_dev , 0x06 , 0x00 ) ;
/* Depth Stream Format 0x03: 11 bit stream | 0x02: 10 bit */
write_register ( gspca_dev , 0x12 , 0x02 ) ;
/* Depth Stream Resolution 1: standard (640x480) */
write_register ( gspca_dev , 0x13 , 0x01 ) ;
/* Depth Framerate / 0x1e (30): 30 fps */
write_register ( gspca_dev , 0x14 , 0x1e ) ;
/* Depth Stream Control / 2: Open Depth Stream */
write_register ( gspca_dev , 0x06 , 0x02 ) ;
/* disable depth hflip / LSB = 0: Smoothing Disabled */
write_register ( gspca_dev , 0x17 , 0x00 ) ;
return 0 ;
}
static void sd_stopN_video ( struct gspca_dev * gspca_dev )
2011-04-07 19:45:52 +04:00
{
/* reset video stream */
write_register ( gspca_dev , 0x05 , 0x00 ) ;
}
2014-06-25 13:27:57 +04:00
static void sd_stopN_depth ( struct gspca_dev * gspca_dev )
{
/* reset depth stream */
write_register ( gspca_dev , 0x06 , 0x00 ) ;
}
2011-04-07 19:45:52 +04:00
static void sd_pkt_scan ( struct gspca_dev * gspca_dev , u8 * __data , int len )
{
struct sd * sd = ( struct sd * ) gspca_dev ;
struct pkt_hdr * hdr = ( void * ) __data ;
uint8_t * data = __data + sizeof ( * hdr ) ;
int datalen = len - sizeof ( * hdr ) ;
uint8_t sof = sd - > stream_flag | 1 ;
uint8_t mof = sd - > stream_flag | 2 ;
uint8_t eof = sd - > stream_flag | 5 ;
if ( len < 12 )
return ;
if ( hdr - > magic [ 0 ] ! = ' R ' | | hdr - > magic [ 1 ] ! = ' B ' ) {
2011-08-22 02:56:57 +04:00
pr_warn ( " [Stream %02x] Invalid magic %02x%02x \n " ,
sd - > stream_flag , hdr - > magic [ 0 ] , hdr - > magic [ 1 ] ) ;
2011-04-07 19:45:52 +04:00
return ;
}
if ( hdr - > flag = = sof )
gspca_frame_add ( gspca_dev , FIRST_PACKET , data , datalen ) ;
else if ( hdr - > flag = = mof )
gspca_frame_add ( gspca_dev , INTER_PACKET , data , datalen ) ;
else if ( hdr - > flag = = eof )
gspca_frame_add ( gspca_dev , LAST_PACKET , data , datalen ) ;
else
2011-08-22 02:56:57 +04:00
pr_warn ( " Packet type not recognized... \n " ) ;
2011-04-07 19:45:52 +04:00
}
/* sub-driver description */
2014-06-25 13:27:57 +04:00
static const struct sd_desc sd_desc_video = {
2011-04-07 19:45:52 +04:00
. name = MODULE_NAME ,
2014-06-25 13:27:57 +04:00
. config = sd_config_video ,
2011-04-07 19:45:52 +04:00
. init = sd_init ,
2014-06-25 13:27:57 +04:00
. start = sd_start_video ,
. stopN = sd_stopN_video ,
. pkt_scan = sd_pkt_scan ,
/*
. get_streamparm = sd_get_streamparm ,
. set_streamparm = sd_set_streamparm ,
*/
} ;
static const struct sd_desc sd_desc_depth = {
. name = MODULE_NAME ,
. config = sd_config_depth ,
. init = sd_init ,
. start = sd_start_depth ,
. stopN = sd_stopN_depth ,
2011-04-07 19:45:52 +04:00
. pkt_scan = sd_pkt_scan ,
/*
. get_streamparm = sd_get_streamparm ,
. set_streamparm = sd_set_streamparm ,
*/
} ;
/* -- module initialisation -- */
2011-05-17 10:56:09 +04:00
static const struct usb_device_id device_table [ ] = {
2011-04-07 19:45:52 +04:00
{ USB_DEVICE ( 0x045e , 0x02ae ) } ,
2012-12-10 03:18:25 +04:00
{ USB_DEVICE ( 0x045e , 0x02bf ) } ,
2011-04-07 19:45:52 +04:00
{ }
} ;
MODULE_DEVICE_TABLE ( usb , device_table ) ;
/* -- device connect -- */
static int sd_probe ( struct usb_interface * intf , const struct usb_device_id * id )
{
2014-06-25 13:27:57 +04:00
if ( depth_mode )
return gspca_dev_probe ( intf , id , & sd_desc_depth ,
sizeof ( struct sd ) , THIS_MODULE ) ;
else
return gspca_dev_probe ( intf , id , & sd_desc_video ,
sizeof ( struct sd ) , THIS_MODULE ) ;
2011-04-07 19:45:52 +04:00
}
static struct usb_driver sd_driver = {
. name = MODULE_NAME ,
. id_table = device_table ,
. probe = sd_probe ,
. disconnect = gspca_disconnect ,
# ifdef CONFIG_PM
. suspend = gspca_suspend ,
. resume = gspca_resume ,
2012-06-30 13:44:47 +04:00
. reset_resume = gspca_resume ,
2011-04-07 19:45:52 +04:00
# endif
} ;
2011-11-18 21:46:12 +04:00
module_usb_driver ( sd_driver ) ;
2014-06-25 13:27:57 +04:00
module_param ( depth_mode , bool , 0644 ) ;
MODULE_PARM_DESC ( depth_mode , " 0=video 1=depth " ) ;