1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /**************************************************************** |
3 | |
4 | Siano Mobile Silicon, Inc. |
5 | MDTV receiver kernel modules. |
6 | Copyright (C) 2005-2009, Uri Shkolnik, Anatoly Greenblat |
7 | |
8 | |
9 | ****************************************************************/ |
10 | |
11 | #include "smscoreapi.h" |
12 | |
13 | #include <linux/kernel.h> |
14 | #include <linux/init.h> |
15 | #include <linux/usb.h> |
16 | #include <linux/firmware.h> |
17 | #include <linux/slab.h> |
18 | #include <linux/module.h> |
19 | #include <media/media-device.h> |
20 | |
21 | #include "sms-cards.h" |
22 | #include "smsendian.h" |
23 | |
24 | #define USB1_BUFFER_SIZE 0x1000 |
25 | #define USB2_BUFFER_SIZE 0x2000 |
26 | |
27 | #define MAX_BUFFERS 50 |
28 | #define MAX_URBS 10 |
29 | |
30 | struct smsusb_device_t; |
31 | |
32 | enum smsusb_state { |
33 | SMSUSB_DISCONNECTED, |
34 | SMSUSB_SUSPENDED, |
35 | SMSUSB_ACTIVE |
36 | }; |
37 | |
38 | struct smsusb_urb_t { |
39 | struct list_head entry; |
40 | struct smscore_buffer_t *cb; |
41 | struct smsusb_device_t *dev; |
42 | |
43 | struct urb urb; |
44 | |
45 | /* For the bottom half */ |
46 | struct work_struct wq; |
47 | }; |
48 | |
49 | struct smsusb_device_t { |
50 | struct usb_device *udev; |
51 | struct smscore_device_t *coredev; |
52 | |
53 | struct smsusb_urb_t surbs[MAX_URBS]; |
54 | |
55 | int response_alignment; |
56 | int buffer_size; |
57 | |
58 | unsigned char in_ep; |
59 | unsigned char out_ep; |
60 | enum smsusb_state state; |
61 | }; |
62 | |
63 | static int smsusb_submit_urb(struct smsusb_device_t *dev, |
64 | struct smsusb_urb_t *surb); |
65 | |
66 | /* |
67 | * Completing URB's callback handler - bottom half (process context) |
68 | * submits the URB prepared on smsusb_onresponse() |
69 | */ |
70 | static void do_submit_urb(struct work_struct *work) |
71 | { |
72 | struct smsusb_urb_t *surb = container_of(work, struct smsusb_urb_t, wq); |
73 | struct smsusb_device_t *dev = surb->dev; |
74 | |
75 | smsusb_submit_urb(dev, surb); |
76 | } |
77 | |
78 | /* |
79 | * Completing URB's callback handler - top half (interrupt context) |
80 | * adds completing sms urb to the global surbs list and activtes the worker |
81 | * thread the surb |
82 | * IMPORTANT - blocking functions must not be called from here !!! |
83 | |
84 | * @param urb pointer to a completing urb object |
85 | */ |
86 | static void smsusb_onresponse(struct urb *urb) |
87 | { |
88 | struct smsusb_urb_t *surb = (struct smsusb_urb_t *) urb->context; |
89 | struct smsusb_device_t *dev = surb->dev; |
90 | |
91 | if (urb->status == -ESHUTDOWN) { |
92 | pr_err("error, urb status %d (-ESHUTDOWN), %d bytes\n" , |
93 | urb->status, urb->actual_length); |
94 | return; |
95 | } |
96 | |
97 | if ((urb->actual_length > 0) && (urb->status == 0)) { |
98 | struct sms_msg_hdr *phdr = (struct sms_msg_hdr *)surb->cb->p; |
99 | |
100 | smsendian_handle_message_header(phdr); |
101 | if (urb->actual_length >= phdr->msg_length) { |
102 | surb->cb->size = phdr->msg_length; |
103 | |
104 | if (dev->response_alignment && |
105 | (phdr->msg_flags & MSG_HDR_FLAG_SPLIT_MSG)) { |
106 | |
107 | surb->cb->offset = |
108 | dev->response_alignment + |
109 | ((phdr->msg_flags >> 8) & 3); |
110 | |
111 | /* sanity check */ |
112 | if (((int) phdr->msg_length + |
113 | surb->cb->offset) > urb->actual_length) { |
114 | pr_err("invalid response msglen %d offset %d size %d\n" , |
115 | phdr->msg_length, |
116 | surb->cb->offset, |
117 | urb->actual_length); |
118 | goto exit_and_resubmit; |
119 | } |
120 | |
121 | /* move buffer pointer and |
122 | * copy header to its new location */ |
123 | memcpy((char *) phdr + surb->cb->offset, |
124 | phdr, sizeof(struct sms_msg_hdr)); |
125 | } else |
126 | surb->cb->offset = 0; |
127 | |
128 | pr_debug("received %s(%d) size: %d\n" , |
129 | smscore_translate_msg(phdr->msg_type), |
130 | phdr->msg_type, phdr->msg_length); |
131 | |
132 | smsendian_handle_rx_message((struct sms_msg_data *) phdr); |
133 | |
134 | smscore_onresponse(dev->coredev, surb->cb); |
135 | surb->cb = NULL; |
136 | } else { |
137 | pr_err("invalid response msglen %d actual %d\n" , |
138 | phdr->msg_length, urb->actual_length); |
139 | } |
140 | } else |
141 | pr_err("error, urb status %d, %d bytes\n" , |
142 | urb->status, urb->actual_length); |
143 | |
144 | |
145 | exit_and_resubmit: |
146 | INIT_WORK(&surb->wq, do_submit_urb); |
147 | schedule_work(work: &surb->wq); |
148 | } |
149 | |
150 | static int smsusb_submit_urb(struct smsusb_device_t *dev, |
151 | struct smsusb_urb_t *surb) |
152 | { |
153 | if (!surb->cb) { |
154 | /* This function can sleep */ |
155 | surb->cb = smscore_getbuffer(dev->coredev); |
156 | if (!surb->cb) { |
157 | pr_err("smscore_getbuffer(...) returned NULL\n" ); |
158 | return -ENOMEM; |
159 | } |
160 | } |
161 | |
162 | usb_fill_bulk_urb( |
163 | urb: &surb->urb, |
164 | dev: dev->udev, |
165 | usb_rcvbulkpipe(dev->udev, dev->in_ep), |
166 | transfer_buffer: surb->cb->p, |
167 | buffer_length: dev->buffer_size, |
168 | complete_fn: smsusb_onresponse, |
169 | context: surb |
170 | ); |
171 | surb->urb.transfer_flags |= URB_FREE_BUFFER; |
172 | |
173 | return usb_submit_urb(urb: &surb->urb, GFP_ATOMIC); |
174 | } |
175 | |
176 | static void smsusb_stop_streaming(struct smsusb_device_t *dev) |
177 | { |
178 | int i; |
179 | |
180 | for (i = 0; i < MAX_URBS; i++) { |
181 | usb_kill_urb(urb: &dev->surbs[i].urb); |
182 | if (dev->surbs[i].wq.func) |
183 | cancel_work_sync(work: &dev->surbs[i].wq); |
184 | |
185 | if (dev->surbs[i].cb) { |
186 | smscore_putbuffer(dev->coredev, dev->surbs[i].cb); |
187 | dev->surbs[i].cb = NULL; |
188 | } |
189 | } |
190 | } |
191 | |
192 | static int smsusb_start_streaming(struct smsusb_device_t *dev) |
193 | { |
194 | int i, rc; |
195 | |
196 | for (i = 0; i < MAX_URBS; i++) { |
197 | rc = smsusb_submit_urb(dev, surb: &dev->surbs[i]); |
198 | if (rc < 0) { |
199 | pr_err("smsusb_submit_urb(...) failed\n" ); |
200 | smsusb_stop_streaming(dev); |
201 | break; |
202 | } |
203 | } |
204 | |
205 | return rc; |
206 | } |
207 | |
208 | static int smsusb_sendrequest(void *context, void *buffer, size_t size) |
209 | { |
210 | struct smsusb_device_t *dev = (struct smsusb_device_t *) context; |
211 | struct sms_msg_hdr *phdr; |
212 | int dummy, ret; |
213 | |
214 | if (dev->state != SMSUSB_ACTIVE) { |
215 | pr_debug("Device not active yet\n" ); |
216 | return -ENOENT; |
217 | } |
218 | |
219 | phdr = kmemdup(p: buffer, size, GFP_KERNEL); |
220 | if (!phdr) |
221 | return -ENOMEM; |
222 | |
223 | pr_debug("sending %s(%d) size: %d\n" , |
224 | smscore_translate_msg(phdr->msg_type), phdr->msg_type, |
225 | phdr->msg_length); |
226 | |
227 | smsendian_handle_tx_message((struct sms_msg_data *) phdr); |
228 | smsendian_handle_message_header((struct sms_msg_hdr *)phdr); |
229 | ret = usb_bulk_msg(usb_dev: dev->udev, usb_sndbulkpipe(dev->udev, 2), |
230 | data: phdr, len: size, actual_length: &dummy, timeout: 1000); |
231 | |
232 | kfree(objp: phdr); |
233 | return ret; |
234 | } |
235 | |
236 | static char *smsusb1_fw_lkup[] = { |
237 | "dvbt_stellar_usb.inp" , |
238 | "dvbh_stellar_usb.inp" , |
239 | "tdmb_stellar_usb.inp" , |
240 | "none" , |
241 | "dvbt_bda_stellar_usb.inp" , |
242 | }; |
243 | |
244 | static inline char *sms_get_fw_name(int mode, int board_id) |
245 | { |
246 | char **fw = sms_get_board(board_id)->fw; |
247 | return (fw && fw[mode]) ? fw[mode] : smsusb1_fw_lkup[mode]; |
248 | } |
249 | |
250 | static int smsusb1_load_firmware(struct usb_device *udev, int id, int board_id) |
251 | { |
252 | const struct firmware *fw; |
253 | u8 *fw_buffer; |
254 | int rc, dummy; |
255 | char *fw_filename; |
256 | |
257 | if (id < 0) |
258 | id = sms_get_board(board_id)->default_mode; |
259 | |
260 | if (id < DEVICE_MODE_DVBT || id > DEVICE_MODE_DVBT_BDA) { |
261 | pr_err("invalid firmware id specified %d\n" , id); |
262 | return -EINVAL; |
263 | } |
264 | |
265 | fw_filename = sms_get_fw_name(mode: id, board_id); |
266 | |
267 | rc = request_firmware(fw: &fw, name: fw_filename, device: &udev->dev); |
268 | if (rc < 0) { |
269 | pr_warn("failed to open '%s' mode %d, trying again with default firmware\n" , |
270 | fw_filename, id); |
271 | |
272 | fw_filename = smsusb1_fw_lkup[id]; |
273 | rc = request_firmware(fw: &fw, name: fw_filename, device: &udev->dev); |
274 | if (rc < 0) { |
275 | pr_warn("failed to open '%s' mode %d\n" , |
276 | fw_filename, id); |
277 | |
278 | return rc; |
279 | } |
280 | } |
281 | |
282 | fw_buffer = kmemdup(p: fw->data, size: fw->size, GFP_KERNEL); |
283 | if (fw_buffer) { |
284 | rc = usb_bulk_msg(usb_dev: udev, usb_sndbulkpipe(udev, 2), |
285 | data: fw_buffer, len: fw->size, actual_length: &dummy, timeout: 1000); |
286 | |
287 | pr_debug("sent %zu(%d) bytes, rc %d\n" , fw->size, dummy, rc); |
288 | |
289 | kfree(objp: fw_buffer); |
290 | } else { |
291 | pr_err("failed to allocate firmware buffer\n" ); |
292 | rc = -ENOMEM; |
293 | } |
294 | pr_debug("read FW %s, size=%zu\n" , fw_filename, fw->size); |
295 | |
296 | release_firmware(fw); |
297 | |
298 | return rc; |
299 | } |
300 | |
301 | static void smsusb1_detectmode(void *context, int *mode) |
302 | { |
303 | char *product_string = |
304 | ((struct smsusb_device_t *) context)->udev->product; |
305 | |
306 | *mode = DEVICE_MODE_NONE; |
307 | |
308 | if (!product_string) { |
309 | product_string = "none" ; |
310 | pr_err("product string not found\n" ); |
311 | } else if (strstr(product_string, "DVBH" )) |
312 | *mode = 1; |
313 | else if (strstr(product_string, "BDA" )) |
314 | *mode = 4; |
315 | else if (strstr(product_string, "DVBT" )) |
316 | *mode = 0; |
317 | else if (strstr(product_string, "TDMB" )) |
318 | *mode = 2; |
319 | |
320 | pr_debug("%d \"%s\"\n" , *mode, product_string); |
321 | } |
322 | |
323 | static int smsusb1_setmode(void *context, int mode) |
324 | { |
325 | struct sms_msg_hdr msg = { MSG_SW_RELOAD_REQ, 0, HIF_TASK, |
326 | sizeof(struct sms_msg_hdr), 0 }; |
327 | |
328 | if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_DVBT_BDA) { |
329 | pr_err("invalid firmware id specified %d\n" , mode); |
330 | return -EINVAL; |
331 | } |
332 | |
333 | return smsusb_sendrequest(context, buffer: &msg, size: sizeof(msg)); |
334 | } |
335 | |
336 | static void smsusb_term_device(struct usb_interface *intf) |
337 | { |
338 | struct smsusb_device_t *dev = usb_get_intfdata(intf); |
339 | |
340 | if (dev) { |
341 | dev->state = SMSUSB_DISCONNECTED; |
342 | |
343 | smsusb_stop_streaming(dev); |
344 | |
345 | /* unregister from smscore */ |
346 | if (dev->coredev) |
347 | smscore_unregister_device(dev->coredev); |
348 | |
349 | pr_debug("device 0x%p destroyed\n" , dev); |
350 | kfree(objp: dev); |
351 | } |
352 | |
353 | usb_set_intfdata(intf, NULL); |
354 | } |
355 | |
356 | static void *siano_media_device_register(struct smsusb_device_t *dev, |
357 | int board_id) |
358 | { |
359 | #ifdef CONFIG_MEDIA_CONTROLLER_DVB |
360 | struct media_device *mdev; |
361 | struct usb_device *udev = dev->udev; |
362 | struct sms_board *board = sms_get_board(board_id); |
363 | int ret; |
364 | |
365 | mdev = kzalloc(size: sizeof(*mdev), GFP_KERNEL); |
366 | if (!mdev) |
367 | return NULL; |
368 | |
369 | media_device_usb_init(mdev, udev, board->name); |
370 | |
371 | ret = media_device_register(mdev); |
372 | if (ret) { |
373 | media_device_cleanup(mdev); |
374 | kfree(objp: mdev); |
375 | return NULL; |
376 | } |
377 | |
378 | pr_info("media controller created\n" ); |
379 | |
380 | return mdev; |
381 | #else |
382 | return NULL; |
383 | #endif |
384 | } |
385 | |
386 | static int smsusb_init_device(struct usb_interface *intf, int board_id) |
387 | { |
388 | struct smsdevice_params_t params; |
389 | struct smsusb_device_t *dev; |
390 | void *mdev; |
391 | int i, rc; |
392 | int align = 0; |
393 | |
394 | /* create device object */ |
395 | dev = kzalloc(size: sizeof(struct smsusb_device_t), GFP_KERNEL); |
396 | if (!dev) |
397 | return -ENOMEM; |
398 | |
399 | memset(¶ms, 0, sizeof(params)); |
400 | usb_set_intfdata(intf, data: dev); |
401 | dev->udev = interface_to_usbdev(intf); |
402 | dev->state = SMSUSB_DISCONNECTED; |
403 | |
404 | for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) { |
405 | struct usb_endpoint_descriptor *desc = |
406 | &intf->cur_altsetting->endpoint[i].desc; |
407 | |
408 | if (desc->bEndpointAddress & USB_DIR_IN) { |
409 | dev->in_ep = desc->bEndpointAddress; |
410 | align = usb_endpoint_maxp(desc) - sizeof(struct sms_msg_hdr); |
411 | } else { |
412 | dev->out_ep = desc->bEndpointAddress; |
413 | } |
414 | } |
415 | |
416 | pr_debug("in_ep = %02x, out_ep = %02x\n" , dev->in_ep, dev->out_ep); |
417 | if (!dev->in_ep || !dev->out_ep || align < 0) { /* Missing endpoints? */ |
418 | smsusb_term_device(intf); |
419 | return -ENODEV; |
420 | } |
421 | |
422 | params.device_type = sms_get_board(board_id)->type; |
423 | |
424 | switch (params.device_type) { |
425 | case SMS_STELLAR: |
426 | dev->buffer_size = USB1_BUFFER_SIZE; |
427 | |
428 | params.setmode_handler = smsusb1_setmode; |
429 | params.detectmode_handler = smsusb1_detectmode; |
430 | break; |
431 | case SMS_UNKNOWN_TYPE: |
432 | pr_err("Unspecified sms device type!\n" ); |
433 | fallthrough; |
434 | default: |
435 | dev->buffer_size = USB2_BUFFER_SIZE; |
436 | dev->response_alignment = align; |
437 | |
438 | params.flags |= SMS_DEVICE_FAMILY2; |
439 | break; |
440 | } |
441 | |
442 | params.device = &dev->udev->dev; |
443 | params.usb_device = dev->udev; |
444 | params.buffer_size = dev->buffer_size; |
445 | params.num_buffers = MAX_BUFFERS; |
446 | params.sendrequest_handler = smsusb_sendrequest; |
447 | params.context = dev; |
448 | usb_make_path(dev: dev->udev, buf: params.devpath, size: sizeof(params.devpath)); |
449 | |
450 | mdev = siano_media_device_register(dev, board_id); |
451 | |
452 | /* register in smscore */ |
453 | rc = smscore_register_device(¶ms, &dev->coredev, 0, mdev); |
454 | if (rc < 0) { |
455 | pr_err("smscore_register_device(...) failed, rc %d\n" , rc); |
456 | goto err_unregister_device; |
457 | } |
458 | |
459 | smscore_set_board_id(dev->coredev, board_id); |
460 | |
461 | dev->coredev->is_usb_device = true; |
462 | |
463 | /* initialize urbs */ |
464 | for (i = 0; i < MAX_URBS; i++) { |
465 | dev->surbs[i].dev = dev; |
466 | usb_init_urb(urb: &dev->surbs[i].urb); |
467 | } |
468 | |
469 | pr_debug("smsusb_start_streaming(...).\n" ); |
470 | rc = smsusb_start_streaming(dev); |
471 | if (rc < 0) { |
472 | pr_err("smsusb_start_streaming(...) failed\n" ); |
473 | goto err_unregister_device; |
474 | } |
475 | |
476 | dev->state = SMSUSB_ACTIVE; |
477 | |
478 | rc = smscore_start_device(dev->coredev); |
479 | if (rc < 0) { |
480 | pr_err("smscore_start_device(...) failed\n" ); |
481 | goto err_unregister_device; |
482 | } |
483 | |
484 | pr_debug("device 0x%p created\n" , dev); |
485 | |
486 | return rc; |
487 | |
488 | err_unregister_device: |
489 | smsusb_term_device(intf); |
490 | #ifdef CONFIG_MEDIA_CONTROLLER_DVB |
491 | media_device_unregister(mdev); |
492 | #endif |
493 | kfree(objp: mdev); |
494 | return rc; |
495 | } |
496 | |
497 | static int smsusb_probe(struct usb_interface *intf, |
498 | const struct usb_device_id *id) |
499 | { |
500 | struct usb_device *udev = interface_to_usbdev(intf); |
501 | char devpath[32]; |
502 | int i, rc; |
503 | |
504 | pr_info("board id=%lu, interface number %d\n" , |
505 | id->driver_info, |
506 | intf->cur_altsetting->desc.bInterfaceNumber); |
507 | |
508 | if (sms_get_board(id->driver_info)->intf_num != |
509 | intf->cur_altsetting->desc.bInterfaceNumber) { |
510 | pr_debug("interface %d won't be used. Expecting interface %d to popup\n" , |
511 | intf->cur_altsetting->desc.bInterfaceNumber, |
512 | sms_get_board(id->driver_info)->intf_num); |
513 | return -ENODEV; |
514 | } |
515 | |
516 | if (intf->num_altsetting > 1) { |
517 | rc = usb_set_interface(dev: udev, |
518 | ifnum: intf->cur_altsetting->desc.bInterfaceNumber, |
519 | alternate: 0); |
520 | if (rc < 0) { |
521 | pr_err("usb_set_interface failed, rc %d\n" , rc); |
522 | return rc; |
523 | } |
524 | } |
525 | |
526 | pr_debug("smsusb_probe %d\n" , |
527 | intf->cur_altsetting->desc.bInterfaceNumber); |
528 | for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) { |
529 | pr_debug("endpoint %d %02x %02x %d\n" , i, |
530 | intf->cur_altsetting->endpoint[i].desc.bEndpointAddress, |
531 | intf->cur_altsetting->endpoint[i].desc.bmAttributes, |
532 | intf->cur_altsetting->endpoint[i].desc.wMaxPacketSize); |
533 | if (intf->cur_altsetting->endpoint[i].desc.bEndpointAddress & |
534 | USB_DIR_IN) |
535 | rc = usb_clear_halt(dev: udev, usb_rcvbulkpipe(udev, |
536 | intf->cur_altsetting->endpoint[i].desc.bEndpointAddress)); |
537 | else |
538 | rc = usb_clear_halt(dev: udev, usb_sndbulkpipe(udev, |
539 | intf->cur_altsetting->endpoint[i].desc.bEndpointAddress)); |
540 | } |
541 | if ((udev->actconfig->desc.bNumInterfaces == 2) && |
542 | (intf->cur_altsetting->desc.bInterfaceNumber == 0)) { |
543 | pr_debug("rom interface 0 is not used\n" ); |
544 | return -ENODEV; |
545 | } |
546 | |
547 | if (id->driver_info == SMS1XXX_BOARD_SIANO_STELLAR_ROM) { |
548 | /* Detected a Siano Stellar uninitialized */ |
549 | |
550 | snprintf(buf: devpath, size: sizeof(devpath), fmt: "usb\\%d-%s" , |
551 | udev->bus->busnum, udev->devpath); |
552 | pr_info("stellar device in cold state was found at %s.\n" , |
553 | devpath); |
554 | rc = smsusb1_load_firmware( |
555 | udev, id: smscore_registry_getmode(devpath), |
556 | board_id: id->driver_info); |
557 | |
558 | /* This device will reset and gain another USB ID */ |
559 | if (!rc) |
560 | pr_info("stellar device now in warm state\n" ); |
561 | else |
562 | pr_err("Failed to put stellar in warm state. Error: %d\n" , |
563 | rc); |
564 | |
565 | return rc; |
566 | } else { |
567 | rc = smsusb_init_device(intf, board_id: id->driver_info); |
568 | } |
569 | |
570 | pr_info("Device initialized with return code %d\n" , rc); |
571 | sms_board_load_modules(id->driver_info); |
572 | return rc; |
573 | } |
574 | |
575 | static void smsusb_disconnect(struct usb_interface *intf) |
576 | { |
577 | smsusb_term_device(intf); |
578 | } |
579 | |
580 | static int smsusb_suspend(struct usb_interface *intf, pm_message_t msg) |
581 | { |
582 | struct smsusb_device_t *dev = usb_get_intfdata(intf); |
583 | printk(KERN_INFO "%s Entering status %d.\n" , __func__, msg.event); |
584 | dev->state = SMSUSB_SUSPENDED; |
585 | /*smscore_set_power_mode(dev, SMS_POWER_MODE_SUSPENDED);*/ |
586 | smsusb_stop_streaming(dev); |
587 | return 0; |
588 | } |
589 | |
590 | static int smsusb_resume(struct usb_interface *intf) |
591 | { |
592 | int rc, i; |
593 | struct smsusb_device_t *dev = usb_get_intfdata(intf); |
594 | struct usb_device *udev = interface_to_usbdev(intf); |
595 | |
596 | printk(KERN_INFO "%s Entering.\n" , __func__); |
597 | usb_clear_halt(dev: udev, usb_rcvbulkpipe(udev, dev->in_ep)); |
598 | usb_clear_halt(dev: udev, usb_sndbulkpipe(udev, dev->out_ep)); |
599 | |
600 | for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) |
601 | printk(KERN_INFO "endpoint %d %02x %02x %d\n" , i, |
602 | intf->cur_altsetting->endpoint[i].desc.bEndpointAddress, |
603 | intf->cur_altsetting->endpoint[i].desc.bmAttributes, |
604 | intf->cur_altsetting->endpoint[i].desc.wMaxPacketSize); |
605 | |
606 | if (intf->num_altsetting > 0) { |
607 | rc = usb_set_interface(dev: udev, |
608 | ifnum: intf->cur_altsetting->desc. |
609 | bInterfaceNumber, alternate: 0); |
610 | if (rc < 0) { |
611 | printk(KERN_INFO "%s usb_set_interface failed, rc %d\n" , |
612 | __func__, rc); |
613 | return rc; |
614 | } |
615 | } |
616 | |
617 | smsusb_start_streaming(dev); |
618 | return 0; |
619 | } |
620 | |
621 | static const struct usb_device_id smsusb_id_table[] = { |
622 | /* This device is only present before firmware load */ |
623 | { USB_DEVICE(0x187f, 0x0010), |
624 | .driver_info = SMS1XXX_BOARD_SIANO_STELLAR_ROM }, |
625 | /* This device pops up after firmware load */ |
626 | { USB_DEVICE(0x187f, 0x0100), |
627 | .driver_info = SMS1XXX_BOARD_SIANO_STELLAR }, |
628 | |
629 | { USB_DEVICE(0x187f, 0x0200), |
630 | .driver_info = SMS1XXX_BOARD_SIANO_NOVA_A }, |
631 | { USB_DEVICE(0x187f, 0x0201), |
632 | .driver_info = SMS1XXX_BOARD_SIANO_NOVA_B }, |
633 | { USB_DEVICE(0x187f, 0x0300), |
634 | .driver_info = SMS1XXX_BOARD_SIANO_VEGA }, |
635 | { USB_DEVICE(0x2040, 0x1700), |
636 | .driver_info = SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT }, |
637 | { USB_DEVICE(0x2040, 0x1800), |
638 | .driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A }, |
639 | { USB_DEVICE(0x2040, 0x1801), |
640 | .driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B }, |
641 | { USB_DEVICE(0x2040, 0x2000), |
642 | .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD }, |
643 | { USB_DEVICE(0x2040, 0x2009), |
644 | .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2 }, |
645 | { USB_DEVICE(0x2040, 0x200a), |
646 | .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD }, |
647 | { USB_DEVICE(0x2040, 0x2010), |
648 | .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD }, |
649 | { USB_DEVICE(0x2040, 0x2011), |
650 | .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD }, |
651 | { USB_DEVICE(0x2040, 0x2019), |
652 | .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD }, |
653 | { USB_DEVICE(0x2040, 0x5500), |
654 | .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, |
655 | { USB_DEVICE(0x2040, 0x5510), |
656 | .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, |
657 | { USB_DEVICE(0x2040, 0x5520), |
658 | .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, |
659 | { USB_DEVICE(0x2040, 0x5530), |
660 | .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, |
661 | { USB_DEVICE(0x2040, 0x5580), |
662 | .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, |
663 | { USB_DEVICE(0x2040, 0x5590), |
664 | .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, |
665 | { USB_DEVICE(0x2040, 0xb900), |
666 | .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, |
667 | { USB_DEVICE(0x2040, 0xb910), |
668 | .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, |
669 | { USB_DEVICE(0x2040, 0xb980), |
670 | .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, |
671 | { USB_DEVICE(0x2040, 0xb990), |
672 | .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, |
673 | { USB_DEVICE(0x2040, 0xc000), |
674 | .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, |
675 | { USB_DEVICE(0x2040, 0xc010), |
676 | .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, |
677 | { USB_DEVICE(0x2040, 0xc080), |
678 | .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, |
679 | { USB_DEVICE(0x2040, 0xc090), |
680 | .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, |
681 | { USB_DEVICE(0x2040, 0xc0a0), |
682 | .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, |
683 | { USB_DEVICE(0x2040, 0xf5a0), |
684 | .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, |
685 | { USB_DEVICE(0x187f, 0x0202), |
686 | .driver_info = SMS1XXX_BOARD_SIANO_NICE }, |
687 | { USB_DEVICE(0x187f, 0x0301), |
688 | .driver_info = SMS1XXX_BOARD_SIANO_VENICE }, |
689 | { USB_DEVICE(0x187f, 0x0302), |
690 | .driver_info = SMS1XXX_BOARD_SIANO_VENICE }, |
691 | { USB_DEVICE(0x187f, 0x0310), |
692 | .driver_info = SMS1XXX_BOARD_SIANO_MING }, |
693 | { USB_DEVICE(0x187f, 0x0500), |
694 | .driver_info = SMS1XXX_BOARD_SIANO_PELE }, |
695 | { USB_DEVICE(0x187f, 0x0600), |
696 | .driver_info = SMS1XXX_BOARD_SIANO_RIO }, |
697 | { USB_DEVICE(0x187f, 0x0700), |
698 | .driver_info = SMS1XXX_BOARD_SIANO_DENVER_2160 }, |
699 | { USB_DEVICE(0x187f, 0x0800), |
700 | .driver_info = SMS1XXX_BOARD_SIANO_DENVER_1530 }, |
701 | { USB_DEVICE(0x19D2, 0x0086), |
702 | .driver_info = SMS1XXX_BOARD_ZTE_DVB_DATA_CARD }, |
703 | { USB_DEVICE(0x19D2, 0x0078), |
704 | .driver_info = SMS1XXX_BOARD_ONDA_MDTV_DATA_CARD }, |
705 | { USB_DEVICE(0x3275, 0x0080), |
706 | .driver_info = SMS1XXX_BOARD_SIANO_RIO }, |
707 | { USB_DEVICE(0x2013, 0x0257), |
708 | .driver_info = SMS1XXX_BOARD_PCTV_77E }, |
709 | { } /* Terminating entry */ |
710 | }; |
711 | |
712 | MODULE_DEVICE_TABLE(usb, smsusb_id_table); |
713 | |
714 | static struct usb_driver smsusb_driver = { |
715 | .name = "smsusb" , |
716 | .probe = smsusb_probe, |
717 | .disconnect = smsusb_disconnect, |
718 | .id_table = smsusb_id_table, |
719 | |
720 | .suspend = smsusb_suspend, |
721 | .resume = smsusb_resume, |
722 | }; |
723 | |
724 | module_usb_driver(smsusb_driver); |
725 | |
726 | MODULE_DESCRIPTION("Driver for the Siano SMS1xxx USB dongle" ); |
727 | MODULE_AUTHOR("Siano Mobile Silicon, Inc. <uris@siano-ms.com>" ); |
728 | MODULE_LICENSE("GPL" ); |
729 | |