cdc-dlm.c 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. /*
  2. * cdc-dlm.c
  3. *
  4. * CDC Direct Line Modem control for MC97 modem
  5. *
  6. * Copyright (C) 2021 Sylvain Munaut
  7. * SPDX-License-Identifier: LGPL-3.0-or-later
  8. */
  9. #include <no2usb/usb.h>
  10. #include <no2usb/usb_hw.h>
  11. #include <no2usb/usb_priv.h>
  12. #include <no2usb/usb_cdc_proto.h>
  13. #include "cdc-dlm.h"
  14. #include "mc97.h"
  15. #define INTF_CDC_DLM 4
  16. #define EP_CDC_DLM_NOTIF 0x83
  17. static void
  18. dlm_send_notif_ring_detect(void)
  19. {
  20. const struct usb_ctrl_req notif = {
  21. .bmRequestType = USB_REQ_READ | USB_REQ_TYPE_CLASS | USB_REQ_RCPT_INTF,
  22. .bRequest = USB_NOTIF_CDC_RING_DETECT,
  23. .wValue = 0,
  24. .wIndex = INTF_CDC_DLM,
  25. .wLength = 0,
  26. };
  27. usb_data_write(usb_ep_regs[3].in.bd[0].ptr, &notif, sizeof(struct usb_ctrl_req));
  28. usb_ep_regs[3].in.bd[0].csr = USB_BD_STATE_RDY_DATA | USB_BD_LEN(sizeof(struct usb_ctrl_req));
  29. }
  30. static enum usb_fnd_resp
  31. dlm_ctrl_req(struct usb_ctrl_req *req, struct usb_xfer *xfer)
  32. {
  33. /* Check it's a class request to an interface */
  34. if (USB_REQ_TYPE_RCPT(req) != (USB_REQ_TYPE_CLASS | USB_REQ_RCPT_INTF))
  35. return USB_FND_CONTINUE;
  36. /* Check it's for the DLM interface */
  37. if ((req->wIndex & 0xff) != INTF_CDC_DLM)
  38. return USB_FND_CONTINUE;
  39. switch (req->wRequestAndType)
  40. {
  41. case USB_RT_CDC_SET_HOOK_STATE:
  42. switch (req->wValue) {
  43. case 0: mc97_set_hook(ON_HOOK); break;
  44. case 1: mc97_set_hook(OFF_HOOK); break;
  45. case 2: mc97_set_hook(CALLER_ID); break;
  46. default: return USB_FND_ERROR;
  47. }
  48. return USB_FND_SUCCESS;
  49. case USB_RT_CDC_SET_AUX_LINE_STATE:
  50. /* Control the relay with that */
  51. mc97_set_aux_relay(!req->wValue);
  52. return USB_FND_SUCCESS;
  53. case USB_RT_CDC_RING_AUX_JACK:
  54. /* Can't do that ... */
  55. return USB_FND_SUCCESS;
  56. /* Pulse is not supported yet (also disabled in bmCapabilities) */
  57. case USB_RT_CDC_PULSE_SETUP:
  58. case USB_RT_CDC_SEND_PULSE:
  59. case USB_RT_CDC_SET_PULSE_TIME:
  60. return USB_FND_ERROR;
  61. /* TODO: Maybe implement SET_COMM_FEATURE for country selection ? */
  62. /* In theory not part of DLM but it's the closest to a standard
  63. * thing to support tweaking the codec params to match local specs
  64. * for a phone line */
  65. }
  66. return USB_FND_ERROR;
  67. }
  68. static enum usb_fnd_resp
  69. dlm_set_conf(const struct usb_conf_desc *conf)
  70. {
  71. const struct usb_intf_desc *intf;
  72. intf = usb_desc_find_intf(conf, INTF_CDC_DLM, 0, NULL);
  73. usb_ep_boot(intf, EP_CDC_DLM_NOTIF, false);
  74. return USB_FND_SUCCESS;
  75. }
  76. static struct usb_fn_drv _dlm_drv = {
  77. .ctrl_req = dlm_ctrl_req,
  78. .set_conf = dlm_set_conf,
  79. };
  80. void
  81. cdc_dlm_init(void)
  82. {
  83. /* Register function driver */
  84. usb_register_function_driver(&_dlm_drv);
  85. }
  86. void
  87. cdc_dlm_poll(void)
  88. {
  89. /* Nothing to do for now */
  90. /* TODO:
  91. * - Pulse timing when pulse is implemented
  92. * - Ring detection (note that it's not simply calling
  93. * mc97_get_ring_detect(), it must be analyzed to see if the
  94. * ringing frequency matches something between 10 and 100 Hz)
  95. */
  96. }