Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
au6610.c
Go to the documentation of this file.
1 /*
2  * DVB USB Linux driver for Alcor Micro AU6610 DVB-T USB2.0.
3  *
4  * Copyright (C) 2006 Antti Palosaari <[email protected]>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20 
21 #include "au6610.h"
22 #include "zl10353.h"
23 #include "qt1010.h"
24 
26 
27 static int au6610_usb_msg(struct dvb_usb_device *d, u8 operation, u8 addr,
28  u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
29 {
30  int ret;
31  u16 index;
32  u8 *usb_buf;
33 
34  /*
35  * allocate enough for all known requests,
36  * read returns 5 and write 6 bytes
37  */
38  usb_buf = kmalloc(6, GFP_KERNEL);
39  if (!usb_buf)
40  return -ENOMEM;
41 
42  switch (wlen) {
43  case 1:
44  index = wbuf[0] << 8;
45  break;
46  case 2:
47  index = wbuf[0] << 8;
48  index += wbuf[1];
49  break;
50  default:
51  dev_err(&d->udev->dev, "%s: wlen=%d, aborting\n",
52  KBUILD_MODNAME, wlen);
53  ret = -EINVAL;
54  goto error;
55  }
56 
57  ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), operation,
58  USB_TYPE_VENDOR|USB_DIR_IN, addr << 1, index,
59  usb_buf, 6, AU6610_USB_TIMEOUT);
60 
61  dvb_usb_dbg_usb_control_msg(d->udev, operation,
62  (USB_TYPE_VENDOR|USB_DIR_IN), addr << 1, index,
63  usb_buf, 6);
64 
65  if (ret < 0)
66  goto error;
67 
68  switch (operation) {
71  /* requested value is always 5th byte in buffer */
72  rbuf[0] = usb_buf[4];
73  }
74 error:
75  kfree(usb_buf);
76  return ret;
77 }
78 
79 static int au6610_i2c_msg(struct dvb_usb_device *d, u8 addr,
80  u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
81 {
82  u8 request;
83  u8 wo = (rbuf == NULL || rlen == 0); /* write-only */
84 
85  if (wo) {
86  request = AU6610_REQ_I2C_WRITE;
87  } else { /* rw */
88  request = AU6610_REQ_I2C_READ;
89  }
90 
91  return au6610_usb_msg(d, request, addr, wbuf, wlen, rbuf, rlen);
92 }
93 
94 
95 /* I2C */
96 static int au6610_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
97  int num)
98 {
99  struct dvb_usb_device *d = i2c_get_adapdata(adap);
100  int i;
101 
102  if (num > 2)
103  return -EINVAL;
104 
105  if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
106  return -EAGAIN;
107 
108  for (i = 0; i < num; i++) {
109  /* write/read request */
110  if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
111  if (au6610_i2c_msg(d, msg[i].addr, msg[i].buf,
112  msg[i].len, msg[i+1].buf,
113  msg[i+1].len) < 0)
114  break;
115  i++;
116  } else if (au6610_i2c_msg(d, msg[i].addr, msg[i].buf,
117  msg[i].len, NULL, 0) < 0)
118  break;
119  }
120 
121  mutex_unlock(&d->i2c_mutex);
122  return i;
123 }
124 
125 
126 static u32 au6610_i2c_func(struct i2c_adapter *adapter)
127 {
128  return I2C_FUNC_I2C;
129 }
130 
131 static struct i2c_algorithm au6610_i2c_algo = {
132  .master_xfer = au6610_i2c_xfer,
133  .functionality = au6610_i2c_func,
134 };
135 
136 /* Callbacks for DVB USB */
137 static struct zl10353_config au6610_zl10353_config = {
138  .demod_address = 0x0f,
139  .no_tuner = 1,
140  .parallel_ts = 1,
141 };
142 
143 static int au6610_zl10353_frontend_attach(struct dvb_usb_adapter *adap)
144 {
145  adap->fe[0] = dvb_attach(zl10353_attach, &au6610_zl10353_config,
146  &adap_to_d(adap)->i2c_adap);
147  if (adap->fe[0] == NULL)
148  return -ENODEV;
149 
150  return 0;
151 }
152 
153 static struct qt1010_config au6610_qt1010_config = {
154  .i2c_address = 0x62
155 };
156 
157 static int au6610_qt1010_tuner_attach(struct dvb_usb_adapter *adap)
158 {
159  return dvb_attach(qt1010_attach, adap->fe[0],
160  &adap_to_d(adap)->i2c_adap,
161  &au6610_qt1010_config) == NULL ? -ENODEV : 0;
162 }
163 
164 static int au6610_init(struct dvb_usb_device *d)
165 {
166  /* TODO: this functionality belongs likely to the streaming control */
167  /* bInterfaceNumber 0, bAlternateSetting 5 */
168  return usb_set_interface(d->udev, 0, 5);
169 }
170 
171 static struct dvb_usb_device_properties au6610_props = {
172  .driver_name = KBUILD_MODNAME,
173  .owner = THIS_MODULE,
174  .adapter_nr = adapter_nr,
175 
176  .i2c_algo = &au6610_i2c_algo,
177  .frontend_attach = au6610_zl10353_frontend_attach,
178  .tuner_attach = au6610_qt1010_tuner_attach,
179  .init = au6610_init,
180 
181  .num_adapters = 1,
182  .adapter = {
183  {
184  .stream = DVB_USB_STREAM_ISOC(0x82, 5, 40, 942, 1),
185  },
186  },
187 };
188 
189 static const struct usb_device_id au6610_id_table[] = {
191  &au6610_props, "Sigmatek DVB-110", NULL) },
192  { }
193 };
194 MODULE_DEVICE_TABLE(usb, au6610_id_table);
195 
196 static struct usb_driver au6610_driver = {
197  .name = KBUILD_MODNAME,
198  .id_table = au6610_id_table,
199  .probe = dvb_usbv2_probe,
200  .disconnect = dvb_usbv2_disconnect,
201  .suspend = dvb_usbv2_suspend,
202  .resume = dvb_usbv2_resume,
203  .reset_resume = dvb_usbv2_reset_resume,
204  .no_dynamic_id = 1,
205  .soft_unbind = 1,
206 };
207 
208 module_usb_driver(au6610_driver);
209 
210 MODULE_AUTHOR("Antti Palosaari <[email protected]>");
211 MODULE_DESCRIPTION("Driver for Alcor Micro AU6610 DVB-T USB2.0");
212 MODULE_VERSION("0.1");
213 MODULE_LICENSE("GPL");